Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/@graphql-tools_import-7229-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-tools/import": patch
---
dependencies updates:
- Added dependency [`@theguild/federation-composition@^0.18.3` ↗︎](https://www.npmjs.com/package/@theguild/federation-composition/v/0.18.3) (to `dependencies`)
5 changes: 5 additions & 0 deletions .changeset/strong-melons-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-tools/import': patch
---

Adjust dependency and definition logic to include federated links
1 change: 1 addition & 0 deletions packages/import/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
},
"dependencies": {
"@graphql-tools/utils": "^10.8.6",
"@theguild/federation-composition": "^0.18.3",
"resolve-from": "5.0.0",
"tslib": "^2.4.0"
},
Expand Down
72 changes: 67 additions & 5 deletions packages/import/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,21 @@ import {
} from 'graphql';
import resolveFrom from 'resolve-from';
import { parseGraphQLSDL } from '@graphql-tools/utils';
import { extractLinkImplementations } from '@theguild/federation-composition';

const builtinTypes = ['String', 'Float', 'Int', 'Boolean', 'ID', 'Upload'];

const federationV1Directives = ['key', 'provides', 'requires', 'external'];
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed link from this list per our discussion


const builtinDirectives = [
'deprecated',
'skip',
'include',
'cacheControl',
'key',
'external',
'requires',
'provides',
'connection',
'client',
'specifiedBy',
'link',
...federationV1Directives,
];

const IMPORT_FROM_REGEX = /^import\s+(\*|(.*))\s+from\s+('|")(.*)('|");?$/;
Expand Down Expand Up @@ -264,6 +263,66 @@ export function extractDependencies(
};
}

function importFederatedSchemaLinks(
definition: DefinitionNode,
definitionsByName: Map<string, Set<DefinitionNode>>,
) {
const addDefinition = (name: string) => {
definitionsByName.set(name.replace(/^@/g, ''), new Set());
};

// extract links from this definition
const { links, matchesImplementation, resolveImportName } = extractLinkImplementations({
kind: Kind.DOCUMENT,
definitions: [definition],
});

if (links.length) {
const federationUrl = 'https://specs.apollo.dev/federation';
const linkUrl = 'https://specs.apollo.dev/link';

/**
* Official Federated imports are special because they can be referenced without specifyin the import.
* To handle this case, we must prepare a list of all the possible valid usages to check against.
* Note that this versioning is not technically correct, since some definitions are after v2.0.
* But this is enough information to be comfortable not blocking the imports at this phase. It's
* the job of the composer to validate the versions.
* */
if (matchesImplementation(federationUrl, 'v2.0')) {
const federationImports = [
'@composeDirective',
'@extends',
'@external',
'@inaccessible',
'@interfaceObject',
'@key',
'@override',
'@provides',
'@requires',
'@shareable',
'@tag',
'FieldSet',
];
for (const i of federationImports) {
addDefinition(resolveImportName(federationUrl, i));
}
}
if (matchesImplementation(linkUrl, 'v1.0')) {
const linkImports = ['Purpose', 'Import', '@link'];
for (const i of linkImports) {
addDefinition(resolveImportName(linkUrl, i));
}
}

const imported = links
.filter(l => ![linkUrl, federationUrl].includes(l.identity))
.flatMap(l => l.imports.map(i => i.as ?? i.name));
for (const namedImport of imported) {
addDefinition(namedImport);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall that, thankfully, for whatever reason, non-federation packages dont support the namespaced import syntax.

}
}
}

function visitDefinition(
definition: DefinitionNode,
definitionsByName: Map<string, Set<DefinitionNode>>,
Expand All @@ -288,6 +347,7 @@ function visitDefinition(
dependencySet = new Set();
dependenciesByDefinitionName.set(definitionName, dependencySet);
}

switch (definition.kind) {
case Kind.OPERATION_DEFINITION:
visitOperationDefinitionNode(definition, dependencySet);
Expand Down Expand Up @@ -317,9 +377,11 @@ function visitDefinition(
visitScalarDefinitionNode(definition, dependencySet);
break;
case Kind.SCHEMA_DEFINITION:
importFederatedSchemaLinks(definition, definitionsByName);
visitSchemaDefinitionNode(definition, dependencySet);
break;
case Kind.SCHEMA_EXTENSION:
importFederatedSchemaLinks(definition, definitionsByName);
visitSchemaExtensionDefinitionNode(definition, dependencySet);
break;
case Kind.OBJECT_TYPE_EXTENSION:
Expand Down
6 changes: 5 additions & 1 deletion packages/import/tests/schema/fixtures/directive/h.graphql
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
extend schema @link(url: "https://the-guild.dev/graphql/tools", import: ["@foo"])
extend schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
@link(url: "https://the-guild.dev/graphql/tools", import: ["@foo"])

directive @foo on FIELD_DEFINITION

extend type User @key(fields: "id") {
id: ID!
email: String @foo
ssn: String @federation__tag(name: "private")
}
6 changes: 5 additions & 1 deletion packages/import/tests/schema/import-schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,13 +469,17 @@ describe('importSchema', () => {

test('importSchema: link directive', () => {
const expectedSDL = /* GraphQL */ `
extend schema @link(url: "https://the-guild.dev/graphql/tools", import: ["@foo"])
extend schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
@link(url: "https://the-guild.dev/graphql/tools", import: ["@foo"])
directive @foo on FIELD_DEFINITION
extend type User @key(fields: "id") {
id: ID!
email: String @foo
ssn: String @federation__tag(name: "private")
}
`;
expect(importSchema('./fixtures/directive/h.graphql')).toBeSimilarGqlDoc(expectedSDL);
Expand Down
33 changes: 33 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3062,6 +3062,16 @@
tailwind-merge "^2.5.2"
unist-util-visit "5.0.0"

"@theguild/federation-composition@^0.18.3":
version "0.18.3"
resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.18.3.tgz#7e475b7834f4fd0b4e5139f22b31eeaf763fdce8"
integrity sha512-UGa3R3JMDHzoKi6k32kq/bB8SbZrkkxLrfinuEPcsv0sU8r1CW+4jwCBbQGC5vnoU9asg+MuCjMYpLZSOJS/dw==
dependencies:
constant-case "^3.0.4"
debug "4.4.0"
json5 "^2.2.3"
lodash.sortby "^4.7.0"

"@theguild/prettier-config@3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@theguild/prettier-config/-/prettier-config-3.0.1.tgz#83b662197277a0de8a211c09cd43fd4cc59be614"
Expand Down Expand Up @@ -4983,6 +4993,15 @@ consola@^3.0.0:
resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7"
integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==

constant-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case "^2.0.2"

content-disposition@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2"
Expand Down Expand Up @@ -5464,6 +5483,13 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, d
dependencies:
ms "^2.1.3"

debug@4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
dependencies:
ms "^2.1.3"

debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
Expand Down Expand Up @@ -12325,6 +12351,13 @@ update-browserslist-db@^1.1.3:
escalade "^3.2.0"
picocolors "^1.1.1"

upper-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a"
integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==
dependencies:
tslib "^2.0.3"

uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
Expand Down