Skip to content

Commit 7457e4d

Browse files
feat: prevent schema check approval when policy errors are present (#7185)
1 parent 0d9681c commit 7457e4d

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

.changeset/nasty-emus-win.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'hive': patch
3+
---
4+
5+
Fix schema check approval to properly reject checks with policy errors and return descriptive error
6+
message instead of generic error

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

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,3 +2231,132 @@ test.concurrent(
22312231
});
22322232
},
22332233
);
2234+
2235+
test.concurrent('schema policy errors prevent schema check approval', async ({ expect }) => {
2236+
const { createOrg, ownerToken } = await initSeed().createOwner();
2237+
const { createProject, organization } = await createOrg();
2238+
const { createTargetAccessToken, setProjectSchemaPolicy, project, target } = await createProject(
2239+
ProjectType.Single,
2240+
);
2241+
2242+
await setProjectSchemaPolicy(createPolicy(RuleInstanceSeverityLevel.Error));
2243+
2244+
const writeToken = await createTargetAccessToken({});
2245+
await writeToken
2246+
.publishSchema({
2247+
sdl: /* GraphQL */ `
2248+
type Query {
2249+
ping: String
2250+
}
2251+
`,
2252+
})
2253+
.then(r => r.expectNoGraphQLErrors());
2254+
2255+
const checkResult = await writeToken
2256+
.checkSchema(/* GraphQL */ `
2257+
type Query {
2258+
ping: Float
2259+
}
2260+
`)
2261+
.then(r => r.expectNoGraphQLErrors());
2262+
2263+
const check = checkResult.schemaCheck;
2264+
2265+
if (check.__typename !== 'SchemaCheckError') {
2266+
throw new Error(`Expected SchemaCheckError, got ${check.__typename}`);
2267+
}
2268+
2269+
const schemaCheckId = check.schemaCheck?.id;
2270+
2271+
if (schemaCheckId == null) {
2272+
throw new Error('Missing schema check id.');
2273+
}
2274+
2275+
const mutationResult = await execute({
2276+
document: ApproveFailedSchemaCheckMutation,
2277+
variables: {
2278+
input: {
2279+
organizationSlug: organization.slug,
2280+
projectSlug: project.slug,
2281+
targetSlug: target.slug,
2282+
schemaCheckId,
2283+
},
2284+
},
2285+
authToken: ownerToken,
2286+
}).then(r => r.expectNoGraphQLErrors());
2287+
2288+
expect(mutationResult).toEqual({
2289+
approveFailedSchemaCheck: {
2290+
ok: null,
2291+
error: {
2292+
message: 'Schema check has schema policy errors that must be resolved before approval.',
2293+
},
2294+
},
2295+
});
2296+
});
2297+
2298+
test.concurrent(
2299+
'schema policy errors prevent schema check approval with safe changes',
2300+
async ({ expect }) => {
2301+
const { createOrg, ownerToken } = await initSeed().createOwner();
2302+
const { createProject, organization } = await createOrg();
2303+
const { createTargetAccessToken, setProjectSchemaPolicy, project, target } =
2304+
await createProject(ProjectType.Single);
2305+
2306+
await setProjectSchemaPolicy(createPolicy(RuleInstanceSeverityLevel.Error));
2307+
2308+
const writeToken = await createTargetAccessToken({});
2309+
await writeToken
2310+
.publishSchema({
2311+
sdl: /* GraphQL */ `
2312+
type Query {
2313+
ping: String
2314+
}
2315+
`,
2316+
})
2317+
.then(r => r.expectNoGraphQLErrors());
2318+
2319+
const checkResult = await writeToken
2320+
.checkSchema(/* GraphQL */ `
2321+
type Query {
2322+
ping: String
2323+
pong: String
2324+
}
2325+
`)
2326+
.then(r => r.expectNoGraphQLErrors());
2327+
2328+
const check = checkResult.schemaCheck;
2329+
2330+
if (check.__typename !== 'SchemaCheckError') {
2331+
throw new Error(`Expected SchemaCheckError, got ${check.__typename}`);
2332+
}
2333+
2334+
const schemaCheckId = check.schemaCheck?.id;
2335+
2336+
if (schemaCheckId == null) {
2337+
throw new Error('Missing schema check id.');
2338+
}
2339+
2340+
const mutationResult = await execute({
2341+
document: ApproveFailedSchemaCheckMutation,
2342+
variables: {
2343+
input: {
2344+
organizationSlug: organization.slug,
2345+
projectSlug: project.slug,
2346+
targetSlug: target.slug,
2347+
schemaCheckId,
2348+
},
2349+
},
2350+
authToken: ownerToken,
2351+
}).then(r => r.expectNoGraphQLErrors());
2352+
2353+
expect(mutationResult).toEqual({
2354+
approveFailedSchemaCheck: {
2355+
ok: null,
2356+
error: {
2357+
message: 'Schema check has schema policy errors that must be resolved before approval.',
2358+
},
2359+
},
2360+
});
2361+
},
2362+
);

packages/services/api/src/modules/schema/providers/schema-manager.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,19 @@ export class SchemaManager {
955955
});
956956

957957
if (!schemaCheck) {
958+
// Re-fetch the schema check to determine why approval failed
959+
const recheck = await this.storage.findSchemaCheck({
960+
targetId: args.targetId,
961+
schemaCheckId: args.schemaCheckId,
962+
});
963+
964+
if (recheck?.schemaPolicyErrors !== null) {
965+
return {
966+
type: 'error',
967+
reason: 'Schema check has schema policy errors that must be resolved before approval.',
968+
} as const;
969+
}
970+
958971
return {
959972
type: 'error',
960973
reason: "Schema check doesn't exist.",

packages/services/storage/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4060,6 +4060,7 @@ export async function createStorage(
40604060
"id" = ${args.schemaCheckId}
40614061
AND "is_success" = false
40624062
AND "schema_composition_errors" IS NULL
4063+
AND "schema_policy_errors" IS NULL
40634064
RETURNING
40644065
"id"
40654066
`);
@@ -4078,6 +4079,7 @@ export async function createStorage(
40784079
"id" = ${args.schemaCheckId}
40794080
AND "is_success" = false
40804081
AND "schema_composition_errors" IS NULL
4082+
AND "schema_policy_errors" IS NULL
40814083
RETURNING
40824084
"id"
40834085
`);

0 commit comments

Comments
 (0)