Skip to content

Commit

Permalink
Merge branch 'main' into improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
SaachiNayyer authored May 17, 2024
2 parents 46a5eee + 56c3a07 commit e5e7d16
Show file tree
Hide file tree
Showing 9 changed files with 712 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/shiny-pumpkins-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axis-backstage/plugin-jira-dashboard-backend': minor
---

The Backstage user entity profile email is now used as default for "Assigned to me" filters. Made the JIRA_EMAIL_SUFFIX optional, so it still can be used if Backstage email does not match the one in Jira.
4 changes: 2 additions & 2 deletions plugins/jira-dashboard-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The Jira Dashboard plugin requires the following YAML to be added to your app-co
jiraDashboard:
token: ${JIRA_TOKEN}
baseUrl: ${JIRA_BASE_URL}
userEmailSuffix: ${JIRA_EMAIL_SUFFIX}
userEmailSuffix: ${JIRA_EMAIL_SUFFIX} # Optional
annotationPrefix: ${JIRA_ANNOTATION_PREFIX} # Optional
```
Expand All @@ -34,7 +34,7 @@ jiraDashboard:
- `JIRA_TOKEN`: The "Authorization" header used for Jira authentication.
> Note: The JIRA_TOKEN variable from [Roadie's Backstage Jira plugin](https://roadie.io/backstage/plugins/jira) can not be reused here because of the added encoding in this token.
- `JIRA_BASE_URL`: The base url for Jira in your company, including the API version. For instance: https://jira.se.your-company.com/rest/api/2/
- `JIRA_EMAIL_SUFFIX`: The email suffix used for retrieving a specific Jira user in a company. For instance: @your-company.com
- `JIRA_EMAIL_SUFFIX`: Optional email suffix used for retrieving a specific Jira user in a company. For instance: @your-company.com. If not provided, the user entity profile email is used instead.
- `JIRA_ANNOTATION_PREFIX`: Optional annotation prefix for retrieving a custom annotation. Defaut value is jira.com. If you want to configure the plugin to be compatible with [Roadie's Backstage Jira Plugin](https://roadie.io/backstage/plugins/jira/), use the following annotation prefix:

```yaml
Expand Down
2 changes: 1 addition & 1 deletion plugins/jira-dashboard-backend/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface Config {
baseUrl: string;

/**
* Optional email suffix used for retreiving a specific Jira user in a company. For instance @your-company.com. Default value is ''
* Optional email suffix used for retrieving a specific Jira user in a company. For instance: @your-company.com. If not provided, the user entity profile email is used instead.
*/
userEmailSuffix?: string;

Expand Down
1 change: 1 addition & 0 deletions plugins/jira-dashboard-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/backend-test-utils": "^0.3.8",
"@backstage/cli": "^0.26.3",
"@types/supertest": "^2.0.12",
"msw": "^1.0.0",
Expand Down
4 changes: 2 additions & 2 deletions plugins/jira-dashboard-backend/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export function resolveJiraToken(config: Config): string {
}
}

