@apollo/federation
Version:
Apollo Federation Utilities
338 lines • 14.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.printBlockString = exports.printType = exports.printIntrospectionSchema = exports.printSupergraphSdl = void 0;
const graphql_1 = require("graphql");
const utilities_1 = require("../utilities");
const utils_1 = require("../composition/utils");
function printSupergraphSdl(schema, graphNameToEnumValueName) {
const context = {
graphNameToEnumValueName,
};
return printFilteredSchema(schema, (n) => !(0, graphql_1.isSpecifiedDirective)(n), isDefinedType, context);
}
exports.printSupergraphSdl = printSupergraphSdl;
function printIntrospectionSchema(schema) {
return printFilteredSchema(schema, graphql_1.isSpecifiedDirective, graphql_1.isIntrospectionType, {});
}
exports.printIntrospectionSchema = printIntrospectionSchema;
function isDefinedType(type) {
return !(0, graphql_1.isSpecifiedScalarType)(type) && !(0, graphql_1.isIntrospectionType)(type);
}
function printFilteredSchema(schema, directiveFilter, typeFilter, context) {
const directives = schema.getDirectives().filter(directiveFilter);
const types = Object.values(schema.getTypeMap()).filter(typeFilter);
return ([
printSchemaDefinition(schema),
...directives.map((directive) => printDirective(directive)),
...types.map((type) => printType(type, context)),
]
.filter(Boolean)
.join('\n\n') + '\n');
}
function printSchemaDefinition(schema) {
const operationTypes = [];
const queryType = schema.getQueryType();
if (queryType) {
operationTypes.push(` query: ${queryType.name}`);
}
const mutationType = schema.getMutationType();
if (mutationType) {
operationTypes.push(` mutation: ${mutationType.name}`);
}
const subscriptionType = schema.getSubscriptionType();
if (subscriptionType) {
operationTypes.push(` subscription: ${subscriptionType.name}`);
}
return (printDescription(schema) +
'schema' +
printCoreDirectives(schema) +
`\n{\n${operationTypes.join('\n')}\n}`);
}
const alwaysIncludedCoreDirectives = {
core: { feature: 'https://specs.apollo.dev/core/v0.2' },
join: { feature: 'https://specs.apollo.dev/join/v0.1', purpose: 'EXECUTION' },
};
const supportedCoreDirectives = {
tag: { feature: 'https://specs.apollo.dev/tag/v0.1' },
};
function printCoreDirectives(schema) {
const supportedCoreDirectiveNames = Object.keys(supportedCoreDirectives);
const schemaDirectiveNames = schema.getDirectives().map(({ name }) => name);
const supportedCoreDirectiveNamesToInclude = schemaDirectiveNames.filter((name) => supportedCoreDirectiveNames.includes(name));
const supportedCoreDirectivesToInclude = supportedCoreDirectiveNamesToInclude.map((name) => supportedCoreDirectives[name]);
return [
...Object.values(alwaysIncludedCoreDirectives),
...supportedCoreDirectivesToInclude,
].map(({ feature, purpose }) => `\n @core(feature: ${printStringLiteral(feature)}${purpose ? `, for: ${purpose}` : ''})`);
}
function printType(type, context) {
if ((0, graphql_1.isScalarType)(type)) {
return printScalar(type);
}
if ((0, graphql_1.isObjectType)(type)) {
return printObject(type, context);
}
if ((0, graphql_1.isInterfaceType)(type)) {
return printInterface(type, context);
}
if ((0, graphql_1.isUnionType)(type)) {
return printUnion(type);
}
if ((0, graphql_1.isEnumType)(type)) {
return printEnum(type);
}
if ((0, graphql_1.isInputObjectType)(type)) {
return printInputObject(type);
}
throw Error('Unexpected type: ' + type.toString());
}
exports.printType = printType;
function printScalar(type) {
return (printDescription(type) + `scalar ${type.name}` + printSpecifiedByURL(type));
}
function printImplementedInterfaces(type) {
const interfaces = type.getInterfaces();
return interfaces.length
? ' implements ' + interfaces.map((i) => i.name).join(' & ')
: '';
}
function printObject(type, context) {
return (printDescription(type) +
`type ${type.name}` +
printImplementedInterfaces(type) +
printTypeJoinDirectives(type, context) +
printKnownDirectiveUsagesOnType(type) +
printFields(type, context));
}
function printKnownDirectiveUsagesOnType(type) {
var _a, _b, _c, _d;
const tagUsages = (_d = (_c = (_b = (_a = type.extensions) === null || _a === void 0 ? void 0 : _a.federation) === null || _b === void 0 ? void 0 : _b.directiveUsages) === null || _c === void 0 ? void 0 : _c.get('tag')) !== null && _d !== void 0 ? _d : [];
if (tagUsages.length === 0)
return '';
return '\n ' + tagUsages.map(graphql_1.print).join('\n ');
}
function printTypeJoinDirectives(type, context) {
var _a, _b;
const metadata = (_a = type.extensions) === null || _a === void 0 ? void 0 : _a.federation;
if (!metadata)
return '';
const { serviceName: ownerService, keys } = metadata;
if (!ownerService || !keys)
return '';
const { [ownerService]: ownerKeys = [], ...restKeys } = keys;
const ownerEntry = [
ownerService,
ownerKeys,
];
const restEntries = Object.entries(restKeys);
const shouldPrintOwner = (0, graphql_1.isObjectType)(type);
const ownerGraphEnumValue = (_b = context.graphNameToEnumValueName) === null || _b === void 0 ? void 0 : _b[ownerService];
(0, utilities_1.assert)(ownerGraphEnumValue, `Unexpected enum value missing for subgraph ${ownerService}`);
const joinOwnerString = shouldPrintOwner
? `\n @join__owner(graph: ${ownerGraphEnumValue})`
: '';
return (joinOwnerString +
[ownerEntry, ...restEntries]
.map(([service, keys = []]) => keys
.map((selections) => {
var _a;
const typeGraphEnumValue = (_a = context.graphNameToEnumValueName) === null || _a === void 0 ? void 0 : _a[service];
(0, utilities_1.assert)(typeGraphEnumValue, `Unexpected enum value missing for subgraph ${service}`);
return `\n @join__type(graph: ${typeGraphEnumValue}, key: ${printStringLiteral((0, utils_1.printFieldSet)(selections))})`;
})
.join(''))
.join(''));
}
function printInterface(type, context) {
return (printDescription(type) +
`interface ${type.name}` +
printImplementedInterfaces(type) +
printTypeJoinDirectives(type, context) +
printKnownDirectiveUsagesOnType(type) +
printFields(type, context));
}
function printUnion(type) {
const types = type.getTypes();
const knownDirectiveUsages = printKnownDirectiveUsagesOnType(type);
const possibleTypes = types.length
? `${knownDirectiveUsages.length ? '\n' : ' '}= ` + types.join(' | ')
: '';
return (printDescription(type) +
'union ' +
type.name +
knownDirectiveUsages +
possibleTypes);
}
function printEnum(type) {
const values = type
.getValues()
.map((value, i) => printDescription(value, ' ', !i) +
' ' +
value.name +
printDeprecated(value.deprecationReason) +
printDirectivesOnEnumValue(type, value));
return (printDescription(type) + `enum ${type.name}` + printBlock(values));
}
function printDirectivesOnEnumValue(type, value) {
var _a;
if (type.name === "join__Graph") {
return ` @join__graph(name: ${printStringLiteral((value.value.name))} url: ${printStringLiteral((_a = value.value.url) !== null && _a !== void 0 ? _a : '')})`;
}
return '';
}
function printInputObject(type) {
const fields = Object.values(type.getFields()).map((f, i) => printDescription(f, ' ', !i) + ' ' + printInputValue(f));
return printDescription(type) + `input ${type.name}` + printBlock(fields);
}
function printFields(type, context) {
var _a, _b, _c, _d, _e, _f;
const fields = Object.values(type.getFields()).map((f, i) => printDescription(f, ' ', !i) +
' ' +
f.name +
printArgs(f.args, ' ') +
': ' +
String(f.type) +
printDeprecated(f.deprecationReason) +
((0, graphql_1.isObjectType)(type)
? printJoinFieldDirectives(f, type, context) +
printKnownDirectiveUsagesOnFields(f)
: ''));
const isEntity = Boolean((_b = (_a = type.extensions) === null || _a === void 0 ? void 0 : _a.federation) === null || _b === void 0 ? void 0 : _b.keys);
const hasTags = Boolean((_f = (_e = (_d = (_c = type.extensions) === null || _c === void 0 ? void 0 : _c.federation) === null || _d === void 0 ? void 0 : _d.directiveUsages) === null || _e === void 0 ? void 0 : _e.get('tag')) === null || _f === void 0 ? void 0 : _f.length);
return printBlock(fields, isEntity || hasTags);
}
function printJoinFieldDirectives(field, parentType, context) {
var _a, _b, _c, _d;
const directiveArgs = [];
const fieldMetadata = (_a = field.extensions) === null || _a === void 0 ? void 0 : _a.federation;
let serviceName = fieldMetadata === null || fieldMetadata === void 0 ? void 0 : fieldMetadata.serviceName;
if (!serviceName && ((_b = parentType.extensions) === null || _b === void 0 ? void 0 : _b.federation).keys) {
serviceName = ((_c = parentType.extensions) === null || _c === void 0 ? void 0 : _c.federation).serviceName;
}
if (serviceName) {
const enumValue = (_d = context.graphNameToEnumValueName) === null || _d === void 0 ? void 0 : _d[serviceName];
(0, utilities_1.assert)(enumValue, `Unexpected enum value missing for subgraph ${serviceName}`);
directiveArgs.push(`graph: ${enumValue}`);
}
const requires = fieldMetadata === null || fieldMetadata === void 0 ? void 0 : fieldMetadata.requires;
if (requires && requires.length > 0) {
directiveArgs.push(`requires: ${printStringLiteral((0, utils_1.printFieldSet)(requires))}`);
}
const provides = fieldMetadata === null || fieldMetadata === void 0 ? void 0 : fieldMetadata.provides;
if (provides && provides.length > 0) {
directiveArgs.push(`provides: ${printStringLiteral((0, utils_1.printFieldSet)(provides))}`);
}
if (directiveArgs.length < 1)
return '';
return ` @join__field(${directiveArgs.join(', ')})`;
}
function printKnownDirectiveUsagesOnFields(field) {
var _a, _b, _c;
const tagUsages = (_c = (_b = (_a = field.extensions) === null || _a === void 0 ? void 0 : _a.federation) === null || _b === void 0 ? void 0 : _b.directiveUsages) === null || _c === void 0 ? void 0 : _c.get('tag');
if (!tagUsages || tagUsages.length < 1)
return '';
return ` ${tagUsages
.slice()
.sort((a, b) => a.name.value.localeCompare(b.name.value))
.map(graphql_1.print)
.join(' ')}`;
}
;
function printBlock(items, onNewLine) {
return items.length !== 0
? onNewLine
? '\n{\n' + items.join('\n') + '\n}'
: ' {\n' + items.join('\n') + '\n}'
: '';
}
function printArgs(args, indentation = '') {
if (args.length === 0) {
return '';
}
if (args.every((arg) => !arg.description)) {
return '(' + args.map(printInputValue).join(', ') + ')';
}
return ('(\n' +
args
.map((arg, i) => printDescription(arg, ' ' + indentation, !i) +
' ' +
indentation +
printInputValue(arg))
.join('\n') +
'\n' +
indentation +
')');
}
function printInputValue(arg) {
const defaultAST = (0, graphql_1.astFromValue)(arg.defaultValue, arg.type);
let argDecl = arg.name + ': ' + String(arg.type);
if (defaultAST) {
argDecl += ` = ${(0, graphql_1.print)(defaultAST)}`;
}
return argDecl + printDeprecated(arg.deprecationReason);
}
function printDirective(directive) {
return (printDescription(directive) +
'directive @' +
directive.name +
printArgs(directive.args) +
(directive.isRepeatable ? ' repeatable' : '') +
' on ' +
directive.locations.join(' | '));
}
function printDeprecated(reason) {
if (reason == null) {
return '';
}
if (reason !== graphql_1.DEFAULT_DEPRECATION_REASON) {
const astValue = (0, graphql_1.print)({ kind: graphql_1.Kind.STRING, value: reason });
return ` @deprecated(reason: ${astValue})`;
}
return ' @deprecated';
}
function printSpecifiedByURL(scalar) {
var _a;
const specifiedByURL = (_a = scalar.specifiedByUrl) !== null && _a !== void 0 ? _a : scalar.specifiedByURL;
if (specifiedByURL == null) {
return '';
}
const astValue = (0, graphql_1.print)({
kind: graphql_1.Kind.STRING,
value: specifiedByURL,
});
return ` @specifiedBy(url: ${astValue})`;
}
function printDescription(def, indentation = '', firstInBlock = true) {
const { description } = def;
if (description == null) {
return '';
}
const preferMultipleLines = description.length > 70;
const blockString = printBlockString(description, preferMultipleLines);
const prefix = indentation && !firstInBlock ? '\n' + indentation : indentation;
return prefix + blockString.replace(/\n/g, '\n' + indentation) + '\n';
}
function printStringLiteral(value) {
return JSON.stringify(value);
}
function printBlockString(value, preferMultipleLines = false) {
const isSingleLine = !value.includes('\n');
const hasLeadingSpace = value[0] === ' ' || value[0] === '\t';
const hasTrailingQuote = value[value.length - 1] === '"';
const hasTrailingSlash = value[value.length - 1] === '\\';
const printAsMultipleLines = !isSingleLine ||
hasTrailingQuote ||
hasTrailingSlash ||
preferMultipleLines;
let result = '';
if (printAsMultipleLines && !(isSingleLine && hasLeadingSpace)) {
result += '\n';
}
result += value;
if (printAsMultipleLines) {
result += '\n';
}
return '"""' + result.replace(/"""/g, '\\"""') + '"""';
}
exports.printBlockString = printBlockString;
//# sourceMappingURL=printSupergraphSdl.js.map
;