Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@apollo/federation

Package Overview
Dependencies
Maintainers
1
Versions
144
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@apollo/federation - npm Package Compare versions

Comparing version 0.32.0 to 0.33.0

2

CHANGELOG.md

@@ -7,3 +7,3 @@ # CHANGELOG for `@apollo/federation`

- _Nothing yet! Stay tuned!_
- Add flexibility for @tag directive definition validation in subgraphs. @tag definitions are now permitted to be a subset of the spec's definition. This means that within the definition, `repeatable` is optional as are each of the directive locations. [PR #1022](https://github.com/apollographql/federation/pull/1022)

@@ -10,0 +10,0 @@ ## v0.32.0

@@ -1,2 +0,2 @@

import { StringValueNode, NameNode, DocumentNode, DirectiveNode, GraphQLNamedType, GraphQLError, GraphQLSchema, GraphQLObjectType, GraphQLField, SelectionNode, TypeDefinitionNode, TypeExtensionNode, ASTNode, DirectiveDefinitionNode, GraphQLDirective, OperationTypeNode } from 'graphql';
import { StringValueNode, NameNode, DocumentNode, DirectiveNode, GraphQLNamedType, GraphQLError, GraphQLSchema, GraphQLObjectType, GraphQLField, SelectionNode, TypeDefinitionNode, TypeExtensionNode, ASTNode, DirectiveDefinitionNode, GraphQLDirective, OperationTypeNode, NonNullTypeNode, NamedTypeNode } from 'graphql';
import { ExternalFieldDefinition, DefaultRootOperationTypeName, Maybe, FederationType, FederationDirective, FederationField, ServiceDefinition } from './types';

@@ -6,2 +6,4 @@ import { ASTNodeWithDirectives } from '../directives';

export declare function isDirectiveDefinitionNode(node: any): node is DirectiveDefinitionNode;
export declare function isNonNullTypeNode(node: any): node is NonNullTypeNode;
export declare function isNamedTypeNode(node: any): node is NamedTypeNode;
export declare function mapFieldNamesToServiceName<Node extends {

@@ -17,3 +19,2 @@ name: NameNode;

};
export declare function stripDescriptions(astNode: ASTNode): any;
export declare function stripTypeSystemDirectivesFromTypeDefs(typeDefs: DocumentNode): DocumentNode;

@@ -20,0 +21,0 @@ export declare function parseSelections(source: string): ReadonlyArray<SelectionNode>;

@@ -22,3 +22,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.getFederationMetadata = exports.assertCompositionFailure = exports.assertCompositionSuccess = exports.compositionHasErrors = exports.defaultRootOperationNameLookup = exports.reservedRootFields = exports.isFederationDirective = exports.isApolloTypeSystemDirective = exports.executableDirectiveLocations = exports.defKindToExtKind = exports.findTypeNodeInServiceList = exports.typeNodesAreEquivalent = exports.diffTypeNodes = exports.isTypeNodeAnEntity = exports.selectionIncludesField = exports.findFieldsThatReturnType = exports.findTypesContainingFieldWithReturnType = exports.errorWithCode = exports.logDirective = exports.logServiceAndType = exports.hasMatchingFieldInDirectives = exports.parseSelections = exports.stripTypeSystemDirectivesFromTypeDefs = exports.stripDescriptions = exports.stripExternalFieldsFromTypeDefs = exports.findSelectionSetOnNode = exports.printFieldSet = exports.findDirectivesOnNode = exports.mapFieldNamesToServiceName = exports.isDirectiveDefinitionNode = exports.isStringValueNode = void 0;
exports.getFederationMetadata = exports.assertCompositionFailure = exports.assertCompositionSuccess = exports.compositionHasErrors = exports.defaultRootOperationNameLookup = exports.reservedRootFields = exports.isFederationDirective = exports.isApolloTypeSystemDirective = exports.executableDirectiveLocations = exports.defKindToExtKind = exports.findTypeNodeInServiceList = exports.typeNodesAreEquivalent = exports.diffTypeNodes = exports.isTypeNodeAnEntity = exports.selectionIncludesField = exports.findFieldsThatReturnType = exports.findTypesContainingFieldWithReturnType = exports.errorWithCode = exports.logDirective = exports.logServiceAndType = exports.hasMatchingFieldInDirectives = exports.parseSelections = exports.stripTypeSystemDirectivesFromTypeDefs = exports.stripExternalFieldsFromTypeDefs = exports.findSelectionSetOnNode = exports.printFieldSet = exports.findDirectivesOnNode = exports.mapFieldNamesToServiceName = exports.isNamedTypeNode = exports.isNonNullTypeNode = exports.isDirectiveDefinitionNode = exports.isStringValueNode = void 0;
const graphql_1 = require("graphql");

