UNPKG

@omnigraph/json-schema

Version:

This package generates GraphQL Schema from JSON Schema and sample JSON request and responses. You can define your root field endpoints like below in your GraphQL Config for example;

569 lines (568 loc) • 23.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExampleDirective = exports.OneOfDirective = exports.EnumDirective = exports.StatusCodeTypeNameDirective = exports.processDirectives = exports.processDictionaryDirective = exports.DictionaryDirective = exports.processLinkFieldAnnotations = exports.LinkResolverDirective = exports.LinkDirective = exports.processResponseMetadataAnnotations = exports.ResponseMetadataDirective = exports.GlobalOptionsDirective = exports.HTTPOperationDirective = exports.processScalarType = exports.processTypeScriptAnnotations = exports.TypeScriptDirective = exports.processPubSubOperationAnnotations = exports.PubSubOperationDirective = exports.processRegExpAnnotations = exports.RegExpDirective = exports.processResolveRootFieldAnnotations = exports.ResolveRootFieldDirective = exports.processResolveRootAnnotations = exports.ResolveRootDirective = exports.processDiscriminatorAnnotations = exports.DiscriminatorDirective = exports.processLengthAnnotations = exports.LengthDirective = void 0; const tslib_1 = require("tslib"); const string_interpolation_1 = require("@graphql-mesh/string-interpolation"); const utils_1 = require("@graphql-tools/utils"); const graphql_1 = require("graphql"); const lodash_set_1 = tslib_1.__importDefault(require("lodash.set")); const addRootFieldResolver_js_1 = require("./addRootFieldResolver.js"); const getTypeResolverFromOutputTCs_js_1 = require("./getTypeResolverFromOutputTCs.js"); const scalars_js_1 = require("./scalars.js"); const graphql_scalars_1 = require("graphql-scalars"); exports.LengthDirective = new graphql_1.GraphQLDirective({ name: 'length', locations: [graphql_1.DirectiveLocation.SCALAR], args: { min: { type: graphql_1.GraphQLInt, }, max: { type: graphql_1.GraphQLInt, }, }, }); function processLengthAnnotations(scalar, { min: minLength, max: maxLength, }) { function coerceString(value) { if (value != null) { const vStr = value.toString(); if (typeof minLength !== 'undefined' && vStr.length < minLength) { throw new Error(`${scalar.name} cannot be less than ${minLength} but given ${vStr}`); } if (typeof maxLength !== 'undefined' && vStr.length > maxLength) { throw new Error(`${scalar.name} cannot be more than ${maxLength} but given ${vStr}`); } return vStr; } } scalar.serialize = coerceString; scalar.parseValue = coerceString; scalar.parseLiteral = ast => { if ('value' in ast) { return coerceString(ast.value); } return null; }; } exports.processLengthAnnotations = processLengthAnnotations; exports.DiscriminatorDirective = new graphql_1.GraphQLDirective({ name: 'discriminator', locations: [graphql_1.DirectiveLocation.INTERFACE, graphql_1.DirectiveLocation.UNION], args: { field: { type: graphql_1.GraphQLString, }, }, }); function processDiscriminatorAnnotations(interfaceType, fieldName) { interfaceType.resolveType = root => root[fieldName]; } exports.processDiscriminatorAnnotations = processDiscriminatorAnnotations; exports.ResolveRootDirective = new graphql_1.GraphQLDirective({ name: 'resolveRoot', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], }); function processResolveRootAnnotations(field) { field.resolve = root => root; } exports.processResolveRootAnnotations = processResolveRootAnnotations; exports.ResolveRootFieldDirective = new graphql_1.GraphQLDirective({ name: 'resolveRootField', locations: [ graphql_1.DirectiveLocation.FIELD_DEFINITION, graphql_1.DirectiveLocation.ARGUMENT_DEFINITION, graphql_1.DirectiveLocation.INPUT_FIELD_DEFINITION, ], args: { field: { type: graphql_1.GraphQLString, }, }, }); function isOriginallyListType(type) { if ((0, graphql_1.isNonNullType)(type)) { return isOriginallyListType(type.ofType); } return (0, graphql_1.isListType)(type); } function processResolveRootFieldAnnotations(field, propertyName) { if (!field.resolve || field.resolve.name === 'defaultFieldResolver') { field.resolve = (root, args, context, info) => { const actualFieldObj = root[propertyName]; if (actualFieldObj != null) { const isArray = Array.isArray(actualFieldObj); const isListType = isOriginallyListType(info.returnType); if (isListType && !isArray) { return [actualFieldObj]; } else if (!isListType && isArray) { return actualFieldObj[0]; } } return actualFieldObj; }; } } exports.processResolveRootFieldAnnotations = processResolveRootFieldAnnotations; exports.RegExpDirective = new graphql_1.GraphQLDirective({ name: 'regexp', locations: [graphql_1.DirectiveLocation.SCALAR], args: { pattern: { type: graphql_1.GraphQLString, }, }, }); function processRegExpAnnotations(scalar, pattern) { function coerceString(value) { if (value != null) { const vStr = value.toString(); const regexp = new RegExp(pattern); if (!regexp.test(vStr)) { throw new Error(`${scalar.name} must match ${pattern} but given ${vStr}`); } return vStr; } } scalar.serialize = coerceString; scalar.parseValue = coerceString; scalar.parseLiteral = ast => { if ('value' in ast) { return coerceString(ast.value); } return null; }; } exports.processRegExpAnnotations = processRegExpAnnotations; exports.PubSubOperationDirective = new graphql_1.GraphQLDirective({ name: 'pubsubOperation', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], args: { pubsubTopic: { type: graphql_1.GraphQLString, }, }, }); function processPubSubOperationAnnotations({ field, globalPubsub, pubsubTopic, logger, }) { field.subscribe = (root, args, context, info) => { const operationLogger = logger.child(`${info.parentType.name}.${field.name}`); const pubsub = (context === null || context === void 0 ? void 0 : context.pubsub) || globalPubsub; if (!pubsub) { return (0, utils_1.createGraphQLError)(`You should have PubSub defined in either the config or the context!`); } const interpolationData = { root, args, context, info, env: process.env }; let interpolatedPubSubTopic = string_interpolation_1.stringInterpolator.parse(pubsubTopic, interpolationData); if (interpolatedPubSubTopic.startsWith('webhook:')) { const [, expectedMethod, expectedUrl] = interpolatedPubSubTopic.split(':'); const expectedPath = new URL(expectedUrl, 'http://localhost').pathname; interpolatedPubSubTopic = `webhook:${expectedMethod}:${expectedPath}`; } operationLogger.debug(`=> Subscribing to pubSubTopic: ${interpolatedPubSubTopic}`); return pubsub.asyncIterator(interpolatedPubSubTopic); }; field.resolve = (root, args, context, info) => { const operationLogger = logger.child(`${info.parentType.name}.${field.name}`); operationLogger.debug('Received ', root, ' from ', pubsubTopic); return root; }; } exports.processPubSubOperationAnnotations = processPubSubOperationAnnotations; exports.TypeScriptDirective = new graphql_1.GraphQLDirective({ name: 'typescript', locations: [graphql_1.DirectiveLocation.SCALAR, graphql_1.DirectiveLocation.ENUM], args: { type: { type: graphql_1.GraphQLString, }, }, }); function processTypeScriptAnnotations(type, typeDefinition) { type.extensions = type.extensions || {}; type.extensions.codegenScalarType = typeDefinition; } exports.processTypeScriptAnnotations = processTypeScriptAnnotations; function addExecutionLogicToScalar(nonExecutableScalar, actualScalar) { Object.defineProperties(nonExecutableScalar, { serialize: { value: actualScalar.serialize, }, parseValue: { value: actualScalar.parseValue, }, parseLiteral: { value: actualScalar.parseLiteral, }, extensions: { value: { ...actualScalar.extensions, ...nonExecutableScalar.extensions, }, }, }); } function processScalarType(schema, type) { if (type.name in graphql_scalars_1.resolvers) { const actualScalar = graphql_scalars_1.resolvers[type.name]; addExecutionLogicToScalar(type, actualScalar); } if (type.name === 'ObjMap') { addExecutionLogicToScalar(type, scalars_js_1.ObjMapScalar); } const directiveAnnotations = (0, utils_1.getDirectives)(schema, type); for (const directiveAnnotation of directiveAnnotations) { switch (directiveAnnotation.name) { case 'length': processLengthAnnotations(type, directiveAnnotation.args); break; case 'regexp': processRegExpAnnotations(type, directiveAnnotation.args.pattern); break; case 'typescript': processTypeScriptAnnotations(type, directiveAnnotation.args.type); break; } } } exports.processScalarType = processScalarType; exports.HTTPOperationDirective = new graphql_1.GraphQLDirective({ name: 'httpOperation', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], args: { path: { type: graphql_1.GraphQLString, }, operationSpecificHeaders: { type: scalars_js_1.ObjMapScalar, }, httpMethod: { type: new graphql_1.GraphQLEnumType({ name: 'HTTPMethod', values: { GET: { value: 'GET' }, HEAD: { value: 'HEAD' }, POST: { value: 'POST' }, PUT: { value: 'PUT' }, DELETE: { value: 'DELETE' }, CONNECT: { value: 'CONNECT' }, OPTIONS: { value: 'OPTIONS' }, TRACE: { value: 'TRACE' }, PATCH: { value: 'PATCH' }, }, }), }, isBinary: { type: graphql_1.GraphQLBoolean, }, requestBaseBody: { type: scalars_js_1.ObjMapScalar, }, queryParamArgMap: { type: scalars_js_1.ObjMapScalar, }, queryStringOptionsByParam: { type: scalars_js_1.ObjMapScalar, }, }, }); exports.GlobalOptionsDirective = new graphql_1.GraphQLDirective({ name: 'globalOptions', locations: [graphql_1.DirectiveLocation.OBJECT], args: { sourceName: { type: graphql_1.GraphQLString, }, endpoint: { type: graphql_1.GraphQLString, }, operationHeaders: { type: scalars_js_1.ObjMapScalar, }, queryStringOptions: { type: scalars_js_1.ObjMapScalar, }, queryParams: { type: scalars_js_1.ObjMapScalar, }, }, }); exports.ResponseMetadataDirective = new graphql_1.GraphQLDirective({ name: 'responseMetadata', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], }); function processResponseMetadataAnnotations(field) { field.resolve = function responseMetadataResolver(root) { return { url: root.$url, headers: root.$response.header, method: root.$method, status: root.$statusCode, statusText: root.$statusText, body: root.$response.body, }; }; } exports.processResponseMetadataAnnotations = processResponseMetadataAnnotations; exports.LinkDirective = new graphql_1.GraphQLDirective({ name: 'link', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], args: { defaultRootType: { type: graphql_1.GraphQLString, }, defaultField: { type: graphql_1.GraphQLString, }, }, }); exports.LinkResolverDirective = new graphql_1.GraphQLDirective({ name: 'linkResolver', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], args: { linkResolverMap: { type: scalars_js_1.ObjMapScalar, }, }, }); function linkResolver({ linkObjArgs, targetTypeName, targetFieldName }, { root, args, context, info, env }) { for (const argKey in linkObjArgs) { const argInterpolation = linkObjArgs[argKey]; const actualValue = typeof argInterpolation === 'string' ? string_interpolation_1.stringInterpolator.parse(argInterpolation, { root, args, context, info, env, }) : argInterpolation; (0, lodash_set_1.default)(args, argKey, actualValue); } const type = info.schema.getType(targetTypeName); const field = type.getFields()[targetFieldName]; return field.resolve(root, args, context, info); } function getLinkResolverMap(schema, field) { const parentFieldLinkResolverDirectives = (0, utils_1.getDirective)(schema, field, 'linkResolver'); if (parentFieldLinkResolverDirectives === null || parentFieldLinkResolverDirectives === void 0 ? void 0 : parentFieldLinkResolverDirectives.length) { const linkResolverMap = parentFieldLinkResolverDirectives[0].linkResolverMap; if (linkResolverMap) { return linkResolverMap; } } } function findLinkResolverMap({ schema, operationType, defaultRootTypeName, defaultFieldName, }) { const parentType = schema.getRootType(operationType); const parentField = parentType.getFields()[operationType]; if (parentField) { const linkResolverMap = getLinkResolverMap(schema, parentField); if (linkResolverMap) { return linkResolverMap; } } const defaultRootType = schema.getType(defaultRootTypeName); if (defaultRootType) { const defaultField = defaultRootType.getFields()[defaultFieldName]; if (defaultField) { const linkResolverMap = getLinkResolverMap(schema, defaultField); if (linkResolverMap) { return linkResolverMap; } } } } function processLinkFieldAnnotations(field, defaultRootTypeName, defaultFieldName) { field.resolve = (root, args, context, info) => { const linkResolverMap = findLinkResolverMap({ schema: info.schema, defaultRootTypeName, defaultFieldName, parentFieldName: root.$field, operationType: info.operation.operation, }); const linkResolverOpts = linkResolverMap[field.name]; return linkResolver(linkResolverOpts, { root, args, context, info, env: process.env }); }; } exports.processLinkFieldAnnotations = processLinkFieldAnnotations; exports.DictionaryDirective = new graphql_1.GraphQLDirective({ name: 'dictionary', locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION], }); function processDictionaryDirective(fieldMap, field) { field.resolve = root => { const result = []; for (const key in root) { if (key in fieldMap) { continue; } result.push({ key, value: root[key], }); } return result; }; } exports.processDictionaryDirective = processDictionaryDirective; function processDirectives({ schema, globalFetch, logger, pubsub, ...extraGlobalOptions }) { const nonExecutableObjMapScalar = schema.getType('ObjMap'); if (nonExecutableObjMapScalar && (0, graphql_1.isScalarType)(nonExecutableObjMapScalar)) { addExecutionLogicToScalar(nonExecutableObjMapScalar, scalars_js_1.ObjMapScalar); } let [globalOptions = {}] = ((0, utils_1.getDirective)(schema, schema.getQueryType(), 'globalOptions') || []); globalOptions = { ...globalOptions, ...extraGlobalOptions, }; const typeMap = schema.getTypeMap(); for (const typeName in typeMap) { const type = typeMap[typeName]; const exampleAnnotations = (0, utils_1.getDirective)(schema, type, 'example'); if (exampleAnnotations === null || exampleAnnotations === void 0 ? void 0 : exampleAnnotations.length) { const examples = []; for (const exampleAnnotation of exampleAnnotations) { if (exampleAnnotation === null || exampleAnnotation === void 0 ? void 0 : exampleAnnotation.value) { examples.push(exampleAnnotation.value); } } type.extensions = type.extensions || {}; type.extensions.examples = examples; } if ((0, graphql_1.isScalarType)(type)) { processScalarType(schema, type); } if ((0, graphql_1.isInterfaceType)(type)) { const directiveAnnotations = (0, utils_1.getDirectives)(schema, type); for (const directiveAnnotation of directiveAnnotations) { switch (directiveAnnotation.name) { case 'discriminator': processDiscriminatorAnnotations(type, directiveAnnotation.args.field); break; } } } if ((0, graphql_1.isUnionType)(type)) { const directiveAnnotations = (0, utils_1.getDirectives)(schema, type); let statusCodeTypeNameIndexMap; let discriminatorField; for (const directiveAnnotation of directiveAnnotations) { switch (directiveAnnotation.name) { case 'statusCodeTypeName': statusCodeTypeNameIndexMap = statusCodeTypeNameIndexMap || {}; statusCodeTypeNameIndexMap[directiveAnnotation.args.statusCode] = directiveAnnotation.args.typeName; break; case 'discriminator': discriminatorField = directiveAnnotation.args.field; break; } } type.resolveType = (0, getTypeResolverFromOutputTCs_js_1.getTypeResolverFromOutputTCs)(type.getTypes(), discriminatorField, statusCodeTypeNameIndexMap); } if ((0, graphql_1.isEnumType)(type)) { const directiveAnnotations = (0, utils_1.getDirectives)(schema, type); for (const directiveAnnotation of directiveAnnotations) { switch (directiveAnnotation.name) { case 'typescript': processTypeScriptAnnotations(type, directiveAnnotation.args.type); break; } } const enumValues = type.getValues(); for (const enumValue of enumValues) { const directiveAnnotations = (0, utils_1.getDirectives)(schema, enumValue); for (const directiveAnnotation of directiveAnnotations) { switch (directiveAnnotation.name) { case 'enum': { const realValue = JSON.parse(directiveAnnotation.args.value); enumValue.value = realValue; type._valueLookup.set(realValue, enumValue); break; } } } } } if ('getFields' in type) { const fields = type.getFields(); for (const fieldName in fields) { const field = fields[fieldName]; const directiveAnnotations = (0, utils_1.getDirectives)(schema, field); for (const directiveAnnotation of directiveAnnotations) { switch (directiveAnnotation.name) { case 'resolveRoot': processResolveRootAnnotations(field); break; case 'resolveRootField': processResolveRootFieldAnnotations(field, directiveAnnotation.args.field); break; case 'pubsubOperation': processPubSubOperationAnnotations({ field: field, pubsubTopic: directiveAnnotation.args.pubsubTopic, globalPubsub: pubsub, logger, }); break; case 'httpOperation': (0, addRootFieldResolver_js_1.addHTTPRootFieldResolver)(schema, field, logger, globalFetch, directiveAnnotation.args, globalOptions); break; case 'responseMetadata': processResponseMetadataAnnotations(field); break; case 'link': processLinkFieldAnnotations(field, directiveAnnotation.args.defaultRootType, directiveAnnotation.args.defaultField); break; case 'dictionary': processDictionaryDirective(fields, field); } } } } } return schema; } exports.processDirectives = processDirectives; exports.StatusCodeTypeNameDirective = new graphql_1.GraphQLDirective({ name: 'statusCodeTypeName', locations: [graphql_1.DirectiveLocation.UNION], isRepeatable: true, args: { typeName: { type: graphql_1.GraphQLString, }, statusCode: { type: graphql_1.GraphQLID, }, }, }); exports.EnumDirective = new graphql_1.GraphQLDirective({ name: 'enum', locations: [graphql_1.DirectiveLocation.ENUM_VALUE], args: { value: { type: graphql_1.GraphQLString, }, }, }); exports.OneOfDirective = new graphql_1.GraphQLDirective({ name: 'oneOf', locations: [graphql_1.DirectiveLocation.OBJECT, graphql_1.DirectiveLocation.INTERFACE], }); exports.ExampleDirective = new graphql_1.GraphQLDirective({ name: 'example', locations: [ graphql_1.DirectiveLocation.FIELD_DEFINITION, graphql_1.DirectiveLocation.OBJECT, graphql_1.DirectiveLocation.INPUT_OBJECT, graphql_1.DirectiveLocation.ENUM, graphql_1.DirectiveLocation.SCALAR, ], args: { value: { type: scalars_js_1.ObjMapScalar, }, }, isRepeatable: true, });