Skip to content

Commit

Permalink
feat(npm): add backend to fetch data from private registries
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
  • Loading branch information
christoph-jerolimov committed Nov 3, 2024
1 parent 3a5081c commit 272b83e
Show file tree
Hide file tree
Showing 36 changed files with 1,357 additions and 66 deletions.
1 change: 1 addition & 0 deletions workspaces/npm/packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
},
"dependencies": {
"@backstage-community/plugin-npm-backend": "workspace:^",
"@backstage/backend-defaults": "^0.5.2",
"@backstage/config": "^1.2.0",
"@backstage/plugin-app-backend": "^0.3.76",
Expand Down
1 change: 1 addition & 0 deletions workspaces/npm/packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ backend.add(import('@backstage/plugin-search-backend-module-pg/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));

backend.add(import('@backstage-community/plugin-npm-backend'));
backend.start();
1 change: 1 addition & 0 deletions workspaces/npm/plugins/npm-backend/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
28 changes: 28 additions & 0 deletions workspaces/npm/plugins/npm-backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# npm

This plugin backend was templated using the Backstage CLI. You should replace this text with a description of your plugin backend.

## Installation

This plugin is installed via the `@backstage-community/plugin-npm-backend` package. To install it to your backend package, run the following command:

```bash
# From your root directory
yarn --cwd packages/backend add @backstage-community/plugin-npm-backend
```

Then add the plugin to your backend in `packages/backend/src/index.ts`:

```ts
const backend = createBackend();
// ...
backend.add(import('@backstage-community/plugin-npm-backend'));
```

## Development

This plugin backend can be started in a standalone mode from directly in this
package with `yarn start`. It is a limited setup that is most convenient when
developing the plugin backend itself.

If you want to run the entire project, including the frontend, run `yarn dev` from the root directory.
10 changes: 10 additions & 0 deletions workspaces/npm/plugins/npm-backend/catalog-info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-plugin-npm
title: '@backstage-community/plugin-npm-backend'
description: A Backstage plugin that shows meta info and latest versions from a npm registry
spec:
lifecycle: experimental
type: backstage-backend-plugin
owner: maintainers
75 changes: 75 additions & 0 deletions workspaces/npm/plugins/npm-backend/dev/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2024 The Backstage Authors
*
* 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 { createBackend } from '@backstage/backend-defaults';
import { mockServices } from '@backstage/backend-test-utils';
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';

// TEMPLATE NOTE:
// This is the development setup for your plugin that wires up a
// minimal backend that can use both real and mocked plugins and services.
//
// Start up the backend by running `yarn start` in the package directory.
// Once it's up and running, try out the following requests:
//
// Create a new todo item, standalone or for the sample component:
//
// curl http://localhost:7007/api/npm/todos -H 'Content-Type: application/json' -d '{"title": "My Todo"}'
// curl http://localhost:7007/api/npm/todos -H 'Content-Type: application/json' -d '{"title": "My Todo", "entityRef": "component:default/sample"}'
//
// List TODOs:
//
// curl http://localhost:7007/api/npm/todos
//
// Explicitly make an unauthenticated request, or with service auth:
//
// curl http://localhost:7007/api/npm/todos -H 'Authorization: Bearer mock-none-token'
// curl http://localhost:7007/api/npm/todos -H 'Authorization: Bearer mock-service-token'

const backend = createBackend();

// TEMPLATE NOTE:
// Mocking the auth and httpAuth service allows you to call your plugin API without
// having to authenticate.
//
// If you want to use real auth, you can install the following instead:
// backend.add(import('@backstage/plugin-auth-backend'));
// backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
backend.add(mockServices.auth.factory());
backend.add(mockServices.httpAuth.factory());

// TEMPLATE NOTE:
// Rather than using a real catalog you can use a mock with a fixed set of entities.
backend.add(
catalogServiceMock.factory({
entities: [
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'sample',
title: 'Sample Component',
},
spec: {
type: 'service',
},
},
],
}),
);

backend.add(import('../src'));

backend.start();
68 changes: 68 additions & 0 deletions workspaces/npm/plugins/npm-backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "@backstage-community/plugin-npm-backend",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "backend-plugin",
"supported-versions": "1.28.0",
"pluginId": "npm",
"pluginPackage": "@backstage-community/plugin-npm-backend",
"pluginPackages": [
"@backstage-community/plugin-npm",
"@backstage-community/plugin-npm-backend"
]
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage-community/plugin-npm-common": "workspace:^",
"@backstage/backend-defaults": "^0.5.1",
"@backstage/backend-plugin-api": "^1.0.1",
"@backstage/catalog-client": "^1.7.1",
"@backstage/errors": "^1.2.4",
"@backstage/plugin-catalog-node": "^1.13.1",
"express": "^4.17.1",
"express-promise-router": "^4.1.0"
},
"devDependencies": {
"@backstage/backend-test-utils": "^1.0.1",
"@backstage/cli": "^0.28.0",
"@types/express": "*",
"@types/supertest": "^2.0.12",
"supertest": "^6.2.4"
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/backstage/community-plugins",
"directory": "workspaces/npm/plugins/npm-backend"
},
"keywords": [
"backstage",
"plugin",
"npm"
],
"homepage": "https://github.com/backstage/community-plugins/tree/main/workspaces/npm/plugins/npm-backend",
"bugs": "https://github.com/backstage/community-plugins/issues",
"maintainers": [
"jerolimov",
"karthikjeeyar"
],
"author": "Christoph Jerolimov"
}
16 changes: 16 additions & 0 deletions workspaces/npm/plugins/npm-backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright 2024 The Backstage Authors
*
* 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.
*/
export { npmPlugin as default } from './plugin';
100 changes: 100 additions & 0 deletions workspaces/npm/plugins/npm-backend/src/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2024 The Backstage Authors
*
* 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 {
mockCredentials,
startTestBackend,
} from '@backstage/backend-test-utils';
import { npmPlugin } from './plugin';
import request from 'supertest';
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';

