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

Feature: New Backend System Migration #192

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 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/orange-buses-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axis-backstage/plugin-jira-dashboard-backend': major
---

Migrated Jira Dashboard Backend to new Backstage backend system
ionSurf marked this conversation as resolved.
Show resolved Hide resolved
43 changes: 0 additions & 43 deletions plugins/jira-dashboard-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,49 +96,6 @@ jiraDashboard:

### Integrating

Here's how to get the backend plugin up and running:

1. Create a new file named `packages/backend/src/plugins/jiraDashboard.ts`, and add the following to it:

```ts
import { createRouter } from '@axis-backstage/plugin-jira-dashboard-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';

export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
discovery: env.discovery,
identity: env.identity,
tokenManager: env.tokenManager,
});
}
```

2. Wire this into the overall backend router by adding the following to `packages/backend/src/index.ts`:

```ts
import jiraDashboard from './plugins/jiraDashboard';
...

async function main() {
// Add this line under the other lines that follow the useHotMemoize pattern
const jiraDashboardEnv = useHotMemoize(module, () => createEnv('jira-dashboard'),

// Add this under the lines that add their routers to apiRouter
apiRouter.use('/jira-dashboard', await jiraDashboard(jiraDashboardEnv));
}
```

3. Now run `yarn start-backend` from the repo root.

4. In another terminal, run the command: `curl localhost:7007/api/jira-dashboard/health`. The request should return `{"status":"ok"}`.

### New Backend System

The Jira Dashboard backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/). Here is how you can set it up:

In your `packages/backend/src/index.ts` make the following changes:
Expand Down
12 changes: 4 additions & 8 deletions plugins/jira-dashboard-backend/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ import { BackendFeatureCompat } from '@backstage/backend-plugin-api';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import express from 'express';
import { HttpAuthService } from '@backstage/backend-plugin-api';
import { IdentityService } from '@backstage/backend-plugin-api';
import { Issue } from '@axis-backstage/plugin-jira-dashboard-common';
import { LoggerService } from '@backstage/backend-plugin-api';
import { RootConfigService } from '@backstage/backend-plugin-api';
import { TokenManagerService } from '@backstage/backend-plugin-api';
import { UserInfoService } from '@backstage/backend-plugin-api';

// @public @deprecated
// @public
export function createRouter(options: RouterOptions): Promise<express.Router>;

// @public
Expand All @@ -36,15 +34,13 @@ export type JqlQueryBuilderArgs = {
query?: string;
};

// @public @deprecated
// @public
export interface RouterOptions {
auth?: AuthService;
auth: AuthService;
config: RootConfigService;
discovery: DiscoveryService;
httpAuth?: HttpAuthService;
identity?: IdentityService;
httpAuth: HttpAuthService;
logger: LoggerService;
tokenManager?: TokenManagerService;
userInfo: UserInfoService;
}

Expand Down
4 changes: 4 additions & 0 deletions plugins/jira-dashboard-backend/dev/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
backend.add(import('../src'));

