Skip to content

Commit

Permalink
Alternative approach for findSchemaChanges (#4235)
Browse files Browse the repository at this point in the history
Alternative to #2181

This adds a new `findSchemaChanges` export and deprecates the breaking
and dangerous alternative to be removed in v18. While adding
safe-schema-changes I noticed that we lack directive arg validation
here, we should probably add these to breaking/dangerous changes in the
future.
  • Loading branch information
JoviDeCroock authored Oct 18, 2024
1 parent beff1d5 commit f531737
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 10 deletions.
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,8 +477,10 @@ export {
// Compares two GraphQLSchemas and detects breaking changes.
BreakingChangeType,
DangerousChangeType,
SafeChangeType,
findBreakingChanges,
findDangerousChanges,
findSchemaChanges,
} from './utilities/index.js';

export type {
Expand Down Expand Up @@ -506,6 +508,7 @@ export type {
IntrospectionDirective,
BuildSchemaOptions,
BreakingChange,
SafeChange,
DangerousChange,
TypedQueryDocumentNode,
} from './utilities/index.js';
2 changes: 1 addition & 1 deletion src/utilities/__tests__/findBreakingChanges-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
DangerousChangeType,
findBreakingChanges,
findDangerousChanges,
} from '../findBreakingChanges.js';
} from '../findSchemaChanges.js';

describe('findBreakingChanges', () => {
it('should detect if a type was removed or not', () => {
Expand Down
180 changes: 180 additions & 0 deletions src/utilities/__tests__/findSchemaChanges-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import { buildSchema } from '../buildASTSchema.js';
import { findSchemaChanges, SafeChangeType } from '../findSchemaChanges.js';

describe('findSchemaChanges', () => {
it('should detect if a type was added', () => {
const newSchema = buildSchema(`
type Type1
type Type2
`);

const oldSchema = buildSchema(`
type Type1
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
type: SafeChangeType.TYPE_ADDED,
description: 'Type2 was added.',
},
]);
});

it('should detect if a field was added', () => {
const oldSchema = buildSchema(`
type Query {
foo: String
}
`);

const newSchema = buildSchema(`
type Query {
foo: String
bar: String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
type: SafeChangeType.FIELD_ADDED,
description: 'Field Query.bar was added.',
},
]);
});

it('should detect if a default value was added', () => {
const oldSchema = buildSchema(`
type Query {
foo(x: String): String
}
`);

const newSchema = buildSchema(`
type Query {
foo(x: String = "bar"): String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
type: SafeChangeType.ARG_DEFAULT_VALUE_ADDED,
description: 'Query.foo(x:) added a defaultValue "bar".',
},
]);
});

it('should detect if an arg value changes safely', () => {
const oldSchema = buildSchema(`
type Query {
foo(x: String!): String
}
`);

const newSchema = buildSchema(`
type Query {
foo(x: String): String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
type: SafeChangeType.ARG_CHANGED_KIND_SAFE,
description:
'Argument Query.foo(x:) has changed type from String! to String.',
},
]);
});

it('should detect if a directive was added', () => {
const oldSchema = buildSchema(`
type Query {
foo: String
}
`);

const newSchema = buildSchema(`
directive @Foo on FIELD_DEFINITION
type Query {
foo: String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
description: 'Directive @Foo was added.',
type: SafeChangeType.DIRECTIVE_ADDED,
},
]);
});

it('should detect if a directive becomes repeatable', () => {
const oldSchema = buildSchema(`
directive @Foo on FIELD_DEFINITION
type Query {
foo: String
}
`);

const newSchema = buildSchema(`
directive @Foo repeatable on FIELD_DEFINITION
type Query {
foo: String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
description: 'Repeatable flag was added to @Foo.',
type: SafeChangeType.DIRECTIVE_REPEATABLE_ADDED,
},
]);
});

it('should detect if a directive adds locations', () => {
const oldSchema = buildSchema(`
directive @Foo on FIELD_DEFINITION
type Query {
foo: String
}
`);

const newSchema = buildSchema(`
directive @Foo on FIELD_DEFINITION | QUERY
type Query {
foo: String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
description: 'QUERY was added to @Foo.',
type: SafeChangeType.DIRECTIVE_LOCATION_ADDED,
},
]);
});

it('should detect if a directive arg gets added', () => {
const oldSchema = buildSchema(`
directive @Foo on FIELD_DEFINITION
type Query {
foo: String
}
`);

const newSchema = buildSchema(`
directive @Foo(arg1: String) on FIELD_DEFINITION
type Query {
foo: String
}
`);
expect(findSchemaChanges(oldSchema, newSchema)).to.deep.equal([
{
description: 'An optional argument @Foo(arg1:) was added.',
type: SafeChangeType.OPTIONAL_DIRECTIVE_ARG_ADDED,
},
]);
});
});
Loading

0 comments on commit f531737

Please sign in to comment.