export function resolveUserEmailSuffix(config: Config): string {
return config.getOptionalString(JIRA_USER_CONFIG_EMAIL_SUFFIX) || '';
export function resolveUserEmailSuffix(config: Config): string | undefined {
return config.getOptionalString(JIRA_USER_CONFIG_EMAIL_SUFFIX);
}

export function resolveAnnotationPrefix(config: Config): string {
Expand Down
60 changes: 60 additions & 0 deletions plugins/jira-dashboard-backend/src/filters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { getDefaultFiltersForUser } from './filters';
import { mockServices } from '@backstage/backend-test-utils';
import { UserEntity } from '@backstage/catalog-model';

describe('getDefaultFiltersForUser', () => {
const mockConfig = mockServices.rootConfig({
data: {
jiraDashboard: {
userEmailSuffix: '@backstage.com',
},
},
});

const mockUserEntity: UserEntity = {
apiVersion: 'backstage.io/v1beta1',
kind: 'User',
metadata: {
namespace: 'default',
name: 'fridaja',
description: 'Software Developer',
},

spec: {
profile: {
displayName: 'Frida Jacobsson',
email: '[email protected]',
},
},
};

it('returns userEmailSuffix email when config is provided', () => {
const filters = getDefaultFiltersForUser(mockConfig, mockUserEntity);
expect(filters).toHaveLength(3);
expect(filters).toContainEqual(
expect.objectContaining({
shortName: 'ME',
query: expect.stringContaining('[email protected]'),
}),
);
});

it('returns backstage email when userEmailSuffix config is not provided', () => {
const filters = getDefaultFiltersForUser(
mockServices.rootConfig(),
mockUserEntity,
);
expect(filters).toHaveLength(3);
expect(filters).toContainEqual(
expect.objectContaining({
shortName: 'ME',
query: expect.stringContaining('[email protected]'),
}),
);
});

it('do not return Assigned to me filter when userEntity is not provided', () => {
const filters = getDefaultFiltersForUser(mockConfig);
expect(filters).toHaveLength(2);
});
});
48 changes: 26 additions & 22 deletions plugins/jira-dashboard-backend/src/filters.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { Config } from '@backstage/config';
import { resolveUserEmailSuffix } from './config';
import { Filter } from '@axis-backstage/plugin-jira-dashboard-common';

const getUsernameFromRef = (userRef: string) => {
return userRef?.split('/').slice(1)[0];
};
import { UserEntity } from '@backstage/catalog-model';

const openFilter: Filter = {
name: 'Open Issues',
Expand All @@ -18,26 +15,33 @@ const incomingFilter: Filter = {
query: 'status = New ORDER BY created ASC',
};

export const getDefaultFilters = (
const getUserEmail = (
userEntity: UserEntity,
config: Config,
userRef?: string,
): Filter[] => {
if (!userRef) {
return [openFilter, incomingFilter];
}
const username = getUsernameFromRef(userRef);
): string | undefined => {
const emailSuffixConfig = resolveUserEmailSuffix(config);

if (!username) {
return [openFilter, incomingFilter];
}
return emailSuffixConfig
? `${userEntity.metadata.name}${emailSuffixConfig}`
: userEntity.spec?.profile?.email;
};

const assignedToMeFilter: Filter = {
name: 'Assigned to me',
shortName: 'ME',
query: `assignee = "${username}${resolveUserEmailSuffix(
config,
)}" AND resolution = Unresolved ORDER BY updated DESC`,
};
export const getDefaultFiltersForUser = (
config: Config,
userEntity?: UserEntity,
): Filter[] => {
if (!userEntity) return [openFilter, incomingFilter];

return [openFilter, incomingFilter, assignedToMeFilter];
return [
openFilter,
incomingFilter,
{
name: 'Assigned to me',
shortName: 'ME',
query: `assignee = "${getUserEmail(
userEntity,
config,
)}" AND resolution = Unresolved ORDER BY updated DESC`,
},
];
};
23 changes: 14 additions & 9 deletions plugins/jira-dashboard-backend/src/service/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import {
DiscoveryService,
HttpAuthService,
} from '@backstage/backend-plugin-api';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { UserEntity, stringifyEntityRef } from '@backstage/catalog-model';
import express from 'express';
import Router from 'express-promise-router';
import { Config } from '@backstage/config';
import { Logger } from 'winston';
import { CatalogClient } from '@backstage/catalog-client';
import { IdentityApi } from '@backstage/plugin-auth-node';

import { getDefaultFilters } from '../filters';
import { getDefaultFiltersForUser } from '../filters';
import {
type Filter,
type JiraResponse,
Expand Down Expand Up @@ -151,22 +151,27 @@ export async function createRouter(
return;
}

const credentials = await httpAuth.credentials(request, {
allow: ['user'],
});
let userEntity: UserEntity | undefined;

const userEntityRef = credentials.principal.userEntityRef;
try {
const credentials = await httpAuth.credentials(request, {
allow: ['user'],
});
const userIdentity = credentials.principal.userEntityRef;

if (!userEntityRef) {
logger.warn(`Could not find user identity`);
userEntity = (await catalogClient.getEntityByRef(userIdentity, {
token,
})) as UserEntity;
} catch (err) {
logger.warn('Could not find user identity');
}

let filters: Filter[] = [];

const customFilterAnnotations =
entity.metadata.annotations?.[filtersAnnotation]?.split(',')!;

filters = getDefaultFilters(config, userEntityRef);
filters = getDefaultFiltersForUser(config, userEntity);

if (customFilterAnnotations) {
filters.push(
Expand Down
Loading

0 comments on commit e5e7d16

Please sign in to comment.