Skip to content

Commit

Permalink
Merge pull request #371 from thefrontside/dl/relation-entity-check
Browse files Browse the repository at this point in the history
Resolve node to null if entity doesn't exist
  • Loading branch information
wKich authored Dec 18, 2023
2 parents 4d18f47 + d329856 commit 1e119fb
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-news-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@frontside/backstage-plugin-graphql-backend-module-catalog': patch
---

Resolve node to null if entity doesn't exist
Original file line number Diff line number Diff line change
Expand Up @@ -563,4 +563,68 @@ describe('mapRelationDirective', () => {
},
});
});

it('should return null for not existing entities', async () => {
const TestModule = createModule({
id: 'test',
typeDefs: gql`
extend interface Node @discriminates(with: "kind")
type Entity @implements(interface: "Node") {
ownedBy: User @relation
owner: User @relation(name: "ownedBy")
group: Group @relation(name: "ownedBy", kind: "Group")
}
type User @implements(interface: "Node") {
name: String! @field(at: "name")
}
type Group @implements(interface: "Node") {
name: String! @field(at: "name")
}
`,
});
const entity = {
kind: 'Entity',
relations: [
{ type: 'ownedBy', targetRef: 'user:default/john' },
{ type: 'ownedBy', targetRef: 'group:default/team-a' },
],
};
const user = {
kind: 'User',
name: 'John',
};
const loader = () =>
new DataLoader(async ids =>
ids.map(id => {
const { query: { ref } = {} } = decodeId(id as string);
if (ref === 'user:default/john') return user;
if (ref === 'group:default/team-a') return null;
return entity;
}),
);
const query = await createGraphQLAPI(TestModule, loader);
const result = await query(/* GraphQL */ `
node(id: ${JSON.stringify(
encodeId({
source: 'Mock',
typename: 'Entity',
query: { ref: 'entity' },
}),
)}) {
...on Entity {
ownedBy { name }
owner { name }
group { name }
}
}
`);
expect(result).toEqual({
node: {
ownedBy: { name: 'John' },
owner: { name: 'John' },
group: null,
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
encodeId,
isConnectionType,
createConnectionType,
getNodeTypeForConnection
getNodeTypeForConnection,
} from '@frontside/hydraphql';
import { CATALOG_SOURCE } from './constants';

Expand Down Expand Up @@ -62,7 +62,7 @@ export function relationDirectiveMapper(
if (directive.nodeType) {
const nodeType = getNodeTypeForConnection(
directive.nodeType,
(name) => api.typeMap[name],
name => api.typeMap[name],
(name, type) => (api.typeMap[name] = type),
);

Expand Down Expand Up @@ -102,7 +102,7 @@ export function relationDirectiveMapper(

field.resolve = async ({ id }, args, { loader }) => {
const ids = filterEntityRefs(
await loader.load(id) as Entity,
(await loader.load(id)) as Entity,
directive.name,
directive.kind,
).map(ref => ({
Expand All @@ -112,15 +112,21 @@ export function relationDirectiveMapper(
query: { ref },
}),
}));
const connection = connectionFromArray<{ id: string } | null>(ids, args);
(
await loader.loadMany(connection.edges.map(({ node }) => node!.id))
).forEach(
(entity, index) => !entity && (connection.edges[index].node = null),
);
return {
...connectionFromArray(ids, args),
...connection,
count: ids.length,
};
};
} else {
field.resolve = async ({ id }, _, { loader }) => {
const ids = filterEntityRefs(
await loader.load(id) as Entity,
const ids: ({ id: string } | null)[] = filterEntityRefs(
(await loader.load(id)) as Entity,
directive.name,
directive.kind,
).map(ref => ({
Expand All @@ -130,6 +136,10 @@ export function relationDirectiveMapper(
query: { ref },
}),
}));
if (!isList) ids.splice(1);
(await loader.loadMany(ids.map(node => node!.id))).forEach(
(entity, index) => !entity && (ids[index] = null),
);
return isList ? ids : ids[0] ?? null;
};
}
Expand Down

0 comments on commit 1e119fb

Please sign in to comment.