Skip to content

Commit fba9973

Browse files
dimaMachinadotansimha
authored andcommitted
upd
upd upd upd upd upd upd package.json fix typecheck upd graphql-eslint fix lint try fix lockfile fixes fixes added v3 -> v4 test config (post-migration) to validate configuration added a migration and a migration testing for v3 -> v4 existing configs added more tests to v4 migration, fixed migration testing, added more snapshots reproduce issues with no-unreachable-types ok fix lint fix
1 parent 5ff2aaa commit fba9973

25 files changed

+3254
-583
lines changed

.eslintrc.cjs

+10-12
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ const guildConfig = require('@theguild/eslint-config/base');
55
const { REACT_RESTRICTED_SYNTAX, RESTRICTED_SYNTAX } = require('@theguild/eslint-config/constants');
66
const path = require('path');
77

8-
const SCHEMA_PATH = './packages/services/api/src/modules/*/module.graphql.ts';
9-
const OPERATIONS_PATHS = [
10-
'./packages/web/app/**/*.ts',
11-
'./packages/web/app/**/*.tsx',
12-
'./packages/web/app/**/*.graphql',
13-
];
14-
158
const rulesToExtends = Object.fromEntries(
169
Object.entries(guildConfig.rules).filter(([key]) =>
1710
[
@@ -70,20 +63,25 @@ module.exports = {
7063
parser: '@graphql-eslint/eslint-plugin',
7164
plugins: ['@graphql-eslint'],
7265
parserOptions: {
73-
schema: SCHEMA_PATH,
74-
operations: OPERATIONS_PATHS,
66+
graphQLConfig: {
67+
schema: './packages/services/api/src/modules/*/module.graphql.ts',
68+
documents: [
69+
'./packages/web/app/**/*.ts',
70+
'./packages/web/app/**/*.tsx',
71+
'./packages/web/app/**/*.graphql',
72+
],
73+
},
7574
},
7675
},
7776
{
7877
// Setup processor for operations/fragments definitions on code-files
79-
files: ['packages/web/app/**/*.tsx', 'packages/web/app/**/*.ts'],
78+
files: ['packages/web/app/**/*.{ts,tsx}'],
8079
processor: '@graphql-eslint/graphql',
8180
},
8281
{
8382
files: ['packages/web/app/**/*.graphql'],
84-
plugins: ['@graphql-eslint'],
8583
rules: {
86-
'@graphql-eslint/require-id-when-available': 'error',
84+
'@graphql-eslint/require-selections': 'error',
8785
'@graphql-eslint/no-deprecated': 'error',
8886
},
8987
},

integration-tests/tests/api/policy/policy-check.spec.ts

+161
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,167 @@ export const createPolicy = (level: RuleInstanceSeverityLevel): SchemaPolicyInpu
1717

1818
describe('Schema policy checks', () => {
1919
describe('model: composite', () => {
20+
it('no-unreachable-types issue: directives are not scanned/marked as unused', async () => {
21+
const policyObject: SchemaPolicyInput = {
22+
rules: [
23+
{
24+
ruleId: 'no-unreachable-types',
25+
severity: RuleInstanceSeverityLevel.Error,
26+
},
27+
],
28+
};
29+
30+
const { tokens, policy } = await prepareProject(ProjectType.Federation);
31+
await policy.setOrganizationSchemaPolicy(policyObject, true);
32+
const cli = createCLI(tokens.registry);
33+
34+
await cli.publish({
35+
sdl: /* GraphQL */ `
36+
type Query {
37+
a: String!
38+
}
39+
`,
40+
serviceName: 'a',
41+
serviceUrl: 'https://api.com/a',
42+
expect: 'latest-composable',
43+
});
44+
45+
await cli.publish({
46+
sdl: /* GraphQL */ `
47+
type Query {
48+
b: String!
49+
}
50+
`,
51+
serviceName: 'b',
52+
serviceUrl: 'https://api.com/b',
53+
expect: 'latest-composable',
54+
});
55+
56+
// In this example, the policy checks sees the "hasRole" directive in the schema
57+
// because we are using composeDirective.
58+
const rawMessage = await cli.check({
59+
sdl: /* GraphQL */ `
60+
extend schema
61+
@link(url: "https://specs.apollo.dev/link/v1.0")
62+
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@composeDirective"])
63+
@link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@hasRole"])
64+
@composeDirective(name: "@hasRole")
65+
66+
scalar Unused
67+
68+
scalar Used
69+
70+
scalar UsedInInput
71+
72+
directive @hasRole(role: Role!) on QUERY | MUTATION | FIELD_DEFINITION
73+
74+
enum Role {
75+
admin
76+
user
77+
}
78+
79+
enum Permission {
80+
read
81+
write
82+
create
83+
delete
84+
}
85+
86+
type Query {
87+
userRole(roleID: Int!): UserRole! @hasRole(role: admin)
88+
scalar(input: UsedInInput!): Used
89+
}
90+
91+
type UserRole {
92+
id: ID!
93+
name: String!
94+
}
95+
`,
96+
serviceName: 'c',
97+
expect: 'rejected',
98+
});
99+
const message = stripAnsi(rawMessage);
100+
101+
expect(message).toContain(`Detected 2 errors`);
102+
expect(message.split('\n').slice(1)).toEqual([
103+
'✖ Detected 2 errors',
104+
'',
105+
' - Scalar type `Unused` is unreachable. (source: policy-no-unreachable-types)',
106+
' - Enum type `Permission` is unreachable. (source: policy-no-unreachable-types)',
107+
'',
108+
'ℹ Detected 9 changes',
109+
'',
110+
' Safe changes:',
111+
' - Type Permission was added',
112+
' - Type Role was added',
113+
' - Type Unused was added',
114+
' - Type Used was added',
115+
' - Type UsedInInput was added',
116+
' - Type UserRole was added',
117+
' - Field scalar was added to object type Query',
118+
' - Field userRole was added to object type Query',
119+
' - Directive hasRole was added',
120+
'',
121+
'View full report:',
122+
expect.any(String),
123+
'',
124+
]);
125+
126+
// But in this one, we are not using composeDirective, so the final compose directive
127+
// is not visible by the policy checker, and the policy checker will not detect it.
128+
// This is why it's being reported an unused, and also other related inputs/types.
129+
const rawMessage2 = await cli.check({
130+
sdl: /* GraphQL */ `
131+
scalar Unused
132+
133+
scalar Used
134+
135+
scalar UsedInInput
136+
137+
directive @hasRole(role: Role!) on QUERY | MUTATION | FIELD_DEFINITION
138+
139+
enum Role {
140+
admin
141+
user
142+
}
143+
144+
enum Permission {
145+
read
146+
write
147+
create
148+
delete
149+
}
150+
151+
type Query {
152+
userRole(roleID: Int!): UserRole! @hasRole(role: admin)
153+
scalar(input: UsedInInput!): Used
154+
}
155+
156+
type UserRole {
157+
id: ID!
158+
name: String!
159+
}
160+
`,
161+
serviceName: 'c',
162+
expect: 'rejected',
163+
});
164+
const message2 = stripAnsi(rawMessage2);
165+
166+
expect(message2).toContain(`Detected 4 errors`);
167+
expect(message2).toContain(
168+
`Scalar type \`Unused\` is unreachable. (source: policy-no-unreachable-types)`,
169+
);
170+
expect(message2).toContain(
171+
`Directive \`hasRole\` is unreachable. (source: policy-no-unreachable-types)`,
172+
);
173+
expect(message2).toContain(
174+
`Enum type \`Role\` is unreachable. (source: policy-no-unreachable-types)`,
175+
);
176+
expect(message2).toContain(
177+
`Enum type \`Permission\` is unreachable. (source: policy-no-unreachable-types)`,
178+
);
179+
});
180+
20181
it('Federation project with policy with only warnings, should check only the part that was changed', async () => {
21182
const { tokens, policy } = await prepareProject(ProjectType.Federation);
22183
await policy.setOrganizationSchemaPolicy(

0 commit comments

Comments
 (0)