-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: add federation-link-util library #6452
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
const metaSpec = new LinkableSpec('https://specs.graphql-hive.com/metadata', { | ||
// The return value could be used to map sdl, collect information, or create a graphql yoga plugin. | ||
// In this test, it's used to collect metadata information from the schema. | ||
'v0.1': resolveImportName => (typeDefs: DocumentNode) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is the best example of how I intended this API to be used.
The LinkableSpec
defines how the linked schemas are implemented.
The Link. resolveImportName
function is then passed to the handler to be used to resolve the names of nodes as they are referred to within the subgraph... I chose to only pass resolveImportName
to keep the API simpler.
And then the implementation uses that to figure out the names of things it supports.
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe pull request introduces enhancements to the Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant EL as extractLinkImplementations
participant FL as FederatedLink.fromTypedefs
participant FI as FederatedLinkImport
Client->>EL: Provide GraphQL DocumentNode
EL->>FL: Extract @link directives from typedefs
FL->>FI: Parse link imports and namespacing
FI-->>FL: Return parsed link imports
FL-->>EL: Return collection of FederatedLink instances
EL-->>Client: Provide resolveImportName & matchesImplementation functions
sequenceDiagram
participant URLStr as URL String
participant FUrl as FederatedLinkUrl.fromUrl
participant FUrlObj as FederatedLinkUrl
URLStr->>FUrl: Parse URL string
FUrl->>FUrlObj: Extract identity, name, and version
note right of FUrlObj: Perform internal version parsing and compatibility checks
FUrlObj-->>FUrl: Return FederatedLinkUrl instance
FUrlObj->>FUrlObj: Invoke supports(...) for version check
FUrlObj-->>Caller: Return compatibility result
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
66dfc13
to
bf8c6e8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (6)
packages/libraries/linkable-specs/src/__tests__/index.spec.ts (3)
1-17
: LGTM! Consider adding JSDoc comments.The test setup provides a clear example of the API usage with proper imports and SDL definition. Consider adding JSDoc comments to document the test's purpose and the demonstrated functionality.
+/** + * Tests the integration between LinkableSpec and detectLinkedImplementations + * demonstrating how to implement versioned metadata collection from linked schemas. + */ test('LinkableSpec and detectLinkedImplementations can be used to easily implement linked schema functionality', () => {
32-44
: Extract repeated directive argument extraction logic.The pattern for extracting string values from directive arguments is duplicated. Consider extracting this into a helper function to improve maintainability and reduce duplication.
+const getDirectiveStringArg = (directive: any, argName: string) => + (directive.arguments?.find(a => a.name.value === argName)?.value as StringValueNode | undefined)?.value ?? '??'; -metaData['name'] = - ( - meta.arguments?.find(a => a.name.value === 'name')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; -metaData['content'] = - ( - meta.arguments?.find(a => a.name.value === 'content')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; +metaData['name'] = getDirectiveStringArg(meta, 'name'); +metaData['content'] = getDirectiveStringArg(meta, 'content'); -metaData['eg'] = - ( - example.arguments?.find(a => a.name.value === 'eg')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; +metaData['eg'] = getDirectiveStringArg(example, 'eg');Also applies to: 48-54
65-68
: Add TODO comment for v0.2 implementation.The v0.2 implementation is currently a placeholder. Consider adding a TODO comment to track this for future implementation.
-'v0.2': _resolveImportName => (_typeDefs: DocumentNode) => { +'v0.2': _resolveImportName => (_typeDefs: DocumentNode) => { + // TODO: Implement v0.2 metadata collection logic // collect metadata return `running on v0.2...`; },packages/libraries/linkable-specs/src/link.ts (3)
5-32
: Consider explicit handling of unknown arguments or removing the commented-out log.
Currently, unrecognized arguments trigger the default case (line 23) with a commented-outconsole.warn
. Either remove the commented log or convert it into a meaningful handling or user feedback mechanism to clarify unsupported arguments.default: { - // console.warn('Unknown argument'); + console.warn(`Unknown argument: ${arg.name.value}`); }
44-66
: Ensure the@link
directive is relevant to the intended specification.
While filtering directives named'link'
is correct for Apollo’s federated spec, be cautious if multiple specifications or custom'link'
directives exist. Make sure that the directive name matches the intended spec usage or version.
85-119
: Replacets-expect-error
with a more robust type definition.
The overload notation forsupports()
could be handled via a single function signature that performs runtime checks on its parameters. This would eliminate the need forts-expect-error
and improve type clarity.- supports(...args: [string] | [number, number] | [FederatedLinkUrl]): boolean { - /** @ts-expect-error: ignore tuple error. These are tuples and can be spread. tsc is wrong. */ - return this.url.supports(...args); - } + supports(version: string | number | FederatedLinkUrl, minor?: number): boolean { + if (typeof version === 'string') { + return this.url.supports(version); + } else if (typeof version === 'number' && typeof minor === 'number') { + return this.url.supports(version, minor); + } else { + return this.url.supports(version); + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
packages/libraries/linkable-specs/README.md
(1 hunks)packages/libraries/linkable-specs/package.json
(1 hunks)packages/libraries/linkable-specs/src/__tests__/index.spec.ts
(1 hunks)packages/libraries/linkable-specs/src/__tests__/link-url.spec.ts
(1 hunks)packages/libraries/linkable-specs/src/__tests__/link.spec.ts
(1 hunks)packages/libraries/linkable-specs/src/__tests__/linkable-spec.spec.ts
(1 hunks)packages/libraries/linkable-specs/src/index.ts
(1 hunks)packages/libraries/linkable-specs/src/link-import.ts
(1 hunks)packages/libraries/linkable-specs/src/link-url.ts
(1 hunks)packages/libraries/linkable-specs/src/link.ts
(1 hunks)packages/libraries/linkable-specs/src/linkable-spec.ts
(1 hunks)packages/libraries/linkable-specs/tsconfig.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- packages/libraries/linkable-specs/README.md
- packages/libraries/linkable-specs/tsconfig.json
- packages/libraries/linkable-specs/src/tests/linkable-spec.spec.ts
- packages/libraries/linkable-specs/src/tests/link.spec.ts
- packages/libraries/linkable-specs/src/index.ts
- packages/libraries/linkable-specs/src/tests/link-url.spec.ts
- packages/libraries/linkable-specs/src/link-import.ts
- packages/libraries/linkable-specs/package.json
- packages/libraries/linkable-specs/src/link-url.ts
- packages/libraries/linkable-specs/src/linkable-spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/libraries/linkable-specs/src/__tests__/index.spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/linkable-specs/src/link.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
🔇 Additional comments (4)
packages/libraries/linkable-specs/src/__tests__/index.spec.ts (1)
70-78
: Consider adding more test cases.The current test only covers the happy path for v0.1. Consider adding test cases for:
- v0.2 implementation
- Error cases (malformed directives, missing required arguments)
- Edge cases (no directives, multiple directives)
Would you like me to help generate these additional test cases?
packages/libraries/linkable-specs/src/link.ts (3)
1-4
: Imports look clean and straightforward.
No issues found in these imports; they accurately capture the dependencies needed to handleConstArgumentNode
and related types.
34-42
: Namespace function logic is clear.
The logic for prefixing names with a namespace is succinct and easy to read. No immediate concerns.
68-84
: Validate or sanitize user-provided identifiers intoString()
.
BecausetoString()
directly interpolatesurl
andimports
into an SDL string, confirm that upstream code or the parsing logic won’t cause malformed SDL or injection-like concerns if the input is external.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/libraries/linkable-specs/src/link-import.ts (2)
9-11
: Add JSDoc documentation for the toString method.While the implementation is correct, adding documentation would improve clarity about the expected string format.
+ /** + * Returns a string representation of the import in GraphQL SDL format. + * @returns {string} For imports with alias: { name: "name", as: "alias" } + * For imports without alias: "name" + */ public toString(): string {
13-47
: Add JSDoc documentation and empty string validation.
- Add JSDoc documentation for better API understanding.
- Add validation for empty strings in the 'name' field.
+ /** + * Parses GraphQL ConstValueNode to extract @link import declarations. + * @param {ConstValueNode} node - The node containing import declarations + * @returns {FederatedLinkImport[]} Array of parsed import declarations + * @throws {Error} If the node structure is invalid + */ static fromTypedefs(node: ConstValueNode): FederatedLinkImport[] { // ... existing code ... name = f.value.value; + if (name.trim() === '') { + throw new Error('Empty string is not allowed for @link "name" field'); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (1)
packages/libraries/linkable-specs/src/link-import.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/linkable-specs/src/link-import.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (1)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🔇 Additional comments (2)
packages/libraries/linkable-specs/src/link-import.ts (2)
1-7
: LGTM! Clean and concise class declaration.The imports are appropriate, and the constructor uses TypeScript's parameter property syntax effectively.
17-17
: Replace 'this' with class name in static context.Using 'this' in static methods can be confusing as it refers to the class itself.
- return new this(v.value, null); + return new FederatedLinkImport(v.value, null); - return new this(name, as); + return new FederatedLinkImport(name, as);Also applies to: 40-40
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @jdolle my review is accumulating some questions and there's still a ways to go. Going to flush what I have so far in case you could already answer some. It would probably help me better understand what is going on etc. :)
[Hive](https://the-guild.dev/graphql/hive) is a fully open-source schema registry, analytics, | ||
metrics and gateway for [GraphQL federation](https://the-guild.dev/graphql/hive/federation) and | ||
other GraphQL APIs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if could make this even more maintainable by avoiding specific feature references, e.g.
... is a delightful MIT licensed platform for developing and operating your GraphQL services from idea to production, solo to enterprise.
And maybe there is/this is a template we can turn into a copy standard? CC @dotansimha @Urigo
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/libraries/linkable-specs/README.md (2)
9-12
: Grammar Correction in Custom Features DescriptionThe sentence “This library can be used to make a custom features for GraphQL schemas backed by Federation's directive.” contains a grammatical issue with the article used before the plural noun “features.” Consider revising to either:
- “This library can be used to create custom features for GraphQL schemas…”
or- “This library can be used to make a custom feature for GraphQL schemas…”
This change improves clarity and correctness.
🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...(VB_A_JJ_NNS)
66-68
: Clarify the "Yoga Plugin" RecommendationThe note stating “always return a yoga plugin” might be ambiguous to users who are not familiar with the term. Consider providing a brief explanation or a hyperlink to documentation that explains what a yoga plugin is and why it is recommended.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/libraries/linkable-specs/README.md
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/linkable-specs/README.md (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (2)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/linkable-specs/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/linkable-specs/README.md
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...
(VB_A_JJ_NNS)
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: alpha / cli-artifacts
- GitHub Check: SonarCloud
🔇 Additional comments (4)
packages/libraries/linkable-specs/README.md (4)
1-2
: Header is Clear and InformativeThe header “GraphQL Hive - linkable-specs” clearly identifies the package and purpose.
3-5
: Adopt a Consistent Copy Standard for the IntroductionThe introductory text provides a good overview; however, consider revising it to follow a consistent copy standard. Previous feedback suggested using a generic template that avoids overly specific feature references. For example, instead of specific claims, a template outlining capabilities in a maintainable way could be beneficial.
13-18
: Features Section Looks GoodThe bulleted features list is concise and clearly communicates the key capabilities of the package.
19-34
: GraphQL Usage Example is InformativeThe provided GraphQL schema snippet adequately demonstrates the usage of the
@link
directive in context. The inclusion of a concrete example helps users understand how to apply the feature.
I considered using that package but federation links only have major and minor, have special requirements for v0.x that don't follow semantic versioning, and I didnt want to introduce a dependency for just the one or two relatively simple functions to write. If the link version spec is ever modified to specifically use semantic versions then we can add this package. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/libraries/linkable-specs/README.md (2)
9-12
: Grammar Correction Needed in Description.The sentence "This library can be used to make a custom features for GraphQL schemas backed by Federation's" contains a grammatical error ("a custom features"). Consider rephrasing to "This library can be used to create custom features for GraphQL schemas backed by Federation's" (or another phrasing that fits the intended meaning).
Diff Suggestion:
-This library can be used to make a custom features for GraphQL schemas backed by Federation's +This library can be used to create custom features for GraphQL schemas backed by Federation's🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...(VB_A_JJ_NNS)
65-68
: Clarify Recommendation on Consistent Implementation.The concluding note recommends that all LinkedSpecs always return a yoga plugin. It would be beneficial to provide a brief explanation or example of why this consistency is important and how it benefits the overall implementation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/libraries/linkable-specs/README.md
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/linkable-specs/README.md (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (2)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/linkable-specs/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/linkable-specs/README.md
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...
(VB_A_JJ_NNS)
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: typescript / typecheck
- GitHub Check: alpha / cli-artifacts
- GitHub Check: test / unit
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: SonarCloud
🔇 Additional comments (6)
packages/libraries/linkable-specs/README.md (6)
1-2
: Title and Header Are Clear.The title "GraphQL Hive - linkable-specs" succinctly informs the reader about the package’s purpose.
3-6
: Clear Description Provided.The introductory paragraph effectively describes GraphQL Hive as an open-source schema registry, analytics, and gateway for GraphQL APIs.
13-18
: Features Section is Concise and Informative.The bullet points clearly list the package’s key capabilities: link version support, import
as
/namespacing following the link spec, and maintaining minimal dependencies.
19-20
: Usage Section Header is Clear.The dedicated "Usage" section helps users quickly locate practical examples.
21-35
: GraphQL Schema Example Effectively Demonstrates Usage.The provided GraphQL code block clearly illustrates how to define a directive and utilize the
@link
directive. This practical example aligns well with the package’s objective of managing schemas with custom link features.
36-64
: TypeScript Example Illustrates Implementation Correctly.The TypeScript example demonstrates the instantiation of
LinkableSpec
and the resolution of directive names usingresolveImportName
, which is consistent with the automatic namespacing feature noted in the retrieved learnings. Ensure that functions likevisit
,parse
, anddetectLinkedImplementations
are either included in the context or referenced in documentation for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (9)
packages/libraries/linkable-specs/README.md (3)
9-11
: Grammar Correction in Usage DescriptionThe sentence "This library can be used to make a custom features for GraphQL schemas backed by Federation's" contains a grammatical error due to the use of the singular article "a" before the plural noun "features." Consider revising it to either "make custom features" (if plural is intended) or "make a custom feature" (if singular is intended).
🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...(VB_A_JJ_NNS)
36-63
: TypeScript Example: Usage Clarity and CompletenessThe TypeScript sample clearly illustrates how to create and configure a
LinkableSpec
instance, resolve a base directive name (e.g.,@example
), and extract metadata via traversing type definitions. As this snippet is intended for documentation purposes, consider adding import statements or comments indicating where functions (such asvisit
,parse
) and types (DocumentNode
,StringValueNode
) are sourced from. This can help users better adapt the example into a production context.
65-67
: Concluding Remarks and ConsistencyThe concluding section effectively communicates that the
LinkableSpec
is unopinionated regarding implementation while recommending consistency across all linked specs (e.g., always returning a yoga plugin). For enhanced clarity and in alignment with internal documentation styles, consider reviewing the phrasing (for example, verifying the use of "I.e." or "i.e.," as per your copy standards).packages/libraries/linkable-specs/src/link-url.ts (4)
1-10
: Consider tightening version constraints.The version regex pattern is quite permissive, allowing major versions up to 99 and minor versions up to 9999. Consider adding numeric range validation to ensure versions stay within reasonable bounds for federation links.
-const VERSION_MATCH = /v(\d{1,2})\.(\d{1,4})/i; +const VERSION_MATCH = /v(\d{1,2})\.(\d{1,3})/i; function parseVersion(version: string | null): [number, number] { const versionParts = version?.match(VERSION_MATCH); if (versionParts?.length) { const [_full, major, minor] = versionParts; + const majorNum = Number(major); + const minorNum = Number(minor); + if (majorNum > 20 || minorNum > 999) { + return [-1, -1]; + } - return [Number(major), Number(minor)]; + return [majorNum, minorNum]; } return [-1, -1]; }
12-29
: Enhance class documentation.The class documentation could be more detailed about the parameters and their purpose.
/** * A wrapper around the `@link` url -- this parses all necessary data to identify the link * and determine which version is most appropriate to use. + * + * @param identity - The base URL or identifier of the link + * @param name - Optional name of the link specification + * @param version - Optional version string in the format 'vX.Y' */
53-75
: Improve error message specificity.The error message for unsupported version arguments could be more helpful by indicating the expected types.
- throw new Error(`Unsupported version argument: ${args}.`); + throw new Error( + `Unsupported version argument: ${args}. Expected a version string (e.g., 'v1.0'), ` + + `version numbers (major, minor), or FederatedLinkUrl instance.` + );
77-85
: Document version compatibility rules.The version compatibility logic, especially the special handling of v0.x versions, should be documented for clarity.
+ /** + * Checks if this version is compatible with the given version. + * + * Version compatibility rules: + * - For v0.x: Versions must match exactly (no backward compatibility) + * - For v1.x and above: Compatible if major versions match and current minor version >= given minor version + */ private isCompatibleVersion(major: number, minor: number): boolean {packages/libraries/linkable-specs/src/link.ts (2)
42-50
: Add JSDoc documentation and input validation.The utility function would benefit from proper documentation and input validation.
+/** + * Prefixes a name with a namespace if provided. + * @param namespace - The namespace to prefix the name with + * @param name - The name to be namespaced + * @returns The namespaced name or the original name if no namespace is provided + * @throws {Error} If name is empty or contains invalid characters + */ function namespaced(namespace: string | null, name: string) { + if (!name || typeof name !== 'string') { + throw new Error('Name must be a non-empty string'); + } + if (namespace?.length) { if (name.startsWith('@')) { return `@${namespace}__${name.substring(1)}`; } return `${namespace}__${name}`; } return name; }
120-130
: Simplify resolveImportName logic and improve error handling.The method contains complex string manipulation and could benefit from clearer logic separation.
resolveImportName(elementName: string): string { + if (!elementName) { + throw new Error('Element name must be provided'); + } + + // Handle default link import case if (this.url.name && elementName === `@${this.url.name}`) { - // @note: default is a directive... So remove the `@` - return this.defaultImport!.substring(1); + const defaultImport = this.defaultImport; + if (!defaultImport) { + throw new Error('Default import not available'); + } + return defaultImport.substring(1); } + + // Find explicit import const imported = this.imports.find(i => i.name === elementName); - let resolvedName = imported?.as ?? imported?.name ?? namespaced(this.namespace, elementName); - // Strip the `@` prefix for directives because in all implementations of mapping or visiting a schema, - // directive names are not prefixed with `@`. The `@` is only for SDL. + const resolvedName = imported?.as ?? imported?.name ?? namespaced(this.namespace, elementName); + + // Remove @ prefix for directives as they're not used in schema visitors return resolvedName.startsWith('@') ? resolvedName.substring(1) : resolvedName; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (3)
packages/libraries/linkable-specs/README.md
(1 hunks)packages/libraries/linkable-specs/src/link-url.ts
(1 hunks)packages/libraries/linkable-specs/src/link.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/libraries/linkable-specs/README.md (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/linkable-specs/src/link-url.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/linkable-specs/src/link.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (2)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/linkable-specs/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/linkable-specs/README.md
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...
(VB_A_JJ_NNS)
⏰ Context from checks skipped due to timeout of 90000ms (12)
- GitHub Check: alpha / npm / snapshot
- GitHub Check: alpha / cli-artifacts
- GitHub Check: test / unit
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: db-types / diff
- GitHub Check: test / db-migrations
- GitHub Check: SonarCloud
🔇 Additional comments (6)
packages/libraries/linkable-specs/README.md (3)
1-8
: Introduction and Overview Section ReviewThe introductory section clearly presents the package and its purpose. The title and accompanying description effectively introduce "GraphQL Hive - linkable-specs" and set the context for the utility library.
13-18
: Features List ClarityThe features list is well-structured and clearly highlights the package’s functionalities, including link version support, import
as
/namespacing based on the link spec, and the minimal dependency ongraphql
.
19-34
: GraphQL Code Snippet ValidationThe provided GraphQL code snippet is syntactically correct and effectively demonstrates how to declare a directive and extend the schema using the
@link
directive. The inclusion of relevant hyperlinks enhances clarity and offers users an avenue for further exploration of the concept.packages/libraries/linkable-specs/src/link-url.ts (1)
31-33
: LGTM!The toString implementation correctly handles both versioned and unversioned URLs.
packages/libraries/linkable-specs/src/link.ts (2)
5-40
: Improve type validation and return type.The function's type validation and return type could be improved for better type safety and clarity.
Consider these improvements:
- Add explicit type validation before using values
- Make the return type more explicit
- Add input validation for required fields
-function linkFromArgs(args: readonly ConstArgumentNode[]): FederatedLink | undefined { +function linkFromArgs(args: readonly ConstArgumentNode[]): FederatedLink | null { let url: FederatedLinkUrl | undefined, imports: FederatedLinkImport[] = [], as: string | null = null; + + // Validate required arguments + const urlArg = args.find(arg => arg.name.value === 'url'); + if (!urlArg) { + throw new Error('Missing required "url" argument in @link directive'); + } + for (const arg of args) { switch (arg.name.value) { case 'url': { - if (arg.value.kind === Kind.STRING) { - url = FederatedLinkUrl.fromUrl(arg.value.value); - } else { - console.warn(`Unexpected kind, ${arg.value.kind}, for argument "url" in @link.`); + if (arg.value.kind !== Kind.STRING) { + throw new Error(`Unexpected kind ${arg.value.kind} for argument "url" in @link directive. Expected ${Kind.STRING}.`); } + url = FederatedLinkUrl.fromUrl(arg.value.value); break; } // ... similar changes for 'as' argument } } if (url !== undefined) { return new FederatedLink(url, as, imports); } - return; + return null; }
53-53
: Address the TODO comment about import names.The TODO comment raises an important architectural question about import name handling.
Could you clarify if import names are needed for every feature or just the namespace? This decision could impact the implementation's flexibility and performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (13)
packages/libraries/federation-link-utils/src/link-url.ts (1)
1-10
: Consider supporting patch versions or alternative version formats.
This approach correctly extracts major and minor versions using a concise regex. However, if federated links ever adopt full semantic versioning (including patch versions or more digits), you may need to adjust the regex or your approach.packages/libraries/federation-link-utils/src/link.ts (1)
52-132
: Potential improvement: incorporate stronger validations or error handling.
WhileFederatedLink.fromTypedefs
collects links effectively, you might consider validating the resulting links (e.g., ensuring they have valid URLs, checking for conflicts) before returning them. This could prevent issues later in the pipeline.packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)
5-19
: Enhance test coverage with additional test cases.While the current test verifies basic version compatibility, consider adding test cases for:
- Edge cases (no matching version)
- Warning message when version is unsupported
- Multiple links in the schema
- Invalid URLs
- Version compatibility edge cases (v0.x handling)
Example test cases to add:
test('warns when no supporting version is found', () => { const spec = new LinkableSpec('https://specs.graphql-hive.com/example', { 'v1.0': _resolveImportName => 'Version 1.0 used.', }); const sdl = ` extend schema @link(url: "https://specs.graphql-hive.com/example/v2.0") `; const consoleSpy = jest.spyOn(console, 'warn'); const links = FederatedLink.fromTypedefs(parse(sdl)); const specImpl = spec.detectImplementation(links); expect(specImpl).toBeUndefined(); expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Cannot apply @link')); }); test('handles multiple links in schema', () => { const spec = new LinkableSpec('https://specs.graphql-hive.com/example', { 'v2.0': _resolveImportName => 'Version 2.0 used.', 'v1.0': _resolveImportName => 'Version 1.0 used.', }); const sdl = ` extend schema @link(url: "https://specs.graphql-hive.com/other/v1.0") @link(url: "https://specs.graphql-hive.com/example/v1.1") `; const links = FederatedLink.fromTypedefs(parse(sdl)); const specImpl = spec.detectImplementation(links); expect(specImpl).toBe('Version 1.0 used.'); });packages/libraries/federation-link-utils/src/link-import.ts (1)
13-47
: Enhance error handling and type safety.Consider the following improvements:
- Add validation for empty name
- Use type guards for better type safety
- Make error messages more descriptive by including the actual value
Apply this diff to improve the implementation:
static fromTypedefs(node: ConstValueNode): FederatedLinkImport[] { if (node.kind == Kind.LIST) { const imports = node.values.map(v => { if (v.kind === Kind.STRING) { + if (!v.value.trim()) { + throw new Error('Empty string is not allowed as @link import name'); + } return new FederatedLinkImport(v.value, null); } if (v.kind === Kind.OBJECT) { let name: string = ''; let as: string | null = null; v.fields.forEach(f => { if (f.name.value === 'name') { if (f.value.kind !== Kind.STRING) { throw new Error( - `Expected string value for @link "name" field but got "${f.value.kind}"`, + `Expected string value for @link "name" field but got "${f.value.kind}" (${JSON.stringify(f.value)})`, ); } + if (!f.value.value.trim()) { + throw new Error('Empty string is not allowed as @link import name'); + } name = f.value.value; } else if (f.name.value === 'as') { if (f.value.kind !== Kind.STRING) { throw new Error( - `Expected string value for @link "as" field but got "${f.value.kind}"`, + `Expected string value for @link "as" field but got "${f.value.kind}" (${JSON.stringify(f.value)})`, ); } + if (!f.value.value.trim()) { + throw new Error('Empty string is not allowed as @link import alias'); + } as = f.value.value; } }); + if (!name) { + throw new Error('Missing required "name" field in @link import'); + } return new FederatedLinkImport(name, as); } - throw new Error(`Unexpected value kind "${v.kind}" in @link import declaration`); + throw new Error(`Unexpected value kind "${v.kind}" in @link import declaration (${JSON.stringify(v)})`); }); return imports; } - throw new Error(`Expected a list of @link imports but got "${node.kind}"`); + throw new Error(`Expected a list of @link imports but got "${node.kind}" (${JSON.stringify(node)})`); }packages/libraries/federation-link-utils/src/linkable-spec.ts (3)
15-19
: Improve version comparison logic.The current version comparison assumes a simple major.minor format. Consider:
- Adding validation for version format
- Handling pre-release versions
- Supporting patch versions if needed
this.sortedVersionKeys = Object.keys(versions).sort((a, b) => { + const versionRegex = /^v(\d+)\.(\d+)(?:\.(\d+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; + const matchA = a.match(versionRegex); + const matchB = b.match(versionRegex); + if (!matchA || !matchB) { + throw new Error(`Invalid version format: ${!matchA ? a : b}`); + } - const [aMajor, aMinor] = a.split('.').map(Number); - const [bMajor, bMinor] = b.split('.').map(Number); + const [, aMajor, aMinor] = matchA.map(Number); + const [, bMajor, bMinor] = matchB.map(Number); return bMajor !== aMajor ? bMajor - aMajor : bMinor - aMinor; });
47-50
: Fix typo in warning message.The warning message contains a duplicate word "compatible".
- `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible compatible with.`, + `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible with.`,
39-52
: Add JSDoc for public method.The
detectImplementation
method would benefit from more detailed documentation.+ /** + * Detects and returns an implementation based on the provided links. + * + * @param links - Array of FederatedLink instances to search through + * @returns The implementation for the highest compatible version found, + * or undefined if no compatible version is found + * @throws {Error} If the version format is invalid + * @example + * ```typescript + * const spec = new LinkableSpec('https://example.com', { + * 'v2.0': resolveImportName => new Implementation(resolveImportName), + * }); + * const links = FederatedLink.fromTypedefs(parse(sdl)); + * const impl = spec.detectImplementation(links); + * ``` + */ public detectImplementation(links: FederatedLink[]): T | undefined {packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts (1)
4-25
: Improve test descriptions and add error cases.While the test coverage is good, consider:
- Making test descriptions more descriptive
- Adding error cases for invalid URLs
- Adding test cases for malformed versions
test.each([ { description: 'parses URL with schema name and version in path', url: 'https://spec.example.com/a/b/mySchema/v1.0/', expected: { identity: 'https://spec.example.com/a/b/mySchema', name: 'mySchema', version: 'v1.0' } }, // ... existing test cases ... ])('fromUrl $description', ({ url, expected }) => { const spec = FederatedLinkUrl.fromUrl(url); expect(spec.identity).toBe(expected.identity); expect(spec.name).toBe(expected.name); expect(spec.version).toBe(expected.version); }); test.each([ ['invalid://url', 'Invalid URL protocol'], ['https://spec.example.com/mySchema/invalid-version', 'Invalid version format'], ])('fromUrl throws error for invalid input: %s', (url, expectedError) => { expect(() => FederatedLinkUrl.fromUrl(url)).toThrow(expectedError); });packages/libraries/federation-link-utils/README.md (2)
9-11
: Fix grammar in the introduction.The article "a" should not be used before the plural noun "features".
-This library can be used to make a custom features for GraphQL schemas backed by Federation's +This library can be used to make custom features for GraphQL schemas backed by Federation's🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...(VB_A_JJ_NNS)
13-67
: Consider enhancing the documentation.While the documentation provides a good overview and usage example, consider adding the following sections to make it more comprehensive:
- Installation instructions
- API reference
- Contributing guidelines
- License information
packages/libraries/federation-link-utils/src/__tests__/link.spec.ts (2)
12-37
: Consider adding negative test cases.The
fromTypedefs
test suite only covers successful scenarios. Consider adding test cases for:
- Invalid SDL syntax
- Missing or malformed @link directives
- Empty import arrays
39-71
: Add test descriptions for better readability.The test cases in
resolveImportName
would benefit from descriptive titles usingtest.each
or separatetest
blocks for each scenario.- test('resolveImportName', () => { + describe('resolveImportName', () => { + test('resolves aliased imports correctly', () => { // Test aliased imports + }); + test('resolves unimported directives with namespace', () => { // Test unimported directives + }); + // ... more test cases });packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1)
31-43
: Simplify type assertions using optional chaining.The nested type assertions and null checks can be simplified using optional chaining.
-metaData['name'] = - ( - meta.arguments?.find(a => a.name.value === 'name')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; +metaData['name'] = (meta.arguments?.find(a => a.name.value === 'name')?.value as StringValueNode)?.value ?? '??';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
packages/libraries/federation-link-utils/README.md
(1 hunks)packages/libraries/federation-link-utils/package.json
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/link.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-import.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-url.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/src/linkable-spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/version.ts
(1 hunks)packages/libraries/federation-link-utils/tsconfig.json
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- packages/libraries/federation-link-utils/src/version.ts
- packages/libraries/federation-link-utils/package.json
- packages/libraries/federation-link-utils/tsconfig.json
🧰 Additional context used
📓 Path-based instructions (10)
packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/index.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/link-import.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/linkable-spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/__tests__/link.spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/README.md (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/link-url.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/link.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (3)
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...
(VB_A_JJ_NNS)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: SonarCloud
🔇 Additional comments (6)
packages/libraries/federation-link-utils/src/link-url.ts (3)
21-29
: Constructor usage looks sound.
Storing both the parsed major/minor and the original version string effectively supports flexible version checks while preserving raw data.
35-51
: Handle invalid or malformed URLs more gracefully.
Currently, ifnew URL(urlSource)
receives an invalid URL, it will throw an exception. Consider wrapping it in a try/catch and logging or handling the error to avoid unexpected crashes.
53-85
: Version compatibility logic is appropriate.
The current rules for version handling (exact match for major=0, otherwisethis.minor >= minor
) is consistent with the stated requirements. This ensures backward compatibility of minor versions while forcing exact matches for v0.x.packages/libraries/federation-link-utils/src/link.ts (2)
5-40
: Validate unknown argument handling.
InlinkFromArgs
, unrecognized arguments are silently ignored. Ensure that ignoring these arguments aligns with desired behavior, or add warnings for unexpected parameters if needed.
42-50
: Utility function is straightforward.
Thenamespaced
function is straightforward and well-documented. It correctly separates directives from normal types by adjusting the "@" prefix.packages/libraries/federation-link-utils/src/index.ts (1)
10-20
: Function design is clear and modular.
detectLinkedImplementations
provides a simple, modular approach to link detection. It neatly delegates link parsing toFederatedLink.fromTypedefs
and leverages eachsupportedSpec
to detect potential matches, keeping concerns well-separated.
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
Outdated
Show resolved
Hide resolved
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/libraries/federation-link-utils/src/link.ts (3)
5-40
: Consider more robust error or fallback handling for missing or invalid arguments.
Currently, ifurl
is not a valid string, the function returnsundefined
, silently skipping link creation. You might consider either throwing an error or returning a diagnostic result to make the behavior explicit for callers.
42-50
: Function name clarity.
The functionnamespaced
is concise and descriptive, but it may be clearer to name it something likeaddNamespacePrefix
to communicate exactly what it does.
52-131
: Use a configurable logger or error mechanism instead ofconsole.warn
.
Usingconsole.warn
(lines 15, 27) directly in a library can be noisy for consumers. A configurable logger or custom error-handling mechanism might give library users more control over log output.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/libraries/federation-link-utils/src/index.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/link.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
⏰ Context from checks skipped due to timeout of 90000ms (10)
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: test / db-migrations
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: alpha / cli-artifacts
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: SonarCloud
🔇 Additional comments (3)
packages/libraries/federation-link-utils/src/link.ts (1)
1-4
: Looks well-structured and provides clear imports.
No issues found in these lines.packages/libraries/federation-link-utils/src/index.ts (2)
1-9
: Import and export declarations look good.
They cleanly expose the library’s capabilities.
10-21
: Verify error handling for partial or invalid spec detection.
If adetectImplementation
call throws an exception or returns a partial result, it might be beneficial to handle or log that scenario.Would you like to run a codebase analysis script to identify places where
detectImplementation
might fail or return unexpected values?
🐋 This PR was built and pushed to the following Docker images: Targets: Platforms: Image Tag: Docker Bake metadata{
"app": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/api/health",
"build-arg:IMAGE_DESCRIPTION": "The app of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/app",
"build-arg:PORT": "3000",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/app",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/cxgzm6f0o50eg89cc68gm5c0v",
"containerimage.config.digest": "sha256:48f3ce6f83515a034ba53bcab3a974ba28de9fd66dbd9df5cb20b32e35aa8d70",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:1927ae68691ac06c2392a0b04ae6628c3d9241d8dd29a9f3bc787c35a55bbeb4",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:1927ae68691ac06c2392a0b04ae6628c3d9241d8dd29a9f3bc787c35a55bbeb4",
"image.name": "ghcr.io/graphql-hive/app:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/app:linkable_spec_lib-amd64"
},
"buildx.build.warnings": [
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAyMSk=",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 21
},
"end": {
"line": 21
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIxKQ==",
"detail": [
"TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
],
"url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 21
},
"end": {
"line": 21
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIyKQ==",
"detail": [
"TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
],
"url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 22
},
"end": {
"line": 22
}
}
]
},
{
"vertex": "sha256:bd5c66f1d0f48576def83ef418f0fcbd3b2c357d9cb67aa6fdc8568aa0672e4d",
"level": 1,
"short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEwKQ==",
"detail": [
"TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
],
"url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
"sourceInfo": {
"filename": "migrations.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 10
},
"end": {
"line": 10
}
}
]
},
{
"vertex": "sha256:bd5c66f1d0f48576def83ef418f0fcbd3b2c357d9cb67aa6fdc8568aa0672e4d",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTcp",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "migrations.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 17
},
"end": {
"line": 17
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTIp",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 12
},
"end": {
"line": 12
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMyk=",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 13
},
"end": {
"line": 13
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIwKQ==",
"detail": [
"TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
],
"url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 20
},
"end": {
"line": 20
}
}
]
},
{
"vertex": "sha256:bd5c66f1d0f48576def83ef418f0fcbd3b2c357d9cb67aa6fdc8568aa0672e4d",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTUp",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "migrations.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 15
},
"end": {
"line": 15
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTQp",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 14
},
"end": {
"line": 14
}
}
]
},
{
"vertex": "sha256:d9a3ce21e7d65628dc98df242361c9b3d2bd6fdcdb8863d37e6b350785483d88",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRQT1JUJyAobGluZSAyMik=",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "services.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 22
},
"end": {
"line": 22
}
}
]
},
{
"vertex": "sha256:bd5c66f1d0f48576def83ef418f0fcbd3b2c357d9cb67aa6fdc8568aa0672e4d",
"level": 1,
"short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMik=",
"detail": [
"VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
],
"url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
"sourceInfo": {
"filename": "migrations.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 12
},
"end": {
"line": 12
}
}
]
},
{
"vertex": "sha256:bd5c66f1d0f48576def83ef418f0fcbd3b2c357d9cb67aa6fdc8568aa0672e4d",
"level": 1,
"short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDExKQ==",
"detail": [
"TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
],
"url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
"sourceInfo": {
"filename": "migrations.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 11
},
"end": {
"line": 11
}
}
]
},
{
"vertex": "sha256:bd5c66f1d0f48576def83ef418f0fcbd3b2c357d9cb67aa6fdc8568aa0672e4d",
"level": 1,
"short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEyKQ==",
"detail": [
"TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
],
"url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
"sourceInfo": {
"filename": "migrations.dockerfile",
"data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
"language": "Dockerfile"
},
"range": [
{
"start": {
"line": 12
},
"end": {
"line": 12
}
}
]
}
],
"composition-federation-2": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "Federation 2 Composition Service for GraphQL Hive.",
"build-arg:IMAGE_TITLE": "graphql-hive/composition-federation-2",
"build-arg:PORT": "3069",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/external-composition",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/qgft2h7gn46m6ojcezjdcnuxl",
"containerimage.config.digest": "sha256:244a02c60abd10fed1b071f507e305722293e102de8c94b38ce6e7e7950a9d4d",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:00853d18cd97b63d62d6b1ddb856f6c42e2b399c27a2c19cdff201808a9d99e3",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:00853d18cd97b63d62d6b1ddb856f6c42e2b399c27a2c19cdff201808a9d99e3",
"image.name": "ghcr.io/graphql-hive/composition-federation-2:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/composition-federation-2:linkable_spec_lib-amd64"
},
"emails": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The emails service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/emails",
"build-arg:PORT": "3006",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/emails",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/gvbx66c43femnvnshgkxrgpxz",
"containerimage.config.digest": "sha256:d044389cef617a6cb7eea7ddf5fa6c344363263ba62c37a120d632f60b3eb808",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:129a2d5363d1c3b782b3ae2d8bf205ae58321f9d262de48f09061a2747a785f1",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:129a2d5363d1c3b782b3ae2d8bf205ae58321f9d262de48f09061a2747a785f1",
"image.name": "ghcr.io/graphql-hive/emails:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/emails:linkable_spec_lib-amd64"
},
"policy": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The policy service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/policy",
"build-arg:PORT": "3012",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/policy",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/reg97oz2r6byiuhah4fmnhe0l",
"containerimage.config.digest": "sha256:ec7a0468830efa0d61aae688552dd2a5b61f26caf3aa88aec9b26358e72a9956",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:a430d9ee76d5c8dd86b98a83d2a953231edc2ea6a6c5c2a370c1f3c7e141f0ff",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:a430d9ee76d5c8dd86b98a83d2a953231edc2ea6a6c5c2a370c1f3c7e141f0ff",
"image.name": "ghcr.io/graphql-hive/policy:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/policy:linkable_spec_lib-amd64"
},
"rate-limit": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The rate limit service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/rate-limit",
"build-arg:PORT": "3009",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/rate-limit",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/wg64egzlay0mqdvg772184lgs",
"containerimage.config.digest": "sha256:e83218a39603ce4466e1032ae5ee6ab9f7a8b12d6c898e676796fbbbbf2d14c0",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:ea5918ad75bc235abbbdce7752c4152008f7036c8c23ac5b5946f4d563db19f5",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:ea5918ad75bc235abbbdce7752c4152008f7036c8c23ac5b5946f4d563db19f5",
"image.name": "ghcr.io/graphql-hive/rate-limit:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/rate-limit:linkable_spec_lib-amd64"
},
"schema": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The schema service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/schema",
"build-arg:PORT": "3002",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/schema",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/kp1bv7w9sp21fy2o3fbuar545",
"containerimage.config.digest": "sha256:0b269507a2273170942e5b23f1d8156ce0da7673f075239912f7bf590ae90357",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:07288439dcf80b1527b49a919d99e0f095e747b351e5fb2db8c537468ff5fd56",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:07288439dcf80b1527b49a919d99e0f095e747b351e5fb2db8c537468ff5fd56",
"image.name": "ghcr.io/graphql-hive/schema:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/schema:linkable_spec_lib-amd64"
},
"server": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The server service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/server",
"build-arg:PORT": "3001",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/server",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/wae9tyol5dk1eurqe083vbo9y",
"containerimage.config.digest": "sha256:3a798d5b39b2d48edc964e5e947d33f9493d9cb1d8c90ac07b732ff7eb3f0178",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:4b1e896745080e5c842a2fa76809d7f07578fba297b859c6dd7c6dfbc8aeb05b",
"size": 2076,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:4b1e896745080e5c842a2fa76809d7f07578fba297b859c6dd7c6dfbc8aeb05b",
"image.name": "ghcr.io/graphql-hive/server:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/server:linkable_spec_lib-amd64"
},
"storage": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "migrations.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:IMAGE_DESCRIPTION": "The migrations service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/storage",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/kpl67m4rj2myxjewu6kuom4vi",
"containerimage.config.digest": "sha256:34c0340f85264b734459e9aaadf4a907d22eae065accbd5df890b5d0847d0c9f",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:56468b11067d914b6fda853004b1e4966ea3770834aea286c48c74a18e4319e5",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:56468b11067d914b6fda853004b1e4966ea3770834aea286c48c74a18e4319e5",
"image.name": "ghcr.io/graphql-hive/storage:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/storage:linkable_spec_lib-amd64"
},
"stripe-billing": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The stripe billing service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/stripe-billing",
"build-arg:PORT": "3010",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/stripe-billing",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/yfdxylagvmt20nrfobsdbljgb",
"containerimage.config.digest": "sha256:c349b598ebe204aa274b1566e4204a07c089a49164ca43121a5709b90c7b133f",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:3ee0f88bcb384b0c9146c9b59fec892625f2ea3d05629df0fd3399dd18b200cb",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:3ee0f88bcb384b0c9146c9b59fec892625f2ea3d05629df0fd3399dd18b200cb",
"image.name": "ghcr.io/graphql-hive/stripe-billing:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/stripe-billing:linkable_spec_lib-amd64"
},
"tokens": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The tokens service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/tokens",
"build-arg:PORT": "3003",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/tokens",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/aeu6pox5jskv9btlup1ajpa2v",
"containerimage.config.digest": "sha256:f6569ae12aef33a875297c0bf745924a1420da3ffbfd137eda3edee65ca3e1a8",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:3da573ef69654b77081454570d276eb51cfbc186ba1b6de510d7aa72840f81bd",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:3da573ef69654b77081454570d276eb51cfbc186ba1b6de510d7aa72840f81bd",
"image.name": "ghcr.io/graphql-hive/tokens:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/tokens:linkable_spec_lib-amd64"
},
"usage": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/usage",
"build-arg:PORT": "3006",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/usage",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/vhj14wflbvksb2u8z61ypo3nt",
"containerimage.config.digest": "sha256:1566a48292105b2430fe28679e47bf1ed9b6d2b84c4165b0e056ad709a0c0be8",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:cabbad0f4b075c70d3fe8c81c3d7b043562d0d394d691a75a71ee52e9cdde89a",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:cabbad0f4b075c70d3fe8c81c3d7b043562d0d394d691a75a71ee52e9cdde89a",
"image.name": "ghcr.io/graphql-hive/usage:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/usage:linkable_spec_lib-amd64"
},
"usage-estimator": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The usage estimator service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/usage-estimator",
"build-arg:PORT": "3008",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/usage-estimator",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/v18xlxpgdqw1b9dqmkl4qw0wd",
"containerimage.config.digest": "sha256:676213864c6eaab62b341d3971a52a8be906ab330c7800815776408caee2daf6",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:00ca9c8d5de47c6b04971a2eec37463a44827fb9ab9de365e498dc9decaf9a2e",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:00ca9c8d5de47c6b04971a2eec37463a44827fb9ab9de365e498dc9decaf9a2e",
"image.name": "ghcr.io/graphql-hive/usage-estimator:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/usage-estimator:linkable_spec_lib-amd64"
},
"usage-ingestor": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/usage-ingestor",
"build-arg:PORT": "3007",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/usage-ingestor",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/v5g7fqwull5b79zhr9qr25pvh",
"containerimage.config.digest": "sha256:1ffc0823eb0f1cc06c219e387d79de8c3cd4169acdb4fc878f6dfd1d0a03d2c1",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:c93d7cb9cd6911bf23e287aa8d99578faa4b5266b2ebd29c7557a8fc03ac5aa6",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:c93d7cb9cd6911bf23e287aa8d99578faa4b5266b2ebd29c7557a8fc03ac5aa6",
"image.name": "ghcr.io/graphql-hive/usage-ingestor:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/usage-ingestor:linkable_spec_lib-amd64"
},
"webhooks": {
"buildx.build.provenance": {
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/node@22.13.0-slim?platform=linux%2Famd64",
"digest": {
"sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "services.dockerfile"
},
"parameters": {
"frontend": "dockerfile.v0",
"args": {
"build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
"build-arg:IMAGE_DESCRIPTION": "The webhooks ingestor service of the GraphQL Hive project.",
"build-arg:IMAGE_TITLE": "graphql-hive/webhooks",
"build-arg:PORT": "3005",
"build-arg:RELEASE": "e345a1310373345bc0f1b9e74c8ed18def212e68",
"build-arg:SERVICE_DIR_NAME": "@hive/webhooks",
"context:dist": "local:dist",
"context:shared": "local:shared",
"frontend.caps": "moby.buildkit.frontend.contexts+forward",
"local-sessionid:context": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:dockerfile": "qwkb9iy4ohi6d2lsxi3yxyl8h",
"local-sessionid:shared": "qwkb9iy4ohi6d2lsxi3yxyl8h"
},
"locals": [
{
"name": "context"
},
{
"name": "dist"
},
{
"name": "dockerfile"
},
{
"name": "shared"
}
]
},
"environment": {
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-f85b03ac-26b5-4e09-9504-288dde8ca645/builder-f85b03ac-26b5-4e09-9504-288dde8ca6450/kju8qgauts8vli2v4ceja13i4",
"containerimage.config.digest": "sha256:65e677ff7e2a49d3186911fdd91886b8a913827b8266e19bf08fceeac8c4b87a",
"containerimage.descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:2dd9529224de9c72b116275dbf3ff643a0d56887cc0bd4649c8669d3a927334f",
"size": 2075,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"containerimage.digest": "sha256:2dd9529224de9c72b116275dbf3ff643a0d56887cc0bd4649c8669d3a927334f",
"image.name": "ghcr.io/graphql-hive/webhooks:e345a1310373345bc0f1b9e74c8ed18def212e68-amd64,ghcr.io/graphql-hive/webhooks:linkable_spec_lib-amd64"
}
} |
Is supporting Federation 1 out of the scope of this? E.g. we support Thinking about this, I would like to see this library being used on something like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/libraries/federation-link-utils/README.md (2)
9-12
: Correct Grammar in the DescriptionThere is a minor grammatical error on line 9: the phrase "make a custom features" should be revised for correctness. Consider rephrasing it to "create custom features" or "make custom features" for clarity.
-This library can be used to make a custom features for GraphQL schemas backed by Federation's +This library can be used to create custom features for GraphQL schemas backed by Federation's🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...(VB_A_JJ_NNS)
66-68
: Clarify Final Notes on LinkableSpec ImplementationThe concluding explanation regarding
LinkableSpec
is helpful, but it could benefit from additional context. A brief note or a reference link explaining what it means to "always return a yoga plugin" would add clarity for users who might not be familiar with that convention.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/libraries/federation-link-utils/README.md
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/federation-link-utils/README.md (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (1)
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...
(VB_A_JJ_NNS)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: alpha / cli-artifacts
🔇 Additional comments (6)
packages/libraries/federation-link-utils/README.md (6)
1-2
: Descriptive Title and OverviewThe file title "GraphQL Hive - federation-link-utils" is clear and descriptive. Consider adding a brief tagline that summarizes the library’s purpose to further orient new users.
3-6
: Clear Introduction ProvidedThe introduction effectively explains what GraphQL Hive offers and provides useful links for more information. Please ensure that the external links remain up-to-date as the project evolves.
7-8
: Well-Placed Section SeparatorThe horizontal rule is appropriately used to separate the introductory content from the rest of the document.
13-18
: Feature List is Clear and ConciseThe features section succinctly lists the library’s core capabilities. In future iterations, you might consider providing additional details or examples for each feature to enhance the user’s understanding.
19-35
: Usage Example for GraphQL Schema is InformativeThe provided GraphQL schema example clearly demonstrates how to extend a schema using the
@link
directive. This example is valuable for users looking to grasp the practical application of the library.
36-64
: Enhance Code Snippet Clarity with Required ImportsThe TypeScript snippet effectively demonstrates how to create a
LinkableSpec
and utilizedetectLinkedImplementations
. However, to improve clarity for developers integrating this code, consider explicitly adding the necessary imports (e.g.,visit
,parse
,DocumentNode
, andStringValueNode
) from the appropriate modules.+// Ensure to import required dependencies from 'graphql' +import { DocumentNode, StringValueNode, visit, parse } from 'graphql';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/libraries/federation-link-utils/src/index.ts (1)
12-32
: Consider adding JSDoc documentation for the function.While the function implementation looks good, adding JSDoc documentation would help users understand:
- The purpose of the function
- The expected format of input parameters
- The structure of the return value
- Example usage
+/** + * Detects and returns implementations from a list of supported specifications based on the links defined in the schema. + * @param typeDefs - The GraphQL schema as a DocumentNode + * @param supportedSpecs - Array of LinkableSpec instances to detect implementations for + * @returns Array of detected implementations + * @example + * ```ts + * const typeDefs = parse(sdl); + * const specs = [new LinkableSpec(...)]; + * const implementations = detectLinkedImplementations(typeDefs, specs); + * ``` + */ export function detectLinkedImplementations<T>( typeDefs: DocumentNode, supportedSpecs: LinkableSpec<T>[], ): T[] {packages/libraries/federation-link-utils/src/linkable-spec.ts (1)
50-53
: Consider using a structured logging solution.Instead of using console.warn directly, consider using a structured logging solution that can be configured based on the environment and allows for better error tracking.
- console.warn( - `Cannot apply @link due to unsupported version found for "${this.identity}". ` + - `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible compatible with.`, - ); + const message = `Cannot apply @link due to unsupported version found for "${this.identity}". ` + + `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible with.`; + this.logger?.warn?.(message) ?? console.warn(message);Also, there's a typo in the message: "compatible" is repeated twice.
packages/libraries/federation-link-utils/README.md (1)
40-76
: Enhance the TypeScript example.The example code could be improved in several ways:
- Add type annotations for the
sdl
variable- Show error handling for the
apply
function- Document the expected shape of the return value
// specs.ts import { detectLinkedImplementations, FEDERATION_V1, - LinkableSpec + LinkableSpec, + DocumentNode } from '@graphql-hive/federation-link-utils' +// Define the shape of your implementation +type ExampleImpl = Record<string, string>; + +// Your SDL as a string +const sdl = `...`; + const exampleSpec = new LinkableSpec('https://specs.graphql-hive.com/example', { [FEDERATION_V1]: resolveImportName => (typeDefs: DocumentNode) => { // option to support federation 1 schemas. Be extremely cautious here because versioning // cannot be done safely. }, 'v0.1': resolveImportName => (typeDefs: DocumentNode) => { const examples: Record<string, string> = {} // ... rest of the implementation return examples } }) const typeDefs = parse(sdl) const linkedSpecs = detectLinkedImplementations(typeDefs, [exampleSpec]) -const result = linkedSpecs.map(apply => apply(typeDefs)) +const result = linkedSpecs.map(apply => { + try { + return apply(typeDefs) as ExampleImpl; + } catch (error) { + console.error('Failed to apply spec:', error); + return {}; + } +});
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
packages/libraries/federation-link-utils/README.md
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/src/linkable-spec.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/libraries/federation-link-utils/src/tests/index.spec.ts
- packages/libraries/federation-link-utils/src/link.ts
🧰 Additional context used
📓 Path-based instructions (3)
packages/libraries/federation-link-utils/src/linkable-spec.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/index.ts (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/README.md (1)
Pattern packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
📓 Learnings (1)
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...
(VB_A_JJ_NNS)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: alpha / publish_docker / Build CLI Docker Image
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: alpha / cli-artifacts
🔇 Additional comments (2)
packages/libraries/federation-link-utils/src/index.ts (1)
5-8
: LGTM! Clean and well-organized exports.The exports are properly organized and provide a clear public API for the library.
packages/libraries/federation-link-utils/README.md (1)
9-11
: Fix grammar in library description.There is a grammatical issue in the description.
-This library can be used to make a custom features for GraphQL schemas backed by Federation's +This library can be used to create custom features for GraphQL schemas backed by Federation's🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...(VB_A_JJ_NNS)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/libraries/federation-link-utils/src/linkable-spec.ts (1)
23-41
:⚠️ Potential issueAdd validation for version numbers.
While the code handles invalid version formats gracefully, it lacks explicit validation for version numbers. This could lead to unexpected behavior with invalid version strings.
Add validation before sorting:
constructor( public readonly identity: string, public readonly versions: Versioned<T>, ) { // sort the versions in descending order for quicker lookups this.sortedVersionKeys = Object.keys(versions) .map( ( key, ): { key: string; split?: number[]; } => { + const match = key.match(VERSION_KEY_MATCH); + if (match) { + const [major, minor] = match.slice(1).map(Number); + if (isNaN(major) || isNaN(minor)) { + console.warn(`Invalid version format: ${key}`); + return { key }; + } + return { key, split: [major, minor] }; + } return { key }; - }): { key, split: key.match(VERSION_KEY_MATCH)?.map(Number).slice(1) }), }) .filter((v): v is Required<typeof v> => v.split !== undefined) .sort(({ split: [aMajor, aMinor] }, { split: [bMajor, bMinor] }) => { return bMajor !== aMajor ? bMajor - aMajor : bMinor - aMinor; }); }
🧹 Nitpick comments (7)
packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (2)
12-21
: Enhance test coverage with additional version combinations.The test verifies basic version compatibility, but could be strengthened by adding test cases for:
- Major version compatibility (e.g., v2.x vs v1.x)
- Edge cases (e.g., highest compatible version when multiple versions match)
- Invalid version formats
23-32
: Add assertions for warning messages.The test verifies null version handling, but could be improved by:
- Adding test cases for warning messages when unsupported versions are encountered
- Verifying that
NULL_VERSION
is used as a last resortpackages/libraries/federation-link-utils/src/linkable-spec.ts (1)
74-77
: Improve warning message clarity.The warning message about available versions could be clearer.
Apply this diff to improve the message:
- `Cannot apply @link due to unsupported version found for "${this.identity}". ` + - `Available versions: ${this.sortedVersionKeys.map(v => `${v.split.join('.')}`).join(', ')} and any version these are compatible compatible with.`, + `Cannot apply @link due to unsupported version found for "${this.identity}". ` + + `Available versions: ${this.sortedVersionKeys.map(v => `v${v.split.join('.')}`).join(', ')}. ` + + `Note: Each version is compatible with higher minor versions within the same major version.`,packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (4)
11-53
: Refactor metadata collection logic to reduce duplication.The directive argument extraction logic is repeated multiple times. Consider extracting this into a helper function.
+' const getDirectiveArgValue = (directive: any, argName: string) => { + return ( + directive.arguments?.find(a => a.name.value === argName)?.value as + StringValueNode + | undefined + )?.value ?? '??'; + }; + 'v0.1': resolveImportName => (typeDefs: DocumentNode) => { const collectedMeta: Record<string, Record<string, string>> = {}; const metaName = resolveImportName('@meta'); const exampleName = resolveImportName('@example'); visit(typeDefs, { FieldDefinition: node => { let metaData: Record<string, string> = {}; const fieldName = node.name.value; const meta = node.directives?.find(d => d.name.value === metaName); if (meta) { - metaData['name'] = - ( - meta.arguments?.find(a => a.name.value === 'name')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; - metaData['content'] = - ( - meta.arguments?.find(a => a.name.value === 'content')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; + metaData['name'] = getDirectiveArgValue(meta, 'name'); + metaData['content'] = getDirectiveArgValue(meta, 'content'); } const example = node.directives?.find(d => d.name.value === exampleName); if (example) { - metaData['eg'] = - ( - example.arguments?.find(a => a.name.value === 'eg')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; + metaData['eg'] = getDirectiveArgValue(example, 'eg'); }
61-82
: Enhance test coverage with explicit assertions.While snapshot testing is useful, consider adding explicit assertions to verify:
- The presence of both named and namespaced directives in the collected metadata
- The structure of the collected metadata object
const typeDefs = parse(sdl); const linked = detectLinkedImplementations(typeDefs, [metaSpec]); - expect(linked.map(link => link(typeDefs))).toMatchInlineSnapshot(` - [ - running on v0.1. - Found metadata: {"ping":{"name":"owner","content":"hive-console-team"},"pong":{"eg":"1...2...3... Pong"}}}, - ] - `); + const result = linked.map(link => link(typeDefs)); + + // Snapshot for regression testing + expect(result).toMatchInlineSnapshot(` + [ + running on v0.1. + Found metadata: {"ping":{"name":"owner","content":"hive-console-team"},"pong":{"eg":"1...2...3... Pong"}}}, + ] + `); + + // Explicit assertions + const metadata = JSON.parse(result[0].split('Found metadata: ')[1]); + expect(metadata).toHaveProperty('ping.name', 'owner'); + expect(metadata).toHaveProperty('ping.content', 'hive-console-team'); + expect(metadata).toHaveProperty('pong.eg', '1...2...3... Pong');
84-99
: Add test description and assertions for Federation v1 fallback.The test could be more explicit about the expected behavior when the link directive is missing.
-test('LinkableSpec and detectLinkedImplementations can be used to apply linked schema in schemas that are missing the link directive', () => { +test('falls back to Federation v1 implementation when @link directive is missing', () => { const sdl = ` directive @meta(name: String!, content: String!) on SCHEMA | FIELD type Query { ping: String @meta(name: "owner", content: "hive-console-team") } `; const typeDefs = parse(sdl); const linked = detectLinkedImplementations(typeDefs, [metaSpec]); - expect(linked.map(link => link(typeDefs))).toMatchInlineSnapshot(` - [ - Missing federation 2 support, - ] - `); + const result = linked.map(link => link(typeDefs)); + + // Verify Federation v1 fallback + expect(result).toHaveLength(1); + expect(result[0]).toBe('Missing federation 2 support'); + + // Keep snapshot for regression testing + expect(result).toMatchInlineSnapshot(` + [ + Missing federation 2 support, + ] + `);
101-119
: Enhance version underscore test coverage.Consider adding more test cases to thoroughly verify version handling:
- Different underscore patterns
- Version comparison logic
+test('LinkableSpec handles various version formats correctly', () => { + const testCases = [ + { + version: 'v0_3', + expected: 'Version 0.3 used', + }, + { + version: 'v0_3_1', + expected: 'Missing federation 2 support', // Falls back to v1 for unknown versions + }, + ]; + + testCases.forEach(({ version, expected }) => { + const sdl = ` + directive @meta(name: String!, content: String!) on SCHEMA | FIELD + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.3") + @link(url: "https://specs.graphql-hive.com/metadata/${version}", import: ["@meta"]) + + type Query { + ping: String @meta(name: "owner", content: "hive-console-team") + } + `; + const typeDefs = parse(sdl); + const linked = detectLinkedImplementations(typeDefs, [metaSpec]); + expect(linked.map(link => link(typeDefs))[0]).toBe(expected); + }); +});
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/libraries/federation-link-utils/README.md
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-url.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/src/linkable-spec.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/libraries/federation-link-utils/README.md
- packages/libraries/federation-link-utils/src/link-url.ts
- packages/libraries/federation-link-utils/src/link.ts
🧰 Additional context used
📓 Path-based instructions (1)
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts
packages/libraries/federation-link-utils/src/linkable-spec.ts
📓 Learnings (2)
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (2)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/federation-link-utils/src/__tests__/index.spec.ts:6-7
Timestamp: 2025-02-03T23:57:17.342Z
Learning: The federation-link-utils library supports two styles of directive imports:
1. Named imports (e.g., `@meta`)
2. Namespaced imports (e.g., `@metadata__example`)
Both styles can be used together in the same schema.
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🔇 Additional comments (2)
packages/libraries/federation-link-utils/src/linkable-spec.ts (2)
3-12
: LGTM! Well-documented constants and flexible version pattern.The version pattern and constants are well-defined and documented. The regex pattern is flexible enough to handle both dot and underscore separators while maintaining reasonable limits on version numbers.
14-18
: LGTM! Well-structured type definition.The type definition is well-structured and correctly handles all version cases including string versions, federation v1, and null version.
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
Outdated
Show resolved
Hide resolved
9c2e70e
to
4d50901
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
packages/libraries/federation-link-utils/src/link-import.ts (2)
9-11
: Add JSDoc documentation for better code maintainability.Consider adding JSDoc documentation to explain the format of the returned string and provide examples.
+ /** + * Returns a string representation of the federated link import. + * @returns {string} For imports with alias: `{ name: "name", as: "alias" }` + * For imports without alias: `"name"` + * @example + * new FederatedLinkImport("users", null).toString() // returns `"users"` + * new FederatedLinkImport("users", "u").toString() // returns `{ name: "users", as: "u" }` + */ public toString(): string {
13-47
: Add input validation and improve code structure.The method handles the parsing well but could benefit from some improvements:
- Add validation for empty name
- Use early returns to reduce nesting
- Add JSDoc documentation
+ /** + * Parses a GraphQL AST node to create FederatedLinkImport instances. + * @param {ConstValueNode} node - The AST node containing @link import declarations + * @returns {FederatedLinkImport[]} Array of parsed imports + * @throws {Error} If the node is not a list or contains invalid values + */ static fromTypedefs(node: ConstValueNode): FederatedLinkImport[] { if (node.kind !== Kind.LIST) { + throw new Error(`Expected a list of @link imports but got "${node.kind}"`); + } + + return node.values.map(v => { + if (v.kind === Kind.STRING) { + if (!v.value.trim()) { + throw new Error('Empty string is not allowed as @link import name'); + } + return new FederatedLinkImport(v.value, null); + } + + if (v.kind !== Kind.OBJECT) { + throw new Error(`Unexpected value kind "${v.kind}" in @link import declaration`); + } + + let name = ''; + let as: string | null = null; + + v.fields.forEach(f => { + if (f.name.value === 'name') { + if (f.value.kind !== Kind.STRING) { + throw new Error( + `Expected string value for @link "name" field but got "${f.value.kind}"`, + ); + } + name = f.value.value; + } else if (f.name.value === 'as') { + if (f.value.kind !== Kind.STRING) { + throw new Error( + `Expected string value for @link "as" field but got "${f.value.kind}"`, + ); + } + as = f.value.value; + } + }); + + if (!name.trim()) { + throw new Error('Empty string is not allowed as @link import name'); + } + + return new FederatedLinkImport(name, as); + }); - if (node.kind == Kind.LIST) { - const imports = node.values.map(v => { - if (v.kind === Kind.STRING) { - return new FederatedLinkImport(v.value, null); - } - if (v.kind === Kind.OBJECT) { - let name: string = ''; - let as: string | null = null; - - v.fields.forEach(f => { - if (f.name.value === 'name') { - if (f.value.kind !== Kind.STRING) { - throw new Error( - `Expected string value for @link "name" field but got "${f.value.kind}"`, - ); - } - name = f.value.value; - } else if (f.name.value === 'as') { - if (f.value.kind !== Kind.STRING) { - throw new Error( - `Expected string value for @link "as" field but got "${f.value.kind}"`, - ); - } - as = f.value.value; - } - }); - return new FederatedLinkImport(name, as); - } - throw new Error(`Unexpected value kind "${v.kind}" in @link import declaration`); - }); - return imports; - } - throw new Error(`Expected a list of @link imports but got "${node.kind}"`); }packages/libraries/federation-link-utils/src/link-url.ts (2)
1-10
: Consider verifying the match array length explicitly.Currently, the check
if (versionParts?.length)
only ensures a non-empty match array. Consider explicitly verifying thatversionParts
has at least 3 elements (the full match plus the two capture groups) for extra safety.
53-79
: Consider unifying overloads for maintainability.The overload approach works but can be simplified with union types or a single function signature. Also, verify if identity checks should apply for the
null
version case, since they're currently omitted.packages/libraries/federation-link-utils/src/index.ts (2)
10-10
: Optionally export this constant if it has broader use.Defining
FEDERATION_IDENTITY
here is fine. If other modules need it, consider re-exporting it to avoid duplication.
12-32
: Implementation is straightforward.The logic cleanly handles the Federation 1 fallback vs. Federation 2 detection. Ensure that ignoring possible
null
returns indetectImplementation
is acceptable; currently the code filters out onlyundefined
values.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
packages/libraries/federation-link-utils/README.md
(1 hunks)packages/libraries/federation-link-utils/package.json
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/link.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-import.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-url.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/src/linkable-spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/version.ts
(1 hunks)packages/libraries/federation-link-utils/tsconfig.json
(1 hunks)tsconfig.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
- packages/libraries/federation-link-utils/src/version.ts
- packages/libraries/federation-link-utils/tsconfig.json
- packages/libraries/federation-link-utils/src/tests/linkable-spec.spec.ts
- packages/libraries/federation-link-utils/package.json
- packages/libraries/federation-link-utils/README.md
- packages/libraries/federation-link-utils/src/tests/link-url.spec.ts
- packages/libraries/federation-link-utils/src/tests/link.spec.ts
- packages/libraries/federation-link-utils/src/tests/index.spec.ts
- tsconfig.json
- packages/libraries/federation-link-utils/src/linkable-spec.ts
- packages/libraries/federation-link-utils/src/link.ts
🧰 Additional context used
📓 Path-based instructions (1)
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/link-import.ts
packages/libraries/federation-link-utils/src/index.ts
packages/libraries/federation-link-utils/src/link-url.ts
⏰ Context from checks skipped due to timeout of 90000ms (12)
- GitHub Check: alpha / npm / snapshot
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: db-types / diff
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: test / db-migrations
- GitHub Check: alpha / cli-artifacts
- GitHub Check: SonarCloud
🔇 Additional comments (5)
packages/libraries/federation-link-utils/src/link-import.ts (1)
1-7
: LGTM! Well-structured class declaration.The class is well-designed with appropriate types and clear purpose.
packages/libraries/federation-link-utils/src/link-url.ts (3)
16-34
: Constructor logic is clear and well-structured.The constructor correctly sets the
major
andminor
fields using the parsed version tuple. The JSDoc above is also clear in describing the class functionality.
35-51
: Confirm ignoring query parameters and fragments is intentional.At the moment, any query parameters or fragments in the URL (
?foo=bar
,#baz
) are stripped away. Verify that this behavior meets the intended requirements.
81-89
: Pre-1.0 version compatibility logic looks correct.Requiring an exact minor match for major version 0 matches common semver practices. This approach will help avoid subtle breaks in 0.x releases.
packages/libraries/federation-link-utils/src/index.ts (1)
1-9
: Neat organization of imports and exports.Exporting modules from a single entry point helps maintain clarity and discoverability. This looks good.
4d50901
to
379ba8e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
packages/libraries/federation-link-utils/src/link-url.ts (3)
21-33
: Constructor validation
The constructor’s straightforward approach of parsing the version, then storing major/minor is clear. However, if invalid versions are common, consider providing warnings or throwing an error instead of silently setting them to -1.
35-51
: Consider robust URL parsing
This static method neatly parses and segments the URL. If you anticipate invalid or protocol-relative URLs, consider handling the potential exceptions that may arise from the URL constructor.
53-80
: Consolidate overloaded supports method
While overloading is valid, this method might benefit from a helper for each type of argument to reduce complexity and improve readability.packages/libraries/federation-link-utils/src/linkable-spec.ts (1)
66-79
: Refine unsupported version handling
Logging a warning on unsupported versions is helpful for debugging. As an enhancement, consider throwing a typed error or returning metadata about the mismatch instead of just using console.warn.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
packages/libraries/federation-link-utils/README.md
(1 hunks)packages/libraries/federation-link-utils/package.json
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/index.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/link.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-import.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-url.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/src/linkable-spec.ts
(1 hunks)packages/libraries/federation-link-utils/src/version.ts
(1 hunks)packages/libraries/federation-link-utils/tsconfig.json
(1 hunks)tsconfig.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
- packages/libraries/federation-link-utils/src/version.ts
- packages/libraries/federation-link-utils/src/tests/link-url.spec.ts
- packages/libraries/federation-link-utils/package.json
- packages/libraries/federation-link-utils/src/tests/index.spec.ts
- tsconfig.json
- packages/libraries/federation-link-utils/README.md
- packages/libraries/federation-link-utils/src/link-import.ts
- packages/libraries/federation-link-utils/tsconfig.json
- packages/libraries/federation-link-utils/src/tests/link.spec.ts
- packages/libraries/federation-link-utils/src/tests/linkable-spec.spec.ts
- packages/libraries/federation-link-utils/src/link.ts
🧰 Additional context used
📓 Path-based instructions (1)
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/index.ts
packages/libraries/federation-link-utils/src/linkable-spec.ts
packages/libraries/federation-link-utils/src/link-url.ts
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: alpha / cli-artifacts
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: SonarCloud
🔇 Additional comments (9)
packages/libraries/federation-link-utils/src/link-url.ts (3)
1-10
: Confirm the intended version range
The regex and logic extract only major and minor from a “v.” format, defaulting to [-1, -1] if no match is found. Ensure this covers all intended scenarios (e.g., ignoring patch versions and handling uppercase or extra segments) and verify that returning [-1, -1] is the correct fallback.
16-20
: Visibility and naming for major/minor
Declaring these version components as private fields is good for encapsulation. The naming is consistent and makes the code self-explanatory.
81-89
: Clear version compatibility check
The logic in “isCompatibleVersion” is concise and well-structured for your major-minor usage, especially handling major===0 as an exact match for the minor version. No additional suggestions here.packages/libraries/federation-link-utils/src/index.ts (2)
1-9
: Concise re-export pattern
The import and export statements are neatly organized, making the various utilities discoverable from a single entry point.
12-32
: Ensure partial federation usage is intentional
This function toggles between Federation V1 and V2 logic based on the presence of Federation V2 links. If partial or mixed usage of Federation versions is ever expected, consider clarifying or handling that edge case.packages/libraries/federation-link-utils/src/linkable-spec.ts (4)
1-19
: Symbols and typed definitions
Defining FEDERATION_V1, NULL_VERSION, and Versioned clarifies important fallback behavior and indicates special-case handling.
20-41
: Repeat: Validate version strings before sorting
Similar to past feedback, the sorting logic assumes strictly valid “v.” versions, and this could lead to unexpected behavior if invalid strings slip in.
43-60
: Highest matching version retrieval
“detectLinkVersion” scans for the largest compatible version. The logic is logically sound for descending-sorted version keys and graceful fallback to null if the link supports no known version.
62-64
: Straightforward link lookup
“findLinkByIdentity” is concise and effectively retrieves the correct link instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (8)
packages/libraries/federation-link-utils/src/linkable-spec.ts (2)
3-3
: Consider tightening the version regex pattern.The current regex pattern
v(\d{1,3})[\._](\d{1,4})/i
is quite permissive:
- Allows up to 3 digits for major version (e.g., v999)
- Allows up to 4 digits for minor version (e.g., v1.9999)
- Accepts both dot and underscore as separators
- Is case-insensitive
This could potentially allow invalid or unconventional version formats.
-const VERSION_KEY_MATCH = /v(\d{1,3})[\._](\d{1,4})/i; +const VERSION_KEY_MATCH = /^v(\d{1,2})\.(\d{1,3})$/;
76-79
: Improve error message readability.The error message construction can be improved for better readability and to fix the repeated word "compatible".
- throw new Error( - `Cannot apply @link due to unsupported version found for "${maybeLink}". ` + - `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.`, - ); + const availableVersions = this.sortedVersionKeys + .map(v => `${v.split.join('.')}`) + .join(', '); + throw new Error( + `Cannot apply @link due to unsupported version found for "${maybeLink}". ` + + `Available versions: ${availableVersions} and any version these are compatible with. ` + + `Try downgrading the link version to match these versions.` + );packages/libraries/federation-link-utils/tests/link.spec.ts (1)
13-37
: Consider adding error case tests.While the happy path tests are comprehensive, consider adding test cases for:
- Invalid URL formats
- Missing required fields
- Duplicate link definitions
packages/libraries/federation-link-utils/tests/linkable-spec.spec.ts (1)
5-66
: Consider adding edge case tests.While the current test suite is good, consider adding test cases for:
- Invalid version formats (e.g., v1, v1.0.0)
- Version numbers at boundary conditions (e.g., v999.9999)
- Multiple links with the same identity
packages/libraries/federation-link-utils/tests/index.spec.ts (1)
4-59
: Consider refactoring the metadata collection logic for better maintainability.The metadata collection logic in the v0.1 handler could be simplified by extracting the directive argument parsing into a helper function.
Consider this refactor:
'v0.1': resolveImportName => (typeDefs: DocumentNode) => { + const getDirectiveArgValue = (directive: any, argName: string) => + (directive?.arguments?.find(a => a.name.value === argName)?.value as StringValueNode | undefined)?.value ?? '??'; + const collectedMeta: Record<string, Record<string, string>> = {}; const metaName = resolveImportName('@meta'); const exampleName = resolveImportName('@example'); visit(typeDefs, { FieldDefinition: node => { let metaData: Record<string, string> = {}; const fieldName = node.name.value; const meta = node.directives?.find(d => d.name.value === metaName); if (meta) { - metaData['name'] = - ( - meta.arguments?.find(a => a.name.value === 'name')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; - metaData['content'] = - ( - meta.arguments?.find(a => a.name.value === 'content')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; + metaData['name'] = getDirectiveArgValue(meta, 'name'); + metaData['content'] = getDirectiveArgValue(meta, 'content'); } const example = node.directives?.find(d => d.name.value === exampleName); if (example) { - metaData['eg'] = - ( - example.arguments?.find(a => a.name.value === 'eg')?.value as - | StringValueNode - | undefined - )?.value ?? '??'; + metaData['eg'] = getDirectiveArgValue(example, 'eg'); }packages/libraries/federation-link-utils/src/link.ts (3)
5-40
: Enhance error handling and type safety.Consider these improvements:
- Add more descriptive warning messages
- Consider throwing errors for invalid arguments instead of just logging warnings
- Add type guards for better type safety
Consider this refactor:
function linkFromArgs(args: readonly ConstArgumentNode[]): FederatedLink | undefined { let url: FederatedLinkUrl | undefined, imports: FederatedLinkImport[] = [], as: string | null = null; for (const arg of args) { switch (arg.name.value) { case 'url': { if (arg.value.kind === Kind.STRING) { url = FederatedLinkUrl.fromUrl(arg.value.value); } else { - console.warn(`Unexpected kind, ${arg.value.kind}, for argument "url" in @link.`); + console.warn( + `Invalid @link "url" argument: Expected string, got ${arg.value.kind}. ` + + 'This may cause federation link resolution to fail.' + ); } break; } case 'import': { + if (!FederatedLinkImport.isValidImport(arg.value)) { + console.warn( + `Invalid @link "import" argument: ${arg.value.kind}. ` + + 'This may cause federation link resolution to fail.' + ); + break; + } imports = FederatedLinkImport.fromTypedefs(arg.value); break; } case 'as': { if (arg.value.kind === Kind.STRING) { as = arg.value.value ?? null; } else { - console.warn(`Unexpected kind, ${arg.value.kind}, for argument "as" in @link.`); + console.warn( + `Invalid @link "as" argument: Expected string, got ${arg.value.kind}. ` + + 'This may cause federation link resolution to fail.' + ); } break; }
52-74
: Consider optimizing array operations and adding input validation.The
fromTypedefs
method could be optimized to reduce array operations and add input validation.Consider this refactor:
static fromTypedefs(typeDefs: DocumentNode): FederatedLink[] { + if (!typeDefs?.definitions?.length) { + return []; + } - let links: FederatedLink[] = []; return typeDefs.definitions - .filter(definition => + .reduce((links, definition) => { if (definition.kind === Kind.SCHEMA_EXTENSION || definition.kind === Kind.SCHEMA_DEFINITION) { const defLinks = definition.directives?.filter( directive => directive.name.value === 'link', ); const parsedLinks = defLinks?.map(l => linkFromArgs(l.arguments ?? [])).filter(l => l !== undefined) ?? []; - links = links.concat(parsedLinks); + links.push(...parsedLinks); } + return links; + }, [] as FederatedLink[]); - return links; }
98-114
: Improve type handling and documentation for version support methods.The ts-expect-error comment could be more descriptive, and the methods could benefit from better documentation.
Consider these improvements:
+/** + * Checks if this link supports a specific version. + * @param version - Version string (e.g., "v2.3"), version numbers, FederatedLinkUrl instance, or null + * @returns true if the version is supported + */ supports(version: string): boolean; supports(major: number, minor: number): boolean; supports(version: FederatedLinkUrl): boolean; supports(version: null): boolean; supports(...args: [string] | [number, number] | [FederatedLinkUrl] | [null]): boolean { - /** @ts-expect-error: ignore tuple error. These are tuples and can be spread. tsc is wrong. */ + /** @ts-expect-error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter. + * This is a false positive as we're explicitly handling tuple types in the function signature. + */ return this.url.supports(...args); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
packages/libraries/federation-link-utils/src/link-url.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/src/linkable-spec.ts
(1 hunks)packages/libraries/federation-link-utils/tests/index.spec.ts
(1 hunks)packages/libraries/federation-link-utils/tests/link-url.spec.ts
(1 hunks)packages/libraries/federation-link-utils/tests/link.spec.ts
(1 hunks)packages/libraries/federation-link-utils/tests/linkable-spec.spec.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/libraries/federation-link-utils/src/link-url.ts
🧰 Additional context used
📓 Path-based instructions (1)
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/tests/link-url.spec.ts
packages/libraries/federation-link-utils/tests/linkable-spec.spec.ts
packages/libraries/federation-link-utils/tests/link.spec.ts
packages/libraries/federation-link-utils/tests/index.spec.ts
packages/libraries/federation-link-utils/src/linkable-spec.ts
packages/libraries/federation-link-utils/src/link.ts
📓 Learnings (3)
packages/libraries/federation-link-utils/tests/linkable-spec.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/federation-link-utils/tests/link.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/federation-link-utils/src/__tests__/index.spec.ts:6-7
Timestamp: 2025-02-03T23:57:17.342Z
Learning: The federation-link-utils library supports two styles of directive imports:
1. Named imports (e.g., `@meta`)
2. Namespaced imports (e.g., `@metadata__example`)
Both styles can be used together in the same schema.
packages/libraries/federation-link-utils/tests/index.spec.ts (2)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/federation-link-utils/src/__tests__/index.spec.ts:6-7
Timestamp: 2025-02-03T23:57:17.342Z
Learning: The federation-link-utils library supports two styles of directive imports:
1. Named imports (e.g., `@meta`)
2. Namespaced imports (e.g., `@metadata__example`)
Both styles can be used together in the same schema.
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: alpha / cli-artifacts
- GitHub Check: SonarCloud
🔇 Additional comments (8)
packages/libraries/federation-link-utils/src/linkable-spec.ts (2)
28-40
: Add validation for version format.The version sorting logic assumes all version strings are valid. Consider adding validation to handle invalid version formats.
this.sortedVersionKeys = Object.keys(versions) .map( ( key, ): { key: string; split?: number[]; - } => ({ key, split: key.match(VERSION_KEY_MATCH)?.map(Number).slice(1) }), + } => { + const match = key.match(VERSION_KEY_MATCH); + if (match) { + const [major, minor] = match.slice(1).map(Number); + if (Number.isNaN(major) || Number.isNaN(minor)) { + throw new Error(`Invalid version format: ${key}`); + } + return { key, split: [major, minor] }; + } + return { key, split: undefined }; + }, )
51-52
: Address the TODO comment about version ordering.The comment suggests that the version finding logic should be reversed to find the first version in ascending order. This could affect version compatibility checks.
Could you clarify why the version finding logic needs to be reversed? This would help in implementing the correct solution.
packages/libraries/federation-link-utils/tests/link-url.spec.ts (1)
1-60
: Well-structured test suite with good coverage!The test suite thoroughly covers:
- URL parsing with various formats
- Version compatibility checks
- URL normalization
packages/libraries/federation-link-utils/tests/index.spec.ts (3)
61-82
: LGTM! Comprehensive test coverage for Federation 2.x support.The test effectively validates both named and namespaced directive imports, aligning with the supported directive import styles.
84-99
: LGTM! Good error case coverage.The test effectively validates the fallback behavior when Federation 2 support is missing.
101-119
: LGTM! Good version format coverage.The test effectively validates support for underscores in version identifiers.
packages/libraries/federation-link-utils/src/link.ts (2)
42-50
: LGTM! Clean and focused implementation.The function correctly handles directive namespacing, including the special case for @ prefixes.
130-140
: LGTM! Well-documented and robust implementation.The method correctly handles all directive import styles and includes clear documentation explaining the @ prefix handling.
return this.as ? `{ name: "${this.name}", as: "${this.as}" }` : `"${this.name}"`; | ||
} | ||
|
||
static fromTypedefs(node: ConstValueNode): FederatedLinkImport[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If see that this code (and some other in the lib implementation) is throwing errors, maybe we should use better kind of errors instead of just Error
with a message? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a few minor comments. let's also wait for @kamilkisiela and @n1ru4l to review?
thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (7)
packages/libraries/federation-link-utils/src/index.ts (1)
1-4
: Enhance documentation with spec reference.Consider adding a link to the Federation V2
@link
specification for better context and maintainability./** * Exposes a simple and efficient API for interacting with Federation V2's `@link` directives - * according to spec. + * according to the Federation V2 specification: + * https://www.apollographql.com/docs/federation/federation-spec/ */packages/libraries/federation-link-utils/src/link.ts (3)
39-39
: Use explicit return undefined.For better code clarity and maintainability, consider using an explicit return statement.
- return; + return undefined;
9-35
: Add validation for duplicate arguments.The current implementation silently accepts duplicate arguments, which could lead to unexpected behavior. Consider adding validation to ensure each argument appears only once.
53-57
: Add input validation in constructor.Consider adding validation for constructor parameters to ensure the class is initialized with valid data:
constructor( private readonly url: FederatedLinkUrl, private readonly as: string | null, private readonly imports: FederatedLinkImport[], - ) {} + ) { + if (!url) { + throw new Error('URL is required'); + } + if (as !== null && typeof as !== 'string') { + throw new Error('If provided, "as" must be a string'); + } + if (!Array.isArray(imports)) { + throw new Error('imports must be an array'); + } + }packages/libraries/federation-link-utils/src/link-url.ts (3)
1-1
: Document version number constraints.The regex allows specific digit limits (3 for major, 4 for minor) and is case-insensitive. Please document these constraints and the rationale behind them.
-const VERSION_MATCH = /v(\d{1,3})\.(\d{1,4})/i; +/** + * Matches version strings in the format v<major>.<minor> where: + * - major: 1-3 digits (0-999) + * - minor: 1-4 digits (0-9999) + * - case-insensitive 'v' prefix + */ +const VERSION_MATCH = /v(\d{1,3})\.(\d{1,4})/i;
3-10
: Document special return values and add input validation.The function returns
[-1, -1]
for invalid versions without documenting this behavior. Also, consider adding input validation.+/** + * Parses a version string in the format v<major>.<minor>. + * @param version - The version string to parse + * @returns A tuple of [major, minor] version numbers. + * Returns [-1, -1] if the version string is invalid or null. + */ function parseVersion(version: string | null): [number, number] { + if (!version) { + return [-1, -1]; + } const versionParts = version?.match(VERSION_MATCH); if (versionParts?.length) { const [_full, major, minor] = versionParts;
35-51
: Add input validation and improve URL parsing robustness.The
fromUrl
method could benefit from input validation and more robust URL parsing.static fromUrl = (urlSource: string): FederatedLinkUrl => { + if (!urlSource) { + throw new Error('URL source cannot be empty'); + } + + try { const url = new URL(urlSource); const parts = url.pathname.split('/').filter(Boolean); const versionOrName = parts[parts.length - 1]; if (versionOrName) { if (VERSION_MATCH.test(versionOrName)) { const maybeName = parts[parts.length - 2]; return new FederatedLinkUrl( url.origin + (maybeName ? `/${parts.slice(0, parts.length - 1).join('/')}` : ''), maybeName ?? null, versionOrName, ); } return new FederatedLinkUrl(`${url.origin}/${parts.join('/')}`, versionOrName, null); } return new FederatedLinkUrl(url.origin, null, null); + } catch (error) { + throw new Error(`Invalid URL format: ${error.message}`); + } };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/libraries/federation-link-utils/README.md
(1 hunks)packages/libraries/federation-link-utils/package.json
(1 hunks)packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/libraries/federation-link-utils/src/link-url.ts
(1 hunks)packages/libraries/federation-link-utils/src/link.ts
(1 hunks)packages/libraries/federation-link-utils/tests/index.spec.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/libraries/federation-link-utils/README.md
- packages/libraries/federation-link-utils/package.json
- packages/libraries/federation-link-utils/tests/index.spec.ts
🧰 Additional context used
📓 Path-based instructions (1)
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/index.ts
packages/libraries/federation-link-utils/src/link-url.ts
packages/libraries/federation-link-utils/src/link.ts
⏰ Context from checks skipped due to timeout of 90000ms (10)
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: alpha / cli-artifacts
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: test / unit
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: test / db-migrations
- GitHub Check: SonarCloud
🔇 Additional comments (7)
packages/libraries/federation-link-utils/src/index.ts (3)
9-13
: LGTM! Well-defined types and constants.The
LinkVersion
type effectively captures all possible version formats, including the special case for Federation V1 as mentioned in the PR objectives.
15-38
: Consider renaming the function toextractLinkedImplementations
.As suggested in a previous review, the name
extractLinkedImplementations
might better reflect the function's purpose.-export function extractLinkImplementations(typeDefs: DocumentNode): { +export function extractLinkedImplementations(typeDefs: DocumentNode): {
39-76
: LGTM! Robust implementation with good error handling.The implementation effectively:
- Handles both Federation V1 and V2 as discussed in the PR objectives
- Provides clear error messages guiding users to check implementation status
- Comprehensively handles all version formats
packages/libraries/federation-link-utils/src/link.ts (2)
42-50
: LGTM! Clean and efficient implementation.The function correctly handles both regular and directive names, with proper edge case handling for '@' prefixed names.
60-73
: LGTM! Well-implemented static factory method.The
fromTypedefs
method efficiently collects and processes@link
directives from GraphQL type definitions.packages/libraries/federation-link-utils/src/link-url.ts (2)
81-89
: LGTM! Well-implemented version compatibility logic.The version compatibility implementation correctly handles:
- Federation 2 versioning requirements
- Special case for v0.x versions requiring exact matches
- Standard version comparison for v1+ versions
12-15
: Verify alignment with Federation 2 requirements.The implementation aligns with the PR objectives for managing Federation 2 link directives. However, let's verify that the version constraints match the Federation 2 specification.
✅ Verification successful
🌐 Web query:
What are the version number constraints in Federation 2 link specification?
💡 Result:
Apollo Federation 2 introduces specific versioning requirements for its
@link
directive and tooling compatibility:Version Constraints in
@link
Directives
Explicit Version Specification
Subgraphs must declare Federation 2 compatibility using an exact version in their@link
URL:extend schema @link(url: "https://specs.apollo.dev/federation/v2.x", import: [...])Supported versions range from
v2.0
tov2.9
[7][10]. Partial matches likev2
are invalid – full semantic versioning (e.g.,v2.3
) is required[1][7].Backward Compatibility
Newer minor versions (e.g.,v2.5
) include all features from prior versions (v2.0
-v2.4
)[7]. Subgraphs using older versions remain compatible with newer composition tools[1][9].Tooling Requirements
Component Version Constraints @apollo/gateway
≥ 2.x supports Fed 1 & 2 schemas[1] GraphOS Router Requires explicit Fed 2 opt-in[1][9] Rover CLI ≥ 0.5.x for Fed 2 composition[1][9] Subgraph Libraries Must support Fed 2 directives (e.g., @apollo/subgraph
≥ 2.x)[1][3]Key Constraints
- Managed Federation requires explicit Fed 2 opt-in via Apollo Studio[1][9]
- Mixed Environments allow incremental adoption – Fed 1 and Fed 2 subgraphs can coexist during migration[1]
- Directive Import must explicitly list all used federation directives in
@link(import:)
[1][7]For example, valid Fed 2 declarations include:
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key", "@shareable"])While invalid declarations would omit the full minor version or mismatch tooling capabilities[7][10]. Always verify your gateway/router supports your chosen Fed 2.x version before deployment[1][9].
Citations:
- 1: https://www.apollographql.com/docs/graphos/reference/migration/to-federation-version-2
- 2: Link to the federation specifcation v2 is not detected by rover apollographql/rover#1115
- 3: https://github.com/apollographql/federation-hotchocolate
- 4: https://openid.net/specs/openid-federation-1_0.html
- 5: https://a.ml/docs/related-docs/graphql_federation/
- 6: https://specifications.openehr.org/releases/AM/latest/ADL2.html
- 7: https://www.apollographql.com/docs/graphos/reference/federation/versions
- 8: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html
- 9: https://github.com/apollographql/federation/blob/main/ROADMAP.md
- 10: https://specs.apollo.dev
Federation 2 Version Constraints Comply with Specifications
- The code and its documentation enforce the use of explicit semantic versions (e.g., “v2.3”) for Federation 2, as required.
- Partial versions like “v2” are disallowed, matching the Apollo Federation 2 specification.
- The version range (e.g., from v2.0 to v2.9) and explicit minor version requirement are consistent with the documentation.
if (arg.value.kind === Kind.STRING) { | ||
url = FederatedLinkUrl.fromUrl(arg.value.value); | ||
} else { | ||
console.warn(`Unexpected kind, ${arg.value.kind}, for argument "url" in @link.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Replace console.warn with a proper logging mechanism.
As this is a library package that will be used by customers, using console.warn
directly is not recommended. Consider:
- Using a proper logging interface that can be configured by the consuming application
- Throwing validation errors instead of warnings for invalid argument types
Also applies to: 27-27
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/libraries/federation-link-utils/README.md (2)
13-17
: Enhance features section with more details and consistent formatting.While the features are clearly listed, consider these improvements:
- Use consistent code formatting for technical terms (e.g.,
`graphql`
as a peer dependency).- Add brief explanations for each feature to help users understand their benefits.
## Features - Link version support. - Import `as`/namespacing support that follows the [link spec](https://specs.apollo.dev/link/v1.0/). -- Only `graphql` as a peer dependency. +- Only `` `graphql` `` as a peer dependency. + +### Feature Details + +- **Link version support**: Enables handling multiple versions of Federation 2 features. +- **Import `as`/namespacing support**: Follows the link specification for proper import naming and namespacing. +- **Minimal dependencies**: Only requires `` `graphql` `` as a peer dependency, keeping the library lightweight.
19-65
: Enhance usage documentation with additional examples and error handling.The current example is good but could be more comprehensive. Consider adding:
- More inline comments explaining the TypeScript code's functionality
- Error handling examples
- Additional use cases (e.g., multiple versions, different directives)
// specs.ts import { extractLinkImplementations } from '@graphql-hive/federation-link-utils' const typeDefs = parse(sdl) +// Extract link implementations and get utility functions const { matchesImplementation, resolveImportName } = extractLinkImplementations(typeDefs); + +// Check if the schema implements the specific version of our spec if (matchesImplementation('https://specs.graphql-hive.com/example', 'v1.0')) { const examples: Record<string, string> = {} + // Resolve the actual directive name used in the schema (handles 'as' imports) const exampleName = resolveImportName('https://specs.graphql-hive.com/example', '@example') + + try { visit(typeDefs, { FieldDefinition: node => { const example = node.directives?.find(d => d.name.value === exampleName) if (example) { examples[node.name.value] = ( example.arguments?.find(a => a.name.value === 'eg')?.value as | StringValueNode | undefined )?.value } } }) return examples + } catch (error) { + console.error('Failed to process schema:', error) + throw error + } } + +// Example with multiple versions +if (matchesImplementation('https://specs.graphql-hive.com/example', 'v2.0')) { + // Handle v2.0 implementation +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/libraries/federation-link-utils/README.md
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/README.md
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: alpha / cli-artifacts
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: SonarCloud
🔇 Additional comments (2)
packages/libraries/federation-link-utils/README.md (2)
1-5
: LGTM! Clear and informative introduction.The introduction effectively establishes context and provides relevant links to help users understand the project's background.
9-11
: LGTM! Clear purpose statement with relevant documentation link.The library's purpose is well-defined and includes a helpful link to the Federation
@link
directive documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (15)
packages/services/schema/src/lib/compose.ts (5)
95-142
: Performance consideration for supergraph-to-public schema
This method transforms the supergraph to a public schema on every composition. Consider caching for large-scale usage to avoid repeated parse/print overhead.
259-283
: Broker-based external calls
This thin wrapper aroundcallExternalService
is clear. You might consider merging if usage remains minimal.
285-414
: Robust external compose call
Comprehensive retry logic, structured error objects, and tracing with spans. Watch out for potential overhead of 5 retries. Adjust if needed for performance-critical environments.
416-419
: Explanatory error mapping
codeToExplanationMap
is well-structured for known error codes. For additional clarity, consider logging unknown codes to assist troubleshooting.
421-427
:translateMessage
fallback
The fallback to no message is acceptable, but providing a default fallback string for unknown codes might improve debugging.packages/services/schema/src/lib/federation-tag-extraction.ts (2)
401-404
: Extracting tags from document
The function effectively collects directive-based tags. Consider validating unexpected arguments or missing directive fields if the schema might contain partial data.
438-440
: Minor doc comment
You might expand these lines to better explain the purpose of the extraction or link relevant resources.packages/services/schema/src/orchestrators.ts (3)
216-221
: Extracting global tags
Automatically extracting tags from the supergraph is helpful for additional logic. Consider verifying the performance impact for extremely large schemas.
279-279
: Preserving subgraph identity
Spreadingsubgraph
fields while replacingtypeDefs
ensures minimal duplication.
282-282
: Asynchronous composition
Awaits each contract’s composition. Works seamlessly unless concurrency needs bounding with a queue or pool.packages/services/schema/src/lib/errors.ts (1)
10-29
: Consider using Zod for input validation.Since the codebase prefers Zod for input/output validations, consider using it to validate the error structure.
+import { z } from 'zod'; + +const CompositionFailureErrorSchema = z.object({ + message: z.string(), + source: z.enum(['graphql', 'composition']), +}); + export function toValidationError(error: any, source: CompositionErrorSource) { + const errorMessage = error instanceof GraphQLError || error instanceof Error + ? error.message + : String(error); + + return CompositionFailureErrorSchema.parse({ + message: errorMessage, + source, + }); - if (error instanceof GraphQLError) { - return { - message: error.message, - source, - }; - } - - if (error instanceof Error) { - return { - message: error.message, - source, - }; - } - - return { - message: error as string, - source, - }; }packages/services/schema/src/lib/add-inaccessible-to-unreachable-types.ts (2)
6-19
: Enhance JSDoc documentation.The JSDoc comments should include parameter descriptions and return type information.
/** * Adds inaccessible directive to unreachable types * - * @param resolveName - * @param compositionResult - * @param supergraphSDL - * @returns + * @param resolveName - Function to resolve directive names + * @param compositionResult - Result of the composition operation + * @param supergraphSDL - The supergraph schema in DocumentNode format + * @returns {ComposerMethodResult} Updated composition result with inaccessible directives added */
20-32
: Consider extracting federation types to a constant.The federation types set could be moved to a constant to improve maintainability and reusability.
+const FEDERATION_TYPES = [ + ['https://specs.apollo.dev/join', 'FieldSet'], + ['https://specs.apollo.dev/join', 'Graph'], + ['https://specs.apollo.dev/link', 'Import'], + ['https://specs.apollo.dev/link', 'Purpose'], + ['https://specs.apollo.dev/federation', 'Policy'], + ['https://specs.apollo.dev/federation', 'Scope'], + ['https://specs.apollo.dev/join', 'DirectiveArguments'], +] as const; + export const addInaccessibleToUnreachableTypes = ( resolveName: (identity: string, name: string) => string, compositionResult: ComposerMethodResult, supergraphSDL: DocumentNode, ): ComposerMethodResult => { const inaccessibleDirectiveName = resolveName( 'https://specs.apollo.dev/inaccessible', '@inaccessible', ); - const federationTypes = new Set([ - resolveName('https://specs.apollo.dev/join', 'FieldSet'), - resolveName('https://specs.apollo.dev/join', 'Graph'), - resolveName('https://specs.apollo.dev/link', 'Import'), - resolveName('https://specs.apollo.dev/link', 'Purpose'), - resolveName('https://specs.apollo.dev/federation', 'Policy'), - resolveName('https://specs.apollo.dev/federation', 'Scope'), - resolveName('https://specs.apollo.dev/join', 'DirectiveArguments'), - ]); + const federationTypes = new Set( + FEDERATION_TYPES.map(([identity, name]) => resolveName(identity, name)) + );packages/services/schema/src/lib/__tests__/add-inaccessible-to-unreachable-types.spec.ts (1)
6-28
: Add more test cases for edge scenarios.The test only covers a basic scenario. Consider adding tests for:
- Empty schema
- Schema with only federation types
- Schema with circular dependencies
Example test case:
test('Handles empty schema', async () => { let compositionResult = composeFederationV2( [{ name: 'empty', url: 'https://empty-example.graphql-hive.com', typeDefs: parse('type Query { _empty: String }'), }], console as any, ); // ... rest of the test });packages/libraries/federation-link-utils/src/index.ts (1)
6-8
: Consider using type-only imports for DocumentNode.Since
DocumentNode
is only used as a type, consider using a type-only import to optimize the bundle size.-import type { DocumentNode } from 'graphql'; +import { type DocumentNode } from 'graphql';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
integration-tests/tests/schema/contracts.spec.ts
(0 hunks)packages/libraries/federation-link-utils/src/index.ts
(1 hunks)packages/services/schema/package.json
(1 hunks)packages/services/schema/src/api.ts
(1 hunks)packages/services/schema/src/lib/__tests__/add-inaccessible-to-unreachable-types.spec.ts
(1 hunks)packages/services/schema/src/lib/add-inaccessible-to-unreachable-types.ts
(1 hunks)packages/services/schema/src/lib/compose.ts
(1 hunks)packages/services/schema/src/lib/errors.ts
(1 hunks)packages/services/schema/src/lib/federation-tag-extraction.spec.ts
(1 hunks)packages/services/schema/src/lib/federation-tag-extraction.ts
(5 hunks)packages/services/schema/src/orchestrators.ts
(5 hunks)packages/services/schema/src/types.ts
(1 hunks)
💤 Files with no reviewable changes (1)
- integration-tests/tests/schema/contracts.spec.ts
✅ Files skipped from review due to trivial changes (1)
- packages/services/schema/src/types.ts
🧰 Additional context used
📓 Path-based instructions (2)
`packages/services/**`: Microservices written in NodeJS and ...
packages/services/**
: Microservices written in NodeJS and TypeScript. Most of the dirs under this directory are packages and deployed as Docker packages.
Interaction between services is done using tRPC.
We prefer writing code that does input/output validations with Zod.
The directories that ends with-worker
are projects that are built on top of CloudFlare Workers infrastructure and deployed there.
Thecdn-worker
is a special one, it serves a high-available, detached CDN on CloudFlare Workers. This is how our end-users fetches the crucial data from our platform, in a way that does not depend/couple on our main API (served as GraphQL API).
packages/services/schema/package.json
packages/services/schema/src/api.ts
packages/services/schema/src/lib/add-inaccessible-to-unreachable-types.ts
packages/services/schema/src/lib/errors.ts
packages/services/schema/src/lib/__tests__/add-inaccessible-to-unreachable-types.spec.ts
packages/services/schema/src/orchestrators.ts
packages/services/schema/src/lib/federation-tag-extraction.ts
packages/services/schema/src/lib/federation-tag-extraction.spec.ts
packages/services/schema/src/lib/compose.ts
`packages/libraries/**`: Most of the dirs here are NPM packa...
packages/libraries/**
: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.
packages/libraries/federation-link-utils/src/index.ts
⏰ Context from checks skipped due to timeout of 90000ms (10)
- GitHub Check: alpha / npm / snapshot
- GitHub Check: test / unit
- GitHub Check: typescript / typecheck
- GitHub Check: static-analysis / analyze (typescript)
- GitHub Check: static-analysis / analyze (javascript)
- GitHub Check: build / dockerize (linux/arm64)
- GitHub Check: build / dockerize (linux/amd64)
- GitHub Check: code-style / eslint-and-prettier
- GitHub Check: alpha / cli-artifacts
- GitHub Check: SonarCloud
🔇 Additional comments (31)
packages/services/schema/src/lib/compose.ts (7)
1-18
: Imports and basic setup look good
No major issues found. The usage of built-in Node crypto, got, zod, and Sentry is well-structured.
19-58
: Validate potential large payload concerns
TheEXTERNAL_COMPOSITION_RESULT
schema provides a robust shape for external composition results. However, if the external service returns very large bodies, consider adding more safeguards, such as request size or schema-level constraints.
59-93
: Apollo Federation v1 composition logic
Implementation is consistent with Federation v1. Good error-checking for composition errors.
144-223
: External composition function
Logic for building the body, generating an HMAC signature, and validating the response via zod is well-organized. Error handling is comprehensive. No immediate security concerns.
224-245
: SDL validity check
checkExternalCompositionCompatibility
properly warns when validation errors arise. Confirm that silent failures aren’t introduced if minimal logging is enforced in production.
247-253
: Graceful error transformation
errorWithPossibleCode
effectively converts GraphQLErrors into structured validation errors. This helps unify error handling across the codebase.
255-257
: Secret hashing uses secure defaults
Creating an HMAC with UTF-8 input is a standard approach. Ensure the secret is strong enough and updated regularly.packages/services/schema/src/lib/federation-tag-extraction.ts (4)
20-20
: Leverages the new federation-link-utils
ImportingextractLinkImplementations
aligns with the new library approach, removing duplication from older custom implementations.
108-113
: Resolving directive names
Good usage ofresolveImportName
for@inaccessible
and@tag
. This strategy keeps the code flexible and version-agnostic.
356-361
: Applying the tag filter to subgraphs
Mapping subgraphs and merging the filter transformation is concise. Confirm that logging or debugging is available if a subgraph transform fails unexpectedly.
422-435
: Dynamic tag directive name extraction
createTagDirectiveNameExtractionStrategy
is well-structured. If future directives gain extra arguments beyondname
, ensure graceful handling.packages/services/schema/src/orchestrators.ts (10)
16-35
: Updated imports & dependencies
Imports forextractLinkImplementations
andcomposeFederationV1/V2
centralize the composition logic. This is a beneficial modular approach.
178-178
: Composition method determination
Inline comment helps clarify the selection logic for Federation v1, Federation v2, or external. No changes needed.
211-211
: Typed composition result
CapturingCompositionResult
with the extension fields is a good practice for clarity and type safety.
223-228
: Combining composition outcome
Merging in thecomposed
result with the spread operator is straightforward. Confirm that no fields inadvertently overwrite prior data.
234-235
: Early exit on no contracts
Short-circuiting is a clear approach. Good usage of optional chaining.
239-239
: Skipping contracts on failure
Skipping contract composition if the main composition fails is logical. Preserves the simpler error shape.
262-262
: Comment clarity
The inline comment clarifies the upcoming contract composition process. No changes needed.
265-265
: Replaces tags with inaccessible directives
This ensures only relevant tags remain accessible, aligning with the contract’s scope.
267-274
: Fan-out approach to multiple tag filters
Constructing a filter object for each contract is a simple yet powerful pattern. Watch for potential memory overhead with many contracts.
290-297
: Adding inaccessible directives to unreachable types
Conditional logic is well-structured. If the contract demands it, we further parse the supergraph and calladdInaccessibleToUnreachableTypes
.packages/services/schema/src/lib/errors.ts (2)
3-8
: LGTM! Well-defined error types.The type definitions are clear and follow TypeScript best practices.
31-33
: LGTM! Clean higher-order function implementation.The
errorWithSource
function is well-implemented as a higher-order function, making it easy to create specialized error handlers.packages/services/schema/src/lib/add-inaccessible-to-unreachable-types.ts (1)
34-37
: LGTM! Good early return pattern.The early return with a clear comment explaining the reason is a good practice.
packages/services/schema/src/api.ts (1)
10-10
: LGTM! Good modularization of error types.Moving error types to a dedicated module improves code organization.
packages/services/schema/src/lib/__tests__/add-inaccessible-to-unreachable-types.spec.ts (1)
30-100
: LGTM! Good use of snapshots for schema validation.The test effectively validates both the supergraph and public schema transformations.
packages/libraries/federation-link-utils/src/index.ts (3)
1-5
: LGTM! Clear and concise documentation.The file header effectively communicates the purpose of the utility and its relationship to the Federation V2 specification.
10-12
: LGTM! Well-defined type and constant.The
FEDERATION_V1
symbol andLinkVersion
type are well-defined and provide good type safety for version handling.
14-82
: LGTM! Well-structured and documented function.The
extractLinkImplementations
function is:
- Well-documented with clear JSDoc comments
- Handles edge cases properly
- Returns a clean interface with two methods
However, based on the past review comment, consider renaming the function:
-export function extractLinkImplementations(typeDefs: DocumentNode): { +export function extractLinkedImplementations(typeDefs: DocumentNode): {packages/services/schema/src/lib/federation-tag-extraction.spec.ts (1)
1662-1664
: Consider adding the federation link directive.The comment indicates that federation must be linked, but the directive is commented out. Consider either:
- Adding the federation link directive if it's required
- Clarifying in the comment why the directive is commented out
packages/services/schema/package.json (1)
14-14
: LGTM! Correctly added workspace dependency.The new dependency
@graphql-hive/federation-link-utils
is properly added as a workspace dependency, consistent with the project's dependency management pattern.
// we retrieve the list of reachable types from the public api sdl | ||
const reachableTypeNames = getReachableTypes(parse(compositionResult.result.sdl!)); | ||
// apollo router does not like @inaccessible on federation types... | ||
for (const federationType of federationTypes) { | ||
reachableTypeNames.add(federationType); | ||
} | ||
|
||
// then we apply the filter to the supergraph SDL (which is the source for the public api sdl) | ||
supergraphSDL = addDirectiveOnTypes({ | ||
documentNode: supergraphSDL, | ||
excludedTypeNames: reachableTypeNames, | ||
directiveName: inaccessibleDirectiveName, | ||
}); | ||
return { | ||
...compositionResult, | ||
result: { | ||
...compositionResult.result, | ||
supergraph: print(supergraphSDL), | ||
sdl: print(transformSupergraphToPublicSchema(supergraphSDL)), | ||
}, | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding error handling for transformation failures.
The transformation operations could fail and should be handled gracefully.
// then we apply the filter to the supergraph SDL (which is the source for the public api sdl)
- supergraphSDL = addDirectiveOnTypes({
- documentNode: supergraphSDL,
- excludedTypeNames: reachableTypeNames,
- directiveName: inaccessibleDirectiveName,
- });
+ try {
+ supergraphSDL = addDirectiveOnTypes({
+ documentNode: supergraphSDL,
+ excludedTypeNames: reachableTypeNames,
+ directiveName: inaccessibleDirectiveName,
+ });
+ return {
+ ...compositionResult,
+ result: {
+ ...compositionResult.result,
+ supergraph: print(supergraphSDL),
+ sdl: print(transformSupergraphToPublicSchema(supergraphSDL)),
+ },
+ };
+ } catch (error) {
+ return {
+ type: 'failure',
+ errors: [{
+ message: `Failed to add inaccessible directive: ${error.message}`,
+ source: 'composition'
+ }]
+ };
+ }
- return {
- ...compositionResult,
- result: {
- ...compositionResult.result,
- supergraph: print(supergraphSDL),
- sdl: print(transformSupergraphToPublicSchema(supergraphSDL)),
- },
- };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// we retrieve the list of reachable types from the public api sdl | |
const reachableTypeNames = getReachableTypes(parse(compositionResult.result.sdl!)); | |
// apollo router does not like @inaccessible on federation types... | |
for (const federationType of federationTypes) { | |
reachableTypeNames.add(federationType); | |
} | |
// then we apply the filter to the supergraph SDL (which is the source for the public api sdl) | |
supergraphSDL = addDirectiveOnTypes({ | |
documentNode: supergraphSDL, | |
excludedTypeNames: reachableTypeNames, | |
directiveName: inaccessibleDirectiveName, | |
}); | |
return { | |
...compositionResult, | |
result: { | |
...compositionResult.result, | |
supergraph: print(supergraphSDL), | |
sdl: print(transformSupergraphToPublicSchema(supergraphSDL)), | |
}, | |
}; | |
}; | |
// we retrieve the list of reachable types from the public api sdl | |
const reachableTypeNames = getReachableTypes(parse(compositionResult.result.sdl!)); | |
// apollo router does not like @inaccessible on federation types... | |
for (const federationType of federationTypes) { | |
reachableTypeNames.add(federationType); | |
} | |
// then we apply the filter to the supergraph SDL (which is the source for the public api sdl) | |
try { | |
supergraphSDL = addDirectiveOnTypes({ | |
documentNode: supergraphSDL, | |
excludedTypeNames: reachableTypeNames, | |
directiveName: inaccessibleDirectiveName, | |
}); | |
return { | |
...compositionResult, | |
result: { | |
...compositionResult.result, | |
supergraph: print(supergraphSDL), | |
sdl: print(transformSupergraphToPublicSchema(supergraphSDL)), | |
}, | |
}; | |
} catch (error) { | |
return { | |
type: 'failure', | |
errors: [{ | |
message: `Failed to add inaccessible directive: ${error.message}`, | |
source: 'composition' | |
}] | |
}; | |
} | |
}; |
`), | ||
}, | ||
], | ||
console as any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid using console as logger in tests.
Using console as any
is not a good practice. Consider creating a proper mock logger.
const mockLogger = {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
Background
This arose from the desire to add a directive for collecting metadata.
Currently there is no great way to ensure you are following the
@link
spec's defined versioning scheme, and it is very difficult to keep differences between versions straight.Description
This utility can be used to apply changes to a schema, or collect metadata from a schema. It's very flexible.