fix: do not cache edge types in graphql eslint (#7936)

This commit is contained in:
jdolle 2026-03-31 00:03:58 -07:00 committed by GitHub
parent 34528114f3
commit 96bd390c7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 147 additions and 9 deletions

View file

@ -0,0 +1,6 @@
---
'hive': patch
---
Do not cache edge types in graphql eslint. This fixes an issue where edge types were cached between
runs and only the cached edge types would be referenced for subsequent runs

View file

@ -241,6 +241,7 @@ export class CompositeModel {
}),
this.checks.policyCheck({
selector,
// if schema check does not compose then there's no point in linting. Pass in null in this case.
incomingSdl: compositionCheck.result?.fullSchemaSdl ?? null,
modifiedSdl: incoming.sdl,
}),

View file

@ -107,7 +107,7 @@ export class SingleModel {
});
if (checksumResult === 'unchanged') {
this.logger.debug('No changes detected, skipping schema check');
this.logger.info('No changes detected, skipping schema check');
return {
conclusion: SchemaCheckConclusion.Skip,
};

View file

@ -225,7 +225,7 @@ export class RegistryChecks {
);
if (!args.existing) {
this.logger.debug('No exiting version');
this.logger.debug('No existing version');
return 'initial' as const;
}
@ -400,7 +400,7 @@ export class RegistryChecks {
this.logger.debug('Skip policy check due to no SDL being composed.');
return {
status: 'skipped',
};
} satisfies CheckResult;
}
const policyResult = await this.policy.checkPolicy(incomingSdl, modifiedSdl, selector);

View file