@@ -35,2 +35,10 @@ const directives_1 = __importStar(require("../directives"));

exports.isDirectiveDefinitionNode = isDirectiveDefinitionNode;
function isNonNullTypeNode(node) {
return node.kind === graphql_1.Kind.NON_NULL_TYPE;
}
exports.isNonNullTypeNode = isNonNullTypeNode;
function isNamedTypeNode(node) {
return node.kind === graphql_1.Kind.NAMED_TYPE;
}
exports.isNamedTypeNode = isNamedTypeNode;
function mapFieldNamesToServiceName(fields, serviceName) {

@@ -72,10 +80,2 @@ return fields.reduce((prev, next) => {

exports.stripExternalFieldsFromTypeDefs = stripExternalFieldsFromTypeDefs;
function stripDescriptions(astNode) {
return (0, graphql_1.visit)(astNode, {
enter(node) {
return 'description' in node ? { ...node, description: undefined } : node;
},
});
}
exports.stripDescriptions = stripDescriptions;
function stripTypeSystemDirectivesFromTypeDefs(typeDefs) {

@@ -82,0 +82,0 @@ const typeDefsWithoutTypeSystemDirectives = (0, graphql_1.visit)(typeDefs, {

@@ -27,9 +27,9 @@ "use strict";

});
if (tagDirectiveDefinition) {
const printedTagDefinition = 'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION';
if ((0, graphql_1.print)(normalizeDirectiveDefinitionNode(tagDirectiveDefinition)) !==
printedTagDefinition) {
errors.push((0, utils_1.errorWithCode)('TAG_DIRECTIVE_DEFINITION_INVALID', (0, utils_1.logDirective)('tag') +
`Found @tag definition in service ${serviceName}, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions matches the following:\n\t${printedTagDefinition}`, tagDirectiveDefinition));
}
const printedTagDefinition = 'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION';
const parsedTagDefinition = (0, graphql_1.parse)(printedTagDefinition)
.definitions[0];
if (tagDirectiveDefinition &&
!(0, directives_1.directiveDefinitionsAreCompatible)(parsedTagDefinition, tagDirectiveDefinition)) {
errors.push((0, utils_1.errorWithCode)('TAG_DIRECTIVE_DEFINITION_INVALID', (0, utils_1.logDirective)('tag') +
`Found @tag definition in service ${serviceName}, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions is compatible with the following:\n\t${printedTagDefinition}`, tagDirectiveDefinition));
}

@@ -39,7 +39,2 @@ return errors.filter(({ message }) => !errorsMessagesToFilter.some((keyWord) => message === keyWord));

exports.tagDirective = tagDirective;
function normalizeDirectiveDefinitionNode(node) {
node = (0, utils_1.stripDescriptions)(node);
node.locations.sort((a, b) => a.value.localeCompare(b.value));
return node;
}
//# sourceMappingURL=tagDirective.js.map

@@ -1,2 +0,2 @@

import { GraphQLDirective, GraphQLNamedType, GraphQLInputObjectType, DirectiveNode, GraphQLField, FieldDefinitionNode, InputValueDefinitionNode, SchemaDefinitionNode, TypeSystemExtensionNode, TypeDefinitionNode, ExecutableDefinitionNode } from 'graphql';
import { GraphQLDirective, GraphQLNamedType, GraphQLInputObjectType, DirectiveNode, GraphQLField, FieldDefinitionNode, InputValueDefinitionNode, SchemaDefinitionNode, TypeSystemExtensionNode, TypeDefinitionNode, ExecutableDefinitionNode, DirectiveDefinitionNode } from 'graphql';
export declare const KeyDirective: GraphQLDirective;

@@ -16,2 +16,3 @@ export declare const ExtendsDirective: GraphQLDirective;

export declare function typeIncludesDirective(type: GraphQLNamedType, directiveName: string): boolean;
export declare function directiveDefinitionsAreCompatible(baseDefinition: DirectiveDefinitionNode, toCompare: DirectiveDefinitionNode): boolean;
//# sourceMappingURL=directives.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.typeIncludesDirective = exports.gatherDirectives = exports.otherKnownDirectiveDefinitions = exports.federationDirectives = exports.TagDirective = exports.ProvidesDirective = exports.RequiresDirective = exports.ExternalDirective = exports.ExtendsDirective = exports.KeyDirective = void 0;
exports.directiveDefinitionsAreCompatible = exports.typeIncludesDirective = exports.gatherDirectives = exports.otherKnownDirectiveDefinitions = exports.federationDirectives = exports.TagDirective = exports.ProvidesDirective = exports.RequiresDirective = exports.ExternalDirective = exports.ExtendsDirective = exports.KeyDirective = void 0;
const graphql_1 = require("graphql");

@@ -89,5 +89,33 @@ exports.KeyDirective = new graphql_1.GraphQLDirective({

const directives = gatherDirectives(type);
return directives.some(directive => directive.name.value === directiveName);
return directives.some((directive) => directive.name.value === directiveName);
}
exports.typeIncludesDirective = typeIncludesDirective;
function directiveDefinitionsAreCompatible(baseDefinition, toCompare) {
var _a, _b, _c, _d;
if (baseDefinition.name.value !== toCompare.name.value)
return false;
if (((_a = baseDefinition.arguments) === null || _a === void 0 ? void 0 : _a.length) !== ((_b = toCompare.arguments) === null || _b === void 0 ? void 0 : _b.length)) {
return false;
}
for (const arg of (_c = baseDefinition.arguments) !== null && _c !== void 0 ? _c : []) {
const toCompareArg = (_d = toCompare.arguments) === null || _d === void 0 ? void 0 : _d.find((a) => a.name.value === arg.name.value);
if (!toCompareArg)
return false;
if ((0, graphql_1.print)(stripDescriptions(arg)) !== (0, graphql_1.print)(stripDescriptions(toCompareArg))) {
return false;
}
}
if (toCompare.locations.some((location) => !baseDefinition.locations.find((baseLocation) => baseLocation.value === location.value))) {
return false;
}
return true;
}
exports.directiveDefinitionsAreCompatible = directiveDefinitionsAreCompatible;
function stripDescriptions(astNode) {
return (0, graphql_1.visit)(astNode, {
enter(node) {
return 'description' in node ? { ...node, description: undefined } : node;
},
});
}
//# sourceMappingURL=directives.js.map
{
"name": "@apollo/federation",
"version": "0.32.0",
"version": "0.33.0",
"description": "Apollo Federation Utilities",

@@ -33,3 +33,3 @@ "main": "dist/index.js",

},
"gitHead": "ef410068ac1925c370e79c3d1b94e4217e6e21a6"
"gitHead": "1074f2af34afd1a59ad59fbc7965d926f8b42dbc"
}

@@ -35,2 +35,4 @@ import {

stripIgnoredCharacters,
NonNullTypeNode,
NamedTypeNode,
} from 'graphql';

@@ -60,2 +62,10 @@ import {

export function isNonNullTypeNode(node: any): node is NonNullTypeNode {
return node.kind === Kind.NON_NULL_TYPE;
}
export function isNamedTypeNode(node: any): node is NamedTypeNode {
return node.kind === Kind.NAMED_TYPE;
}
// Create a map of { fieldName: serviceName } for each field.

@@ -144,10 +154,2 @@ export function mapFieldNamesToServiceName<Node extends { name: NameNode }>(

export function stripDescriptions(astNode: ASTNode) {
return visit(astNode, {
enter(node) {
return 'description' in node ? { ...node, description: undefined } : node;
},
});
}
export function stripTypeSystemDirectivesFromTypeDefs(typeDefs: DocumentNode) {

@@ -154,0 +156,0 @@ const typeDefsWithoutTypeSystemDirectives = visit(typeDefs, {

@@ -68,2 +68,17 @@ import { tagDirective } from '..';

});
it('permits alternative, compatible @tag definitions', () => {
const serviceA = {
typeDefs: gql`
directive @tag(name: String!) on FIELD_DEFINITION | INTERFACE
type Query {
hello: String @tag(name: "hello")
}
`,
name: 'serviceA',
};
const errors = tagDirective(serviceA);
expect(errors).toHaveLength(0);
});
});

@@ -103,31 +118,126 @@

it('when @tag usage and definition exist, but definition is incorrect', () => {
const serviceA = {
typeDefs: gql`
directive @tag(name: String!) on FIELD_DEFINITION
describe('incompatible definition', () => {
it('locations incompatible', () => {
const serviceA = {
typeDefs: gql`
directive @tag(name: String!) on FIELD_DEFINITION | SCHEMA
type Query {
hello: String @tag(name: "hello")
}
`,
name: 'serviceA',
};
type Query {
hello: String @tag(name: "hello")
}
`,
name: 'serviceA',
};
const errors = tagDirective(serviceA);
const errors = tagDirective(serviceA);
expect(errors).toMatchInlineSnapshot(`
Array [
Object {
"code": "TAG_DIRECTIVE_DEFINITION_INVALID",
"locations": Array [
Object {
"column": 1,
"line": 2,
},
],
"message": "[@tag] -> Found @tag definition in service serviceA, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions matches the following:
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION",
},
]
`);
expect(errors).toMatchInlineSnapshot(`
Array [
Object {
"code": "TAG_DIRECTIVE_DEFINITION_INVALID",
"locations": Array [
Object {
"column": 1,
"line": 2,
},
],
"message": "[@tag] -> Found @tag definition in service serviceA, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions is compatible with the following:
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION",
},
]
`);
});
it('name arg missing', () => {
const serviceA = {
typeDefs: gql`
directive @tag on FIELD_DEFINITION
type Query {
hello: String @tag
}
`,
name: 'serviceA',
};
const errors = tagDirective(serviceA);
expect(errors).toMatchInlineSnapshot(`
Array [
Object {
"code": "TAG_DIRECTIVE_DEFINITION_INVALID",
"locations": Array [
Object {
"column": 1,
"line": 2,
},
],
"message": "[@tag] -> Found @tag definition in service serviceA, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions is compatible with the following:
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION",
},
]
`);
});
it('name arg incompatible', () => {
const serviceA = {
typeDefs: gql`
directive @tag(name: String) on FIELD_DEFINITION
type Query {
hello: String @tag(name: "hello")
}
`,
name: 'serviceA',
};
const errors = tagDirective(serviceA);
expect(errors).toMatchInlineSnapshot(`
Array [
Object {
"code": "TAG_DIRECTIVE_DEFINITION_INVALID",
"locations": Array [
Object {
"column": 1,
"line": 2,
},
],
"message": "[@tag] -> Found @tag definition in service serviceA, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions is compatible with the following:
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION",
},
]
`);
});
it('additional args', () => {
const serviceA = {
typeDefs: gql`
directive @tag(name: String!, additional: String) on FIELD_DEFINITION
type Query {
hello: String @tag(name: "hello")
}
`,
name: 'serviceA',
};
const errors = tagDirective(serviceA);
expect(errors).toMatchInlineSnapshot(`
Array [
Object {
"code": "TAG_DIRECTIVE_DEFINITION_INVALID",
"locations": Array [
Object {
"column": 1,
"line": 2,
},
],
"message": "[@tag] -> Found @tag definition in service serviceA, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions is compatible with the following:
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION",
},
]
`);
});
});

@@ -134,0 +244,0 @@

@@ -1,3 +0,6 @@

import { federationDirectives } from '../../../directives';
import {
directiveDefinitionsAreCompatible,
federationDirectives,
} from '../../../directives';
import {
DirectiveDefinitionNode,

@@ -7,4 +10,3 @@ KnownDirectivesRule,

BREAK,
print,
NameNode,
parse,
} from 'graphql';

@@ -15,3 +17,3 @@ import { KnownArgumentNamesOnDirectivesRule } from 'graphql/validation/rules/KnownArgumentNamesRule';

import { ServiceDefinition } from '../../types';
import { errorWithCode, logDirective, stripDescriptions } from '../../utils';
import { errorWithCode, logDirective } from '../../utils';

@@ -52,20 +54,22 @@ // Likely brittle but also will be very obvious if this breaks. Based on the

// Ensure the tag directive definition is correct
if (tagDirectiveDefinition) {
const printedTagDefinition =
'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION';
const printedTagDefinition =
'directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION';
const parsedTagDefinition = parse(printedTagDefinition)
.definitions[0] as DirectiveDefinitionNode;
if (
print(normalizeDirectiveDefinitionNode(tagDirectiveDefinition)) !==
printedTagDefinition
) {
errors.push(
errorWithCode(
'TAG_DIRECTIVE_DEFINITION_INVALID',
logDirective('tag') +
`Found @tag definition in service ${serviceName}, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions matches the following:\n\t${printedTagDefinition}`,
tagDirectiveDefinition,
),
);
}
if (
tagDirectiveDefinition &&
!directiveDefinitionsAreCompatible(
parsedTagDefinition,
tagDirectiveDefinition,
)
) {
errors.push(
errorWithCode(
'TAG_DIRECTIVE_DEFINITION_INVALID',
logDirective('tag') +
`Found @tag definition in service ${serviceName}, but the @tag directive definition was invalid. Please ensure the directive definition in your schema's type definitions is compatible with the following:\n\t${printedTagDefinition}`,
tagDirectiveDefinition,
),
);
}

@@ -78,9 +82,1 @@

};
function normalizeDirectiveDefinitionNode(node: DirectiveDefinitionNode) {
// Remove descriptions from the AST
node = stripDescriptions(node);
// Sort locations alphabetically
(node.locations as NameNode[]).sort((a, b) => a.value.localeCompare(b.value));
return node;
}

@@ -17,2 +17,6 @@ import {

ExecutableDefinitionNode,
DirectiveDefinitionNode,
print,
ASTNode,
visit,
} from 'graphql';

@@ -139,3 +143,46 @@

const directives = gatherDirectives(type as GraphQLNamedTypeWithDirectives);
return directives.some(directive => directive.name.value === directiveName);
return directives.some((directive) => directive.name.value === directiveName);
}
export function directiveDefinitionsAreCompatible(
baseDefinition: DirectiveDefinitionNode,
toCompare: DirectiveDefinitionNode,
) {
if (baseDefinition.name.value !== toCompare.name.value) return false;
// arguments must be equal in length
if (baseDefinition.arguments?.length !== toCompare.arguments?.length) {
return false;
}
// arguments must be equal in type
for (const arg of baseDefinition.arguments ?? []) {
const toCompareArg = toCompare.arguments?.find(
(a) => a.name.value === arg.name.value,
);
if (!toCompareArg) return false;
if (
print(stripDescriptions(arg)) !== print(stripDescriptions(toCompareArg))
) {
return false;
}
}
// toCompare's locations must exist in baseDefinition's locations
if (
toCompare.locations.some(
(location) =>
!baseDefinition.locations.find(
(baseLocation) => baseLocation.value === location.value,
),
)
) {
return false;
}
return true;
}
function stripDescriptions(astNode: ASTNode) {
return visit(astNode, {
enter(node) {
return 'description' in node ? { ...node, description: undefined } : node;
},
});
}

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc