mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
remove compare to latest schema version feature flag (#7584)
This commit is contained in:
parent
e203a7cf89
commit
0f0430f4f5
20 changed files with 1101 additions and 1545 deletions
9
.changeset/slimy-trams-search.md
Normal file
9
.changeset/slimy-trams-search.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
'hive': minor
|
||||
---
|
||||
|
||||
Enable automatic retrieval of schema changes by comparing with the latest composable version. This has already been the default for new projects created after April 2024.
|
||||
|
||||
Federation and schema stitching projects can now publish service schemas to the registry even if those schemas would break composition. This has also been the default behavior for new projects created after April 2024.
|
||||
|
||||
To ensure every version publishd to the schema registry is composable, we recommend to first check the schema against the registry **before** publishing.
|
||||
|
|
@ -1117,11 +1117,10 @@ function runArtifactsCDNTests(
|
|||
'access versioned contract artifact with valid credentials',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target, setNativeFederation } =
|
||||
await createProject(ProjectType.Federation);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
||||
|
|
|
|||
|
|
@ -1925,12 +1925,8 @@ describe.concurrent(
|
|||
() => {
|
||||
test.concurrent('native federation', async () => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken } = await createProject(ProjectType.Federation);
|
||||
|
||||
const token = await createTargetAccessToken({});
|
||||
|
||||
|
|
@ -1976,11 +1972,10 @@ describe.concurrent(
|
|||
|
||||
test.concurrent('legacy fed composition', async () => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', false);
|
||||
await setNativeFederation(false);
|
||||
|
||||
const token = await createTargetAccessToken({});
|
||||
|
|
@ -2020,58 +2015,6 @@ describe.concurrent(
|
|||
}),
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent(
|
||||
'legacy fed composition with compareToPreviousComposableVersion=true',
|
||||
async () => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(false);
|
||||
|
||||
const token = await createTargetAccessToken({});
|
||||
|
||||
// @key(fields:) is invalid - should trigger a composition error
|
||||
const sdl = /* GraphQL */ `
|
||||
type Query {
|
||||
ping: String
|
||||
pong: String
|
||||
foo: User
|
||||
}
|
||||
|
||||
type User @key(fields: "uuid") {
|
||||
id: ID!
|
||||
}
|
||||
`;
|
||||
|
||||
// Publish schema with write rights
|
||||
await token
|
||||
.publishSchema({
|
||||
sdl,
|
||||
service: 'serviceA',
|
||||
url: 'http://localhost:4000',
|
||||
})
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
const result = await token
|
||||
.checkSchema(sdl, 'serviceA')
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(result.schemaCheck).toMatchObject({
|
||||
valid: false,
|
||||
__typename: 'SchemaCheckError',
|
||||
changes: expect.objectContaining({
|
||||
total: 0,
|
||||
}),
|
||||
errors: expect.objectContaining({
|
||||
total: 1,
|
||||
}),
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,12 +32,8 @@ const CreateContractMutation = graphql(`
|
|||
|
||||
test.concurrent('schema check with successful contract checks', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -99,12 +95,8 @@ test.concurrent('schema check with successful contract checks', async ({ expect
|
|||
|
||||
test.concurrent('schema check with failing contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -180,12 +172,8 @@ test.concurrent(
|
|||
'schema check with failing contract composition (multiple contracts)',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -381,12 +369,10 @@ test.concurrent(
|
|||
'approve failed schema check that has breaking change in contract check -> updates the status to successful and attaches meta information to the breaking change',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, organization, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, project, target, setNativeFederation } = await createProject(
|
||||
const { createProject, organization } = await createOrg();
|
||||
const { createTargetAccessToken, project, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -547,8 +533,6 @@ test.concurrent(
|
|||
const { createTargetAccessToken, project, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -739,12 +723,10 @@ test.concurrent(
|
|||
'approving a schema check with contextId containing breaking changes does not allow the changes for subsequent checks with a different contextId',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, organization, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, project, target, setNativeFederation } = await createProject(
|
||||
const { createProject, organization } = await createOrg();
|
||||
const { createTargetAccessToken, project, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -928,12 +910,10 @@ test.concurrent(
|
|||
'subsequent schema check with shared contextId that contains new breaking changes that have not been approved fails',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, organization, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, project, target, setNativeFederation } = await createProject(
|
||||
const { createProject, organization } = await createOrg();
|
||||
const { createTargetAccessToken, project, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -1128,12 +1108,10 @@ test.concurrent(
|
|||
'schema check that has no composition errors in contract check -> can be approved',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, organization, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, project, target, setNativeFederation } = await createProject(
|
||||
const { createProject, organization } = await createOrg();
|
||||
const { createTargetAccessToken, project, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -1237,12 +1215,10 @@ test.concurrent(
|
|||
'schema check that has composition errors in contract check -> can not be approved',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, organization, setFeatureFlag } = await createOrg();
|
||||
const { createProject, organization } = await createOrg();
|
||||
const { createTargetAccessToken, project, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
|
|||
|
|
@ -67,12 +67,8 @@ test.concurrent(
|
|||
'schema publish with successful initial contract composition',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -158,12 +154,8 @@ test.concurrent(
|
|||
|
||||
test.concurrent('schema publish with failing initial contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -239,12 +231,8 @@ test.concurrent('schema publish with failing initial contract composition', asyn
|
|||
|
||||
test.concurrent('schema publish with succeeding contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -330,12 +318,8 @@ test.concurrent('schema publish with succeeding contract composition', async ({
|
|||
|
||||
test.concurrent('schema publish with failing contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -421,12 +405,8 @@ test.concurrent(
|
|||
'schema delete with successful initial contract composition',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -512,12 +492,8 @@ test.concurrent(
|
|||
|
||||
test.concurrent('schema delete with failing initial contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -594,12 +570,8 @@ test.concurrent('schema delete with failing initial contract composition', async
|
|||
|
||||
test.concurrent('schema delete with succeeding contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -684,12 +656,8 @@ test.concurrent('schema delete with succeeding contract composition', async ({ e
|
|||
|
||||
test.concurrent('schema delete with failing contract composition', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, target } = await createProject(ProjectType.Federation);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -768,11 +736,10 @@ test.concurrent(
|
|||
'successful contracts schema can be fetched from the CDN with CDN access token',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target, setNativeFederation } =
|
||||
await createProject(ProjectType.Federation);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -835,8 +802,6 @@ test.concurrent(
|
|||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target, setNativeFederation } =
|
||||
await createProject(ProjectType.Federation);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -904,11 +869,10 @@ const DisabledContractMutation = graphql(`
|
|||
|
||||
test.concurrent('disable contract results in CDN artifacts being removed', async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target, setNativeFederation } =
|
||||
await createProject(ProjectType.Federation);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -993,11 +957,10 @@ test.concurrent(
|
|||
'disable contract delete succeeds if no version/CDN artifacts have been published yet',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target, setNativeFederation } =
|
||||
await createProject(ProjectType.Federation);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
const { createProject } = await createOrg();
|
||||
const { createTargetAccessToken, createCdnAccess, target } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
|
|
@ -1051,15 +1014,8 @@ test.concurrent(
|
|||
'disable contract delete succeeds if no version/CDN artifacts have been published yet',
|
||||
async ({ expect }) => {
|
||||
const { createOrg, ownerToken } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, target, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
// Create a token with write rights
|
||||
const writeToken = await createTargetAccessToken({});
|
||||
const { createProject } = await createOrg();
|
||||
const { target } = await createProject(ProjectType.Federation);
|
||||
|
||||
const createContractResult = await execute({
|
||||
document: CreateContractMutation,
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ test.concurrent(
|
|||
);
|
||||
|
||||
test.concurrent(
|
||||
'composition error is persisted in the database when the super schema schema is not composable',
|
||||
'composition error is persisted in the database when the supergraph is not composable',
|
||||
async ({ expect }) => {
|
||||
let storage: Awaited<ReturnType<typeof createStorage>> | undefined = undefined;
|
||||
|
||||
|
|
@ -263,6 +263,7 @@ test.concurrent(
|
|||
url: 'http://localhost:4000/graphql',
|
||||
})
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(publishService1Result.schemaPublish.__typename).toBe('SchemaPublishSuccess');
|
||||
|
||||
const publishService2Result = await readToken
|
||||
|
|
|
|||
|
|
@ -4001,8 +4001,6 @@ test.concurrent(
|
|||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setNativeFederation(true);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
|
||||
const readWriteToken = await createTargetAccessToken({});
|
||||
|
||||
|
|
@ -4146,8 +4144,6 @@ describe.concurrent(
|
|||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
const token = await createTargetAccessToken({});
|
||||
|
||||
|
|
@ -4294,8 +4290,6 @@ describe.concurrent(
|
|||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', false);
|
||||
await setNativeFederation(false);
|
||||
|
||||
const token = await createTargetAccessToken({});
|
||||
|
||||
|
|
@ -4427,149 +4421,6 @@ describe.concurrent(
|
|||
linkToWebsite: result.schemaPublish.linkToWebsite,
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent(
|
||||
'legacy fed composition with compareToPreviousComposableVersion=true',
|
||||
async () => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { createProject, setFeatureFlag } = await createOrg();
|
||||
const { createTargetAccessToken, setNativeFederation } = await createProject(
|
||||
ProjectType.Federation,
|
||||
);
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(false);
|
||||
|
||||
const token = await createTargetAccessToken({});
|
||||
|
||||
const validSdl = /* GraphQL */ `
|
||||
type Query {
|
||||
ping: String
|
||||
pong: String
|
||||
foo: User
|
||||
}
|
||||
|
||||
type User @key(fields: "id") {
|
||||
id: ID!
|
||||
}
|
||||
`;
|
||||
|
||||
// @key(fields:) is invalid - should trigger a composition error
|
||||
const invalidSdl = /* GraphQL */ `
|
||||
type Query {
|
||||
ping: String
|
||||
pong: String
|
||||
foo: User
|
||||
}
|
||||
|
||||
type User @key(fields: "uuid") {
|
||||
id: ID!
|
||||
}
|
||||
`;
|
||||
|
||||
// Publish schema with write rights
|
||||
const validPublish = await token
|
||||
.publishSchema({
|
||||
sdl: validSdl,
|
||||
service: 'serviceA',
|
||||
url: 'http://localhost:4000',
|
||||
})
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(validPublish.schemaPublish).toMatchObject({
|
||||
valid: true,
|
||||
linkToWebsite: expect.any(String),
|
||||
});
|
||||
|
||||
const invalidPublish = await token
|
||||
.publishSchema({
|
||||
sdl: invalidSdl,
|
||||
service: 'serviceA',
|
||||
url: 'http://localhost:4000',
|
||||
})
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(invalidPublish.schemaPublish).toMatchObject({
|
||||
valid: false,
|
||||
linkToWebsite: expect.any(String),
|
||||
});
|
||||
|
||||
const invalidSdlCheck = await token
|
||||
.checkSchema(invalidSdl, 'serviceA')
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(invalidSdlCheck.schemaCheck).toMatchObject({
|
||||
valid: false,
|
||||
__typename: 'SchemaCheckError',
|
||||
changes: expect.objectContaining({
|
||||
total: 0,
|
||||
}),
|
||||
errors: expect.objectContaining({
|
||||
total: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
const validSdlCheck = await token
|
||||
.checkSchema(validSdl, 'serviceA')
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(validSdlCheck.schemaCheck).toMatchObject({
|
||||
valid: true,
|
||||
__typename: 'SchemaCheckSuccess',
|
||||
changes: expect.objectContaining({
|
||||
total: 0,
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await token
|
||||
.publishSchema({
|
||||
sdl: validSdl,
|
||||
service: 'serviceA',
|
||||
url: 'http://localhost:4000',
|
||||
})
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
expect(result.schemaPublish).toMatchObject({
|
||||
valid: true,
|
||||
linkToWebsite: expect.any(String),
|
||||
});
|
||||
|
||||
if (
|
||||
!('linkToWebsite' in result.schemaPublish) ||
|
||||
!('linkToWebsite' in invalidPublish.schemaPublish) ||
|
||||
!('linkToWebsite' in validPublish.schemaPublish)
|
||||
) {
|
||||
throw new Error('linkToWebsite not found');
|
||||
}
|
||||
|
||||
// If the linkToWebsite is the same as one of the previous versions,
|
||||
// the schema publish was ignored due to unchanged input schemas.
|
||||
// It shouldn't be the case.
|
||||
// That's what we're checking here.
|
||||
|
||||
expect(result.schemaPublish.linkToWebsite).not.toEqual(
|
||||
invalidPublish.schemaPublish.linkToWebsite,
|
||||
);
|
||||
|
||||
expect(result.schemaPublish.linkToWebsite).not.toEqual(
|
||||
validPublish.schemaPublish.linkToWebsite,
|
||||
);
|
||||
|
||||
const ignoredResult = await token
|
||||
.publishSchema({
|
||||
sdl: validSdl,
|
||||
service: 'serviceA',
|
||||
url: 'http://localhost:4000',
|
||||
})
|
||||
.then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
// This time the schema publish should be ignored
|
||||
// and link to the previous version
|
||||
expect(ignoredResult.schemaPublish).toMatchObject({
|
||||
valid: true,
|
||||
linkToWebsite: result.schemaPublish.linkToWebsite,
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -175,10 +175,6 @@ describe('dev --remote', () => {
|
|||
const { secret } = await createTargetAccessToken({});
|
||||
const cli = createCLI({ readwrite: secret, readonly: secret });
|
||||
|
||||
// Once we ship native federation v2 composition by default, we can remove these two lines
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
await cli.publish({
|
||||
sdl: /* GraphQL */ `
|
||||
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
|
||||
|
|
@ -255,10 +251,6 @@ describe('dev --remote', () => {
|
|||
const { secret } = await createTargetAccessToken({});
|
||||
const cli = createCLI({ readwrite: secret, readonly: secret });
|
||||
|
||||
// Once we ship native federation v2 composition by default, we can remove these two lines
|
||||
await setFeatureFlag('compareToPreviousComposableVersion', true);
|
||||
await setNativeFederation(true);
|
||||
|
||||
await cli.publish({
|
||||
sdl: /* GraphQL */ `
|
||||
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ const options = ['targetWithNativeComposition', 'targetWithLegacyComposition'] a
|
|||
|
||||
describe('publish', () => {
|
||||
describe.concurrent.each(options)('%s', caseName => {
|
||||
const legacyComposition = isLegacyComposition(caseName);
|
||||
|
||||
test.concurrent('accepted: composable', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
|
|
@ -55,26 +53,23 @@ describe('publish', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test.concurrent(
|
||||
`${legacyComposition ? 'rejected' : 'accepted'}: not composable (graphql errors)`,
|
||||
async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(caseName);
|
||||
test.concurrent(`accepted: not composable (graphql errors)`, async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(caseName);
|
||||
|
||||
// non-composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
`,
|
||||
serviceName: 'products',
|
||||
serviceUrl: 'http://products:3000/graphql',
|
||||
expect: legacyComposition ? 'rejected' : 'latest',
|
||||
});
|
||||
},
|
||||
);
|
||||
// non-composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
`,
|
||||
serviceName: 'products',
|
||||
serviceUrl: 'http://products:3000/graphql',
|
||||
expect: 'latest',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('accepted: composable, previous version was not', async () => {
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -44,26 +44,23 @@ describe('publish', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test.concurrent(
|
||||
`${legacyComposition ? 'rejected' : 'accepted'}: not composable (graphql errors)`,
|
||||
async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs, legacyComposition);
|
||||
test.concurrent(`accepted: not composable (graphql errors)`, async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs, legacyComposition);
|
||||
|
||||
// non-composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
`,
|
||||
serviceName: 'products',
|
||||
serviceUrl: 'http://products:3000/graphql',
|
||||
expect: legacyComposition ? 'rejected' : 'latest',
|
||||
});
|
||||
},
|
||||
);
|
||||
// non-composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
`,
|
||||
serviceName: 'products',
|
||||
serviceUrl: 'http://products:3000/graphql',
|
||||
expect: 'latest',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('accepted: composable, previous version was not', async () => {
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ export type FeatureFlags = [string, FFValue][];
|
|||
|
||||
export const cases = [
|
||||
['default' as const, [] as FeatureFlags],
|
||||
[
|
||||
'compareToPreviousComposableVersion' as const,
|
||||
[['compareToPreviousComposableVersion', true]] as FeatureFlags,
|
||||
],
|
||||
['@apollo/federation' as const, [] as FeatureFlags],
|
||||
] as const;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,288 +3,118 @@ import { normalizeCliOutput } from '../../../scripts/serializers/cli-output';
|
|||
import { createCLI } from '../../testkit/cli';
|
||||
import { prepareProject } from '../../testkit/registry-models';
|
||||
|
||||
const cases = [
|
||||
['default' as const, [] as [string, boolean][]],
|
||||
[
|
||||
'compareToPreviousComposableVersion' as const,
|
||||
[['compareToPreviousComposableVersion', true]] as [string, boolean][],
|
||||
],
|
||||
] as Array<['default' | 'compareToPreviousComposableVersion', Array<[string, boolean]>]>;
|
||||
|
||||
describe('publish', () => {
|
||||
describe.concurrent.each(cases)('%s', (caseName, ffs) => {
|
||||
test.concurrent('accepted: composable', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs);
|
||||
await publish({
|
||||
sdl: `type Query { topProductName: String }`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('accepted: composable, breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs);
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProductName: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
nooooo: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent(
|
||||
`${caseName === 'default' ? 'rejected' : 'accepted'}: not composable (graphql errors)`,
|
||||
async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs);
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
`,
|
||||
expect: caseName === 'default' ? 'rejected' : 'latest',
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.concurrent('accepted: composable, no changes', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs);
|
||||
|
||||
// composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
// composable but no changes
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'ignored',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('accepted: composable, no changes but modified metadata', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs);
|
||||
|
||||
// composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
// composable but no changes with modified metadata
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v2' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('CLI output', async ({ expect }) => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare(ffs);
|
||||
|
||||
let output = normalizeCliOutput(
|
||||
(await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
|
||||
type Product {
|
||||
id: ID!
|
||||
name: String!
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
})) ?? '',
|
||||
);
|
||||
|
||||
expect(output).toEqual(expect.stringContaining(`v Published initial schema.`));
|
||||
expect(output).toEqual(
|
||||
expect.stringContaining(`i Available at $appUrl/$organization/$project/$target`),
|
||||
);
|
||||
|
||||
output = normalizeCliOutput(
|
||||
(await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
|
||||
type Product {
|
||||
id: ID!
|
||||
name: String!
|
||||
price: Int!
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
})) ?? '',
|
||||
);
|
||||
|
||||
expect(output).toEqual(expect.stringContaining(`v Schema published`));
|
||||
expect(output).toEqual(
|
||||
expect.stringContaining(
|
||||
`i Available at $appUrl/$organization/$project/$target/history/$version`,
|
||||
),
|
||||
);
|
||||
test.concurrent('accepted: composable', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare();
|
||||
await publish({
|
||||
sdl: `type Query { topProductName: String }`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('check', () => {
|
||||
describe.concurrent.each(cases)('%s', (_, ffs) => {
|
||||
test.concurrent('accepted: composable, no breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare(ffs);
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
topProductName: String
|
||||
}
|
||||
`,
|
||||
expect: 'approved',
|
||||
});
|
||||
|
||||
expect(message).toMatch('topProductName');
|
||||
test.concurrent('accepted: composable, breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare();
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProductName: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
test.concurrent('accepted: no changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare(ffs);
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
nooooo: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
});
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
test.concurrent(`accepted: not composable (graphql errors)`, async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare();
|
||||
|
||||
await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'approved',
|
||||
});
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('accepted: composable, no changes', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare();
|
||||
|
||||
// composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
test.concurrent('rejected: composable, breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare(ffs);
|
||||
// composable but no changes
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'ignored',
|
||||
});
|
||||
});
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
test.concurrent('accepted: composable, no changes but modified metadata', async () => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare();
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProductName: String
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
|
||||
expect(message).toMatch('removed');
|
||||
// composable
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
test.concurrent('rejected: not composable, no breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare(ffs);
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
topProductName: Strin
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
|
||||
expect(message).toMatch('Strin');
|
||||
// composable but no changes with modified metadata
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v2' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('rejected: not composable, breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare(ffs);
|
||||
test.concurrent('CLI output', async ({ expect }) => {
|
||||
const {
|
||||
cli: { publish },
|
||||
} = await prepare();
|
||||
|
||||
await publish({
|
||||
let output = normalizeCliOutput(
|
||||
(await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
|
|
@ -292,84 +122,235 @@ describe('check', () => {
|
|||
|
||||
type Product {
|
||||
id: ID!
|
||||
name: String
|
||||
name: String!
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
})) ?? '',
|
||||
);
|
||||
|
||||
const message = await check({
|
||||
expect(output).toEqual(expect.stringContaining(`v Published initial schema.`));
|
||||
expect(output).toEqual(
|
||||
expect.stringContaining(`i Available at $appUrl/$organization/$project/$target`),
|
||||
);
|
||||
|
||||
output = normalizeCliOutput(
|
||||
(await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
product(id: ID!): Product
|
||||
topProduct: Product
|
||||
}
|
||||
|
||||
type Product {
|
||||
id: ID!
|
||||
name: Str
|
||||
name: String!
|
||||
price: Int!
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
expect: 'latest-composable',
|
||||
})) ?? '',
|
||||
);
|
||||
|
||||
expect(message).toMatch('Str');
|
||||
expect(output).toEqual(expect.stringContaining(`v Schema published`));
|
||||
expect(output).toEqual(
|
||||
expect.stringContaining(
|
||||
`i Available at $appUrl/$organization/$project/$target/history/$version`,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('check', () => {
|
||||
test.concurrent('accepted: composable, no breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare();
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
topProductName: String
|
||||
}
|
||||
`,
|
||||
expect: 'approved',
|
||||
});
|
||||
|
||||
expect(message).toMatch('topProductName');
|
||||
});
|
||||
|
||||
test.concurrent('accepted: no changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare();
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'approved',
|
||||
});
|
||||
});
|
||||
|
||||
test.concurrent('rejected: composable, breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare();
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProductName: String
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
|
||||
expect(message).toMatch('removed');
|
||||
});
|
||||
|
||||
test.concurrent('rejected: not composable, no breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare();
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
topProductName: Strin
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
|
||||
expect(message).toMatch('Strin');
|
||||
});
|
||||
|
||||
test.concurrent('rejected: not composable, breaking changes', async () => {
|
||||
const {
|
||||
cli: { publish, check },
|
||||
} = await prepare();
|
||||
|
||||
await publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: Product
|
||||
}
|
||||
|
||||
type Product {
|
||||
id: ID!
|
||||
name: String
|
||||
}
|
||||
`,
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
const message = await check({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
product(id: ID!): Product
|
||||
}
|
||||
|
||||
type Product {
|
||||
id: ID!
|
||||
name: Str
|
||||
}
|
||||
`,
|
||||
expect: 'rejected',
|
||||
});
|
||||
|
||||
expect(message).toMatch('Str');
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
describe.concurrent.each(cases)('%s', (_, ffs) => {
|
||||
test.concurrent('not supported', async () => {
|
||||
const { cli } = await prepare(ffs);
|
||||
test.concurrent('not supported', async () => {
|
||||
const { cli } = await prepare();
|
||||
|
||||
await cli.delete({
|
||||
serviceName: 'test',
|
||||
expect: 'rejected',
|
||||
});
|
||||
await cli.delete({
|
||||
serviceName: 'test',
|
||||
expect: 'rejected',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('others', () => {
|
||||
describe.concurrent.each(cases)('%s', (_, ffs) => {
|
||||
test.concurrent('metadata should always be published as an array', async () => {
|
||||
const { cli, cdn } = await prepare(ffs);
|
||||
test.concurrent('metadata should always be published as an array', async () => {
|
||||
const { cli, cdn } = await prepare();
|
||||
|
||||
await cli.publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
await expect(cdn.fetchMetadata()).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
status: 200,
|
||||
body: { version: 'v1' }, // not an array
|
||||
}),
|
||||
);
|
||||
|
||||
await cli.publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
topProducts: [String]
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v2' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
await expect(cdn.fetchMetadata()).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
status: 200,
|
||||
body: { version: 'v2' }, // not an array
|
||||
}),
|
||||
);
|
||||
await cli.publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v1' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
await expect(cdn.fetchMetadata()).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
status: 200,
|
||||
body: { version: 'v1' }, // not an array
|
||||
}),
|
||||
);
|
||||
|
||||
await cli.publish({
|
||||
sdl: /* GraphQL */ `
|
||||
type Query {
|
||||
topProduct: String
|
||||
topProducts: [String]
|
||||
}
|
||||
`,
|
||||
metadata: { version: 'v2' },
|
||||
expect: 'latest-composable',
|
||||
});
|
||||
|
||||
await expect(cdn.fetchMetadata()).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
status: 200,
|
||||
body: { version: 'v2' }, // not an array
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,7 +15,6 @@ import {
|
|||
buildSchemaCheckFailureState,
|
||||
ContractCheckInput,
|
||||
ContractInput,
|
||||
DeleteFailureReasonCode,
|
||||
isContractChecksSuccessful,
|
||||
PublishFailureReasonCode,
|
||||
PublishIgnoreReasonCode /* Check */,
|
||||
|
|
@ -116,7 +115,6 @@ export class CompositeModel {
|
|||
contracts,
|
||||
failDiffOnDangerousChange,
|
||||
filterNestedChanges,
|
||||
compareToLatestComposableVersion,
|
||||
}: {
|
||||
input: Pick<CompositeSchemaInput, 'sdl' | 'serviceName'> & {
|
||||
// for a schema check the service url is optional
|
||||
|
|
@ -144,7 +142,6 @@ export class CompositeModel {
|
|||
approvedChanges: Map<string, SchemaChangeType>;
|
||||
conditionalBreakingChangeDiffConfig: null | ConditionalBreakingChangeDiffConfig;
|
||||
failDiffOnDangerousChange: null | boolean;
|
||||
compareToLatestComposableVersion: boolean;
|
||||
contracts: Array<
|
||||
ContractInput & {
|
||||
approvedChanges: Map<string, SchemaChangeType> | null;
|
||||
|
|
@ -166,7 +163,6 @@ export class CompositeModel {
|
|||
const schemaSwapResult = latest ? swapServices(latest.schemas, incoming) : null;
|
||||
const schemas = schemaSwapResult ? schemaSwapResult.schemas : [incoming];
|
||||
schemas.sort((a, b) => a.serviceName.localeCompare(b.serviceName));
|
||||
const comparedVersion = compareToLatestComposableVersion ? latestComposable : latest;
|
||||
|
||||
const checksumCheck = await this.checks.checksum({
|
||||
existing: schemaSwapResult?.existing
|
||||
|
|
@ -207,7 +203,7 @@ export class CompositeModel {
|
|||
});
|
||||
|
||||
const previousVersionSdl = await this.checks.retrievePreviousVersionSdl({
|
||||
version: comparedVersion,
|
||||
version: latestComposable,
|
||||
organization,
|
||||
project,
|
||||
targetId: selector.targetId,
|
||||
|
|
@ -318,7 +314,6 @@ export class CompositeModel {
|
|||
contracts,
|
||||
conditionalBreakingChangeDiffConfig,
|
||||
failDiffOnDangerousChange,
|
||||
compareToLatestComposableVersion,
|
||||
}: {
|
||||
input: {
|
||||
sdl: string;
|
||||
|
|
@ -344,7 +339,6 @@ export class CompositeModel {
|
|||
contracts: Array<ContractInput> | null;
|
||||
conditionalBreakingChangeDiffConfig: null | ConditionalBreakingChangeDiffConfig;
|
||||
failDiffOnDangerousChange: null | boolean;
|
||||
compareToLatestComposableVersion: boolean;
|
||||
}): Promise<SchemaPublishResult> {
|
||||
const latestSchemaVersion = latest;
|
||||
const latestServiceVersion = latest?.schemas?.find(
|
||||
|
|
@ -382,9 +376,6 @@ export class CompositeModel {
|
|||
|
||||
const schemas = schemaSwapResult?.schemas ?? [incoming];
|
||||
schemas.sort((a, b) => a.serviceName.localeCompare(b.serviceName));
|
||||
const schemaVersionToCompareAgainst = compareToLatestComposableVersion
|
||||
? latestComposable
|
||||
: latest;
|
||||
|
||||
const checksumCheck = await this.checks.checksum({
|
||||
existing: schemaSwapResult?.existing
|
||||
|
|
@ -452,24 +443,8 @@ export class CompositeModel {
|
|||
};
|
||||
}
|
||||
|
||||
if (
|
||||
compositionCheck.status === 'failed' &&
|
||||
compositionCheck.reason.errorsBySource.graphql.length > 0 &&
|
||||
!compareToLatestComposableVersion
|
||||
) {
|
||||
return {
|
||||
conclusion: SchemaPublishConclusion.Reject,
|
||||
reasons: [
|
||||
{
|
||||
code: PublishFailureReasonCode.CompositionFailure,
|
||||
compositionErrors: compositionCheck.reason.errorsBySource.graphql,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const previousVersionSdl = await this.checks.retrievePreviousVersionSdl({
|
||||
version: schemaVersionToCompareAgainst,
|
||||
version: latestComposable,
|
||||
organization,
|
||||
project,
|
||||
targetId: target.id,
|
||||
|
|
@ -491,7 +466,7 @@ export class CompositeModel {
|
|||
const diffCheck = await this.checks.diff({
|
||||
conditionalBreakingChangeConfig: conditionalBreakingChangeDiffConfig,
|
||||
includeUrlChanges: {
|
||||
schemasBefore: schemaVersionToCompareAgainst?.schemas ?? [],
|
||||
schemasBefore: latestComposable?.schemas ?? [],
|
||||
schemasAfter: schemas,
|
||||
},
|
||||
filterOutFederationChanges: project.type === ProjectType.FEDERATION,
|
||||
|
|
@ -571,7 +546,6 @@ export class CompositeModel {
|
|||
conditionalBreakingChangeDiffConfig,
|
||||
contracts,
|
||||
failDiffOnDangerousChange,
|
||||
compareToLatestComposableVersion,
|
||||
}: {
|
||||
input: {
|
||||
serviceName: string;
|
||||
|
|
@ -597,7 +571,6 @@ export class CompositeModel {
|
|||
contracts: Array<ContractInput> | null;
|
||||
conditionalBreakingChangeDiffConfig: null | ConditionalBreakingChangeDiffConfig;
|
||||
failDiffOnDangerousChange: null | boolean;
|
||||
compareToLatestComposableVersion: boolean;
|
||||
}): Promise<SchemaDeleteResult> {
|
||||
const latestVersion = latest;
|
||||
|
||||
|
|
@ -623,7 +596,7 @@ export class CompositeModel {
|
|||
});
|
||||
|
||||
const previousVersionSdl = await this.checks.retrievePreviousVersionSdl({
|
||||
version: compareToLatestComposableVersion ? latestComposable : latest,
|
||||
version: latestComposable,
|
||||
organization,
|
||||
project,
|
||||
targetId: selector.target,
|
||||
|
|
@ -665,23 +638,6 @@ export class CompositeModel {
|
|||
getAffectedAppDeployments: getAffectedAppDeploymentsForDelete,
|
||||
});
|
||||
|
||||
if (
|
||||
compositionCheck.status === 'failed' &&
|
||||
compositionCheck.reason.errorsBySource.graphql.length > 0
|
||||
) {
|
||||
if (!compareToLatestComposableVersion) {
|
||||
return {
|
||||
conclusion: SchemaDeleteConclusion.Reject,
|
||||
reasons: [
|
||||
{
|
||||
code: DeleteFailureReasonCode.CompositionFailure,
|
||||
compositionErrors: compositionCheck.reason.errorsBySource.graphql,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const { changes, breakingChanges } =
|
||||
diffCheck.status === 'failed'
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -92,9 +92,6 @@ export class SingleModel {
|
|||
};
|
||||
|
||||
const schemas = [incoming] as [SingleSchemaInput];
|
||||
const comparedVersion = organization.featureFlags.compareToPreviousComposableVersion
|
||||
? latestComposable
|
||||
: latest;
|
||||
|
||||
const checksumResult = await this.checks.checksum({
|
||||
existing: latest
|
||||
|
|
@ -126,7 +123,7 @@ export class SingleModel {
|
|||
});
|
||||
|
||||
const previousVersionSdl = await this.checks.retrievePreviousVersionSdl({
|
||||
version: comparedVersion,
|
||||
version: latestComposable,
|
||||
organization,
|
||||
project,
|
||||
targetId: selector.targetId,
|
||||
|
|
@ -236,9 +233,6 @@ export class SingleModel {
|
|||
|
||||
const latestVersion = latest;
|
||||
const schemas = [incoming] as [SingleSchemaInput];
|
||||
const compareToPreviousComposableVersion =
|
||||
organization.featureFlags.compareToPreviousComposableVersion;
|
||||
const comparedVersion = compareToPreviousComposableVersion ? latestComposable : latest;
|
||||
|
||||
const checksumCheck = await this.checks.checksum({
|
||||
existing: latest
|
||||
|
|
@ -276,8 +270,23 @@ export class SingleModel {
|
|||
contracts: null,
|
||||
});
|
||||
|
||||
if (
|
||||
compositionCheck.status === 'failed' &&
|
||||
compositionCheck.reason.errorsBySource.graphql.length > 0
|
||||
) {
|
||||
return {
|
||||
conclusion: SchemaPublishConclusion.Reject,
|
||||
reasons: [
|
||||
{
|
||||
code: PublishFailureReasonCode.CompositionFailure,
|
||||
compositionErrors: compositionCheck.reason.errorsBySource.graphql,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const previousVersionSdl = await this.checks.retrievePreviousVersionSdl({
|
||||
version: comparedVersion,
|
||||
version: latestComposable,
|
||||
organization,
|
||||
project,
|
||||
targetId: target.id,
|
||||
|
|
@ -331,23 +340,6 @@ export class SingleModel {
|
|||
messages.push('Metadata has been updated');
|
||||
}
|
||||
|
||||
if (
|
||||
compositionCheck.status === 'failed' &&
|
||||
compositionCheck.reason.errorsBySource.graphql.length > 0
|
||||
) {
|
||||
if (organization.featureFlags.compareToPreviousComposableVersion === false) {
|
||||
return {
|
||||
conclusion: SchemaPublishConclusion.Reject,
|
||||
reasons: [
|
||||
{
|
||||
code: PublishFailureReasonCode.CompositionFailure,
|
||||
compositionErrors: compositionCheck.reason.errorsBySource.graphql,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
conclusion: SchemaPublishConclusion.Publish,
|
||||
state: {
|
||||
|
|
|
|||
|
|
@ -1096,7 +1096,7 @@ export class SchemaManager {
|
|||
};
|
||||
}
|
||||
|
||||
async getVersionBeforeVersionId(args: {
|
||||
async getComposableVersionBeforeVersionId(args: {
|
||||
organization: string;
|
||||
project: string;
|
||||
target: string;
|
||||
|
|
@ -1105,21 +1105,11 @@ export class SchemaManager {
|
|||
}) {
|
||||
this.logger.debug('Fetch version before version id. (args=%o)', args);
|
||||
|
||||
const [organization, project] = await Promise.all([
|
||||
this.storage.getOrganization({
|
||||
organizationId: args.organization,
|
||||
}),
|
||||
this.storage.getProject({
|
||||
organizationId: args.organization,
|
||||
projectId: args.project,
|
||||
}),
|
||||
]);
|
||||
|
||||
const schemaVersion = await this.storage.getVersionBeforeVersionId({
|
||||
targetId: args.target,
|
||||
beforeVersionId: args.beforeVersionId,
|
||||
beforeVersionCreatedAt: args.beforeVersionCreatedAt,
|
||||
onlyComposable: shouldUseLatestComposableVersion(args.target, project, organization),
|
||||
onlyComposable: true,
|
||||
});
|
||||
|
||||
if (!schemaVersion) {
|
||||
|
|
@ -1362,17 +1352,3 @@ export class SchemaManager {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldUseLatestComposableVersion(
|
||||
targetId: string,
|
||||
project: Project,
|
||||
organization: Organization,
|
||||
) {
|
||||
return (
|
||||
organization.featureFlags.compareToPreviousComposableVersion ||
|
||||
// If the project is a native federation project, we should compare to the latest composable version
|
||||
(project.nativeFederation &&
|
||||
// but only if the target is not forced to use the legacy composition
|
||||
!organization.featureFlags.forceLegacyCompositionInTargets.includes(targetId))
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ import {
|
|||
toCompositeSchemaInput,
|
||||
toSingleSchemaInput,
|
||||
} from './schema-helper';
|
||||
import { SchemaManager, shouldUseLatestComposableVersion } from './schema-manager';
|
||||
import { SchemaManager } from './schema-manager';
|
||||
import { SchemaVersionHelper } from './schema-version-helper';
|
||||
|
||||
const schemaCheckCount = new promClient.Counter({
|
||||
|
|
@ -719,11 +719,6 @@ export class SchemaPublisher {
|
|||
conditionalBreakingChangeDiffConfig:
|
||||
conditionalBreakingChangeConfiguration?.conditionalBreakingChangeDiffConfig ?? null,
|
||||
failDiffOnDangerousChange,
|
||||
compareToLatestComposableVersion: shouldUseLatestComposableVersion(
|
||||
selector.targetId,
|
||||
project,
|
||||
organization,
|
||||
),
|
||||
filterNestedChanges: !input.schemaProposalId,
|
||||
});
|
||||
break;
|
||||
|
|
@ -1398,12 +1393,6 @@ export class SchemaPublisher {
|
|||
}),
|
||||
]);
|
||||
|
||||
const compareToLatestComposableVersion = shouldUseLatestComposableVersion(
|
||||
selector.targetId,
|
||||
project,
|
||||
organization,
|
||||
);
|
||||
|
||||
if (!latestVersion || latestVersion.schemas.length === 0) {
|
||||
throw new HiveError('Registry is empty');
|
||||
}
|
||||
|
|
@ -1476,18 +1465,8 @@ export class SchemaPublisher {
|
|||
conditionalBreakingChangeConfiguration?.conditionalBreakingChangeDiffConfig ?? null,
|
||||
contracts,
|
||||
failDiffOnDangerousChange,
|
||||
compareToLatestComposableVersion,
|
||||
});
|
||||
|
||||
let diffSchemaVersionId: string | null = null;
|
||||
if (compareToLatestComposableVersion && latestComposableVersion) {
|
||||
diffSchemaVersionId = latestComposableVersion.version.id;
|
||||
}
|
||||
|
||||
if (!compareToLatestComposableVersion && latestVersion) {
|
||||
diffSchemaVersionId = latestVersion.version.id;
|
||||
}
|
||||
|
||||
if (deleteResult.conclusion === SchemaDeleteConclusion.Accept) {
|
||||
this.logger.debug('Delete accepted');
|
||||
if (input.dryRun !== true) {
|
||||
|
|
@ -1497,7 +1476,7 @@ export class SchemaPublisher {
|
|||
targetId: selector.targetId,
|
||||
serviceName: input.serviceName,
|
||||
composable: deleteResult.state.composable,
|
||||
diffSchemaVersionId,
|
||||
diffSchemaVersionId: latestComposableVersion?.version.id ?? null,
|
||||
changes: deleteResult.state.changes,
|
||||
coordinatesDiff: deleteResult.state.coordinatesDiff,
|
||||
contracts:
|
||||
|
|
@ -1771,15 +1750,6 @@ export class SchemaPublisher {
|
|||
})
|
||||
: null;
|
||||
|
||||
const compareToLatestComposableVersion = shouldUseLatestComposableVersion(
|
||||
target.id,
|
||||
project,
|
||||
organization,
|
||||
);
|
||||
const comparedSchemaVersion = compareToLatestComposableVersion
|
||||
? latestComposable
|
||||
: latestVersion;
|
||||
|
||||
const latestSchemaVersionContracts = latestVersion
|
||||
? await this.contracts.getContractVersionsForSchemaVersion({
|
||||
schemaVersionId: latestVersion.version.id,
|
||||
|
|
@ -1867,7 +1837,6 @@ export class SchemaPublisher {
|
|||
conditionalBreakingChangeDiffConfig:
|
||||
conditionalBreakingChangeConfiguration?.conditionalBreakingChangeDiffConfig ?? null,
|
||||
failDiffOnDangerousChange,
|
||||
compareToLatestComposableVersion: compareToLatestComposableVersion,
|
||||
});
|
||||
break;
|
||||
default: {
|
||||
|
|
@ -2009,8 +1978,6 @@ export class SchemaPublisher {
|
|||
|
||||
const supergraph = publishResult.state.supergraph ?? null;
|
||||
|
||||
const diffSchemaVersionId = comparedSchemaVersion?.version.id ?? null;
|
||||
|
||||
this.logger.debug(`Assigning ${schemaLogIds.length} schemas to new version`);
|
||||
|
||||
const serviceName = input.service;
|
||||
|
|
@ -2065,7 +2032,7 @@ export class SchemaPublisher {
|
|||
},
|
||||
changes,
|
||||
coordinatesDiff: publishResult.state.coordinatesDiff,
|
||||
diffSchemaVersionId,
|
||||
diffSchemaVersionId: latestComposable?.version.id ?? null,
|
||||
previousSchemaVersion: latestVersion?.version.id ?? null,
|
||||
conditionalBreakingChangeMetadata: await this.getConditionalBreakingChangeMetadata({
|
||||
conditionalBreakingChangeConfiguration,
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ export class SchemaVersionHelper {
|
|||
return null;
|
||||
}
|
||||
|
||||
return await this.schemaManager.getVersionBeforeVersionId({
|
||||
return await this.schemaManager.getComposableVersionBeforeVersionId({
|
||||
organization: schemaVersion.organizationId,
|
||||
project: schemaVersion.projectId,
|
||||
target: schemaVersion.targetId,
|
||||
|
|
|
|||
|
|
@ -176,10 +176,6 @@ export interface Organization {
|
|||
};
|
||||
getStarted: OrganizationGetStarted;
|
||||
featureFlags: {
|
||||
/**
|
||||
* @deprecated This feature flag is now a default for newly created organizations and projects.
|
||||
*/
|
||||
compareToPreviousComposableVersion: boolean;
|
||||
/**
|
||||
* Forces selected targets to use @apollo/federation library
|
||||
* when native composition is enabled for a project.
|
||||
|
|
|
|||
|
|
@ -4902,7 +4902,6 @@ const decodeCDNAccessTokenRecord = (result: unknown): CDNAccessToken => {
|
|||
|
||||
const FeatureFlagsModel = zod
|
||||
.object({
|
||||
compareToPreviousComposableVersion: zod.boolean().default(false),
|
||||
forceLegacyCompositionInTargets: zod.array(zod.string()).default([]),
|
||||
/** whether app deployments are enabled for the given organization */
|
||||
appDeployments: zod.boolean().default(false),
|
||||
|
|
@ -4916,7 +4915,6 @@ const FeatureFlagsModel = zod
|
|||
.transform(
|
||||
val =>
|
||||
val ?? {
|
||||
compareToPreviousComposableVersion: false,
|
||||
forceLegacyCompositionInTargets: [],
|
||||
appDeployments: false,
|
||||
otelTracing: false,
|
||||
|
|
|
|||
Loading…
Reference in a new issue