Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update @frontside/hydraphql to 0.1.1 #351

Merged
merged 2 commits into from
Oct 5, 2023
Merged
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
7 changes: 7 additions & 0 deletions .changeset/friendly-yaks-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@frontside/backstage-plugin-graphql-backend-module-catalog': patch
'@frontside/backstage-plugin-graphql-backend-node': patch
'@frontside/backstage-plugin-graphql-backend': patch
---

Update @frontside/hydraphql to 0.1.1
39 changes: 22 additions & 17 deletions plugins/graphql-backend-module-catalog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,51 @@ Some key features are currently missing. These features may change the schema in
1. `viewer` query for retrieving data for the current user.

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

## GraphQL modules

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

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

For the [Backstage plugin system](https://backstage.io/docs/plugins/backend-plugin),
you can learn how to add GraphQL modules by checking out [GraphQL Modules](../graphql-backend/README.md#graphql-modules)
section in `@frontside/backstage-plugin-graphql-backend` package.

This package exports `Catalog` and `Relation` modules

### Experimental Backend System

For the [experimental backend system](https://backstage.io/docs/plugins/experimental-backend),
you can add them as a plugin modules:

- To use `Catalog` GraphQL module
```ts
// packages/backend/src/index.ts
import { graphqlModuleCatalog } from '@frontside/backstage-plugin-graphql-backend-module-catalog';

const backend = createBackend();

// GraphQL
backend.use(graphqlPlugin());
backend.use(graphqlModuleCatalog());
```

### Relation module

If you don't want to use basic Catalog types for some reason, but
still want to use `@relation` directive, you can install `Relation` module

- To use `Relation` GraphQL module
```ts
// packages/backend/src/index.ts
import { graphqlModuleRelationResolver } from '@frontside/backstage-plugin-graphql-backend-module-catalog';

const backend = createBackend();

// GraphQL
backend.use(graphqlPlugin());
backend.use(graphqlModuleRelationResolver());
```

If you don't want to use basic Catalog types for some reason, but
still want to use `@relation` directive, you can install `Relation` module

## Directives API

### `@relation`
Expand Down
2 changes: 1 addition & 1 deletion plugins/graphql-backend-module-catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@backstage/catalog-model": "^1.4.1",
"@backstage/plugin-catalog-node": "^1.4.3",
"@frontside/backstage-plugin-graphql-backend": "^0.1.0",
"@frontside/hydraphql": "^0.0.1",
"@frontside/hydraphql": "^0.1.1",
"@graphql-tools/load-files": "^7.0.0",
"@graphql-tools/utils": "^10.0.0",
"dataloader": "^2.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ exports[`graphql-catalog codegen should generate the correct code: graphql 1`] =

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

directive @field(at: _DirectiveArgument_!, default: _DirectiveArgument_) on FIELD_DEFINITION
directive @field(at: _DirectiveArgument_, default: _DirectiveArgument_) on FIELD_DEFINITION

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

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

directive @resolve(at: _DirectiveArgument_, from: String) on FIELD_DEFINITION
directive @resolve(at: _DirectiveArgument_, from: String, nodeType: String) on FIELD_DEFINITION

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

export type FieldDirectiveArgs = {
at: Scalars['_DirectiveArgument_']['input'];
at?: Maybe<Scalars['_DirectiveArgument_']['input']>;
default?: Maybe<Scalars['_DirectiveArgument_']['input']>;
};

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

export type ResolveDirectiveResolver<Result, Parent, ContextType = any, Args = ResolveDirectiveArgs> = DirectiveResolverFn<Result, Parent, ContextType, Args>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { JsonObject } from '@backstage/types';
import {
createGraphQLApp,
GraphQLContext,
CoreSync,
} from '@frontside/hydraphql';

import * as graphql from 'graphql';
Expand All @@ -12,15 +11,15 @@ import { Module } from 'graphql-modules';
import { envelop, useEngine } from '@envelop/core';
import { useDataLoader } from '@envelop/dataloader';
import { useGraphQLModules } from '@envelop/graphql-modules';
import { RelationSync } from '../relation';
import { Relation } from '../relation';

export async function createGraphQLAPI(
TestModule: Module,
loader: (context: GraphQLContext) => DataLoader<any, any>,
generateOpaqueTypes?: boolean,
) {
const application = await createGraphQLApp({
modules: [CoreSync(), RelationSync(), TestModule],
modules: [Relation(), TestModule],
generateOpaqueTypes,
});

Expand Down
25 changes: 9 additions & 16 deletions plugins/graphql-backend-module-catalog/src/catalog/catalog.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createModule, TypeDefs } from 'graphql-modules';
import { createModule } from 'graphql-modules';
import GraphQLJSON, { GraphQLJSONObject } from 'graphql-type-json';
import { relationDirectiveMapper } from '../relationDirectiveMapper';
import {
createDirectiveMapperProvider,
GraphQLModule,
encodeId,
} from '@frontside/hydraphql';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { loadFiles, loadFilesSync } from '@graphql-tools/load-files';
import { loadFilesSync } from '@graphql-tools/load-files';
import { resolvePackagePath } from '@backstage/backend-common';
import { CATALOG_SOURCE } from '../constants';

Expand All @@ -16,12 +16,11 @@ const catalogSchemaPath = resolvePackagePath(
);

/** @public */
export const CatalogSync = (
typeDefs: TypeDefs = loadFilesSync(catalogSchemaPath),
) =>
createModule({
export const Catalog = (): GraphQLModule => ({
mappers: { relation: relationDirectiveMapper },
module: createModule({
id: 'catalog',
typeDefs,
typeDefs: loadFilesSync(catalogSchemaPath),
resolvers: {
JSON: GraphQLJSON,
JSONObject: GraphQLJSONObject,
Expand Down Expand Up @@ -55,11 +54,5 @@ export const CatalogSync = (
}),
},
},
providers: [
createDirectiveMapperProvider('relation', relationDirectiveMapper),
],
});

/** @public */
export const Catalog = async () =>
CatalogSync(await loadFiles(catalogSchemaPath));
})
});
25 changes: 9 additions & 16 deletions plugins/graphql-backend-module-catalog/src/relation/relation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TypeDefs, createModule } from 'graphql-modules';
import { createModule } from 'graphql-modules';
import { relationDirectiveMapper } from '../relationDirectiveMapper';
import { createDirectiveMapperProvider } from '@frontside/hydraphql';
import { loadFiles, loadFilesSync } from '@graphql-tools/load-files';
import { GraphQLModule } from '@frontside/hydraphql';
import { loadFilesSync } from '@graphql-tools/load-files';
import { resolvePackagePath } from '@backstage/backend-common';

