@apollo/federation-internals
Advanced tools
Comparing version 2.10.0-alpha.1 to 2.10.0-alpha.2
@@ -11,2 +11,3 @@ import { InputType, Schema } from "./definitions"; | ||
}; | ||
declare function unionValues(values: any[]): any; | ||
export declare const ARGUMENT_COMPOSITION_STRATEGIES: { | ||
@@ -16,3 +17,3 @@ MAX: { | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: any[]) => number; | ||
mergeValues: (values: number[]) => number; | ||
}; | ||
@@ -22,9 +23,4 @@ MIN: { | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: any[]) => number; | ||
mergeValues: (values: number[]) => number; | ||
}; | ||
SUM: { | ||
name: string; | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: any[]) => any; | ||
}; | ||
INTERSECTION: { | ||
@@ -38,3 +34,3 @@ name: string; | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: any[]) => any; | ||
mergeValues: typeof unionValues; | ||
}; | ||
@@ -44,3 +40,3 @@ NULLABLE_AND: { | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: (boolean | null | undefined)[]) => boolean | null | undefined; | ||
mergeValues: (values: (boolean | null | undefined)[]) => boolean | undefined; | ||
}; | ||
@@ -50,10 +46,8 @@ NULLABLE_MAX: { | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: any[]) => any; | ||
mergeValues: (values: (number | null | undefined)[]) => number | undefined; | ||
}; | ||
NULLABLE_UNION: { | ||
name: string; | ||
isTypeSupported: (_: Schema, type: InputType) => { | ||
valid: boolean; | ||
}; | ||
mergeValues: (values: any[]) => unknown[] | undefined; | ||
isTypeSupported: TypeSupportValidator; | ||
mergeValues: (values: any[]) => any; | ||
}; | ||
@@ -60,0 +54,0 @@ }; |
@@ -10,16 +10,31 @@ "use strict"; | ||
const supported = types(schema); | ||
if (!supported.some((t) => (0, types_1.sameType)(t, type))) { | ||
return { valid: false, supportedMsg: `type(s) ${supported.join(', ')}` }; | ||
} | ||
return { valid: true }; | ||
return supported.some((t) => (0, types_1.sameType)(t, type)) | ||
? { valid: true } | ||
: { valid: false, supportedMsg: `type(s) ${supported.join(', ')}` }; | ||
}; | ||
} | ||
function supportAnyNonNullArray() { | ||
return (_, type) => { | ||
if (!(0, definitions_1.isNonNullType)(type) || !(0, definitions_1.isListType)(type.ofType)) { | ||
return { valid: false, supportedMsg: 'non nullable list types of any type' }; | ||
} | ||
return { valid: true }; | ||
return (_, type) => (0, definitions_1.isNonNullType)(type) && (0, definitions_1.isListType)(type.ofType) | ||
? { valid: true } | ||
: { valid: false, supportedMsg: 'non nullable list types of any type' }; | ||
} | ||
function supportAnyArray() { | ||
return (_, type) => (0, definitions_1.isListType)(type) || ((0, definitions_1.isNonNullType)(type) && (0, definitions_1.isListType)(type.ofType)) | ||
? { valid: true } | ||
: { valid: false, supportedMsg: 'list types of any type' }; | ||
} | ||
function mergeNullableValues(mergeValues) { | ||
return (values) => { | ||
const nonNullValues = values.filter((v) => v !== null && v !== undefined); | ||
return nonNullValues.length > 0 | ||
? mergeValues(nonNullValues) | ||
: undefined; | ||
}; | ||
} | ||
function unionValues(values) { | ||
return values.reduce((acc, next) => { | ||
const newValues = next.filter((v1) => !acc.some((v2) => (0, values_1.valueEquals)(v1, v2))); | ||
return acc.concat(newValues); | ||
}, []); | ||
} | ||
exports.ARGUMENT_COMPOSITION_STRATEGIES = { | ||
@@ -36,11 +51,16 @@ MAX: { | ||
}, | ||
SUM: { | ||
name: 'SUM', | ||
isTypeSupported: supportFixedTypes((schema) => [new definitions_1.NonNullType(schema.intType())]), | ||
mergeValues: (values) => values.reduce((acc, val) => acc + val, 0), | ||
}, | ||
INTERSECTION: { | ||
name: 'INTERSECTION', | ||
isTypeSupported: supportAnyNonNullArray(), | ||
mergeValues: (values) => values.reduce((acc, val) => acc.filter((v1) => val.some((v2) => (0, values_1.valueEquals)(v1, v2))), values[0]), | ||
mergeValues: (values) => { | ||
var _a; | ||
return (_a = values.reduce((acc, next) => { | ||
if (acc === undefined) { | ||
return next; | ||
} | ||
else { | ||
return acc.filter((v1) => next.some((v2) => (0, values_1.valueEquals)(v1, v2))); | ||
} | ||
}, undefined)) !== null && _a !== void 0 ? _a : []; | ||
}, | ||
}, | ||
@@ -50,46 +70,26 @@ UNION: { | ||
isTypeSupported: supportAnyNonNullArray(), | ||
mergeValues: (values) => values.reduce((acc, val) => { | ||
const newValues = val.filter((v1) => !acc.some((v2) => (0, values_1.valueEquals)(v1, v2))); | ||
return acc.concat(newValues); | ||
}, []), | ||
mergeValues: unionValues, | ||
}, | ||
NULLABLE_AND: { | ||
name: 'NULLABLE_AND', | ||
isTypeSupported: supportFixedTypes((schema) => [schema.booleanType()]), | ||
mergeValues: (values) => values.reduce((acc, next) => { | ||
if (acc === null || acc === undefined) { | ||
return next; | ||
} | ||
else if (next === null || next === undefined) { | ||
return acc; | ||
} | ||
else { | ||
return acc && next; | ||
} | ||
}, undefined), | ||
isTypeSupported: supportFixedTypes((schema) => [ | ||
schema.booleanType(), | ||
new definitions_1.NonNullType(schema.booleanType()) | ||
]), | ||
mergeValues: mergeNullableValues((values) => values.every((v) => v)), | ||
}, | ||
NULLABLE_MAX: { | ||
name: 'NULLABLE_MAX', | ||
isTypeSupported: supportFixedTypes((schema) => [schema.intType(), new definitions_1.NonNullType(schema.intType())]), | ||
mergeValues: (values) => values.reduce((a, b) => a !== undefined && b !== undefined ? Math.max(a, b) : a !== null && a !== void 0 ? a : b, undefined), | ||
isTypeSupported: supportFixedTypes((schema) => [ | ||
schema.intType(), | ||
new definitions_1.NonNullType(schema.intType()) | ||
]), | ||
mergeValues: mergeNullableValues((values) => Math.max(...values)) | ||
}, | ||
NULLABLE_UNION: { | ||
name: 'NULLABLE_UNION', | ||
isTypeSupported: (_, type) => ({ valid: (0, definitions_1.isListType)(type) }), | ||
mergeValues: (values) => { | ||
if (values.every((v) => v === undefined)) { | ||
return undefined; | ||
} | ||
const combined = new Set(); | ||
for (const subgraphValues of values) { | ||
if (Array.isArray(subgraphValues)) { | ||
for (const value of subgraphValues) { | ||
combined.add(value); | ||
} | ||
} | ||
} | ||
return Array.from(combined); | ||
} | ||
isTypeSupported: supportAnyArray(), | ||
mergeValues: mergeNullableValues(unionValues), | ||
} | ||
}; | ||
//# sourceMappingURL=argumentCompositionStrategies.js.map |
@@ -213,2 +213,3 @@ import { ConstArgumentNode, ASTNode, DirectiveLocation, ConstDirectiveNode, DocumentNode, GraphQLError, GraphQLSchema, Kind, TypeNode, VariableDefinitionNode, VariableNode, DirectiveDefinitionNode, DirectiveNode } from "graphql"; | ||
directiveNameInSchema(name: string): string; | ||
static directiveNameInSchemaForCoreArguments(specUrl: FeatureUrl, specNameInSchema: string, imports: CoreImport[], directiveNameInSpec: string): string; | ||
typeNameInSchema(name: string): string; | ||
@@ -215,0 +216,0 @@ minimumFederationVersion(): FeatureVersion | undefined; |
@@ -134,3 +134,3 @@ "use strict"; | ||
function extractSubgraphsFromSupergraph(supergraph, validateExtractedSubgraphs = true) { | ||
const [coreFeatures, joinSpec] = (0, supergraphs_1.validateSupergraph)(supergraph); | ||
const [coreFeatures, joinSpec, contextSpec, costSpec] = (0, supergraphs_1.validateSupergraph)(supergraph); | ||
const isFed1 = joinSpec.version.equals(new coreSpec_1.FeatureVersion(0, 1)); | ||
@@ -160,3 +160,2 @@ try { | ||
const types = filteredTypes(supergraph, joinSpec, coreFeatures.coreDefinition); | ||
const originalDirectiveNames = getApolloDirectiveNames(supergraph); | ||
const args = { | ||
@@ -166,4 +165,5 @@ supergraph, | ||
joinSpec, | ||
contextSpec, | ||
costSpec, | ||
filteredTypes: types, | ||
originalDirectiveNames, | ||
getSubgraph, | ||
@@ -242,3 +242,5 @@ getSubgraphEnumValue, | ||
const subgraphType = subgraph.schema.addType((0, definitions_1.newNamedType)(type.kind, type.name)); | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.originalDirectiveNames); | ||
if (args.costSpec) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec); | ||
} | ||
} | ||
@@ -256,2 +258,3 @@ break; | ||
function addEmptyType(type, typeApplications, args) { | ||
var _a; | ||
const { supergraph, getSubgraph, getSubgraphEnumValue } = args; | ||
@@ -281,11 +284,3 @@ (0, utils_1.assert)(typeApplications.length > 0, `Missing @join__type on ${type}`); | ||
} | ||
const coreFeatures = supergraph.coreFeatures; | ||
(0, utils_1.assert)(coreFeatures, 'Should have core features'); | ||
const contextFeature = coreFeatures.getByIdentity(_1.ContextSpecDefinition.identity); | ||
let supergraphContextDirective; | ||
if (contextFeature) { | ||
const contextSpec = _1.CONTEXT_VERSIONS.find(contextFeature.url.version); | ||
(0, utils_1.assert)(contextSpec, 'Should have context spec'); | ||
supergraphContextDirective = contextSpec.contextDirective(supergraph); | ||
} | ||
const supergraphContextDirective = (_a = args.contextSpec) === null || _a === void 0 ? void 0 : _a.contextDirective(supergraph); | ||
if (supergraphContextDirective) { | ||
@@ -312,3 +307,2 @@ const contextApplications = type.appliedDirectivesOf(supergraphContextDirective); | ||
(0, utils_1.assert)(implementsDirective, '@join__implements should existing for a fed2 supergraph'); | ||
const originalDirectiveNames = args.originalDirectiveNames; | ||
for (const { type, subgraphsInfo } of info) { | ||
@@ -321,4 +315,6 @@ const implementsApplications = type.appliedDirectivesOf(implementsDirective); | ||
} | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.originalDirectiveNames); | ||
if (args.costSpec) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec); | ||
} | ||
} | ||
@@ -330,3 +326,9 @@ for (const field of type.fields()) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
addSubgraphField({ field, type: subgraphType, subgraph, isShareable, originalDirectiveNames }); | ||
addSubgraphField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, | ||
isShareable, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -346,3 +348,9 @@ } | ||
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph); | ||
addSubgraphField({ field, type: subgraphType, subgraph, isShareable, joinFieldArgs, originalDirectiveNames }); | ||
addSubgraphField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, isShareable, | ||
joinFieldArgs, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -353,25 +361,4 @@ } | ||
} | ||
function getApolloDirectiveNames(supergraph) { | ||
const originalDirectiveNames = {}; | ||
for (const linkDirective of supergraph.schemaDefinition.appliedDirectivesOf("link")) { | ||
if (linkDirective.arguments().url && linkDirective.arguments().import) { | ||
const url = _1.FeatureUrl.maybeParse(linkDirective.arguments().url); | ||
if (!(url === null || url === void 0 ? void 0 : url.identity.includes("specs.apollo.dev"))) { | ||
continue; | ||
} | ||
for (const importedDirective of linkDirective.arguments().import) { | ||
if (importedDirective.name && importedDirective.as) { | ||
originalDirectiveNames[importedDirective.name.replace('@', '')] = importedDirective.as.replace('@', ''); | ||
} | ||
else if (typeof importedDirective === 'string') { | ||
originalDirectiveNames[importedDirective.replace('@', '')] = importedDirective.replace('@', ''); | ||
} | ||
} | ||
} | ||
} | ||
return originalDirectiveNames; | ||
} | ||
function extractInputObjContent(args, info) { | ||
const fieldDirective = args.joinSpec.fieldDirective(args.supergraph); | ||
const originalDirectiveNames = args.originalDirectiveNames; | ||
for (const { type, subgraphsInfo } of info) { | ||
@@ -382,3 +369,8 @@ for (const field of type.fields()) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
addSubgraphInputField({ field, type: subgraphType, subgraph, originalDirectiveNames }); | ||
addSubgraphInputField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -388,8 +380,14 @@ } | ||
for (const application of fieldApplications) { | ||
const args = application.arguments(); | ||
if (!args.graph) { | ||
const joinFieldArgs = application.arguments(); | ||
if (!joinFieldArgs.graph) { | ||
continue; | ||
} | ||
const { type: subgraphType, subgraph } = subgraphsInfo.get(args.graph); | ||
addSubgraphInputField({ field, type: subgraphType, subgraph, joinFieldArgs: args, originalDirectiveNames }); | ||
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph); | ||
addSubgraphInputField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, | ||
joinFieldArgs, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -402,6 +400,7 @@ } | ||
const enumValueDirective = args.joinSpec.enumValueDirective(args.supergraph); | ||
const originalDirectiveNames = args.originalDirectiveNames; | ||
for (const { type, subgraphsInfo } of info) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, originalDirectiveNames); | ||
if (args.costSpec) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec); | ||
} | ||
} | ||
@@ -487,15 +486,15 @@ for (const value of type.values) { | ||
} | ||
function propagateDemandControlDirectives(source, dest, subgraph, originalDirectiveNames) { | ||
const costDirectiveName = originalDirectiveNames === null || originalDirectiveNames === void 0 ? void 0 : originalDirectiveNames[_1.FederationDirectiveName.COST]; | ||
if (costDirectiveName) { | ||
const costDirective = source.appliedDirectivesOf(costDirectiveName).pop(); | ||
if (costDirective) { | ||
dest.applyDirective(subgraph.metadata().costDirective().name, costDirective.arguments()); | ||
function propagateDemandControlDirectives(source, dest, subgraph, costSpec) { | ||
const costDirective = costSpec.costDirective(source.schema()); | ||
if (costDirective) { | ||
const application = source.appliedDirectivesOf(costDirective)[0]; | ||
if (application) { | ||
dest.applyDirective(subgraph.metadata().costDirective().name, application.arguments()); | ||
} | ||
} | ||
const listSizeDirectiveName = originalDirectiveNames === null || originalDirectiveNames === void 0 ? void 0 : originalDirectiveNames[_1.FederationDirectiveName.LIST_SIZE]; | ||
if (listSizeDirectiveName) { | ||
const listSizeDirective = source.appliedDirectivesOf(listSizeDirectiveName).pop(); | ||
if (listSizeDirective) { | ||
dest.applyDirective(subgraph.metadata().listSizeDirective().name, listSizeDirective.arguments()); | ||
const listSizeDirective = costSpec.listSizeDirective(source.schema()); | ||
if (listSizeDirective) { | ||
const application = source.appliedDirectivesOf(listSizeDirective)[0]; | ||
if (application) { | ||
dest.applyDirective(subgraph.metadata().listSizeDirective().name, application.arguments()); | ||
} | ||
@@ -508,3 +507,3 @@ } | ||
} | ||
function addSubgraphField({ field, type, subgraph, isShareable, joinFieldArgs, originalDirectiveNames, }) { | ||
function addSubgraphField({ field, type, subgraph, isShareable, joinFieldArgs, costSpec, }) { | ||
const copiedFieldType = (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.type) | ||
@@ -516,3 +515,5 @@ ? decodeType(joinFieldArgs.type, subgraph.schema, subgraph.name) | ||
const argDef = subgraphField.addArgument(arg.name, copyType(arg.type, subgraph.schema, subgraph.name), arg.defaultValue); | ||
propagateDemandControlDirectives(arg, argDef, subgraph, originalDirectiveNames); | ||
if (costSpec) { | ||
propagateDemandControlDirectives(arg, argDef, subgraph, costSpec); | ||
} | ||
} | ||
@@ -561,6 +562,8 @@ if (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.requires) { | ||
} | ||
propagateDemandControlDirectives(field, subgraphField, subgraph, originalDirectiveNames); | ||
if (costSpec) { | ||
propagateDemandControlDirectives(field, subgraphField, subgraph, costSpec); | ||
} | ||
return subgraphField; | ||
} | ||
function addSubgraphInputField({ field, type, subgraph, joinFieldArgs, originalDirectiveNames, }) { | ||
function addSubgraphInputField({ field, type, subgraph, joinFieldArgs, costSpec, }) { | ||
const copiedType = (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.type) | ||
@@ -571,3 +574,5 @@ ? decodeType(joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.type, subgraph.schema, subgraph.name) | ||
inputField.defaultValue = field.defaultValue; | ||
propagateDemandControlDirectives(field, inputField, subgraph, originalDirectiveNames); | ||
if (costSpec) { | ||
propagateDemandControlDirectives(field, inputField, subgraph, costSpec); | ||
} | ||
return inputField; | ||
@@ -574,0 +579,0 @@ } |
@@ -53,2 +53,3 @@ import { ASTNode, GraphQLError, StringValueNode } from "graphql"; | ||
for?: string; | ||
import: undefined; | ||
}; | ||
@@ -82,4 +83,3 @@ export type LinkDirectiveArgs = { | ||
coreVersion(schema: Schema): FeatureVersion; | ||
applyFeatureToSchema(schema: Schema, feature: FeatureDefinition, as?: string, purpose?: CorePurpose): GraphQLError[]; | ||
applyFeatureAsLink(schema: Schema, feature: FeatureDefinition, purpose?: CorePurpose, imports?: CoreImport[]): GraphQLError[]; | ||
applyFeatureToSchema(schema: Schema, feature: FeatureDefinition, as?: string, purpose?: CorePurpose, imports?: CoreImport[]): GraphQLError[]; | ||
extractFeatureUrl(args: CoreOrLinkDirectiveArgs): FeatureUrl; | ||
@@ -86,0 +86,0 @@ urlArgName(): 'feature' | 'url'; |
@@ -381,3 +381,3 @@ "use strict"; | ||
} | ||
applyFeatureToSchema(schema, feature, as, purpose) { | ||
applyFeatureToSchema(schema, feature, as, purpose, imports) { | ||
const coreDirective = this.coreDirective(schema); | ||
@@ -388,23 +388,18 @@ const args = { | ||
}; | ||
if (this.supportPurposes() && purpose) { | ||
args.for = purpose; | ||
if (purpose) { | ||
if (this.supportPurposes()) { | ||
args.for = purpose; | ||
} | ||
else { | ||
return [new graphql_1.GraphQLError(`Cannot apply feature ${feature} with purpose since the schema's @core/@link version does not support it.`)]; | ||
} | ||
} | ||
schema.schemaDefinition.applyDirective(coreDirective, args); | ||
return feature.addElementsToSchema(schema); | ||
} | ||
applyFeatureAsLink(schema, feature, purpose, imports) { | ||
var _a; | ||
const existing = schema.schemaDefinition.appliedDirectivesOf(exports.linkDirectiveDefaultName).find((link) => link.arguments().url === feature.toString()); | ||
if (existing) { | ||
existing.remove(); | ||
if (imports && imports.length > 0) { | ||
if (this.supportImport()) { | ||
args.import = imports.map(i => i.as ? i : i.name); | ||
} | ||
else { | ||
return [new graphql_1.GraphQLError(`Cannot apply feature ${feature} with imports since the schema's @core/@link version does not support it.`)]; | ||
} | ||
} | ||
const coreDirective = this.coreDirective(schema); | ||
const args = { | ||
url: feature.toString(), | ||
import: ((_a = existing === null || existing === void 0 ? void 0 : existing.arguments().import) !== null && _a !== void 0 ? _a : []).concat(imports === null || imports === void 0 ? void 0 : imports.map((i) => i.as ? { name: `@${i.name}`, as: `@${i.as}` } : `@${i.name}`)), | ||
feature: undefined, | ||
}; | ||
if (this.supportPurposes() && purpose) { | ||
args.for = purpose; | ||
} | ||
schema.schemaDefinition.applyDirective(coreDirective, args); | ||
@@ -411,0 +406,0 @@ return feature.addElementsToSchema(schema); |
import { FeatureDefinition, FeatureDefinitions, FeatureVersion } from './coreSpec'; | ||
import { DirectiveDefinition, Schema } from '../definitions'; | ||
export declare const costIdentity = "https://specs.apollo.dev/cost"; | ||
@@ -6,2 +7,4 @@ export declare class CostSpecDefinition extends FeatureDefinition { | ||
constructor(version: FeatureVersion, minimumFederationVersion: FeatureVersion); | ||
costDirective(schema: Schema): DirectiveDefinition<CostDirectiveArguments> | undefined; | ||
listSizeDirective(schema: Schema): DirectiveDefinition<ListSizeDirectiveArguments> | undefined; | ||
} | ||
@@ -8,0 +11,0 @@ export declare const COST_VERSIONS: FeatureDefinitions<CostSpecDefinition>; |
@@ -44,2 +44,8 @@ "use strict"; | ||
} | ||
costDirective(schema) { | ||
return this.directive(schema, 'cost'); | ||
} | ||
listSizeDirective(schema) { | ||
return this.directive(schema, 'listSize'); | ||
} | ||
} | ||
@@ -46,0 +52,0 @@ exports.CostSpecDefinition = CostSpecDefinition; |
import { DocumentNode } from "graphql"; | ||
import { CoreFeatures, Schema } from "./definitions"; | ||
import { JoinSpecDefinition } from "./specs/joinSpec"; | ||
import { ContextSpecDefinition } from "./specs/contextSpec"; | ||
import { CostSpecDefinition } from "./specs/costSpec"; | ||
import { Subgraphs } from "."; | ||
export declare const DEFAULT_SUPPORTED_SUPERGRAPH_FEATURES: Set<string>; | ||
export declare const ROUTER_SUPPORTED_SUPERGRAPH_FEATURES: Set<string>; | ||
export declare function validateSupergraph(supergraph: Schema): [CoreFeatures, JoinSpecDefinition]; | ||
export declare function validateSupergraph(supergraph: Schema): [ | ||
CoreFeatures, | ||
JoinSpecDefinition, | ||
ContextSpecDefinition | undefined, | ||
CostSpecDefinition | undefined | ||
]; | ||
export declare function isFed1Supergraph(supergraph: Schema): boolean; | ||
@@ -9,0 +16,0 @@ export declare class Supergraph { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Supergraph = exports.isFed1Supergraph = exports.validateSupergraph = exports.ROUTER_SUPPORTED_SUPERGRAPH_FEATURES = exports.DEFAULT_SUPPORTED_SUPERGRAPH_FEATURES = void 0; | ||
const definitions_1 = require("./definitions"); | ||
const coreSpec_1 = require("./specs/coreSpec"); | ||
const definitions_1 = require("./definitions"); | ||
const joinSpec_1 = require("./specs/joinSpec"); | ||
const contextSpec_1 = require("./specs/contextSpec"); | ||
const costSpec_1 = require("./specs/costSpec"); | ||
const buildSchema_1 = require("./buildSchema"); | ||
@@ -82,3 +84,19 @@ const extractSubgraphsFromSupergraph_1 = require("./extractSubgraphsFromSupergraph"); | ||
} | ||
return [coreFeatures, joinSpec]; | ||
const contextFeature = coreFeatures.getByIdentity(contextSpec_1.ContextSpecDefinition.identity); | ||
let contextSpec = undefined; | ||
if (contextFeature) { | ||
contextSpec = contextSpec_1.CONTEXT_VERSIONS.find(contextFeature.url.version); | ||
if (!contextSpec) { | ||
throw error_1.ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(`Invalid supergraph: uses unsupported context spec version ${contextFeature.url.version} (supported versions: ${contextSpec_1.CONTEXT_VERSIONS.versions().join(', ')})`); | ||
} | ||
} | ||
const costFeature = coreFeatures.getByIdentity(costSpec_1.costIdentity); | ||
let costSpec = undefined; | ||
if (costFeature) { | ||
costSpec = costSpec_1.COST_VERSIONS.find(costFeature.url.version); | ||
if (!costSpec) { | ||
throw error_1.ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(`Invalid supergraph: uses unsupported cost spec version ${costFeature.url.version} (supported versions: ${costSpec_1.COST_VERSIONS.versions().join(', ')})`); | ||
} | ||
} | ||
return [coreFeatures, joinSpec, contextSpec, costSpec]; | ||
} | ||
@@ -85,0 +103,0 @@ exports.validateSupergraph = validateSupergraph; |
@@ -67,4 +67,4 @@ "use strict"; | ||
} | ||
if (typeof a === 'object') { | ||
return typeof b === 'object' && objectEquals(a, b); | ||
if (a !== null && typeof a === 'object') { | ||
return b !== null && typeof b === 'object' && objectEquals(a, b); | ||
} | ||
@@ -148,5 +148,8 @@ return a === b; | ||
} | ||
else if ((0, definitions_1.isNonNullType)(field.type)) { | ||
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Field "${field.name}" of required type ${type} was not provided.`); | ||
else if (!(0, definitions_1.isNonNullType)(field.type)) { | ||
updated[field.name] = null; | ||
} | ||
else { | ||
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Required field "${field.name}" of type ${type} was not provided.`); | ||
} | ||
} | ||
@@ -172,5 +175,11 @@ else { | ||
if (value === undefined) { | ||
if (argument.defaultValue) { | ||
if (argument.defaultValue !== undefined) { | ||
return applyDefaultValues(argument.defaultValue, argument.type); | ||
} | ||
else if (!(0, definitions_1.isNonNullType)(argument.type)) { | ||
return null; | ||
} | ||
else { | ||
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Required argument "${argument.coordinate}" was not provided.`); | ||
} | ||
} | ||
@@ -177,0 +186,0 @@ return applyDefaultValues(value, argument.type); |
{ | ||
"name": "@apollo/federation-internals", | ||
"version": "2.10.0-alpha.1", | ||
"version": "2.10.0-alpha.2", | ||
"description": "Apollo Federation internal utilities", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -16,6 +16,5 @@ import { InputType, NonNullType, Schema, isListType, isNonNullType } from "./definitions" | ||
const supported = types(schema); | ||
if (!supported.some((t) => sameType(t, type))) { | ||
return { valid: false, supportedMsg: `type(s) ${supported.join(', ')}` }; | ||
} | ||
return { valid: true }; | ||
return supported.some((t) => sameType(t, type)) | ||
? { valid: true } | ||
: { valid: false, supportedMsg: `type(s) ${supported.join(', ')}` }; | ||
}; | ||
@@ -25,10 +24,36 @@ } | ||
function supportAnyNonNullArray(): TypeSupportValidator { | ||
return (_, type) => { | ||
if (!isNonNullType(type) || !isListType(type.ofType)) { | ||
return { valid: false, supportedMsg: 'non nullable list types of any type'}; | ||
} | ||
return { valid: true }; | ||
return (_, type) => isNonNullType(type) && isListType(type.ofType) | ||
? { valid: true } | ||
: { valid: false, supportedMsg: 'non nullable list types of any type' } | ||
} | ||
function supportAnyArray(): TypeSupportValidator { | ||
return (_, type) => isListType(type) || (isNonNullType(type) && isListType(type.ofType)) | ||
? { valid: true } | ||
: { valid: false, supportedMsg: 'list types of any type' }; | ||
} | ||
// NOTE: This function makes the assumption that for the directive argument | ||
// being merged, it is not "nullable with non-null default" in the supergraph | ||
// schema (this kind of type/default combo is confusing and should be avoided, | ||
// if possible). This assumption allows this function to replace null with | ||
// undefined, which makes for a cleaner supergraph schema. | ||
function mergeNullableValues<T>( | ||
mergeValues: (values: T[]) => T | ||
): (values: (T | null | undefined)[]) => T | undefined { | ||
return (values: (T | null | undefined)[]) => { | ||
const nonNullValues = values.filter((v) => v !== null && v !== undefined) as T[]; | ||
return nonNullValues.length > 0 | ||
? mergeValues(nonNullValues) | ||
: undefined; | ||
}; | ||
} | ||
function unionValues(values: any[]): any { | ||
return values.reduce((acc, next) => { | ||
const newValues = next.filter((v1: any) => !acc.some((v2: any) => valueEquals(v1, v2))); | ||
return acc.concat(newValues); | ||
}, []); | ||
} | ||
export const ARGUMENT_COMPOSITION_STRATEGIES = { | ||
@@ -38,3 +63,3 @@ MAX: { | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]), | ||
mergeValues: (values: any[]) => Math.max(...values), | ||
mergeValues: (values: number[]) => Math.max(...values), | ||
}, | ||
@@ -44,13 +69,22 @@ MIN: { | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]), | ||
mergeValues: (values: any[]) => Math.min(...values), | ||
mergeValues: (values: number[]) => Math.min(...values), | ||
}, | ||
SUM: { | ||
name: 'SUM', | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]), | ||
mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0), | ||
}, | ||
// NOTE: This doesn't work today because directive applications are de-duped | ||
// before being merged, we'd need to modify merge logic if we need this kind | ||
// of behavior. | ||
// SUM: { | ||
// name: 'SUM', | ||
// isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]), | ||
// mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0), | ||
// }, | ||
INTERSECTION: { | ||
name: 'INTERSECTION', | ||
isTypeSupported: supportAnyNonNullArray(), | ||
mergeValues: (values: any[]) => values.reduce((acc, val) => acc.filter((v1: any) => val.some((v2: any) => valueEquals(v1, v2))), values[0]), | ||
mergeValues: (values: any[]) => values.reduce((acc, next) => { | ||
if (acc === undefined) { | ||
return next; | ||
} else { | ||
return acc.filter((v1: any) => next.some((v2: any) => valueEquals(v1, v2))); | ||
} | ||
}, undefined) ?? [], | ||
}, | ||
@@ -60,45 +94,29 @@ UNION: { | ||
isTypeSupported: supportAnyNonNullArray(), | ||
mergeValues: (values: any[]) => | ||
values.reduce((acc, val) => { | ||
const newValues = val.filter((v1: any) => !acc.some((v2: any) => valueEquals(v1, v2))); | ||
return acc.concat(newValues); | ||
}, []), | ||
mergeValues: unionValues, | ||
}, | ||
NULLABLE_AND: { | ||
name: 'NULLABLE_AND', | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [schema.booleanType()]), | ||
mergeValues: (values: (boolean | null | undefined)[]) => values.reduce((acc, next) => { | ||
if (acc === null || acc === undefined) { | ||
return next; | ||
} else if (next === null || next === undefined) { | ||
return acc; | ||
} else { | ||
return acc && next; | ||
} | ||
}, undefined), | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [ | ||
schema.booleanType(), | ||
new NonNullType(schema.booleanType()) | ||
]), | ||
mergeValues: mergeNullableValues( | ||
(values: boolean[]) => values.every((v) => v) | ||
), | ||
}, | ||
NULLABLE_MAX: { | ||
name: 'NULLABLE_MAX', | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [schema.intType(), new NonNullType(schema.intType())]), | ||
mergeValues: (values: any[]) => values.reduce((a: any, b: any) => a !== undefined && b !== undefined ? Math.max(a, b) : a ?? b, undefined), | ||
isTypeSupported: supportFixedTypes((schema: Schema) => [ | ||
schema.intType(), | ||
new NonNullType(schema.intType()) | ||
]), | ||
mergeValues: mergeNullableValues( | ||
(values: number[]) => Math.max(...values) | ||
) | ||
}, | ||
NULLABLE_UNION: { | ||
name: 'NULLABLE_UNION', | ||
isTypeSupported: (_: Schema, type: InputType) => ({ valid: isListType(type) }), | ||
mergeValues: (values: any[]) => { | ||
if (values.every((v) => v === undefined)) { | ||
return undefined; | ||
} | ||
const combined = new Set(); | ||
for (const subgraphValues of values) { | ||
if (Array.isArray(subgraphValues)) { | ||
for (const value of subgraphValues) { | ||
combined.add(value); | ||
} | ||
} | ||
} | ||
return Array.from(combined); | ||
} | ||
isTypeSupported: supportAnyArray(), | ||
mergeValues: mergeNullableValues(unionValues), | ||
} | ||
} |
@@ -43,3 +43,3 @@ import { | ||
import { validateStringContainsBoolean } from "./utils"; | ||
import { CONTEXT_VERSIONS, ContextSpecDefinition, DirectiveDefinition, FeatureUrl, FederationDirectiveName, SchemaElement, errorCauses, isFederationDirectiveDefinedInSchema, printErrors } from "."; | ||
import { ContextSpecDefinition, CostSpecDefinition, SchemaElement, errorCauses, isFederationDirectiveDefinedInSchema, printErrors } from "."; | ||
@@ -198,3 +198,3 @@ function filteredTypes( | ||
export function extractSubgraphsFromSupergraph(supergraph: Schema, validateExtractedSubgraphs: boolean = true): [Subgraphs, Map<string, string>] { | ||
const [coreFeatures, joinSpec] = validateSupergraph(supergraph); | ||
const [coreFeatures, joinSpec, contextSpec, costSpec] = validateSupergraph(supergraph); | ||
const isFed1 = joinSpec.version.equals(new FeatureVersion(0, 1)); | ||
@@ -229,3 +229,2 @@ try { | ||
const types = filteredTypes(supergraph, joinSpec, coreFeatures.coreDefinition); | ||
const originalDirectiveNames = getApolloDirectiveNames(supergraph); | ||
const args: ExtractArguments = { | ||
@@ -235,4 +234,5 @@ supergraph, | ||
joinSpec, | ||
contextSpec, | ||
costSpec, | ||
filteredTypes: types, | ||
originalDirectiveNames, | ||
getSubgraph, | ||
@@ -300,4 +300,5 @@ getSubgraphEnumValue, | ||
joinSpec: JoinSpecDefinition, | ||
contextSpec: ContextSpecDefinition | undefined, | ||
costSpec: CostSpecDefinition | undefined, | ||
filteredTypes: NamedType[], | ||
originalDirectiveNames: Record<string, string>, | ||
getSubgraph: (application: Directive<any, { graph?: string }>) => Subgraph | undefined, | ||
@@ -360,3 +361,5 @@ getSubgraphEnumValue: (subgraphName: string) => string | ||
const subgraphType = subgraph.schema.addType(newNamedType(type.kind, type.name)); | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.originalDirectiveNames); | ||
if (args.costSpec) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec); | ||
} | ||
} | ||
@@ -410,13 +413,4 @@ break; | ||
} | ||
const coreFeatures = supergraph.coreFeatures; | ||
assert(coreFeatures, 'Should have core features'); | ||
const contextFeature = coreFeatures.getByIdentity(ContextSpecDefinition.identity); | ||
let supergraphContextDirective: DirectiveDefinition<{ name: string}> | undefined; | ||
if (contextFeature) { | ||
const contextSpec = CONTEXT_VERSIONS.find(contextFeature.url.version); | ||
assert(contextSpec, 'Should have context spec'); | ||
supergraphContextDirective = contextSpec.contextDirective(supergraph); | ||
} | ||
const supergraphContextDirective = args.contextSpec?.contextDirective(supergraph); | ||
if (supergraphContextDirective) { | ||
@@ -448,4 +442,2 @@ const contextApplications = type.appliedDirectivesOf(supergraphContextDirective); | ||
const originalDirectiveNames = args.originalDirectiveNames; | ||
for (const { type, subgraphsInfo } of info) { | ||
@@ -461,4 +453,6 @@ const implementsApplications = type.appliedDirectivesOf(implementsDirective); | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.originalDirectiveNames); | ||
if (args.costSpec) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec); | ||
} | ||
} | ||
@@ -472,3 +466,9 @@ | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
addSubgraphField({ field, type: subgraphType, subgraph, isShareable, originalDirectiveNames }); | ||
addSubgraphField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, | ||
isShareable, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -491,3 +491,9 @@ } else { | ||
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph)!; | ||
addSubgraphField({ field, type: subgraphType, subgraph, isShareable, joinFieldArgs, originalDirectiveNames }); | ||
addSubgraphField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, isShareable, | ||
joinFieldArgs, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -499,47 +505,4 @@ } | ||
/** | ||
* Builds a map of original name to new name for Apollo feature directives. This is | ||
* used to handle cases where a directive is renamed via an import statement. For | ||
* example, importing a directive with a custom name like | ||
* ```graphql | ||
* @link(url: "https://specs.apollo.dev/cost/v0.1", import: [{ name: "@cost", as: "@renamedCost" }]) | ||
* ``` | ||
* results in a map entry of `cost -> renamedCost` with the `@` prefix removed. | ||
* | ||
* If the directive is imported under its default name, that also results in an entry. So, | ||
* ```graphql | ||
* @link(url: "https://specs.apollo.dev/cost/v0.1", import: ["@cost"]) | ||
* ``` | ||
* results in a map entry of `cost -> cost`. This duals as a way to check if a directive | ||
* is included in the supergraph schema. | ||
* | ||
* **Important:** This map does _not_ include directives imported from identities other | ||
* than `specs.apollo.dev`. This helps us avoid extracting directives to subgraphs | ||
* when a custom directive's name conflicts with that of a default one. | ||
*/ | ||
function getApolloDirectiveNames(supergraph: Schema): Record<string, string> { | ||
const originalDirectiveNames: Record<string, string> = {}; | ||
for (const linkDirective of supergraph.schemaDefinition.appliedDirectivesOf("link")) { | ||
if (linkDirective.arguments().url && linkDirective.arguments().import) { | ||
const url = FeatureUrl.maybeParse(linkDirective.arguments().url); | ||
if (!url?.identity.includes("specs.apollo.dev")) { | ||
continue; | ||
} | ||
for (const importedDirective of linkDirective.arguments().import) { | ||
if (importedDirective.name && importedDirective.as) { | ||
originalDirectiveNames[importedDirective.name.replace('@', '')] = importedDirective.as.replace('@', ''); | ||
} else if (typeof importedDirective === 'string') { | ||
originalDirectiveNames[importedDirective.replace('@', '')] = importedDirective.replace('@', ''); | ||
} | ||
} | ||
} | ||
} | ||
return originalDirectiveNames; | ||
} | ||
function extractInputObjContent(args: ExtractArguments, info: TypeInfo<InputObjectType>[]) { | ||
const fieldDirective = args.joinSpec.fieldDirective(args.supergraph); | ||
const originalDirectiveNames = args.originalDirectiveNames; | ||
@@ -552,15 +515,26 @@ for (const { type, subgraphsInfo } of info) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
addSubgraphInputField({ field, type: subgraphType, subgraph, originalDirectiveNames }); | ||
addSubgraphInputField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
} else { | ||
for (const application of fieldApplications) { | ||
const args = application.arguments(); | ||
const joinFieldArgs = application.arguments(); | ||
// We use a @join__field with no graph to indicates when a field in the supergraph does not come | ||
// directly from any subgraph and there is thus nothing to do to "extract" it. | ||
if (!args.graph) { | ||
if (!joinFieldArgs.graph) { | ||
continue; | ||
} | ||
const { type: subgraphType, subgraph } = subgraphsInfo.get(args.graph)!; | ||
addSubgraphInputField({ field, type: subgraphType, subgraph, joinFieldArgs: args, originalDirectiveNames }); | ||
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph)!; | ||
addSubgraphInputField({ | ||
field, | ||
type: subgraphType, | ||
subgraph, | ||
joinFieldArgs, | ||
costSpec: args.costSpec | ||
}); | ||
} | ||
@@ -575,7 +549,8 @@ } | ||
const enumValueDirective = args.joinSpec.enumValueDirective(args.supergraph); | ||
const originalDirectiveNames = args.originalDirectiveNames; | ||
for (const { type, subgraphsInfo } of info) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, originalDirectiveNames); | ||
if (args.costSpec) { | ||
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) { | ||
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec); | ||
} | ||
} | ||
@@ -695,16 +670,16 @@ | ||
function propagateDemandControlDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>, subgraph: Subgraph, originalDirectiveNames?: Record<string, string>) { | ||
const costDirectiveName = originalDirectiveNames?.[FederationDirectiveName.COST]; | ||
if (costDirectiveName) { | ||
const costDirective = source.appliedDirectivesOf(costDirectiveName).pop(); | ||
if (costDirective) { | ||
dest.applyDirective(subgraph.metadata().costDirective().name, costDirective.arguments()); | ||
function propagateDemandControlDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>, subgraph: Subgraph, costSpec: CostSpecDefinition) { | ||
const costDirective = costSpec.costDirective(source.schema()); | ||
if (costDirective) { | ||
const application = source.appliedDirectivesOf(costDirective)[0]; | ||
if (application) { | ||
dest.applyDirective(subgraph.metadata().costDirective().name, application.arguments()); | ||
} | ||
} | ||
const listSizeDirectiveName = originalDirectiveNames?.[FederationDirectiveName.LIST_SIZE]; | ||
if (listSizeDirectiveName) { | ||
const listSizeDirective = source.appliedDirectivesOf(listSizeDirectiveName).pop(); | ||
if (listSizeDirective) { | ||
dest.applyDirective(subgraph.metadata().listSizeDirective().name, listSizeDirective.arguments()); | ||
const listSizeDirective = costSpec.listSizeDirective(source.schema()); | ||
if (listSizeDirective) { | ||
const application = source.appliedDirectivesOf(listSizeDirective)[0]; | ||
if (application) { | ||
dest.applyDirective(subgraph.metadata().listSizeDirective().name, application.arguments()); | ||
} | ||
@@ -725,3 +700,3 @@ } | ||
joinFieldArgs, | ||
originalDirectiveNames, | ||
costSpec, | ||
}: { | ||
@@ -733,3 +708,3 @@ field: FieldDefinition<ObjectType | InterfaceType>, | ||
joinFieldArgs?: JoinFieldDirectiveArguments, | ||
originalDirectiveNames?: Record<string, string>, | ||
costSpec?: CostSpecDefinition, | ||
}): FieldDefinition<ObjectType | InterfaceType> { | ||
@@ -743,3 +718,5 @@ const copiedFieldType = joinFieldArgs?.type | ||
const argDef = subgraphField.addArgument(arg.name, copyType(arg.type!, subgraph.schema, subgraph.name), arg.defaultValue); | ||
propagateDemandControlDirectives(arg, argDef, subgraph, originalDirectiveNames) | ||
if (costSpec) { | ||
propagateDemandControlDirectives(arg, argDef, subgraph, costSpec); | ||
} | ||
} | ||
@@ -790,3 +767,5 @@ if (joinFieldArgs?.requires) { | ||
propagateDemandControlDirectives(field, subgraphField, subgraph, originalDirectiveNames); | ||
if (costSpec) { | ||
propagateDemandControlDirectives(field, subgraphField, subgraph, costSpec); | ||
} | ||
@@ -801,3 +780,3 @@ return subgraphField; | ||
joinFieldArgs, | ||
originalDirectiveNames, | ||
costSpec, | ||
}: { | ||
@@ -808,3 +787,3 @@ field: InputFieldDefinition, | ||
joinFieldArgs?: JoinFieldDirectiveArguments, | ||
originalDirectiveNames?: Record<string, string> | ||
costSpec?: CostSpecDefinition, | ||
}): InputFieldDefinition { | ||
@@ -818,3 +797,5 @@ const copiedType = joinFieldArgs?.type | ||
propagateDemandControlDirectives(field, inputField, subgraph, originalDirectiveNames); | ||
if (costSpec) { | ||
propagateDemandControlDirectives(field, inputField, subgraph, costSpec); | ||
} | ||
@@ -821,0 +802,0 @@ return inputField; |
@@ -193,3 +193,4 @@ import { ASTNode, DirectiveLocation, GraphQLError, StringValueNode } from "graphql"; | ||
as?: string, | ||
for?: string | ||
for?: string, | ||
import: undefined, | ||
} | ||
@@ -202,3 +203,3 @@ | ||
for?: string, | ||
import?: (string | CoreImport)[] | ||
import?: (string | CoreImport)[], | ||
} | ||
@@ -539,3 +540,9 @@ | ||
applyFeatureToSchema(schema: Schema, feature: FeatureDefinition, as?: string, purpose?: CorePurpose): GraphQLError[] { | ||
applyFeatureToSchema( | ||
schema: Schema, | ||
feature: FeatureDefinition, | ||
as?: string, | ||
purpose?: CorePurpose, | ||
imports?: CoreImport[], | ||
): GraphQLError[] { | ||
const coreDirective = this.coreDirective(schema); | ||
@@ -545,27 +552,21 @@ const args = { | ||
as, | ||
} as CoreDirectiveArgs; | ||
if (this.supportPurposes() && purpose) { | ||
args.for = purpose; | ||
} as CoreOrLinkDirectiveArgs; | ||
if (purpose) { | ||
if (this.supportPurposes()) { | ||
args.for = purpose; | ||
} else { | ||
return [new GraphQLError( | ||
`Cannot apply feature ${feature} with purpose since the schema's @core/@link version does not support it.` | ||
)]; | ||
} | ||
} | ||
schema.schemaDefinition.applyDirective(coreDirective, args); | ||
return feature.addElementsToSchema(schema); | ||
} | ||
applyFeatureAsLink(schema: Schema, feature: FeatureDefinition, purpose?: CorePurpose, imports?: CoreImport[]): GraphQLError[] { | ||
const existing = schema.schemaDefinition.appliedDirectivesOf(linkDirectiveDefaultName).find((link) => link.arguments().url === feature.toString()); | ||
if (existing) { | ||
existing.remove(); | ||
if (imports && imports.length > 0) { | ||
if (this.supportImport()) { | ||
args.import = imports.map(i => i.as ? i : i.name); | ||
} else { | ||
return [new GraphQLError( | ||
`Cannot apply feature ${feature} with imports since the schema's @core/@link version does not support it.` | ||
)]; | ||
} | ||
} | ||
const coreDirective = this.coreDirective(schema); | ||
const args: LinkDirectiveArgs = { | ||
url: feature.toString(), | ||
import: (existing?.arguments().import ?? []).concat(imports?.map((i) => i.as ? { name: `@${i.name}`, as: `@${i.as}` } : `@${i.name}`)), | ||
feature: undefined, | ||
}; | ||
if (this.supportPurposes() && purpose) { | ||
args.for = purpose; | ||
} | ||
schema.schemaDefinition.applyDirective(coreDirective, args); | ||
@@ -572,0 +573,0 @@ return feature.addElementsToSchema(schema); |
import { DirectiveLocation } from 'graphql'; | ||
import { createDirectiveSpecification } from '../directiveAndTypeSpecification'; | ||
import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from './coreSpec'; | ||
import { ListType, NonNullType } from '../definitions'; | ||
import { DirectiveDefinition, ListType, NonNullType, Schema } from '../definitions'; | ||
import { registerKnownFeature } from '../knownCoreFeatures'; | ||
@@ -44,2 +44,10 @@ import { ARGUMENT_COMPOSITION_STRATEGIES } from '../argumentCompositionStrategies'; | ||
} | ||
costDirective(schema: Schema): DirectiveDefinition<CostDirectiveArguments> | undefined { | ||
return this.directive(schema, 'cost'); | ||
} | ||
listSizeDirective(schema: Schema): DirectiveDefinition<ListSizeDirectiveArguments> | undefined { | ||
return this.directive(schema, 'listSize'); | ||
} | ||
} | ||
@@ -46,0 +54,0 @@ |
import { DocumentNode, GraphQLError } from "graphql"; | ||
import { CoreFeatures, Schema, sourceASTs } from "./definitions"; | ||
import { ErrCoreCheckFailed, FeatureUrl, FeatureVersion } from "./specs/coreSpec"; | ||
import { CoreFeatures, Schema, sourceASTs } from "./definitions"; | ||
import { joinIdentity, JoinSpecDefinition, JOIN_VERSIONS } from "./specs/joinSpec"; | ||
import { CONTEXT_VERSIONS, ContextSpecDefinition } from "./specs/contextSpec"; | ||
import { COST_VERSIONS, costIdentity, CostSpecDefinition } from "./specs/costSpec"; | ||
import { buildSchema, buildSchemaFromAST } from "./buildSchema"; | ||
@@ -85,3 +87,8 @@ import { extractSubgraphsNamesAndUrlsFromSupergraph, extractSubgraphsFromSupergraph } from "./extractSubgraphsFromSupergraph"; | ||
export function validateSupergraph(supergraph: Schema): [CoreFeatures, JoinSpecDefinition] { | ||
export function validateSupergraph(supergraph: Schema): [ | ||
CoreFeatures, | ||
JoinSpecDefinition, | ||
ContextSpecDefinition | undefined, | ||
CostSpecDefinition | undefined, | ||
] { | ||
const coreFeatures = supergraph.coreFeatures; | ||
@@ -91,2 +98,3 @@ if (!coreFeatures) { | ||
} | ||
const joinFeature = coreFeatures.getByIdentity(joinIdentity); | ||
@@ -101,3 +109,23 @@ if (!joinFeature) { | ||
} | ||
return [coreFeatures, joinSpec]; | ||
const contextFeature = coreFeatures.getByIdentity(ContextSpecDefinition.identity); | ||
let contextSpec = undefined; | ||
if (contextFeature) { | ||
contextSpec = CONTEXT_VERSIONS.find(contextFeature.url.version); | ||
if (!contextSpec) { | ||
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err( | ||
`Invalid supergraph: uses unsupported context spec version ${contextFeature.url.version} (supported versions: ${CONTEXT_VERSIONS.versions().join(', ')})`); | ||
} | ||
} | ||
const costFeature = coreFeatures.getByIdentity(costIdentity); | ||
let costSpec = undefined; | ||
if (costFeature) { | ||
costSpec = COST_VERSIONS.find(costFeature.url.version); | ||
if (!costSpec) { | ||
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err( | ||
`Invalid supergraph: uses unsupported cost spec version ${costFeature.url.version} (supported versions: ${COST_VERSIONS.versions().join(', ')})`); | ||
} | ||
} | ||
return [coreFeatures, joinSpec, contextSpec, costSpec]; | ||
} | ||
@@ -104,0 +132,0 @@ |
@@ -138,4 +138,6 @@ import { | ||
} | ||
if (typeof a === 'object') { | ||
return typeof b === 'object' && objectEquals(a, b); | ||
// Note that typeof null === 'object', so we have to manually rule that out | ||
// here. | ||
if (a !== null && typeof a === 'object') { | ||
return b !== null && typeof b === 'object' && objectEquals(a, b); | ||
} | ||
@@ -228,4 +230,6 @@ return a === b; | ||
updated[field.name] = applyDefaultValues(field.defaultValue, field.type); | ||
} else if (isNonNullType(field.type)) { | ||
throw ERRORS.INVALID_GRAPHQL.err(`Field "${field.name}" of required type ${type} was not provided.`); | ||
} else if (!isNonNullType(field.type)) { | ||
updated[field.name] = null; | ||
} else { | ||
throw ERRORS.INVALID_GRAPHQL.err(`Required field "${field.name}" of type ${type} was not provided.`); | ||
} | ||
@@ -254,4 +258,8 @@ } else { | ||
if (value === undefined) { | ||
if (argument.defaultValue) { | ||
if (argument.defaultValue !== undefined) { | ||
return applyDefaultValues(argument.defaultValue, argument.type); | ||
} else if (!isNonNullType(argument.type)) { | ||
return null; | ||
} else { | ||
throw ERRORS.INVALID_GRAPHQL.err(`Required argument "${argument.coordinate}" was not provided.`); | ||
} | ||
@@ -258,0 +266,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2196954
34924