Skip to content

Commit 51b4940

Browse files
committed
Move tests outside src; fix compatibility check; add tests
1 parent 379ba8e commit 51b4940

File tree

8 files changed

+124
-42
lines changed

8 files changed

+124
-42
lines changed

packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.

packages/libraries/federation-link-utils/src/link-url.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,33 @@ export class FederatedLinkUrl {
7878
return this.isCompatibleVersion(major, minor);
7979
}
8080

81+
isSupportedBy(version: string): boolean;
82+
isSupportedBy(major: number, minor: number): boolean;
83+
isSupportedBy(version: FederatedLinkUrl): boolean;
84+
isSupportedBy(version: null): boolean;
85+
isSupportedBy(...args: [string] | [number, number] | [FederatedLinkUrl] | [null]): boolean {
86+
const majorOrVersion = args[0];
87+
let major: number, minor: number;
88+
if (typeof majorOrVersion === 'string') {
89+
[major, minor] = parseVersion(majorOrVersion);
90+
} else if (typeof majorOrVersion === 'number') {
91+
[major, minor] = args as [number, number];
92+
} else if (majorOrVersion instanceof FederatedLinkUrl) {
93+
// check that it is the same spec
94+
if (majorOrVersion.identity !== this.identity) {
95+
return false;
96+
}
97+
major = majorOrVersion.major;
98+
minor = majorOrVersion.minor;
99+
} else if (majorOrVersion === null) {
100+
// handles null case
101+
return majorOrVersion === this.version;
102+
} else {
103+
throw new Error(`Unsupported version argument: ${JSON.stringify(args)} [${typeof args}].`);
104+
}
105+
return this.isSupportedByVersion(major, minor);
106+
}
107+
81108
private isCompatibleVersion(major: number, minor: number): boolean {
82109
if (this.major === major) {
83110
if (this.major === 0) {
@@ -87,4 +114,14 @@ export class FederatedLinkUrl {
87114
}
88115
return false;
89116
}
117+
118+
private isSupportedByVersion(major: number, minor: number): boolean {
119+
if (this.major === major) {
120+
if (this.major === 0) {
121+
return this.minor === minor;
122+
}
123+
return this.minor <= minor;
124+
}
125+
return false;
126+
}
90127
}

packages/libraries/federation-link-utils/src/link.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ export class FederatedLink {
104104
return this.url.supports(...args);
105105
}
106106

107+
isSupportedBy(version: string): boolean;
108+
isSupportedBy(major: number, minor: number): boolean;
109+
isSupportedBy(version: FederatedLinkUrl): boolean;
110+
isSupportedBy(version: null): boolean;
111+
isSupportedBy(...args: [string] | [number, number] | [FederatedLinkUrl] | [null]): boolean {
112+
/** @ts-expect-error: ignore tuple error. These are tuples and can be spread. tsc is wrong. */
113+
return this.url.isSupportedBy(...args);
114+
}
115+
107116
/**
108117
* Given the name of an element in a linked schema, this returns the name of that element
109118
* as it has been imported. This accounts for aliasing and namespacing unreferenced imports.

packages/libraries/federation-link-utils/src/linkable-spec.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ export class LinkableSpec<T> {
4848
private detectLinkVersion(link: FederatedLink): string | null | undefined {
4949
// for every link, find the highest supported version
5050
if (link.identity === this.identity) {
51+
// This should be flipped so that it finds the first version (ascending) of this spec that supports
52+
// the link.
5153
return (
5254
this.sortedVersionKeys.find(minVersion =>
53-
link.supports(minVersion.split[0], minVersion.split[1]),
55+
link.isSupportedBy(minVersion.split[0], minVersion.split[1]),
5456
)?.key ||
5557
// check null last since Object.keys doesnt include symbols.
56-
(link.supports(null) && this.versions[NULL_VERSION] ? null : undefined)
58+
(link.isSupportedBy(null) && this.versions[NULL_VERSION] ? null : undefined)
5759
);
5860
}
5961
return undefined;
@@ -71,9 +73,9 @@ export class LinkableSpec<T> {
7173
const activeVersion = this.versions[version || NULL_VERSION];
7274
return activeVersion?.(maybeLink.resolveImportName.bind(maybeLink));
7375
}
74-
console.warn(
75-
`Cannot apply @link due to unsupported version found for "${this.identity}". ` +
76-
`Available versions: ${this.sortedVersionKeys.map(v => `${v.split.join('.')}`).join(', ')} and any version these are compatible compatible with.`,
76+
throw new Error(
77+
`Cannot apply @link due to unsupported version found for "${maybeLink}". ` +
78+
`Available versions: ${this.sortedVersionKeys.map(v => `${v.split.join('.')}`).join(', ')} and any version these are compatible compatible with. Try downgrading the link version to match these versions.`,
7779
);
7880
}
7981
}

packages/libraries/federation-link-utils/src/__tests__/index.spec.ts renamed to packages/libraries/federation-link-utils/tests/index.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DocumentNode, parse, StringValueNode, visit } from 'graphql';
2-
import { detectLinkedImplementations, FEDERATION_V1, LinkableSpec } from '../index';
2+
import { detectLinkedImplementations, FEDERATION_V1, LinkableSpec } from '../src/index';
33

44
const metaSpec = new LinkableSpec('https://specs.graphql-hive.com/metadata', {
55
[FEDERATION_V1]: _resolveImportName => (_typeDefs: DocumentNode) => {
@@ -76,7 +76,7 @@ test('LinkableSpec and detectLinkedImplementations can be used to apply linked s
7676
expect(linked.map(link => link(typeDefs))).toMatchInlineSnapshot(`
7777
[
7878
running on v0.1.
79-
Found metadata: {"ping":{"name":"owner","content":"hive-console-team"},"pong":{"eg":"1...2...3... Pong"}}},
79+
Found metadata: {"ping":{"name":"owner","content":"hive-console-team"},"pong":{"eg":"1...2...3... Pong"}},
8080
]
8181
`);
8282
});

packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts renamed to packages/libraries/federation-link-utils/tests/link-url.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FederatedLinkUrl } from '../link-url';
1+
import { FederatedLinkUrl } from '../src/link-url';
22

33
describe('FederatedLinkUrl', () => {
44
test.each([

packages/libraries/federation-link-utils/src/__tests__/link.spec.ts renamed to packages/libraries/federation-link-utils/tests/link.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { parse } from 'graphql';
2-
import { FederatedLink } from '../link';
2+
import { FederatedLink } from '../src/link';
33

44
function trimMultiline(str: string): string {
55
return str
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { parse } from 'graphql';
2+
import { FederatedLink } from '../src/link';
3+
import { LinkableSpec, NULL_VERSION } from '../src/linkable-spec';
4+
5+
describe('LinkableSpec', () => {
6+
const spec = new LinkableSpec('https://specs.graphql-hive.com/example', {
7+
'v2.3': _resolveImportName => 'Version 2.3 used.',
8+
'v1.0': _resolveImportName => 'Version 1.0 used.',
9+
[NULL_VERSION]: _resolveImportName => 'null version used.',
10+
});
11+
12+
describe('#detectImplementation', () => {
13+
test('returns an exact version.', () => {
14+
const sdl = `
15+
extend schema
16+
@link(url: "https://specs.graphql-hive.com/example/v1.0")
17+
`;
18+
19+
const links = FederatedLink.fromTypedefs(parse(sdl));
20+
const specImpl = spec.detectImplementation(links);
21+
expect(specImpl).toBe('Version 1.0 used.');
22+
});
23+
24+
test('returns a compatible version.', () => {
25+
const sdl = `
26+
extend schema
27+
@link(url: "https://specs.graphql-hive.com/example/v2.1")
28+
`;
29+
30+
const links = FederatedLink.fromTypedefs(parse(sdl));
31+
const specImpl = spec.detectImplementation(links);
32+
expect(specImpl).toBe('Version 2.3 used.');
33+
});
34+
35+
test('throws when the identity is supported but the version is outside what the implementation supports.', () => {
36+
const sdl = `
37+
extend schema
38+
@link(url: "https://specs.graphql-hive.com/example/v2.9")
39+
`;
40+
41+
const links = FederatedLink.fromTypedefs(parse(sdl));
42+
expect(() => spec.detectImplementation(links)).toThrow();
43+
});
44+
45+
test('can return a null version.', () => {
46+
const sdl = `
47+
extend schema
48+
@link(url: "https://specs.graphql-hive.com/example")
49+
`;
50+
51+
const links = FederatedLink.fromTypedefs(parse(sdl));
52+
const specImpl = spec.detectImplementation(links);
53+
expect(specImpl).toBe('null version used.');
54+
});
55+
56+
test('returns undefined when identities do not match.', () => {
57+
const sdl = `
58+
extend schema
59+
@link(url: "https://specs.graphql-hive.com/not-example")
60+
`;
61+
62+
const links = FederatedLink.fromTypedefs(parse(sdl));
63+
const specImpl = spec.detectImplementation(links);
64+
expect(specImpl).toBe(undefined);
65+
});
66+
});
67+
});

0 commit comments

Comments
 (0)