Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/eight-views-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'hive': minor
---

adjust change capture and resolvers to optionally provide a full list or the simplified list of
changes
126 changes: 108 additions & 18 deletions packages/services/api/src/modules/schema/module.graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -879,10 +879,25 @@ export default gql`
"""
Schema changes that were introduced in this schema version (compared to the previous version).
"""
schemaChanges: SchemaChangeConnection @tag(name: "public")
schemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection @tag(name: "public")

breakingSchemaChanges: SchemaChangeConnection
safeSchemaChanges: SchemaChangeConnection
breakingSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
safeSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection

"""
GitHub metadata associated with the schema version.
Expand Down Expand Up @@ -1261,9 +1276,24 @@ export default gql`
"""
hasSchemaChanges: Boolean!

schemaChanges: SchemaChangeConnection @tag(name: "public")
breakingSchemaChanges: SchemaChangeConnection
safeSchemaChanges: SchemaChangeConnection
schemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection @tag(name: "public")
breakingSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
safeSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
schemaPolicyWarnings: SchemaPolicyWarningConnection
schemaPolicyErrors: SchemaPolicyWarningConnection
"""
Expand Down Expand Up @@ -1299,10 +1329,25 @@ export default gql`

schemaCompositionErrors: SchemaErrorConnection @tag(name: "public")

schemaChanges: SchemaChangeConnection @tag(name: "public")
schemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection @tag(name: "public")

breakingSchemaChanges: SchemaChangeConnection
safeSchemaChanges: SchemaChangeConnection
breakingSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
safeSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection

compositeSchemaSDL: String @tag(name: "public")
supergraphSDL: String @tag(name: "public")
Expand Down Expand Up @@ -1345,13 +1390,28 @@ export default gql`
"""
Breaking schema changes for this contract version.
"""
breakingSchemaChanges: SchemaChangeConnection
breakingSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
"""
Safe schema changes for this contract version.
"""
safeSchemaChanges: SchemaChangeConnection
safeSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection

schemaChanges: SchemaChangeConnection @tag(name: "public")
schemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection @tag(name: "public")

