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

Add encodeEntityId/decodeEntityId helpers #364

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

Add `encodeEntityId/decodeEntityId` helpers
51 changes: 47 additions & 4 deletions plugins/graphql-backend-module-catalog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ Some key features are currently missing. These features may change the schema in

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

## GraphQL modules

Expand All @@ -46,13 +46,16 @@ export default async function createPlugin(
logger: env.logger,
modules: [Catalog],
loaders: { ...createCatalogLoader(env.catalogClient) },
// You might want to pass catalog client to the context
// and use it in resolvers, but it's not required
context: ctx => ({ ...ctx, catalog: env.catalogClient }),
});
}
```

### Experimental Backend System
### Backend System

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

- To use `Catalog` GraphQL module
Expand Down Expand Up @@ -122,6 +125,46 @@ type System {
}
```

## Custom GraphQL Resolvers

If you need to implement complicated logic for some fields and can't be
achieved with available [directives][directives-api], you can write
your own resolvers. To do this, you need to define a resolver function
in your [GraphQL module](../graphql-backend/README.md#custom-graphql-module):

```ts
import { createModule } from "graphql-modules";
import type { CatalogClient, QueryEntitiesRequest } from '@backstage/catalog-client';
import { encodeEntityId } from '@frontside/backstage-plugin-graphql-backend-module-catalog';

export const myModule = createModule({
/* ... */
resolvers: {
Task: {
// This resolver utilize 3rd party api to get entity ref and then encodes it to NodeId
// Which will be resolved to an entity
entity: async (_, args, { taskAPI }) => {
const response = await taskAPI.getTask(args.taskId);
return { id: encodeEntityId(response.entityRef) };
},
},
Query: {
// Here you can use catalog client to query entities
entities: async (
_: any,
args: QueryEntitiesRequest,
// If you aren't using Backstage Backend System https://backstage.io/docs/backend-system/
// This requires you to pass catalog client to the context
{ catalog }: { catalog: CatalogClient }
): Promise<{ id: string }[]> => {
const { items: entities } = await catalog.queryEntities(args);
return entities.map(entity => ({ id: encodeEntityId(entity) }));
},
},
},
});
```

[graphql-backend]: ../graphql-backend/README.md
[graphql-modules]: https://the-guild.dev/graphql/modules
[relay]: https://relay.dev/docs/guides/graphql-server-specification
Expand Down
5 changes: 4 additions & 1 deletion plugins/graphql-backend-module-catalog/src/catalogModule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createBackendModule } from '@backstage/backend-plugin-api';
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
import {
graphqlContextExtensionPoint,
graphqlLoadersExtensionPoint,
graphqlModulesExtensionPoint,
} from '@frontside/backstage-plugin-graphql-backend';
Expand All @@ -17,10 +18,12 @@ export const graphqlModuleCatalog = createBackendModule({
catalog: catalogServiceRef,
modules: graphqlModulesExtensionPoint,
loaders: graphqlLoadersExtensionPoint,
context: graphqlContextExtensionPoint,
},
async init({ catalog, modules, loaders }) {
async init({ catalog, modules, loaders, context }) {
modules.addModules([Catalog]);
loaders.addLoaders(createCatalogLoader(catalog));
context.setContext(ctx => ({ ...ctx, catalog }));
},
});
},
Expand Down
18 changes: 18 additions & 0 deletions plugins/graphql-backend-module-catalog/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CompoundEntityRef, Entity, parseEntityRef, stringifyEntityRef } from "@backstage/catalog-model";
import { decodeId, encodeId } from "@frontside/hydraphql";
import { CATALOG_SOURCE } from "./constants";

export function encodeEntityId(entityOrRef: Entity | CompoundEntityRef | string): string {
const ref = typeof entityOrRef === 'string' ? entityOrRef : stringifyEntityRef(entityOrRef);
return encodeId({
source: CATALOG_SOURCE,
typename: 'Node',
query: { ref },
});
}

export function decodeEntityId(id: string): CompoundEntityRef {
const { query: { ref = '' } } = decodeId(id);

return parseEntityRef(ref);
}
1 change: 1 addition & 0 deletions plugins/graphql-backend-module-catalog/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './helpers';
export * from './catalog';
export * from './relation';
export * from './catalogModule';
Expand Down
5 changes: 4 additions & 1 deletion plugins/graphql-backend-module-catalog/src/relationModule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createBackendModule } from '@backstage/backend-plugin-api';
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
import {
graphqlContextExtensionPoint,
graphqlLoadersExtensionPoint,
graphqlModulesExtensionPoint,
} from '@frontside/backstage-plugin-graphql-backend';
Expand All @@ -17,10 +18,12 @@ export const graphqlModuleRelationResolver = createBackendModule({
catalog: catalogServiceRef,
modules: graphqlModulesExtensionPoint,
loaders: graphqlLoadersExtensionPoint,
context: graphqlContextExtensionPoint,
},
async init({ catalog, modules, loaders }) {
async init({ catalog, modules, loaders, context }) {
modules.addModules([Relation]);
loaders.addLoaders(createCatalogLoader(catalog));
context.setContext(ctx => ({ ...ctx, catalog }));
},
});
},
Expand Down
4 changes: 2 additions & 2 deletions plugins/graphql-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ At a minimum, you should install the [graphql-backend-module-catalog][] which ad
schema elements to access the [Backstage Catalog][backstage-catalog] via GraphQL

- [Backstage Plugins](./docs/backend-plugins.md#getting-started)
- [Experimental Backend System](#experimental-backend-system)
- [Backend System](#backend-system)
- [Getting started](#getting-started)
- [GraphQL Modules](#graphql-modules)
- [Custom GraphQL Module](#custom-graphql-module)
Expand All @@ -25,7 +25,7 @@ schema elements to access the [Backstage Catalog][backstage-catalog] via GraphQL
- [Backstage API Docs](#backstage-api-docs)
- [Questions](#questions)

## Experimental Backend System
## Backend System

This approach is suitable for the new [Backstage backend system](https://backstage.io/docs/backend-system/).
For the current [Backstage plugins system](https://backstage.io/docs/plugins/backend-plugin) see [Backstage Plugins](./docs/backend-plugins.md#getting-started)
Expand Down