Skip to content

Commit 15f1ee8

Browse files
authored
Support Multiple Jira Project Cards in Tabbed View (#280)
Signed-off-by: enaysaa saachi.nayyer@ericsson.com Signed-off-by: enaysaa saachi.nayyer@ericsson.com
1 parent c0fff1d commit 15f1ee8

9 files changed

Lines changed: 342 additions & 60 deletions

File tree

.changeset/two-wings-kneel.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@axis-backstage/plugin-jira-dashboard-backend': minor
3+
'@axis-backstage/plugin-jira-dashboard-common': minor
4+
'@axis-backstage/plugin-jira-dashboard': minor
5+
---
6+
7+
Support Multiple Jira Project Cards in Tabbed View

plugins/jira-dashboard-backend/src/service/router.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,17 @@ export async function createRouter(
133133
const projects = fullProjectKeys.map(fullProjectKey =>
134134
splitProjectKey(config, fullProjectKey),
135135
);
136-
137-
let projectResponse;
138-
139-
try {
140-
projectResponse = await getProjectResponse(projects[0], cache);
141-
} catch (err: any) {
142-
logger.error(
143-
`Could not find Jira project ${projects[0].fullProjectKey}: ${err.message}`,
144-
);
136+
const projectResponses: Project[] = [];
137+
138+
for (const project of projects) {
139+
try {
140+
const projectData = await getProjectResponse(project, cache);
141+
projectResponses.push(projectData);
142+
} catch (err: any) {
143+
logger.error(
144+
`Could not find Jira project ${project.fullProjectKey}: ${err.message}`,
145+
);
146+
}
145147
response.status(404).json({
146148
error: `No Jira project found with key ${projects[0].projectKey}`,
147149
});
@@ -215,9 +217,10 @@ export async function createRouter(
215217
}
216218

217219
const jiraResponse: JiraResponse = {
218-
project: projectResponse as Project,
220+
project: projectResponses,
219221
data: issues,
220222
};
223+
221224
response.json(jiraResponse);
222225
},
223226
);

plugins/jira-dashboard-common/report.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export type JiraQueryResults = {
7272

7373
// @public
7474
export type JiraResponse = {
75-
project: Project;
75+
project: Project | Project[];
7676
data: JiraDataResponse[];
7777
};
7878

plugins/jira-dashboard-common/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export type Project = {
7575
* @public
7676
*/
7777
export type JiraResponse = {
78-
project: Project;
78+
project: Project | Project[];
7979
data: JiraDataResponse[];
8080
};
8181

plugins/jira-dashboard/dev/__fixtures__/jiraResponse.json

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
{
2-
"project": {
3-
"name": "Backstage",
4-
"key": "BS",
5-
"description": "This is our Backstage project",
6-
"avatarUrls": {
7-
"48x48": "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
8-
},
9-
"projectTypeKey": "Software",
10-
"projectCategory": {
11-
"name": "Software Portals"
12-
},
13-
"lead": {
14-
"key": "fridaja",
15-
"displayName": "Frida Jacobsson"
2+
"project": [
3+
{
4+
"name": "Backstage",
5+
"key": "BS",
6+
"description": "This is our Backstage project",
7+
"avatarUrls": {
8+
"48x48": "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
9+
},
10+
"projectTypeKey": "Software",
11+
"projectCategory": {
12+
"name": "Software Portals"
13+
},
14+
"lead": {
15+
"key": "fridaja",
16+
"displayName": "Frida Jacobsson"
17+
},
18+
"self": "https://jira.com/project/123"
1619
},
17-
"self": "https://jira.com/project/123"
18-
},
20+
{
21+
"name": "Backstage1",
22+
"key": "BM",
23+
"description": "This is our Backstage project",
24+
"avatarUrls": {
25+
"48x48": "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
26+
},
27+
"projectTypeKey": "Software test",
28+
"projectCategory": {
29+
"name": "Software Portals test"
30+
},
31+
"lead": {
32+
"key": "fridaja",
33+
"displayName": "Frida Jacobsson"
34+
},
35+
"self": "https://jira.com/project/456"
36+
}
37+
],
1938
"data": [
2039
{
2140
"name": "Open Issues",
@@ -45,7 +64,7 @@
4564
},
4665
{
4766
"key": "BS-2",
48-
"self": "https://jira.com/project/123",
67+
"self": "https://jira.com/project/456",
4968
"fields": {
5069
"summary": "Something is not working",
5170
"status": {
@@ -114,7 +133,7 @@
114133
}
115134
},
116135
{
117-
"key": "BS-2",
136+
"key": "BM-2",
118137
"self": "https://jira.com/project/123",
119138
"fields": {
120139
"summary": "Something is not working",
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
{
2+
"project": {
3+
"name": "Backstage",
4+
"key": "BS",
5+
"description": "This is our Backstage project",
6+
"avatarUrls": {
7+
"48x48": "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
8+
},
9+
"projectTypeKey": "Software",
10+
"projectCategory": {
11+
"name": "Software Portals"
12+
},
13+
"lead": {
14+
"key": "fridaja",
15+
"displayName": "Frida Jacobsson"
16+
},
17+
"self": "https://jira.com/project/123"
18+
},
19+
"data": [
20+
{
21+
"name": "Open Issues",
22+
"type": "filter",
23+
"query": "resolution = Unresolved ORDER BY updated DESC",
24+
"issues": [
25+
{
26+
"key": "BS-1",
27+
"self": "https://jira.com/project/123",
28+
"fields": {
29+
"summary": "This would be really good to add",
30+
"status": {
31+
"name": "In Review"
32+
},
33+
"assignee": {
34+
"name": "fridaja@backstage",
35+
"self": "https://jira.com/user/fridaja"
36+
},
37+
"issuetype": {
38+
"name": "Task",
39+
"iconUrl": ""
40+
},
41+
"priority": {
42+
"name": "Minor"
43+
}
44+
}
45+
},
46+
{
47+
"key": "BS-2",
48+
"self": "https://jira.com/project/123",
49+
"fields": {
50+
"summary": "Something is not working",
51+
"status": {
52+
"name": "In Progress"
53+
},
54+
"assignee": {
55+
"name": "fridaja@backstage",
56+
"self": "https://jira.com/user/fridaja"
57+
},
58+
"issuetype": {
59+
"name": "Bug",
60+
"iconUrl": ""
61+
},
62+
"priority": {
63+
"name": "Blocker"
64+
}
65+
}
66+
},
67+
{
68+
"key": "BS-3",
69+
"self": "https://jira.com/project/123",
70+
"fields": {
71+
"summary": "As an user I want to be able to add new data",
72+
"status": {
73+
"name": "In Progress"
74+
},
75+
"assignee": {
76+
"name": "fridaja@backstage",
77+
"self": "https://jira.com/user/fridaja"
78+
},
79+
"issuetype": {
80+
"name": "Story",
81+
"iconUrl": ""
82+
},
83+
"priority": {
84+
"name": "Major"
85+
}
86+
}
87+
}
88+
]
89+
},
90+
{
91+
"name": "New Issues",
92+
"type": "filter",
93+
"query": "resolution = Unresolved ORDER BY updated DESC",
94+
"issues": [
95+
{
96+
"key": "BS-1",
97+
"self": "https://jira.com/project/123",
98+
"fields": {
99+
"summary": "This would be really good to add",
100+
"status": {
101+
"name": "New"
102+
},
103+
"assignee": {
104+
"name": "fridaja@backstage",
105+
"self": "https://jira.com/user/fridaja"
106+
},
107+
"issuetype": {
108+
"name": "Task",
109+
"iconUrl": ""
110+
},
111+
"priority": {
112+
"name": "Minor"
113+
}
114+
}
115+
},
116+
{
117+
"key": "BS-2",
118+
"self": "https://jira.com/project/123",
119+
"fields": {
120+
"summary": "Something is not working",
121+
"status": {
122+
"name": "Selected for development"
123+
},
124+
"assignee": {
125+
"name": "fridaja@backstage",
126+
"self": "https://jira.com/user/fridaja"
127+
},
128+
"issuetype": {
129+
"name": "Bug",
130+
"iconUrl": ""
131+
},
132+
"priority": {
133+
"name": "Blocker"
134+
}
135+
}
136+
},
137+
{
138+
"key": "BS-3",
139+
"self": "https://jira.com/project/123",
140+
"fields": {
141+
"summary": "As an user I want to be able to add new data",
142+
"status": {
143+
"name": "New"
144+
},
145+
"assignee": {
146+
"name": "fridaja@backstage",
147+
"self": "https://jira.com/user/fridaja"
148+
},
149+
"issuetype": {
150+
"name": "Story",
151+
"iconUrl": ""
152+
},
153+
"priority": {
154+
"name": "Major"
155+
}
156+
}
157+
}
158+
]
159+
}
160+
]
161+
}

plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.test.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { JiraDashboardClient, jiraDashboardApiRef } from '../../api';
1313
import { ApiProvider, UrlPatternDiscovery } from '@backstage/core-app-api';
1414
import mockedJiraResponse from '../../../dev/__fixtures__/jiraResponse.json';
1515
import mockedEntity from '../../../dev/__fixtures__/entity.json';
16+
import singleProjectResponse from '../../../dev/__fixtures__/singleProjectResponse.json';
1617

1718
describe('JiraDashboardContent', () => {
1819
const server = setupServer();
@@ -84,3 +85,54 @@ describe('JiraDashboardContent', () => {
8485
expect(getAllByTestId('issue-table')).toHaveLength(2);
8586
});
8687
});
88+
describe('JiraDashboardContent - Single Project View', () => {
89+
const server = setupServer();
90+
registerMswTestHooks(server);
91+
92+
const mockBaseUrl = 'http://localhost:7007/api/jira-dashboard';
93+
let jiraClient: JiraDashboardClient;
94+
let apis: TestApiRegistry;
95+
const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl);
96+
const fetchApi = new MockFetchApi();
97+
98+
const setupHandlers = () => {
99+
server.use(
100+
rest.get(
101+
`${mockBaseUrl}/dashboards/by-entity-ref/:kind/:namespace/:name`,
102+
(_req, res, ctx) => {
103+
return res(ctx.json(singleProjectResponse));
104+
},
105+
),
106+
);
107+
};
108+
109+
beforeEach(() => {
110+
jiraClient = new JiraDashboardClient({ discoveryApi, fetchApi });
111+
apis = TestApiRegistry.from([jiraDashboardApiRef, jiraClient]);
112+
setupHandlers();
113+
});
114+
115+
it('renders project card for single project', async () => {
116+
const { getByTestId } = await renderInTestApp(
117+
<EntityProvider entity={mockedEntity}>
118+
<ApiProvider apis={apis}>
119+
<JiraDashboardContent />
120+
</ApiProvider>
121+
</EntityProvider>,
122+
);
123+
124+
expect(getByTestId('project-card')).toBeInTheDocument();
125+
});
126+
127+
it('renders issue tables for single project', async () => {
128+
const { getAllByTestId } = await renderInTestApp(
129+
<EntityProvider entity={mockedEntity}>
130+
<ApiProvider apis={apis}>
131+
<JiraDashboardContent />
132+
</ApiProvider>
133+
</EntityProvider>,
134+
);
135+
136+
expect(getAllByTestId('issue-table')).toHaveLength(2); // Matches 2 filters in the JSON
137+
});
138+
});

0 commit comments

Comments
 (0)