previousContractVersion: ContractVersion @tag(name: "public")
previousDiffableContractVersion: ContractVersion @tag(name: "public")
Expand Down Expand Up @@ -1412,12 +1472,27 @@ export default gql`
"""
hasSchemaChanges: Boolean!

schemaChanges: SchemaChangeConnection @tag(name: "public")
schemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection @tag(name: "public")
"""
Breaking changes can exist in an successful schema check if the check was manually approved.
"""
breakingSchemaChanges: SchemaChangeConnection
safeSchemaChanges: SchemaChangeConnection
breakingSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
safeSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
schemaPolicyWarnings: SchemaPolicyWarningConnection
"""
Schema policy errors can exist in an successful schema check if the check was manually approved.
Expand Down Expand Up @@ -1509,9 +1584,24 @@ export default gql`
"""
hasSchemaChanges: Boolean!

schemaChanges: SchemaChangeConnection @tag(name: "public")
breakingSchemaChanges: SchemaChangeConnection
safeSchemaChanges: SchemaChangeConnection
schemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection @tag(name: "public")
breakingSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
safeSchemaChanges(
"""
This filters out nested changes. E.g. if a type was add, the simplified changes wouldn't include information about the fields added to that new type.
"""
simplifyChanges: Boolean = true
): SchemaChangeConnection
schemaPolicyWarnings: SchemaPolicyWarningConnection
schemaPolicyErrors: SchemaPolicyWarningConnection

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
type GraphQLSchema,
} from 'graphql';
import { Injectable, Scope } from 'graphql-modules';
import { Change, ChangeType, diff, DiffRule, TypeOfChangeType } from '@graphql-inspector/core';
import { Change, ChangeType, diff, Rule, TypeOfChangeType } from '@graphql-inspector/core';
import { traceFn } from '@hive/service-common';
import { HiveSchemaChangeModel } from '@hive/storage';
import { Logger } from '../../shared/providers/logger';
Expand All @@ -31,10 +31,10 @@ export class Inspector {
'hive.diff.changes.count': result.length,
}),
})
async diff(existing: GraphQLSchema, incoming: GraphQLSchema) {
async diff(existing: GraphQLSchema, incoming: GraphQLSchema, rules?: Rule[]) {
this.logger.debug('Comparing Schemas');

const changes = await diff(existing, incoming, [DiffRule.simplifyChanges]);
const changes = await diff(existing, incoming, rules);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change exposes the rules option up the callstack so it can be modifies.


return changes
.filter(dropTrimmedDescriptionChangedChange)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { URL } from 'node:url';
import { type GraphQLSchema } from 'graphql';
import { Injectable, Scope } from 'graphql-modules';
import hashObject from 'object-hash';
import { ChangeType, CriticalityLevel, TypeOfChangeType } from '@graphql-inspector/core';
import { ChangeType, CriticalityLevel, DiffRule, TypeOfChangeType } from '@graphql-inspector/core';
import type { CheckPolicyResponse } from '@hive/policy';
import type { CompositionFailureError, ContractsInputType } from '@hive/schema';
import { traceFn } from '@hive/service-common';
Expand Down Expand Up @@ -426,6 +426,7 @@ export class RegistryChecks {
/** Settings for fetching conditional breaking changes. */
conditionalBreakingChangeConfig: null | ConditionalBreakingChangeDiffConfig;
failDiffOnDangerousChange: null | boolean;
filterNestedChanges: boolean;
}) {
let existingSchema: GraphQLSchema | null = null;
let incomingSchema: GraphQLSchema | null = null;
Expand Down Expand Up @@ -463,7 +464,11 @@ export class RegistryChecks {
} satisfies CheckResult;
}

let inspectorChanges = await this.inspector.diff(existingSchema, incomingSchema);
let inspectorChanges = await this.inspector.diff(
existingSchema,
incomingSchema,
args.filterNestedChanges ? [DiffRule.simplifyChanges] : [],
);

// Filter out federation specific changes as they are not relevant for the schema diff and were in previous schema versions by accident.
if (args.filterOutFederationChanges === true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
SuccessfulSchemaCheckMapper,
} from '../module.graphql.mappers';
import { Injectable, Scope } from 'graphql-modules';
import { DiffRule } from '@graphql-inspector/core';
import { Storage } from '../../shared/providers/storage';
import { formatNumber } from '../lib/number-formatting';
import { SchemaManager } from './schema-manager';
Expand Down Expand Up @@ -35,27 +36,70 @@ export class SchemaCheckManager {
return !!(schemaCheck.breakingSchemaChanges?.length || schemaCheck.safeSchemaChanges?.length);
}

getSafeSchemaChanges(schemaCheck: SchemaCheck) {
getSafeSchemaChanges(schemaCheck: SchemaCheck, simplifyChanges: boolean = true) {
if (!schemaCheck.safeSchemaChanges?.length) {
return null;
}

if (simplifyChanges) {
/**
* @todo modify the simplifyChanges rule to loosen the argument requirements. This rule
* doesn't need more information than the change type and path fields.
*/
return DiffRule.simplifyChanges({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule's type definition is very strict and that our SchemaCheck model slightly modifies the change structure, but the only thing this specific rule is using is a change's path and a type, which are unmodified. So technically this is safe to use here even though the typing is a mess.

I'd be interested in hearing recommendations for how maybe we can avoid this typecasting.

changes: schemaCheck.safeSchemaChanges as any,
oldSchema: undefined as any,
newSchema: undefined as any,
config: undefined,
}) as unknown as typeof schemaCheck.safeSchemaChanges;
Comment on lines +49 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using as any here bypasses type safety. While the @todo notes a plan to fix this upstream, a safer intermediate step could be to define a local, less strict context type that only includes the properties you are providing (like changes) and cast to that. This would make the code more self-documenting and slightly safer against future changes.

}

return schemaCheck.safeSchemaChanges;
}

getAllSchemaChanges(schemaCheck: SchemaCheck) {
getAllSchemaChanges(schemaCheck: SchemaCheck, simplifyChanges: boolean = true) {
if (!schemaCheck.safeSchemaChanges?.length && !schemaCheck.breakingSchemaChanges?.length) {
return null;
}

return [...(schemaCheck.breakingSchemaChanges ?? []), ...(schemaCheck.safeSchemaChanges ?? [])];
const changes = [
...(schemaCheck.breakingSchemaChanges ?? []),
...(schemaCheck.safeSchemaChanges ?? []),
];
if (simplifyChanges) {
/**
* @todo modify the simplifyChanges rule to loosen the argument requirements. This rule
* doesn't need more information than the change type and path fields.
*/
return DiffRule.simplifyChanges({
changes: changes as any,
oldSchema: undefined as any,
newSchema: undefined as any,
config: undefined,
}) as unknown as typeof changes;
}

return changes;
}

getBreakingSchemaChanges(schemaCheck: SchemaCheck) {
getBreakingSchemaChanges(schemaCheck: SchemaCheck, simplifyChanges: boolean = true) {
if (!schemaCheck.breakingSchemaChanges?.length) {
return null;
}

if (simplifyChanges) {
/**
* @todo modify the simplifyChanges rule to loosen the argument requirements. This rule
* doesn't need more information than the change type and path fields.
*/
return DiffRule.simplifyChanges({
changes: schemaCheck.breakingSchemaChanges as any,
oldSchema: undefined as any,
newSchema: undefined as any,
config: undefined,
});
}

return schemaCheck.breakingSchemaChanges;
}

Expand Down
Loading
Loading