@ -105,6 +105,111 @@ describe('policy checks', () => {
`);
});
it('refreshes edge types per run', async () => {
const caller = schemaPolicyApiRouter.createCaller({ req: { log: console } as any });
const userSchema = `
type Query {
# 'first' limits results; 'after' is the cursor to start from
users(first: Int, after: String): UserConnection!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
interface Node {
id: ID!
}
type User {
id: ID!
name: String!
}
`;
const result = await caller.checkPolicy({
source: userSchema,
schema: userSchema,
target: '1',
policy: {
'relay-edge-types': [
2,
{
withEdgeSuffix: true,
shouldImplementNode: true,
listTypeCanWrapOnlyEdgeType: false,
},
],
},
});
expect(result.length).toBe(1);
expect(result[0].message).toBe("Edge type's field \`node\` must implement \`Node\` interface.");
const noUserSchema = `
type Query {
# 'first' limits results; 'after' is the cursor to start from
noUsers(first: Int, after: String): NoUserConnection!
}
type NoUserConnection {
edges: [NoUserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type NoUserEdge {
node: NoUser!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
interface Node {
id: ID!
}
type NoUser {
id: ID!
name: String!
}
`;
const noUserResult = await caller.checkPolicy({
source: noUserSchema,
schema: noUserSchema,
target: '1',
policy: {
'relay-edge-types': [
2,
{
withEdgeSuffix: true,
shouldImplementNode: true,
listTypeCanWrapOnlyEdgeType: false,
},
],
},
});
expect(noUserResult.length).toBe(1);
expect(noUserResult[0].message).toBe(
"Edge type's field \`node\` must implement \`Node\` interface.",
);
});
/** To ensure existing policies dont break during upgrades */
it.each(policies)('should support existing policies', async policy => {
await expect(

View file

@ -119,4 +119,30 @@ index d952cee1e10976459e7c5836b86ba6540c45fdb6..d314997bfd26477c7d823cad397eb5d6
+
return {
[ruleId]: {
meta: {
meta: {
diff --git a/esm/rules/relay-edge-types.js b/esm/rules/relay-edge-types.js
index 7a7eb179786103d9d72a4a84fb4bf811d11a4286..6fb12a5f56b71f56c9a14018d0494e9c7584f935 100644
--- a/esm/rules/relay-edge-types.js
+++ b/esm/rules/relay-edge-types.js
@@ -12,11 +12,7 @@ const MESSAGE_MUST_BE_OBJECT_TYPE = "MESSAGE_MUST_BE_OBJECT_TYPE";
const MESSAGE_MISSING_EDGE_SUFFIX = "MESSAGE_MISSING_EDGE_SUFFIX";
const MESSAGE_LIST_TYPE_ONLY_EDGE_TYPE = "MESSAGE_LIST_TYPE_ONLY_EDGE_TYPE";
const MESSAGE_SHOULD_IMPLEMENTS_NODE = "MESSAGE_SHOULD_IMPLEMENTS_NODE";
-let edgeTypesCache;
function getEdgeTypes(schema2) {
- if (edgeTypesCache) {
- return edgeTypesCache;
- }
const edgeTypes = /* @__PURE__ */ new Set();
const visitor = {
ObjectTypeDefinition(node) {
@@ -38,8 +34,7 @@ function getEdgeTypes(schema2) {
};
const astNode = getDocumentNodeFromSchema(schema2);
visit(astNode, visitor);
- edgeTypesCache = edgeTypes;
- return edgeTypesCache;
+ return edgeTypes;
}
const schema = {
type: "array",

View file

@ -56,7 +56,7 @@ patchedDependencies:
hash: 2280c6d4f2e9268fc118d06dc95deea7e9b58200010db1b332004627d6793a9f
path: patches/@graphql-codegen__schema-ast.patch
'@graphql-eslint/eslint-plugin@3.20.1':
hash: 695fba67df25ba9d46472c8398c94c6a2ccf75d902321d8f95150f68e940313e
hash: 230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b
path: patches/@graphql-eslint__eslint-plugin@3.20.1.patch
'@oclif/core@3.26.6':
hash: 7432c7b46bd5e3276faff9af4fc733575417c17a0ec31df3c7a229f766e3cffc
@ -134,7 +134,7 @@ importers:
version: 3.0.1(graphql@16.9.0)
'@graphql-eslint/eslint-plugin':
specifier: 3.20.1
version: 3.20.1(patch_hash=695fba67df25ba9d46472c8398c94c6a2ccf75d902321d8f95150f68e940313e)(@babel/core@7.28.5)(@types/node@24.10.9)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)
version: 3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@24.10.9)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)
'@graphql-inspector/cli':
specifier: 6.0.6
version: 6.0.6(@types/node@24.10.9)(encoding@0.1.13)(graphql@16.9.0)
@ -1434,7 +1434,7 @@ importers:
devDependencies:
'@graphql-eslint/eslint-plugin':
specifier: 3.20.1
version: 3.20.1(patch_hash=695fba67df25ba9d46472c8398c94c6a2ccf75d902321d8f95150f68e940313e)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)
version: 3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)
'@hive/service-common':
specifier: workspace:*
version: link:../service-common
@ -22294,7 +22294,7 @@ snapshots:
parse-filepath: 1.0.2
tslib: 2.6.3
'@graphql-eslint/eslint-plugin@3.20.1(patch_hash=695fba67df25ba9d46472c8398c94c6a2ccf75d902321d8f95150f68e940313e)(@babel/core@7.28.5)(@types/node@24.10.9)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)':
'@graphql-eslint/eslint-plugin@3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@24.10.9)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)':
dependencies:
'@babel/code-frame': 7.29.0
'@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.28.5)(graphql@16.9.0)
@ -22317,7 +22317,7 @@ snapshots:
- supports-color
- utf-8-validate
'@graphql-eslint/eslint-plugin@3.20.1(patch_hash=695fba67df25ba9d46472c8398c94c6a2ccf75d902321d8f95150f68e940313e)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)':
'@graphql-eslint/eslint-plugin@3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)':
dependencies:
'@babel/code-frame': 7.29.0
'@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.28.5)(graphql@16.9.0)