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;

150 lines (149 loc) 6.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getContainerTC = getContainerTC; exports.getUnionTypeComposers = getUnionTypeComposers; const graphql_compose_1 = require("graphql-compose"); const directives_js_1 = require("./directives.js"); function getContainerTC(subgraphName, schemaComposer, output) { const containerTypeName = `${output.getTypeName()}_container`; schemaComposer.addDirective(directives_js_1.ResolveRootDirective); return schemaComposer.getOrCreateOTC(containerTypeName, otc => otc.addFields({ [output.getTypeName()]: { type: output, directives: [ { name: 'resolveRoot', args: { subgraph: subgraphName, }, }, ], }, })); } function getUnionTypeComposers({ subgraphName, schemaComposer, typeComposersList, subSchemaAndTypeComposers, logger, }) { if (new Set(typeComposersList).size === 1) { return typeComposersList[0]; } const unionInputFields = {}; const outputTypeComposers = []; let isOutputPlural = false; typeComposersList.forEach(typeComposers => { let { input, output } = typeComposers; if (output instanceof graphql_compose_1.ListComposer) { output = output.getUnwrappedTC(); isOutputPlural = true; } if ((0, graphql_compose_1.isSomeInputTypeComposer)(output)) { outputTypeComposers.push(getContainerTC(subgraphName, schemaComposer, output)); } else { outputTypeComposers.push(output); } if (input) { const inputTypeName = input instanceof graphql_compose_1.ListComposer ? input.getUnwrappedTC().getTypeName() + '_list' : input.getTypeName(); unionInputFields[inputTypeName] = { type: input, }; } if (!input) { logger.debug(`No input type composer found for ${output.getTypeName()}, skipping...`); } }); // Use local variables to avoid direct property assignment on subSchemaAndTypeComposers, // which may have getter-only input/output properties (e.g. when it originates from an // array schema processed by the enter visitor). Assigning to a getter-only property // throws a TypeError in strict mode (Node.js 25+). let resolvedInput = subSchemaAndTypeComposers.input; let resolvedOutput = subSchemaAndTypeComposers.output; if (Object.keys(unionInputFields).length === 1) { resolvedInput = Object.values(unionInputFields)[0].type; } else { // For array+oneOf schemas, resolvedInput may be a ListComposer (getter-only); unwrap it // so addFields is called on the underlying InputObjectTypeComposer. const inputTC = resolvedInput instanceof graphql_compose_1.ListComposer ? resolvedInput.getUnwrappedTC() : resolvedInput; inputTC.addFields(unionInputFields); resolvedInput = inputTC; } if (new Set(outputTypeComposers).size === 1) { resolvedOutput = outputTypeComposers[0]; } else { // For array+oneOf schemas, resolvedOutput may be a ListComposer (getter-only). Unwrap it // to find the underlying UnionTypeComposer, or create one if the inner type is not a union. let unionOutputTC; if (resolvedOutput instanceof graphql_compose_1.ListComposer) { const innerTC = resolvedOutput.getUnwrappedTC(); unionOutputTC = innerTC instanceof graphql_compose_1.UnionTypeComposer ? innerTC : schemaComposer.createUnionTC({ name: innerTC.getTypeName() + '_union', types: [] }); } else { unionOutputTC = resolvedOutput; } const directives = unionOutputTC.getDirectives() || []; const statusCodeOneOfIndexMap = unionOutputTC.getExtension('statusCodeOneOfIndexMap'); const statusCodeOneOfIndexMapEntries = Object.entries(statusCodeOneOfIndexMap || {}); for (const outputTypeComposerIndex in outputTypeComposers) { const outputTypeComposer = outputTypeComposers[outputTypeComposerIndex]; const statusCode = statusCodeOneOfIndexMapEntries.find(([statusCode, index]) => index.toString() === outputTypeComposerIndex.toString())?.[0]; const a = outputTypeComposer; if ('getFields' in outputTypeComposer) { if (statusCode != null) { schemaComposer.addDirective(directives_js_1.StatusCodeTypeNameDirective); directives.push({ name: 'statusCodeTypeName', args: { subgraph: subgraphName, statusCode: statusCode.toString(), typeName: outputTypeComposer.getTypeName(), }, }); } if (outputTypeComposer instanceof graphql_compose_1.InterfaceTypeComposer) { schemaComposer.forEach(tc => { if (tc instanceof graphql_compose_1.ObjectTypeComposer && tc.hasInterface(a)) { unionOutputTC.addType(tc); } }); } else { unionOutputTC.addType(outputTypeComposer); } } else { for (const possibleType of outputTypeComposer.getTypes()) { unionOutputTC.addType(possibleType); } } } unionOutputTC.setDirectives(directives); resolvedOutput = unionOutputTC; } let flatten = false; // TODO: container suffix might not be coming from us if (resolvedOutput.getTypeName().endsWith('_container')) { const fields = resolvedOutput.getFields(); const fieldKeys = Object.keys(fields); if (fieldKeys.length === 1) { resolvedOutput = fields[fieldKeys[0]].type; flatten = isOutputPlural; } } return { input: resolvedInput, output: isOutputPlural ? resolvedOutput.List : resolvedOutput, nullable: subSchemaAndTypeComposers.nullable, readOnly: subSchemaAndTypeComposers.readOnly, writeOnly: subSchemaAndTypeComposers.writeOnly, flatten, }; }