Skip to content

Commit

Permalink
Merge pull request #23 from wayfair-incubator/gwardwell--GQL-1809--co…
Browse files Browse the repository at this point in the history
…nsider-arguments-when-tagging
  • Loading branch information
gwardwell authored Aug 17, 2023
2 parents 1b8fd57 + 5ee25b6 commit f3ca448
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 5 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v3.0.1] - 2023-08-17

### Fix

- Applying the `@tag` directive to an entity's `id` field could fail if the only
`@tag` directive in the entity was applied to a field argument. This fix now
considers field argument `@tag`s as well as field `@tag`s when selecting the
`id` field's `@tag`(s).

## [v3.0.0] - 2023-08-16

### Breaking
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@wayfair/node-froid",
"version": "3.0.0",
"version": "3.0.1",
"description": "Federated GQL Relay Object Identification implementation",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
54 changes: 54 additions & 0 deletions src/schema/__tests__/generateFroidSchema.fed-v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,60 @@ describe('generateFroidSchema for federation v1', () => {
`
);
});

it('tags are identified from field arguments', () => {
const urlSchema = gql`
type TypeA @key(fields: "selections { selectionId }") {
selections: [TypeB!] @inaccessible
fieldWithArgument(argument: Int @tag(name: "storefront")): Boolean
}
extend type TypeB @key(fields: "selectionId") {
selectionId: String! @external
}
`;

const altSchema = gql`
type TypeB @key(fields: "selectionId") {
selectionId: String! @tag(name: "storefront")
}
`;

const subgraphs = new Map();
subgraphs.set('url-subgraph', urlSchema);
subgraphs.set('alt-subgraph', altSchema);

const actual = generateSchema({
subgraphs,
froidSubgraphName: 'relay-subgraph',
contractTags: ['storefront', 'internal'],
});

expect(actual).toEqual(
// prettier-ignore
gql`
directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
type Query {
node(id: ID!): Node @tag(name: "internal") @tag(name: "storefront")
}
interface Node @tag(name: "internal") @tag(name: "storefront") {
id: ID!
}
extend type TypeA implements Node @key(fields: "selections { selectionId }") {
id: ID! @tag(name: "storefront")
selections: [TypeB!] @external
}
extend type TypeB implements Node @key(fields: "selectionId") {
id: ID! @tag(name: "storefront")
selectionId: String! @external
}
`
);
});
});

describe('when generating schema for complex keys', () => {
Expand Down
54 changes: 54 additions & 0 deletions src/schema/__tests__/generateFroidSchema.fed-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,60 @@ describe('generateFroidSchema for federation v2', () => {
`
);
});

it('tags are identified from field arguments', () => {
const urlSchema = gql`
type TypeA @key(fields: "selections { selectionId }") {
selections: [TypeB!] @inaccessible
fieldWithArgument(argument: Int @tag(name: "storefront")): Boolean
}
type TypeB @key(fields: "selectionId", resolvable: false) {
selectionId: String!
}
`;

const altSchema = gql`
type TypeB @key(fields: "selectionId") {
selectionId: String! @tag(name: "storefront")
}
`;

const subgraphs = new Map();
subgraphs.set('url-subgraph', urlSchema);
subgraphs.set('alt-subgraph', altSchema);

const actual = generateSchema({
subgraphs,
froidSubgraphName: 'relay-subgraph',
contractTags: ['storefront', 'internal'],
});

expect(actual).toEqual(
// prettier-ignore
gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@tag"])
type Query {
node(id: ID!): Node @tag(name: "internal") @tag(name: "storefront")
}
interface Node @tag(name: "internal") @tag(name: "storefront") {
id: ID!
}
type TypeA implements Node @key(fields: "selections { selectionId }") {
id: ID! @tag(name: "storefront")
selections: [TypeB!]
}
type TypeB implements Node @key(fields: "selectionId") {
id: ID! @tag(name: "storefront")
selectionId: String!
}
`
);
});
});

describe('when generating schema for complex keys', () => {
Expand Down
12 changes: 8 additions & 4 deletions src/schema/generateFroidSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,20 @@ function getTagDirectivesForIdField(
): ConstDirectiveNode[] {
const tagDirectiveNames = objectNodes
.filter((obj) => obj.name.value === node.name.value)
.flatMap((obj) =>
obj.fields?.flatMap((field) =>
.flatMap((obj) => {
const taggableNodes = obj.fields?.flatMap((field) => [
field,
...(field?.arguments || []),
]);
return taggableNodes?.flatMap((field) =>
field.directives
?.filter((directive) => directive.name.value === TAG_DIRECTIVE)
.map(
(directive) =>
(directive?.arguments?.[0].value as StringValueNode).value
)
)
)
);
})
.filter(Boolean)
.sort() as string[];

Expand Down

0 comments on commit f3ca448

Please sign in to comment.