backend.start();
5 changes: 3 additions & 2 deletions plugins/jira-dashboard-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@
"@backstage/backend-plugin-api": "^0.8.0",
"@backstage/catalog-client": "^1.6.6",
"@backstage/catalog-model": "^1.6.0",
"@backstage/plugin-permission-common": "^0.8.1",
"@types/express": "*",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"node-fetch": "^2.6.7"
},
"devDependencies": {
"@backstage/backend-test-utils": "^0.5.0",
"@backstage/cli": "^0.27.0",
"@backstage/plugin-auth-backend": "^0.23.0",
"@backstage/plugin-auth-backend-module-guest-provider": "^0.2.0",
"@types/express": "*",
"@types/supertest": "^2.0.12",
"msw": "^1.0.0",
"supertest": "^6.2.4"
Expand Down
3 changes: 1 addition & 2 deletions plugins/jira-dashboard-backend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,10 @@ export const getIssuesByComponent = async (
};

export async function getProjectAvatar(url: string, config: RootConfigService) {
const response = await fetch(url, {
return await fetch(url, {
method: 'GET',
headers: {
Authorization: resolveJiraToken(config),
},
});
return response;
}
14 changes: 9 additions & 5 deletions plugins/jira-dashboard-backend/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,37 @@ export const jiraDashboardPlugin = createBackendPlugin({
register(env) {
env.registerInit({
deps: {
auth: coreServices.auth,
httpRouter: coreServices.httpRouter,
logger: coreServices.logger,
config: coreServices.rootConfig,
discovery: coreServices.discovery,
httpRouter: coreServices.httpRouter,
auth: coreServices.auth,
httpAuth: coreServices.httpAuth,
userInfo: coreServices.userInfo,
},
async init({
auth,
httpRouter,
logger,
config,
discovery,
httpRouter,
auth,
httpAuth,
userInfo,
}) {
httpRouter.use(
await createRouter({
auth,
logger,
config,
discovery,
auth,
httpAuth,
userInfo,
}),
);
httpRouter.addAuthPolicy({
path: '/health',
allow: 'unauthenticated',
});
},
});
},
Expand Down
3 changes: 3 additions & 0 deletions plugins/jira-dashboard-backend/src/service/defaultValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const DEFAULT_TTL = 1000 * 60;

export const DEFAULT_MAX_RESULTS_USER_ISSUES = 10;
14 changes: 6 additions & 8 deletions plugins/jira-dashboard-backend/src/service/router.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { mockServices } from '@backstage/backend-test-utils';
import express from 'express';
import request from 'supertest';

import { createRouter } from './router';
import { mockServices } from '@backstage/backend-test-utils';

describe('createRouter', () => {
let app: express.Express;
const tokenManager = mockServices.tokenManager.mock();
const testDiscovery = mockServices.discovery.mock();
const identity = mockServices.identity.mock();

beforeAll(async () => {
const router = await createRouter({
auth: mockServices.auth.mock(),
logger: mockServices.logger.mock(),
config: mockServices.rootConfig(),
discovery: testDiscovery,
identity,
tokenManager,
userInfo: mockServices.userInfo({ userEntityRef: 'user:default/guest' }),
discovery: mockServices.discovery.mock(),
httpAuth: mockServices.httpAuth.mock(),
userInfo: mockServices.userInfo.mock(),
});
app = express().use(router);
});
Expand Down
71 changes: 25 additions & 46 deletions plugins/jira-dashboard-backend/src/service/router.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,74 @@
import { createLegacyAuthAdapters } from '@backstage/backend-common';
import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';
import { CacheManager } from '@backstage/backend-defaults/cache';
import {
AuthService,
DiscoveryService,
HttpAuthService,
LoggerService,
IdentityService,
RootConfigService,
TokenManagerService,
UserInfoService,
} from '@backstage/backend-plugin-api';
import { stringifyEntityRef, UserEntity } from '@backstage/catalog-model';
import express from 'express';
import Router from 'express-promise-router';
import { stringifyEntityRef, UserEntity } from '@backstage/catalog-model';
import { CatalogClient } from '@backstage/catalog-client';

import { getAssigneUser, getDefaultFiltersForUser } from '../filters';
import {
type Filter,
type JiraResponse,
type Project,
} from '@axis-backstage/plugin-jira-dashboard-common';
import stream from 'stream';
import { getProjectAvatar } from '../api';
import { getAnnotations } from '../lib';
import {
getFiltersFromAnnotations,
getIssuesFromComponents,
getIssuesFromFilters,
getProjectResponse,
getUserIssues,
} from './service';
import { getAnnotations } from '../lib';
import { DEFAULT_MAX_RESULTS_USER_ISSUES, DEFAULT_TTL } from './defaultValues';
import { getAssigneUser, getDefaultFiltersForUser } from '../filters';
import {
type Filter,
type JiraResponse,
type Project,
} from '@axis-backstage/plugin-jira-dashboard-common';
import { getProjectAvatar } from '../api';
import stream from 'stream';

/**
* Constructs a jira dashboard router.
* @deprecated Please migrate to the new backend system as this will be removed in the future.
* @public
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "@Deprecation" notice here is a warning that the "RouterOptions" and "createRouter" should not be included in the public api of the plugin, as described here.

So we either need to remove the exports or keep the "deprecations"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say let's go ahead and remove the exports. You already had the deprecated tags in the latest version. According to the documentation, the next step is to remove the exports.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I would be surprised if anyone was using the "old backend" still.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd even go as far as to stop functions that were previously used to create a legacy-wrapper to be exported by the plugin, as suggested by the docs . I'm commiting these changes too, let me know what you think.

Copy link
Author

@ionSurf ionSurf Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed too that as a best practice, all types are usually exported from the common plugin. How about I move those to the types file in the common plugin and export all types from there? That way, the backend plugin will be def cleaner and up to code!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not move any types right now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anicke , all types have been moved back into the jira-dashboard-backend plugin.

*/
export interface RouterOptions {
/**
* Implementation of Winston logger
* Implementation of Authentication Service
*/
auth: AuthService;
/**
* Implementation of Logger Service
*/
logger: LoggerService;

/**
* Backstage config object
* Implementation of Config Service
*/
config: RootConfigService;

/**
* Backstage discovery api instance
* Implementation of Discovery Service
*/
discovery: DiscoveryService;

/**
* Backstage identity api instance
*/
identity?: IdentityService;

/**
* Backstage token manager instance
* Implementation of Http Authentication Service
*/
tokenManager?: TokenManagerService;
httpAuth: HttpAuthService;
/**
* Backstage auth service
*/
auth?: AuthService;
/**
* Backstage httpAuth service
*/
httpAuth?: HttpAuthService;

/**
* Backstage userInfo service
* Implementation of User Info Service
*/
userInfo: UserInfoService;
}

const DEFAULT_TTL = 1000 * 60;

const DEFAULT_MAX_RESULTS_USER_ISSUES = 10;

/**
* Constructs a jira dashboard router.
*
* @deprecated Please migrate to the new backend system as this will be removed in the future.
* @public
*/
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { auth, httpAuth } = createLegacyAuthAdapters(options);
const { logger, config, discovery, userInfo } = options;
const { auth, logger, config, discovery, httpAuth, userInfo } = options;
const catalogClient = new CatalogClient({ discoveryApi: discovery });

const pluginCache =
Expand All @@ -104,6 +80,7 @@ export async function createRouter(
router.use(express.json());

router.get('/health', (_, response) => {
logger.info('PONG!');
response.json({ status: 'ok' });
});

Expand Down Expand Up @@ -326,7 +303,9 @@ export async function createRouter(
ps.pipe(response);
},
);

const middleware = MiddlewareFactory.create({ logger, config });

router.use(middleware.error());
return router;
}
8 changes: 6 additions & 2 deletions plugins/jira-dashboard-backend/src/service/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export const getProjectResponse = async (

projectResponse = (await cache.get(projectKey)) as Project;

if (projectResponse) return projectResponse as Project;
if (projectResponse) {
return projectResponse as Project;
}

try {
projectResponse = await getProjectInfo(projectKey, config);
Expand All @@ -49,7 +51,9 @@ export const getJqlResponse = async (

issuesResponse = (await cache.get(jql)) as Issue[];

if (issuesResponse) return issuesResponse as Issue[];
if (issuesResponse) {
return issuesResponse as Issue[];
}

try {
issuesResponse = await searchJira(config, jql, searchOptions);
Expand Down
Loading
Loading