Skip to content

Commit

Permalink
Validate directives and allow to overwrite specified directives
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela committed Dec 20, 2023
1 parent 707f78f commit 88a3fd0
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/healthy-hornets-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@theguild/federation-composition': patch
---

fix: allow to overwrite specified directives
5 changes: 5 additions & 0 deletions .changeset/rude-boxes-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@theguild/federation-composition': minor
---

Validate directive definitions
34 changes: 34 additions & 0 deletions __tests__/subgraph/errors/INVALID_GRAPHQL.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,4 +439,38 @@ testVersions((api, version) => {
}),
);
});

test('directive overwritten specified directive (like @deprecated) on wrong location', () => {
assertCompositionFailure(
api.composeServices([
{
name: 'mono',
typeDefs: graphql`
schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/federation/${version}", import: ["@shareable"]) {
query: Query
}
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION
directive @shareable repeatable on OBJECT | FIELD_DEFINITION
type Query @shareable {
view(input: Input): View!
}
type View @shareable {
user: String
post: String!
}
input Input {
input: String @deprecated(reason: "impossibru")
}
`,
},
]),
);
});
});
11 changes: 7 additions & 4 deletions src/subgraph/validation/rules/known-directives-rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ export function KnownDirectivesRule(context: {
}

for (const specifiedDirective of specifiedDirectives) {
locationsMap.set(
specifiedDirective.name,
new Set(specifiedDirective.locations.map(loc => String(loc))),
);
// If the directive is already defined by the schema, leave it be.
if (!locationsMap.has(specifiedDirective.name)) {
locationsMap.set(
specifiedDirective.name,
new Set(specifiedDirective.locations.map(loc => String(loc))),
);
}
}

return {
Expand Down
36 changes: 35 additions & 1 deletion src/subgraph/validation/validate-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '../../utils/state.js';
import {
Argument,
Directive,
EnumType,
InputField,
InputObjectType,
Expand Down Expand Up @@ -46,7 +47,7 @@ export function validateSubgraphState(state: SubgraphState) {
}

validateRootTypes(state, reportError);
// validateDirectives(state, reportError); + default values there
validateDirectives(state, reportError);
validateTypes(state, reportError);

return errors;
Expand Down Expand Up @@ -102,6 +103,35 @@ function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}

function validateDirectives(state: SubgraphState, reportError: ReportErrorFn): void {
for (const directive of state.types.values()) {
if (isDirective(directive)) {
// Ensure they are named correctly.
validateName(reportError, directive.name);

// Ensure the arguments are valid.
for (const [argName, arg] of directive.args) {
// Ensure they are named correctly.
validateName(reportError, argName);

// Ensure the type is an input type.
const argInputTypeName = stripTypeModifiers(arg.type);

if (!isInputType(state, argInputTypeName)) {
reportError(
`The type of @${directive.name}(${arg.name}:) must be Input Type ` +
`but got: ${arg.type}.`,
);
}

if (isRequiredArgument(arg) && arg.deprecated?.deprecated === true) {
reportError(`Required argument @${directive.name}(${arg.name}:) cannot be deprecated.`);
}
}
}
}
}

function validateTypes(state: SubgraphState, reportError: ReportErrorFn): void {
const validateInputObjectCircularRefs = createInputObjectCircularRefsValidator(
state,
Expand Down Expand Up @@ -784,3 +814,7 @@ export function isInterfaceType(type: SubgraphType): type is InterfaceType {
export function isUnionType(type: SubgraphType): type is UnionType {
return type.kind === TypeKind.UNION;
}

export function isDirective(type: SubgraphType): type is Directive {
return type.kind === TypeKind.DIRECTIVE;
}

0 comments on commit 88a3fd0

Please sign in to comment.