const relationSchemaPath = resolvePackagePath(
Expand All @@ -10,17 +10,10 @@ const relationSchemaPath = resolvePackagePath(
);

/** @public */
export const RelationSync = (
typeDefs: TypeDefs = loadFilesSync(relationSchemaPath),
) =>
createModule({
export const Relation = (): GraphQLModule => ({
mappers: { relation: relationDirectiveMapper },
module: createModule({
id: 'relation',
typeDefs,
providers: [
createDirectiveMapperProvider('relation', relationDirectiveMapper),
],
});

/** @public */
export const Relation = async () =>
RelationSync(await loadFiles(relationSchemaPath));
typeDefs: loadFilesSync(relationSchemaPath),
})
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
CoreSync,
transformSchema,
encodeId,
decodeId,
Expand All @@ -8,13 +7,12 @@ import DataLoader from 'dataloader';
import { DocumentNode, GraphQLNamedType, printType } from 'graphql';
import { createModule, gql } from 'graphql-modules';
import { createGraphQLAPI } from './__testUtils__';
import { RelationSync } from './relation/relation';
import { Relation } from './relation/relation';

describe('mapRelationDirective', () => {
const transform = (source: DocumentNode) =>
transformSchema([
CoreSync(),
RelationSync(),
Relation(),
createModule({
id: 'mapRelationDirective',
typeDefs: source,
Expand Down
104 changes: 9 additions & 95 deletions plugins/graphql-backend-module-catalog/src/relationDirectiveMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,23 @@ import { connectionFromArray } from 'graphql-relay';
import { Entity, parseEntityRef } from '@backstage/catalog-model';
import {
GraphQLFieldConfig,
GraphQLID,
GraphQLInt,
GraphQLInterfaceType,
GraphQLList,
GraphQLNonNull,
GraphQLObjectType,
GraphQLString,
isInputType,
isInterfaceType,
isListType,
isNonNullType,
isObjectType,
isUnionType,
} from 'graphql';
import {
DirectiveMapperAPI,
ResolverContext,
unboxNamedType,
encodeId,
isConnectionType,
createConnectionType,
getNodeTypeForConnection
} from '@frontside/hydraphql';
import { CATALOG_SOURCE } from './constants';

function isConnectionType(type: unknown): type is GraphQLInterfaceType {
return (
(isInterfaceType(type) && type.name === 'Connection') ||
(isNonNullType(type) && isConnectionType(type.ofType))
);
}

function filterEntityRefs(
entity: Entity | undefined,
relationType?: string,
Expand All @@ -49,42 +37,6 @@ function filterEntityRefs(
);
}

function createConnectionType(
nodeType: GraphQLInterfaceType | GraphQLObjectType,
fieldType: GraphQLInterfaceType,
): GraphQLObjectType {
const wrappedEdgeType = fieldType.getFields().edges.type as GraphQLNonNull<
GraphQLList<GraphQLNonNull<GraphQLInterfaceType>>
>;
const edgeType = wrappedEdgeType.ofType.ofType.ofType as GraphQLInterfaceType;

return new GraphQLObjectType({
name: `${nodeType.name}Connection`,
fields: {
...fieldType.toConfig().fields,
edges: {
type: new GraphQLNonNull(
new GraphQLList(
new GraphQLNonNull(
new GraphQLObjectType({
name: `${nodeType.name}Edge`,
fields: {
...edgeType.toConfig().fields,
node: {
type: new GraphQLNonNull(nodeType),
},
},
interfaces: [edgeType],
}),
),
),
),
},
},
interfaces: [fieldType],
});
}

export function relationDirectiveMapper(
_fieldName: string,
field: GraphQLFieldConfig<{ id: string }, ResolverContext>,
Expand All @@ -108,51 +60,13 @@ export function relationDirectiveMapper(

if (isConnectionType(fieldType)) {
if (directive.nodeType) {
const nodeType = api.typeMap[directive.nodeType];

if (!nodeType) {
throw new Error(
`The interface "${directive.nodeType}" is not defined in the schema`,
);
}
if (isInputType(nodeType)) {
throw new Error(
`The interface "${directive.nodeType}" is an input type and can't be used in a Connection`,
);
}
if (isUnionType(nodeType)) {
const resolveType = nodeType.resolveType;
if (resolveType)
throw new Error(
`The "resolveType" function has already been implemented for "${nodeType.name}" union which may lead to undefined behavior`,
);
const iface = (api.typeMap[directive.nodeType] =
new GraphQLInterfaceType({
name: directive.nodeType,
interfaces: [api.typeMap.Node as GraphQLInterfaceType],
fields: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolveType: (...args) =>
(api.typeMap.Node as GraphQLInterfaceType).resolveType?.(...args),
}));
const types = nodeType.getTypes().map(type => type.name);
types.forEach(typeName => {
const type = api.typeMap[typeName];
if (isInterfaceType(type)) {
api.typeMap[typeName] = new GraphQLInterfaceType({
...type.toConfig(),
interfaces: [...type.getInterfaces(), iface],
});
}
if (isObjectType(type)) {
api.typeMap[typeName] = new GraphQLObjectType({
...type.toConfig(),
interfaces: [...type.getInterfaces(), iface],
});
}
});
const nodeType = getNodeTypeForConnection(
directive.nodeType,
(name) => api.typeMap[name],
(name, type) => (api.typeMap[name] = type),
);

field.type = createConnectionType(iface, fieldType);
} else {
if (nodeType) {
field.type = createConnectionType(nodeType, fieldType);
}
} else {
Expand Down
6 changes: 3 additions & 3 deletions plugins/graphql-backend-module-catalog/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CoreSync, transformSchema } from '@frontside/hydraphql';
import { transformSchema } from '@frontside/hydraphql';
import { printSchemaWithDirectives } from '@graphql-tools/utils';
import { CatalogSync } from './catalog';
import { Catalog } from './catalog';

export const schema = printSchemaWithDirectives(
transformSchema([CoreSync(), CatalogSync()]),
transformSchema([Catalog()]),
);
Loading
Loading