import { IntrospectionField , IntrospectionInputValue , IntrospectionScalarType , IntrospectionType , } from 'graphql' ; import { JSONSchema6 } from 'json-schema' ; import { filter , map , MemoListIterator , reduce } from 'lodash' ; import { isIntrospectionEnumType , isIntrospectionField , isIntrospectionInputObjectType , isIntrospectionInterfaceType , isIntrospectionInputValue , isIntrospectionListTypeRef , isIntrospectionObjectType , isIntrospectionUnionType , isNonNullIntrospectionType , isIntrospectionScalarType , isIntrospectionDefaultScalarType , } from './typeGuards' ; import { graphqlToJSONType , typesMapping } from './typesMapping' ; export type JSONSchema6Acc = { [ k ] : JSONSchema6 ; } ; type ReducerOptions = { nullableArrayItems ? : boolean ; } ; type GetRequiredFieldsType = ReadonlyArray < IntrospectionInputValue | IntrospectionField > ; export const getRequiredFields = ( fields ) => reduce ( fields , ( acc , f ) => { if ( isNonNullIntrospectionType ( f . type ) ) { acc . push ( f . name ) ; } return acc ; } , [ ] ) ; export type IntrospectionFieldReducerItem = IntrospectionField | IntrospectionInputValue ; export const introspectionFieldReducerGenerator = ( options ) => { const introspectionFieldReducer = ( acc , curr ) : JSONSchema6Acc => { if ( isIntrospectionField ( curr ) ) { const returnType = isNonNullIntrospectionType ( curr . type ) ? graphqlToJSONType ( curr . type . ofType , options ) : graphqlToJSONType ( curr . type , options ) ; acc [ curr . name ] = { type : 'object' , properties : { return : returnType , arguments : { type : 'object' , properties : reduce < IntrospectionFieldReducerItem , JSONSchema6Acc > ( curr . args as IntrospectionFieldReducerItem [ ] , introspectionFieldReducer , { } ) , required : getRequiredFields ( curr . args ) , } , } , required : [ ] , } ; } else if ( isIntrospectionInputValue ( curr ) ) { const returnType = isNonNullIntrospectionType ( curr . type ) ? graphqlToJSONType ( curr . type . ofType , options ) : graphqlToJSONType ( curr . type , options ) ; if ( curr . defaultValue ) { returnType . default = resolveDefaultValue ( curr ) ; } acc [ curr . name ] = returnType ; } acc [ curr . name ] . description = curr . description || undefined ; return acc ; } ; return introspectionFieldReducer ; } ; export const resolveDefaultValue = ( curr ) => { let type = curr . type ; let isList = false ; while ( true ) { if ( isIntrospectionListTypeRef ( type ) ) { isList = true ; type = type . ofType ; } else if ( isNonNullIntrospectionType ( type ) ) { type = type . ofType ; } else { break ; } } if ( ! isIntrospectionEnumType ( type ) ) { return JSON . parse ( curr . defaultValue ) ; } if ( ! isList || ! curr . defaultValue || typeof curr . defaultValue !== 'string' ) { return curr . defaultValue ; } return curr . defaultValue . substr ( 1 , curr . defaultValue . length - 2 ) . split ( ',' ) . map ( ( val ) => val . trim ( ) ) ; } ; export const introspectionTypeReducer = ( type , options ) => ( acc , curr ) : JSONSchema6Acc => { const isQueriesOrMutations = type === 'properties' ; if ( isIntrospectionObjectType ( curr ) ) { acc [ curr . name ] = { type : 'object' , properties : reduce < IntrospectionFieldReducerItem , JSONSchema6Acc > ( curr . fields as IntrospectionFieldReducerItem [ ] , introspectionFieldReducerGenerator ( options ) , { } ) , required : isQueriesOrMutations ? [ ] : getRequiredFields ( curr . fields ) , } ; } else if ( isIntrospectionInputObjectType ( curr ) ) { acc [ curr . name ] = { type : 'object' , properties : reduce < IntrospectionFieldReducerItem , JSONSchema6Acc > ( curr . inputFields as IntrospectionFieldReducerItem [ ] , introspectionFieldReducerGenerator ( options ) , { } ) , required : getRequiredFields ( curr . inputFields ) , } ; } else if ( isIntrospectionInterfaceType ( curr ) ) { acc [ curr . name ] = { type : 'object' , properties : reduce < IntrospectionFieldReducerItem , JSONSchema6Acc > ( curr . fields as IntrospectionFieldReducerItem [ ] , introspectionFieldReducerGenerator ( options ) , { } ) , required : type === 'definitions' ? getRequiredFields ( curr . fields ) : [ ] , } ; } else if ( isIntrospectionUnionType ( curr ) ) { acc [ curr . name ] = { oneOf : curr . possibleTypes . map ( ( type ) => graphqlToJSONType ( type , options ) ) , } ; } else if ( isIntrospectionEnumType ( curr ) ) { acc [ curr . name ] = { type : 'string' , anyOf : curr . enumValues . map ( ( item ) => { return { enum : [ item . name ] , title : item . description || item . name , description : item . description || undefined , } ; } ) , } ; } else if ( isIntrospectionDefaultScalarType ( curr ) ) { acc [ curr . name ] = { type : ( typesMapping as any ) [ curr . name ] , title : curr . name , } ; } else if ( isIntrospectionScalarType ( curr ) ) { acc [ ( curr as IntrospectionScalarType ) . name ] = { type : 'object' , title : ( curr as IntrospectionScalarType ) . name , } ; } acc [ curr . name ] . description = curr . description || undefined ; return acc ; } ;