// TEMPLATE NOTE:
// Plugin tests are integration tests for your plugin, ensuring that all pieces
// work together end-to-end. You can still mock injected backend services
// however, just like anyone who installs your plugin might replace the
// services with their own implementations.
describe('plugin', () => {
it('should create and read TODO items', async () => {
const { server } = await startTestBackend({
features: [npmPlugin],
});

await request(server).get('/api/npm/todos').expect(200, {
items: [],
});

const createRes = await request(server)
.post('/api/npm/todos')
.send({ title: 'My Todo' });

expect(createRes.status).toBe(201);
expect(createRes.body).toEqual({
id: expect.any(String),
title: 'My Todo',
createdBy: mockCredentials.user().principal.userEntityRef,
createdAt: expect.any(String),
});

const createdTodoItem = createRes.body;

await request(server)
.get('/api/npm/todos')
.expect(200, {
items: [createdTodoItem],
});

await request(server)
.get(`/api/npm/todos/${createdTodoItem.id}`)
.expect(200, createdTodoItem);
});

it('should create TODO item with catalog information', async () => {
const { server } = await startTestBackend({
features: [
npmPlugin,
catalogServiceMock.factory({
entities: [
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'my-component',
namespace: 'default',
title: 'My Component',
},
spec: {
type: 'service',
owner: 'me',
},
},
],
}),
],
});

const createRes = await request(server)
.post('/api/npm/todos')
.send({ title: 'My Todo', entityRef: 'component:default/my-component' });

expect(createRes.status).toBe(201);
expect(createRes.body).toEqual({
id: expect.any(String),
title: '[My Component] My Todo',
createdBy: mockCredentials.user().principal.userEntityRef,
createdAt: expect.any(String),
});
});
});
56 changes: 56 additions & 0 deletions workspaces/npm/plugins/npm-backend/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2024 The Backstage Authors
*
* 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 {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { createRouter } from './router';
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
import { NpmRegistryServiceImpl } from './services/NpmRegistryServiceImpl';

/**
* npmPlugin backend plugin
*
* @public
*/
export const npmPlugin = createBackendPlugin({
pluginId: 'npm',
register(env) {
env.registerInit({
deps: {
logger: coreServices.logger,
auth: coreServices.auth,
httpAuth: coreServices.httpAuth,
httpRouter: coreServices.httpRouter,
catalog: catalogServiceRef,
},
async init({ logger, auth, httpAuth, httpRouter, catalog }) {
const npmRegistryService = new NpmRegistryServiceImpl({
logger,
auth,
catalog,
});

httpRouter.use(
await createRouter({
httpAuth,
npmRegistryService,
}),
);
},
});
},
});
Loading

0 comments on commit 272b83e

Please sign in to comment.