@apollo/federation-internals
Version:
Apollo Federation internal utilities
286 lines • 13.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CONNECT_VERSIONS = exports.ConnectSpecDefinition = exports.connectIdentity = void 0;
const graphql_1 = require("graphql");
const coreSpec_1 = require("./coreSpec");
const definitions_1 = require("../definitions");
const knownCoreFeatures_1 = require("../knownCoreFeatures");
const directiveAndTypeSpecification_1 = require("../directiveAndTypeSpecification");
const error_1 = require("../error");
const types_1 = require("../types");
const utils_1 = require("../utils");
const values_1 = require("../values");
exports.connectIdentity = 'https://specs.apollo.dev/connect';
const CONNECT = 'connect';
const SOURCE = 'source';
const URL_PATH_TEMPLATE = 'URLPathTemplate';
const JSON_SELECTION = 'JSONSelection';
const CONNECT_HTTP = 'ConnectHTTP';
const CONNECT_BATCH = 'ConnectBatch';
const CONNECTOR_ERRORS = "ConnectorErrors";
const SOURCE_HTTP = "SourceHTTP";
const HTTP_HEADER_MAPPING = 'HTTPHeaderMapping';
class ConnectSpecDefinition extends coreSpec_1.FeatureDefinition {
constructor(version, minimumFederationVersion) {
super(new coreSpec_1.FeatureUrl(exports.connectIdentity, CONNECT, version), minimumFederationVersion);
this.minimumFederationVersion = minimumFederationVersion;
function lookupFeatureTypeInSchema(name, kind, schema, feature) {
(0, utils_1.assert)(feature, `Shouldn't be added without being attached to a @connect spec`);
const typeName = feature.typeNameInSchema(name);
const type = schema.typeOfKind(typeName, kind);
(0, utils_1.assert)(type, () => `Expected "${typeName}" to be defined`);
return type;
}
this.registerType((0, directiveAndTypeSpecification_1.createScalarTypeSpecification)({ name: URL_PATH_TEMPLATE }));
this.registerType((0, directiveAndTypeSpecification_1.createScalarTypeSpecification)({ name: JSON_SELECTION }));
this.registerType(createInputObjectTypeSpecification({
name: CONNECTOR_ERRORS,
inputFieldsFct: (schema, feature) => {
const jsonSelectionType = lookupFeatureTypeInSchema(JSON_SELECTION, 'ScalarType', schema, feature);
return [
{
name: 'message',
type: jsonSelectionType
},
{
name: 'extensions',
type: jsonSelectionType
},
];
}
}));
this.registerType(createInputObjectTypeSpecification({
name: HTTP_HEADER_MAPPING,
inputFieldsFct: (schema) => [
{
name: 'name',
type: new definitions_1.NonNullType(schema.stringType())
},
{
name: 'from',
type: schema.stringType()
},
{
name: 'value',
type: schema.stringType()
},
]
}));
this.registerType(createInputObjectTypeSpecification({
name: CONNECT_BATCH,
inputFieldsFct: (schema) => [
{
name: 'maxSize',
type: schema.intType()
}
]
}));
this.registerType(createInputObjectTypeSpecification({
name: SOURCE_HTTP,
inputFieldsFct: (schema, feature) => {
const jsonSelectionType = lookupFeatureTypeInSchema(JSON_SELECTION, 'ScalarType', schema, feature);
const httpHeaderMappingType = lookupFeatureTypeInSchema(HTTP_HEADER_MAPPING, 'InputObjectType', schema, feature);
return [
{
name: 'baseURL',
type: new definitions_1.NonNullType(schema.stringType())
},
{
name: 'headers',
type: new definitions_1.ListType(new definitions_1.NonNullType(httpHeaderMappingType))
},
{
name: 'path',
type: jsonSelectionType
},
{
name: 'queryParams',
type: jsonSelectionType
}
];
}
}));
this.registerType(createInputObjectTypeSpecification({
name: CONNECT_HTTP,
inputFieldsFct: (schema, feature) => {
const urlPathTemplateType = lookupFeatureTypeInSchema(URL_PATH_TEMPLATE, 'ScalarType', schema, feature);
const jsonSelectionType = lookupFeatureTypeInSchema(JSON_SELECTION, 'ScalarType', schema, feature);
const httpHeaderMappingType = lookupFeatureTypeInSchema(HTTP_HEADER_MAPPING, 'InputObjectType', schema, feature);
return [
{
name: 'GET',
type: urlPathTemplateType
},
{
name: 'POST',
type: urlPathTemplateType
},
{
name: 'PUT',
type: urlPathTemplateType
},
{
name: 'PATCH',
type: urlPathTemplateType
},
{
name: 'DELETE',
type: urlPathTemplateType
},
{
name: 'body',
type: jsonSelectionType
},
{
name: 'headers',
type: new definitions_1.ListType(new definitions_1.NonNullType(httpHeaderMappingType))
},
{
name: 'path',
type: jsonSelectionType
},
{
name: 'queryParams',
type: jsonSelectionType
},
];
}
}));
this.registerDirective((0, directiveAndTypeSpecification_1.createDirectiveSpecification)({
name: CONNECT,
locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION, graphql_1.DirectiveLocation.OBJECT],
repeatable: true,
args: [
{
name: 'source',
type: (schema) => schema.stringType()
},
{
name: 'id',
type: (schema) => schema.stringType()
},
{
name: 'http',
type: (schema, feature) => {
const connectHttpType = lookupFeatureTypeInSchema(CONNECT_HTTP, 'InputObjectType', schema, feature);
return connectHttpType;
}
},
{
name: 'batch',
type: (schema, feature) => lookupFeatureTypeInSchema(CONNECT_BATCH, 'InputObjectType', schema, feature)
},
{
name: 'errors',
type: (schema, feature) => lookupFeatureTypeInSchema(CONNECTOR_ERRORS, 'InputObjectType', schema, feature)
},
{
name: 'selection',
type: (schema, feature) => {
const jsonSelectionType = lookupFeatureTypeInSchema(JSON_SELECTION, 'ScalarType', schema, feature);
return new definitions_1.NonNullType(jsonSelectionType);
}
},
{
name: 'entity',
type: (schema) => schema.booleanType(),
defaultValue: false
},
{
name: 'isSuccess',
type: (schema, feature) => lookupFeatureTypeInSchema(JSON_SELECTION, 'ScalarType', schema, feature)
}
],
composes: false,
}));
this.registerDirective((0, directiveAndTypeSpecification_1.createDirectiveSpecification)({
name: SOURCE,
locations: [graphql_1.DirectiveLocation.SCHEMA],
repeatable: true,
composes: false,
args: [
{
name: 'name',
type: (schema) => new definitions_1.NonNullType(schema.stringType())
},
{
name: 'http',
type: (schema, feature) => {
const sourceHttpType = lookupFeatureTypeInSchema(SOURCE_HTTP, 'InputObjectType', schema, feature);
return new definitions_1.NonNullType(sourceHttpType);
}
},
{
name: 'errors',
type: (schema, feature) => lookupFeatureTypeInSchema(CONNECTOR_ERRORS, 'InputObjectType', schema, feature)
},
{
name: 'isSuccess',
type: (schema, feature) => lookupFeatureTypeInSchema(JSON_SELECTION, 'ScalarType', schema, feature)
}
]
}));
}
get defaultCorePurpose() {
return 'EXECUTION';
}
}
exports.ConnectSpecDefinition = ConnectSpecDefinition;
exports.CONNECT_VERSIONS = new coreSpec_1.FeatureDefinitions(exports.connectIdentity)
.add(new ConnectSpecDefinition(new coreSpec_1.FeatureVersion(0, 1), new coreSpec_1.FeatureVersion(2, 10)))
.add(new ConnectSpecDefinition(new coreSpec_1.FeatureVersion(0, 2), new coreSpec_1.FeatureVersion(2, 10)))
.add(new ConnectSpecDefinition(new coreSpec_1.FeatureVersion(0, 3), new coreSpec_1.FeatureVersion(2, 12)))
.add(new ConnectSpecDefinition(new coreSpec_1.FeatureVersion(0, 4), new coreSpec_1.FeatureVersion(2, 13)), { preview: true });
(0, knownCoreFeatures_1.registerKnownFeature)(exports.CONNECT_VERSIONS);
function createInputObjectTypeSpecification({ name, inputFieldsFct, }) {
return {
name,
checkOrAdd: (schema, feature, asBuiltIn) => {
var _a;
const actualName = (_a = feature === null || feature === void 0 ? void 0 : feature.typeNameInSchema(name)) !== null && _a !== void 0 ? _a : name;
const expectedFields = inputFieldsFct(schema, feature);
const existing = schema.type(actualName);
if (existing) {
let errors = (0, directiveAndTypeSpecification_1.ensureSameTypeKind)('InputObjectType', existing);
if (errors.length > 0) {
return errors;
}
(0, utils_1.assert)((0, definitions_1.isInputObjectType)(existing), 'Should be an input object type');
for (const { name: fieldName, type, defaultValue } of expectedFields) {
const existingField = existing.field(fieldName);
if (!existingField) {
if ((0, definitions_1.isNonNullType)(type) && defaultValue === undefined) {
errors.push(error_1.ERRORS.TYPE_DEFINITION_INVALID.err(`Invalid definition for type ${name}: missing required input field "${fieldName}"`, { nodes: existing.sourceAST }));
}
continue;
}
let existingType = existingField.type;
if ((0, definitions_1.isNonNullType)(existingType) && !(0, definitions_1.isNonNullType)(type)) {
existingType = existingType.ofType;
}
if (!(0, types_1.sameType)(type, existingType)) {
errors.push(error_1.ERRORS.TYPE_DEFINITION_INVALID.err(`Invalid definition for type ${name}: input field "${fieldName}" should have type "${type}" but found type "${existingField.type}"`, { nodes: existingField.sourceAST }));
}
else if (!(0, values_1.valueEquals)(defaultValue, existingField.defaultValue)) {
errors.push(error_1.ERRORS.TYPE_DEFINITION_INVALID.err(`Invalid definition type ${name}: input field "${fieldName}" should have default value ${(0, values_1.valueToString)(defaultValue)} but found default value ${(0, values_1.valueToString)(existingField.defaultValue)}`, { nodes: existingField.sourceAST }));
}
}
for (const existingField of existing.fields()) {
if (!expectedFields.some((field) => field.name === existingField.name)) {
errors.push(error_1.ERRORS.TYPE_DEFINITION_INVALID.err(`Invalid definition for type ${name}: unknown/unsupported input field "${existingField.name}"`, { nodes: existingField.sourceAST }));
}
}
return errors;
}
else {
const createdType = schema.addType(new definitions_1.InputObjectType(actualName, asBuiltIn));
for (const { name, type, defaultValue } of expectedFields) {
const newField = createdType.addField(name, type);
newField.defaultValue = defaultValue;
}
return [];
}
},
};
}
//# sourceMappingURL=connectSpec.js.map