diff --git a/plugins/bogus-graphql/.eslintrc.js b/plugins/bogus-graphql/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/bogus-graphql/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/bogus-graphql/README.md b/plugins/bogus-graphql/README.md new file mode 100644 index 0000000000..3f85ec766e --- /dev/null +++ b/plugins/bogus-graphql/README.md @@ -0,0 +1,13 @@ +# graphql + +Welcome to the graphql backend plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/graphql](http://localhost:3000/graphql). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. diff --git a/plugins/bogus-graphql/package.json b/plugins/bogus-graphql/package.json new file mode 100644 index 0000000000..a5a439a701 --- /dev/null +++ b/plugins/bogus-graphql/package.json @@ -0,0 +1,44 @@ +{ + "name": "@backstage/plugin-bogus-graphql", + "version": "0.1.1-alpha.16", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean", + "mock-data": "./scripts/mock-data.sh" + }, + "dependencies": { + "@backstage/backend-common": "^0.1.1-alpha.16", + "@graphql-modules/core": "^0.7.17", + "@types/express": "^4.17.6", + "@types/whatwg-fetch": "^0.0.33", + "graphql": "^15.3.0", + "node-fetch": "^2.6.0", + "whatwg-fetch": "^2.0.0", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.16", + "@types/supertest": "^2.0.8", + "eslint-plugin-graphql": "^4.0.0", + "msw": "^0.19.5", + "supertest": "^4.0.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/bogus-graphql/scripts/mock-data.sh b/plugins/bogus-graphql/scripts/mock-data.sh new file mode 100755 index 0000000000..ff30921715 --- /dev/null +++ b/plugins/bogus-graphql/scripts/mock-data.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +echo "use this script to load your service with some mock data if needed!" diff --git a/plugins/bogus-graphql/src/graphql/module.ts b/plugins/bogus-graphql/src/graphql/module.ts new file mode 100644 index 0000000000..f247686ea8 --- /dev/null +++ b/plugins/bogus-graphql/src/graphql/module.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger } from 'winston'; +import fs from 'fs'; +import path from 'path'; +import { GraphQLModule } from '@graphql-modules/core'; + +export interface ModuleOptions { + logger: Logger; +} + +export async function createModule( + options: ModuleOptions, +): Promise { + const typeDefs = await fs.promises.readFile( + path.resolve(__dirname, '..', 'schema.gql'), + 'utf-8', + ); + + const resolvers = { + EntityMetadata: { + uppercaseName: metadata => metadata.name.toUpperCase(), + }, + }; + + const module = new GraphQLModule({ + typeDefs, + resolvers, + logger: options.logger as any, + }); + + return module; +} diff --git a/plugins/bogus-graphql/src/index.ts b/plugins/bogus-graphql/src/index.ts new file mode 100644 index 0000000000..dd6508f88a --- /dev/null +++ b/plugins/bogus-graphql/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require('whatwg-fetch'); + +export * from './graphql/module'; diff --git a/plugins/bogus-graphql/src/schema.gql b/plugins/bogus-graphql/src/schema.gql new file mode 100644 index 0000000000..74c0853a92 --- /dev/null +++ b/plugins/bogus-graphql/src/schema.gql @@ -0,0 +1,3 @@ +type EntityMetadata { + uppercaseName: String +} diff --git a/plugins/bogus-graphql/src/setupTests.ts b/plugins/bogus-graphql/src/setupTests.ts new file mode 100644 index 0000000000..ac41ef995a --- /dev/null +++ b/plugins/bogus-graphql/src/setupTests.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +require('whatwg-fetch'); + +export {}; diff --git a/plugins/catalog-graphql/.eslintrc.js b/plugins/catalog-graphql/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/catalog-graphql/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/catalog-graphql/README.md b/plugins/catalog-graphql/README.md new file mode 100644 index 0000000000..3f85ec766e --- /dev/null +++ b/plugins/catalog-graphql/README.md @@ -0,0 +1,13 @@ +# graphql + +Welcome to the graphql backend plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/graphql](http://localhost:3000/graphql). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json new file mode 100644 index 0000000000..57fade9654 --- /dev/null +++ b/plugins/catalog-graphql/package.json @@ -0,0 +1,44 @@ +{ + "name": "@backstage/plugin-catalog-graphql", + "version": "0.1.1-alpha.16", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean", + "mock-data": "./scripts/mock-data.sh" + }, + "dependencies": { + "@backstage/backend-common": "^0.1.1-alpha.16", + "@graphql-modules/core": "^0.7.17", + "@types/express": "^4.17.6", + "@types/whatwg-fetch": "^0.0.33", + "graphql": "^15.3.0", + "node-fetch": "^2.6.0", + "whatwg-fetch": "^2.0.0", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.16", + "@types/supertest": "^2.0.8", + "eslint-plugin-graphql": "^4.0.0", + "msw": "^0.19.5", + "supertest": "^4.0.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/catalog-graphql/scripts/mock-data.sh b/plugins/catalog-graphql/scripts/mock-data.sh new file mode 100755 index 0000000000..ff30921715 --- /dev/null +++ b/plugins/catalog-graphql/scripts/mock-data.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +echo "use this script to load your service with some mock data if needed!" diff --git a/plugins/catalog-graphql/src/graphql/module.ts b/plugins/catalog-graphql/src/graphql/module.ts new file mode 100644 index 0000000000..65239d27db --- /dev/null +++ b/plugins/catalog-graphql/src/graphql/module.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger } from 'winston'; +import fetch from 'node-fetch'; +import fs from 'fs'; +import path from 'path'; +import { GraphQLModule } from '@graphql-modules/core'; + +export interface ModuleOptions { + logger: Logger; +} + +// function wrapLogger(logger: Logger) { +// return { +// log: (msg: string | Error, ...extra: any[]) => +// logger.info(String(msg), ...extra), +// debug: (msg: string | Error, ...extra: any[]) => +// logger.info(String(msg), ...extra), +// info: (msg: string | Error, ...extra: any[]) => +// logger.info(String(msg), ...extra), +// error: (msg: string | Error, ...extra: any[]) => +// logger.info(String(msg), ...extra), +// clientError: (msg: string | Error, ...extra: any[]) => +// logger.info(String(msg), ...extra), +// warn: (msg: string | Error, ...extra: any[]) => +// logger.info(String(msg), ...extra), +// }; +// } + +class CatalogClient { + async list() { + const res = await fetch('http://localhost:7000/catalog/entities'); + if (!res.ok) { + throw new Error(`NOPE, ${await res.text()}`); + } + + return res.json(); + } +} + +export async function createModule( + options: ModuleOptions, +): Promise { + const typeDefs = await fs.promises.readFile( + path.resolve(__dirname, '..', 'schema.gql'), + 'utf-8', + ); + + const catalogClient = new CatalogClient(); + + const resolvers = { + Query: { + catalog: () => true, + }, + CatalogQuery: { + list: () => catalogClient.list(), + }, + CatalogEntity: () => ({}), + }; + + const module = new GraphQLModule({ + typeDefs, + resolvers, + logger: options.logger as any, + }); + + return module; +} diff --git a/plugins/catalog-graphql/src/index.ts b/plugins/catalog-graphql/src/index.ts new file mode 100644 index 0000000000..dd6508f88a --- /dev/null +++ b/plugins/catalog-graphql/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require('whatwg-fetch'); + +export * from './graphql/module'; diff --git a/plugins/catalog-graphql/src/schema.gql b/plugins/catalog-graphql/src/schema.gql new file mode 100644 index 0000000000..ea92d4e04f --- /dev/null +++ b/plugins/catalog-graphql/src/schema.gql @@ -0,0 +1,25 @@ +type EntityMetadataAnnotation { + key: String! + value: String! +} + +type EntityMetadata { + name: String! + namespace: String + annotation(name: String): EntityMetadataAnnotation + annotations: [EntityMetadataAnnotation!]! +} + +type CatalogEntity { + apiVersion: String + kind: String! + metadata: EntityMetadata +} + +type CatalogQuery { + list: [CatalogEntity!]! +} + +type Query { + catalog: CatalogQuery! +} diff --git a/plugins/catalog-graphql/src/setupTests.ts b/plugins/catalog-graphql/src/setupTests.ts new file mode 100644 index 0000000000..ac41ef995a --- /dev/null +++ b/plugins/catalog-graphql/src/setupTests.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +require('whatwg-fetch'); + +export {}; diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 02dc9d3608..b68dcf22be 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -21,6 +21,9 @@ }, "dependencies": { "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/plugin-catalog-graphql": "^0.1.1-alpha.18", + "@backstage/plugin-bogus-graphql": "^0.1.1-alpha.18", + "@graphql-modules/core": "^0.7.17", "@types/express": "^4.17.6", "apollo-server": "^2.16.0", "apollo-server-express": "^2.16.0", diff --git a/plugins/graphql/schema.gql b/plugins/graphql/schema.gql index 5568182f7f..e69de29bb2 100644 --- a/plugins/graphql/schema.gql +++ b/plugins/graphql/schema.gql @@ -1,11 +0,0 @@ -type CatalogEntity { - id: String -} - -type CatalogQuery { - list: [CatalogEntity!]! -} - -type Query { - catalog: CatalogQuery! -} diff --git a/plugins/graphql/src/service/router.ts b/plugins/graphql/src/service/router.ts index 69e2394f11..6845bb366e 100644 --- a/plugins/graphql/src/service/router.ts +++ b/plugins/graphql/src/service/router.ts @@ -20,7 +20,10 @@ import Router from 'express-promise-router'; import { Logger } from 'winston'; import fs from 'fs'; import path from 'path'; +import { GraphQLModule } from '@graphql-modules/core'; import { ApolloServer } from 'apollo-server-express'; +import { createModule as createCatalogModule } from '@backstage/plugin-catalog-graphql'; +import { createModule as createBogusModule } from '@backstage/plugin-bogus-graphql'; const schemaPath = path.resolve( require.resolve('@backstage/plugin-graphql-backend/package.json'), @@ -36,7 +39,14 @@ export async function createRouter( ): Promise { const typeDefs = await fs.promises.readFile(schemaPath, 'utf-8'); - const server = new ApolloServer({ typeDefs, logger: options.logger }); + const catalogModule = await createCatalogModule(options); + const bogusModule = await createBogusModule(options); + const { schema } = new GraphQLModule({ + imports: [catalogModule, bogusModule], + typeDefs, + }); + + const server = new ApolloServer({ schema, logger: options.logger }); const router = Router(); const apolloMiddlware = server.getMiddleware({ path: '/' });