Skip to content

Commit 96506ec

Browse files
authored
Merge pull request #351 from thefrontside/dl/update-hydraphql-0.1.0
Update @frontside/hydraphql to 0.1.1
2 parents 8ea30a0 + 5cf89a0 commit 96506ec

File tree

18 files changed

+448
-202
lines changed

18 files changed

+448
-202
lines changed

.changeset/friendly-yaks-rhyme.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@frontside/backstage-plugin-graphql-backend-module-catalog': patch
3+
'@frontside/backstage-plugin-graphql-backend-node': patch
4+
'@frontside/backstage-plugin-graphql-backend': patch
5+
---
6+
7+
Update @frontside/hydraphql to 0.1.1

plugins/graphql-backend-module-catalog/README.md

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,51 @@ Some key features are currently missing. These features may change the schema in
1616
1. `viewer` query for retrieving data for the current user.
1717

1818
- [GraphQL modules](#graphql-modules)
19-
- [Catalog module](#catalog-module)
20-
- [Relation module](#relation-module)
19+
- [Backstage Plugins](#backstage-plugins)
20+
- [Experimental Backend System](#experimental-backend-system)
2121
- [Directives API](#directives-api)
2222
- [`@relation` directive](#relation-directive)
2323
- [Catalog Data loader](#catalog-data-loader-advanced)
2424

2525
## GraphQL modules
2626

27-
### Catalog module
27+
This package provides two GraphQL modules:
28+
- `Catalog` module – provides basic Catalog GraphQL types and `@relation` directive
29+
- `Relation` module – provides only `@relation` directive
2830

29-
The `Catalog` module is installed just as any other Backstage Module:
30-
[`@frontside/backstage-plugin-graphql-backend`](../graphql-backend/README.md)
31+
### Backstage Plugins
3132

33+
For the [Backstage plugin system](https://backstage.io/docs/plugins/backend-plugin),
34+
you can learn how to add GraphQL modules by checking out [GraphQL Modules](../graphql-backend/README.md#graphql-modules)
35+
section in `@frontside/backstage-plugin-graphql-backend` package.
36+
37+
This package exports `Catalog` and `Relation` modules
38+
39+
### Experimental Backend System
40+
41+
For the [experimental backend system](https://backstage.io/docs/plugins/experimental-backend),
42+
you can add them as a plugin modules:
43+
44+
- To use `Catalog` GraphQL module
3245
```ts
3346
// packages/backend/src/index.ts
3447
import { graphqlModuleCatalog } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
3548

3649
const backend = createBackend();
37-
38-
// GraphQL
39-
backend.use(graphqlPlugin());
4050
backend.use(graphqlModuleCatalog());
4151
```
4252

43-
### Relation module
44-
45-
If you don't want to use basic Catalog types for some reason, but
46-
still want to use `@relation` directive, you can install `Relation` module
47-
53+
- To use `Relation` GraphQL module
4854
```ts
49-
// packages/backend/src/index.ts
5055
import { graphqlModuleRelationResolver } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
5156

5257
const backend = createBackend();
53-
54-
// GraphQL
55-
backend.use(graphqlPlugin());
5658
backend.use(graphqlModuleRelationResolver());
5759
```
5860

61+
If you don't want to use basic Catalog types for some reason, but
62+
still want to use `@relation` directive, you can install `Relation` module
63+
5964
## Directives API
6065

6166
### `@relation`

plugins/graphql-backend-module-catalog/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"@backstage/catalog-model": "^1.4.1",
4747
"@backstage/plugin-catalog-node": "^1.4.3",
4848
"@frontside/backstage-plugin-graphql-backend": "^0.1.0",
49-
"@frontside/hydraphql": "^0.0.1",
49+
"@frontside/hydraphql": "^0.1.1",
5050
"@graphql-tools/load-files": "^7.0.0",
5151
"@graphql-tools/utils": "^10.0.0",
5252
"dataloader": "^2.1.0",

plugins/graphql-backend-module-catalog/src/__snapshots__/codegen.test.ts.snap

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ exports[`graphql-catalog codegen should generate the correct code: graphql 1`] =
55

66
directive @discriminationAlias(type: String!, value: String!) repeatable on INTERFACE
77

8-
directive @field(at: _DirectiveArgument_!, default: _DirectiveArgument_) on FIELD_DEFINITION
8+
directive @field(at: _DirectiveArgument_, default: _DirectiveArgument_) on FIELD_DEFINITION
99

1010
directive @implements(interface: String!) on INTERFACE | OBJECT
1111

1212
directive @relation(kind: String, name: String, nodeType: String) on FIELD_DEFINITION
1313

14-
directive @resolve(at: _DirectiveArgument_, from: String) on FIELD_DEFINITION
14+
directive @resolve(at: _DirectiveArgument_, from: String, nodeType: String) on FIELD_DEFINITION
1515

1616
interface API implements Entity & Node & Ownable {
1717
annotations: [KeyValuePair!]
@@ -2436,7 +2436,7 @@ export type DiscriminationAliasDirectiveArgs = {
24362436
export type DiscriminationAliasDirectiveResolver<Result, Parent, ContextType = any, Args = DiscriminationAliasDirectiveArgs> = DirectiveResolverFn<Result, Parent, ContextType, Args>;
24372437

24382438
export type FieldDirectiveArgs = {
2439-
at: Scalars['_DirectiveArgument_']['input'];
2439+
at?: Maybe<Scalars['_DirectiveArgument_']['input']>;
24402440
default?: Maybe<Scalars['_DirectiveArgument_']['input']>;
24412441
};
24422442

@@ -2459,6 +2459,7 @@ export type RelationDirectiveResolver<Result, Parent, ContextType = any, Args =
24592459
export type ResolveDirectiveArgs = {
24602460
at?: Maybe<Scalars['_DirectiveArgument_']['input']>;
24612461
from?: Maybe<Scalars['String']['input']>;
2462+
nodeType?: Maybe<Scalars['String']['input']>;
24622463
};
24632464

24642465
export type ResolveDirectiveResolver<Result, Parent, ContextType = any, Args = ResolveDirectiveArgs> = DirectiveResolverFn<Result, Parent, ContextType, Args>;

plugins/graphql-backend-module-catalog/src/__testUtils__/createApi.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { JsonObject } from '@backstage/types';
33
import {
44
createGraphQLApp,
55
GraphQLContext,
6-
CoreSync,
76
} from '@frontside/hydraphql';
87

98
import * as graphql from 'graphql';
@@ -12,15 +11,15 @@ import { Module } from 'graphql-modules';
1211
import { envelop, useEngine } from '@envelop/core';
1312
import { useDataLoader } from '@envelop/dataloader';
1413
import { useGraphQLModules } from '@envelop/graphql-modules';
15-
import { RelationSync } from '../relation';
14+
import { Relation } from '../relation';
1615

1716
export async function createGraphQLAPI(
1817
TestModule: Module,
1918
loader: (context: GraphQLContext) => DataLoader<any, any>,
2019
generateOpaqueTypes?: boolean,
2120
) {
2221
const application = await createGraphQLApp({
23-
modules: [CoreSync(), RelationSync(), TestModule],
22+
modules: [Relation(), TestModule],
2423
generateOpaqueTypes,
2524
});
2625

plugins/graphql-backend-module-catalog/src/catalog/catalog.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { createModule, TypeDefs } from 'graphql-modules';
1+
import { createModule } from 'graphql-modules';
22
import GraphQLJSON, { GraphQLJSONObject } from 'graphql-type-json';
33
import { relationDirectiveMapper } from '../relationDirectiveMapper';
44
import {
5-
createDirectiveMapperProvider,
5+
GraphQLModule,
66
encodeId,
77
} from '@frontside/hydraphql';
88
import { stringifyEntityRef } from '@backstage/catalog-model';
9-
import { loadFiles, loadFilesSync } from '@graphql-tools/load-files';
9+
import { loadFilesSync } from '@graphql-tools/load-files';
1010
import { resolvePackagePath } from '@backstage/backend-common';
1111
import { CATALOG_SOURCE } from '../constants';
1212

@@ -16,12 +16,11 @@ const catalogSchemaPath = resolvePackagePath(
1616
);
1717

1818
/** @public */
19-
export const CatalogSync = (
20-
typeDefs: TypeDefs = loadFilesSync(catalogSchemaPath),
21-
) =>
22-
createModule({
19+
export const Catalog = (): GraphQLModule => ({
20+
mappers: { relation: relationDirectiveMapper },
21+
module: createModule({
2322
id: 'catalog',
24-
typeDefs,
23+
typeDefs: loadFilesSync(catalogSchemaPath),
2524
resolvers: {
2625
JSON: GraphQLJSON,
2726
JSONObject: GraphQLJSONObject,
@@ -55,11 +54,5 @@ export const CatalogSync = (
5554
}),
5655
},
5756
},
58-
providers: [
59-
createDirectiveMapperProvider('relation', relationDirectiveMapper),
60-
],
61-
});
62-
63-
/** @public */
64-
export const Catalog = async () =>
65-
CatalogSync(await loadFiles(catalogSchemaPath));
57+
})
58+
});
Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { TypeDefs, createModule } from 'graphql-modules';
1+
import { createModule } from 'graphql-modules';
22
import { relationDirectiveMapper } from '../relationDirectiveMapper';
3-
import { createDirectiveMapperProvider } from '@frontside/hydraphql';
4-
import { loadFiles, loadFilesSync } from '@graphql-tools/load-files';
3+
import { GraphQLModule } from '@frontside/hydraphql';
4+
import { loadFilesSync } from '@graphql-tools/load-files';
55
import { resolvePackagePath } from '@backstage/backend-common';
66

77
const relationSchemaPath = resolvePackagePath(
@@ -10,17 +10,10 @@ const relationSchemaPath = resolvePackagePath(
1010
);
1111

1212
/** @public */
13-
export const RelationSync = (
14-
typeDefs: TypeDefs = loadFilesSync(relationSchemaPath),
15-
) =>
16-
createModule({
13+
export const Relation = (): GraphQLModule => ({
14+
mappers: { relation: relationDirectiveMapper },
15+
module: createModule({
1716
id: 'relation',
18-
typeDefs,
19-
providers: [
20-
createDirectiveMapperProvider('relation', relationDirectiveMapper),
21-
],
22-
});
23-
24-
/** @public */
25-
export const Relation = async () =>
26-
RelationSync(await loadFiles(relationSchemaPath));
17+
typeDefs: loadFilesSync(relationSchemaPath),
18+
})
19+
});

plugins/graphql-backend-module-catalog/src/relationDirectiveMapper.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
CoreSync,
32
transformSchema,
43
encodeId,
54
decodeId,
@@ -8,13 +7,12 @@ import DataLoader from 'dataloader';
87
import { DocumentNode, GraphQLNamedType, printType } from 'graphql';
98
import { createModule, gql } from 'graphql-modules';
109
import { createGraphQLAPI } from './__testUtils__';
11-
import { RelationSync } from './relation/relation';
10+
import { Relation } from './relation/relation';
1211

1312
describe('mapRelationDirective', () => {
1413
const transform = (source: DocumentNode) =>
1514
transformSchema([
16-
CoreSync(),
17-
RelationSync(),
15+
Relation(),
1816
createModule({
1917
id: 'mapRelationDirective',
2018
typeDefs: source,

plugins/graphql-backend-module-catalog/src/relationDirectiveMapper.ts

Lines changed: 9 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,23 @@ import { connectionFromArray } from 'graphql-relay';
22
import { Entity, parseEntityRef } from '@backstage/catalog-model';
33
import {
44
GraphQLFieldConfig,
5-
GraphQLID,
65
GraphQLInt,
76
GraphQLInterfaceType,
8-
GraphQLList,
9-
GraphQLNonNull,
10-
GraphQLObjectType,
117
GraphQLString,
12-
isInputType,
13-
isInterfaceType,
148
isListType,
159
isNonNullType,
16-
isObjectType,
17-
isUnionType,
1810
} from 'graphql';
1911
import {
2012
DirectiveMapperAPI,
2113
ResolverContext,
2214
unboxNamedType,
2315
encodeId,
16+
isConnectionType,
17+
createConnectionType,
18+
getNodeTypeForConnection
2419
} from '@frontside/hydraphql';
2520
import { CATALOG_SOURCE } from './constants';
2621

27-
function isConnectionType(type: unknown): type is GraphQLInterfaceType {
28-
return (
29-
(isInterfaceType(type) && type.name === 'Connection') ||
30-
(isNonNullType(type) && isConnectionType(type.ofType))
31-
);
32-
}
33-
3422
function filterEntityRefs(
3523
entity: Entity | undefined,
3624
relationType?: string,
@@ -49,42 +37,6 @@ function filterEntityRefs(
4937
);
5038
}
5139

52-
function createConnectionType(
53-
nodeType: GraphQLInterfaceType | GraphQLObjectType,
54-
fieldType: GraphQLInterfaceType,
55-
): GraphQLObjectType {
56-
const wrappedEdgeType = fieldType.getFields().edges.type as GraphQLNonNull<
57-
GraphQLList<GraphQLNonNull<GraphQLInterfaceType>>
58-
>;
59-
const edgeType = wrappedEdgeType.ofType.ofType.ofType as GraphQLInterfaceType;
60-
61-
return new GraphQLObjectType({
62-
name: `${nodeType.name}Connection`,
63-
fields: {
64-
...fieldType.toConfig().fields,
65-
edges: {
66-
type: new GraphQLNonNull(
67-
new GraphQLList(
68-
new GraphQLNonNull(
69-
new GraphQLObjectType({
70-
name: `${nodeType.name}Edge`,
71-
fields: {
72-
...edgeType.toConfig().fields,
73-
node: {
74-
type: new GraphQLNonNull(nodeType),
75-
},
76-
},
77-
interfaces: [edgeType],
78-
}),
79-
),
80-
),
81-
),
82-
},
83-
},
84-
interfaces: [fieldType],
85-
});
86-
}
87-
8840
export function relationDirectiveMapper(
8941
_fieldName: string,
9042
field: GraphQLFieldConfig<{ id: string }, ResolverContext>,
@@ -108,51 +60,13 @@ export function relationDirectiveMapper(
10860

10961
if (isConnectionType(fieldType)) {
11062
if (directive.nodeType) {
111-
const nodeType = api.typeMap[directive.nodeType];
112-
113-
if (!nodeType) {
114-
throw new Error(
115-
`The interface "${directive.nodeType}" is not defined in the schema`,
116-
);
117-
}
118-
if (isInputType(nodeType)) {
119-
throw new Error(
120-
`The interface "${directive.nodeType}" is an input type and can't be used in a Connection`,
121-
);
122-
}
123-
if (isUnionType(nodeType)) {
124-
const resolveType = nodeType.resolveType;
125-
if (resolveType)
126-
throw new Error(
127-
`The "resolveType" function has already been implemented for "${nodeType.name}" union which may lead to undefined behavior`,
128-
);
129-
const iface = (api.typeMap[directive.nodeType] =
130-
new GraphQLInterfaceType({
131-
name: directive.nodeType,
132-
interfaces: [api.typeMap.Node as GraphQLInterfaceType],
133-
fields: { id: { type: new GraphQLNonNull(GraphQLID) } },
134-
resolveType: (...args) =>
135-
(api.typeMap.Node as GraphQLInterfaceType).resolveType?.(...args),
136-
}));
137-
const types = nodeType.getTypes().map(type => type.name);
138-
types.forEach(typeName => {
139-
const type = api.typeMap[typeName];
140-
if (isInterfaceType(type)) {
141-
api.typeMap[typeName] = new GraphQLInterfaceType({
142-
...type.toConfig(),
143-
interfaces: [...type.getInterfaces(), iface],
144-
});
145-
}
146-
if (isObjectType(type)) {
147-
api.typeMap[typeName] = new GraphQLObjectType({
148-
...type.toConfig(),
149-
interfaces: [...type.getInterfaces(), iface],
150-
});
151-
}
152-
});
63+
const nodeType = getNodeTypeForConnection(
64+
directive.nodeType,
65+
(name) => api.typeMap[name],
66+
(name, type) => (api.typeMap[name] = type),
67+
);
15368

154-
field.type = createConnectionType(iface, fieldType);
155-
} else {
69+
if (nodeType) {
15670
field.type = createConnectionType(nodeType, fieldType);
15771
}
15872
} else {
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { CoreSync, transformSchema } from '@frontside/hydraphql';
1+
import { transformSchema } from '@frontside/hydraphql';
22
import { printSchemaWithDirectives } from '@graphql-tools/utils';
3-
import { CatalogSync } from './catalog';
3+
import { Catalog } from './catalog';
44

55
export const schema = printSchemaWithDirectives(
6-
transformSchema([CoreSync(), CatalogSync()]),
6+
transformSchema([Catalog()]),
77
);

0 commit comments

Comments
 (0)