Skip to content

Commit 5bfac5d

Browse files
committed
feat(tech-insights): add support for new frontend system
Added alpha exports for the new Backstage frontend system: - techInsightsPlugin: Main plugin for the new frontend system - techInsightsApi: API extension using ApiBlueprint - techInsightsScorecardPage: Page extension at /tech-insights - entityTechInsightsScorecardContent: Entity content tab - entityTechInsightsScorecardCard: Entity overview card Signed-off-by: Tommy Le <[email protected]>
1 parent 6c4aa12 commit 5bfac5d

File tree

12 files changed

+437
-5
lines changed

12 files changed

+437
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@backstage-community/plugin-tech-insights': minor
3+
---
4+
5+
The plugin now supports the new frontend system by importing from `@backstage-community/plugin-tech-insights/alpha`.

workspaces/tech-insights/plugins/tech-insights/README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,20 @@ Main areas covered by this plugin currently are:
1616
yarn --cwd packages/app add @backstage-community/plugin-tech-insights
1717
```
1818

19-
### Add boolean checks overview (Scorecards) page to the EntityPage
19+
### Integrating with the New Frontend System
20+
21+
If you are using Backstage's [new frontend system](https://backstage.io/docs/frontend-system/), the plugin will be auto-discovered and automatically register:
22+
23+
- The Tech Insights API
24+
- The Scorecards page at `/tech-insights`
25+
- Entity content for displaying scorecards on entity pages
26+
- Entity cards for displaying scorecards in entity overview
27+
28+
### Integrating with the Legacy Frontend System
29+
30+
The following sections describe how to integrate the plugin with the legacy frontend system.
31+
32+
#### Add boolean checks overview (Scorecards) page to the EntityPage
2033

2134
```tsx
2235
// packages/app/src/components/catalog/EntityPage.tsx
@@ -119,7 +132,7 @@ If you follow the [Backend Example](../tech-insights-backend#backend-example), o
119132

120133
![Boolean Scorecard Example](./docs/boolean-scorecard-example.png)
121134

122-
### Add overview (Scorecards) page
135+
#### Add overview (Scorecards) page
123136

124137
![Scorecard Overview](./docs/scorecard-overview.png)
125138

workspaces/tech-insights/plugins/tech-insights/package.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,22 @@
1515
]
1616
},
1717
"publishConfig": {
18-
"access": "public",
19-
"main": "dist/index.esm.js",
20-
"types": "dist/index.d.ts"
18+
"access": "public"
19+
},
20+
"exports": {
21+
".": "./src/index.ts",
22+
"./alpha": "./src/alpha.ts",
23+
"./package.json": "./package.json"
24+
},
25+
"typesVersions": {
26+
"*": {
27+
"alpha": [
28+
"src/alpha.ts"
29+
],
30+
"package.json": [
31+
"package.json"
32+
]
33+
}
2134
},
2235
"homepage": "https://backstage.io",
2336
"repository": {
@@ -45,9 +58,11 @@
4558
"@backstage-community/plugin-tech-insights-common": "workspace:^",
4659
"@backstage-community/plugin-tech-insights-react": "workspace:^",
4760
"@backstage/catalog-model": "^1.7.6",
61+
"@backstage/core-compat-api": "^0.5.5",
4862
"@backstage/core-components": "^0.18.4",
4963
"@backstage/core-plugin-api": "^1.12.1",
5064
"@backstage/errors": "^1.2.7",
65+
"@backstage/frontend-plugin-api": "^0.13.2",
5166
"@backstage/plugin-catalog-react": "^1.21.4",
5267
"@backstage/types": "^1.2.2",
5368
"@material-table/exporters": "^1.2.19",
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
## API Report File for "@backstage-community/plugin-tech-insights"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
/// <reference types="react" />
7+
8+
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
9+
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
10+
import { ApiFactory } from '@backstage/frontend-plugin-api';
11+
import { Entity } from '@backstage/catalog-model';
12+
import { EntityCardType } from '@backstage/plugin-catalog-react/alpha';
13+
import { EntityPredicate } from '@backstage/plugin-catalog-react/alpha';
14+
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
15+
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
16+
import { JSX as JSX_2 } from 'react';
17+
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
18+
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
19+
import { RouteRef } from '@backstage/frontend-plugin-api';
20+
21+
// @alpha
22+
const techInsightsPlugin: OverridableFrontendPlugin<
23+
{},
24+
{},
25+
{
26+
'api:tech-insights': OverridableExtensionDefinition<{
27+
kind: 'api';
28+
name: undefined;
29+
config: {};
30+
configInput: {};
31+
output: ExtensionDataRef<AnyApiFactory, 'core.api.factory', {}>;
32+
inputs: {};
33+
params: <
34+
TApi,
35+
TImpl extends TApi,
36+
TDeps extends {
37+
[x: string]: unknown;
38+
},
39+
>(
40+
params: ApiFactory<TApi, TImpl, TDeps>,
41+
) => ExtensionBlueprintParams<AnyApiFactory>;
42+
}>;
43+
'entity-card:tech-insights/scorecards': OverridableExtensionDefinition<{
44+
kind: 'entity-card';
45+
name: 'scorecards';
46+
config: {
47+
filter: EntityPredicate | undefined;
48+
type: 'content' | 'summary' | 'info' | undefined;
49+
};
50+
configInput: {
51+
filter?: EntityPredicate | undefined;
52+
type?: 'content' | 'summary' | 'info' | undefined;
53+
};
54+
output:
55+
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
56+
| ExtensionDataRef<
57+
(entity: Entity) => boolean,
58+
'catalog.entity-filter-function',
59+
{
60+
optional: true;
61+
}
62+
>
63+
| ExtensionDataRef<
64+
string,
65+
'catalog.entity-filter-expression',
66+
{
67+
optional: true;
68+
}
69+
>
70+
| ExtensionDataRef<
71+
EntityCardType,
72+
'catalog.entity-card-type',
73+
{
74+
optional: true;
75+
}
76+
>;
77+
inputs: {};
78+
params: {
79+
loader: () => Promise<JSX.Element>;
80+
filter?: EntityPredicate | ((entity: Entity) => boolean) | undefined;
81+
type?: EntityCardType | undefined;
82+
};
83+
}>;
84+
'entity-content:tech-insights/scorecards': OverridableExtensionDefinition<{
85+
kind: 'entity-content';
86+
name: 'scorecards';
87+
config: {
88+
path: string | undefined;
89+
title: string | undefined;
90+
filter: EntityPredicate | undefined;
91+
group: string | false | undefined;
92+
};
93+
configInput: {
94+
filter?: EntityPredicate | undefined;
95+
title?: string | undefined;
96+
path?: string | undefined;
97+
group?: string | false | undefined;
98+
};
99+
output:
100+
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
101+
| ExtensionDataRef<string, 'core.routing.path', {}>
102+
| ExtensionDataRef<
103+
RouteRef<AnyRouteRefParams>,
104+
'core.routing.ref',
105+
{
106+
optional: true;
107+
}
108+
>
109+
| ExtensionDataRef<
110+
(entity: Entity) => boolean,
111+
'catalog.entity-filter-function',
112+
{
113+
optional: true;
114+
}
115+
>
116+
| ExtensionDataRef<
117+
string,
118+
'catalog.entity-filter-expression',
119+
{
120+
optional: true;
121+
}
122+
>
123+
| ExtensionDataRef<string, 'catalog.entity-content-title', {}>
124+
| ExtensionDataRef<
125+
string,
126+
'catalog.entity-content-group',
127+
{
128+
optional: true;
129+
}
130+
>;
131+
inputs: {};
132+
params: {
133+
defaultPath?: [Error: "Use the 'path' param instead"] | undefined;
134+
path: string;
135+
defaultTitle?: [Error: "Use the 'title' param instead"] | undefined;
136+
title: string;
137+
defaultGroup?: [Error: "Use the 'group' param instead"] | undefined;
138+
group?:
139+
| (string & {})
140+
| 'development'
141+
| 'deployment'
142+
| 'overview'
143+
| 'documentation'
144+
| 'operation'
145+
| 'observability'
146+
| undefined;
147+
loader: () => Promise<JSX.Element>;
148+
routeRef?: RouteRef<AnyRouteRefParams> | undefined;
149+
filter?: EntityPredicate | ((entity: Entity) => boolean) | undefined;
150+
};
151+
}>;
152+
'page:tech-insights': OverridableExtensionDefinition<{
153+
kind: 'page';
154+
name: undefined;
155+
config: {
156+
path: string | undefined;
157+
};
158+
configInput: {
159+
path?: string | undefined;
160+
};
161+
output:
162+
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
163+
| ExtensionDataRef<string, 'core.routing.path', {}>
164+
| ExtensionDataRef<
165+
RouteRef<AnyRouteRefParams>,
166+
'core.routing.ref',
167+
{
168+
optional: true;
169+
}
170+
>;
171+
inputs: {};
172+
params: {
173+
defaultPath?: [Error: "Use the 'path' param instead"] | undefined;
174+
path: string;
175+
loader: () => Promise<JSX.Element>;
176+
routeRef?: RouteRef<AnyRouteRefParams> | undefined;
177+
};
178+
}>;
179+
}
180+
>;
181+
export default techInsightsPlugin;
182+
183+
// (No @packageDocumentation comment for this package)
184+
```
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright 2026 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export { default } from './alpha/index';
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2026 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import {
17+
ApiBlueprint,
18+
discoveryApiRef,
19+
identityApiRef,
20+
} from '@backstage/frontend-plugin-api';
21+
import {
22+
techInsightsApiRef,
23+
TechInsightsClient,
24+
} from '@backstage-community/plugin-tech-insights-react';
25+
26+
/**
27+
* @alpha
28+
*/
29+
export const techInsightsApi = ApiBlueprint.make({
30+
params: defineParams =>
31+
defineParams({
32+
api: techInsightsApiRef,
33+
deps: {
34+
discoveryApi: discoveryApiRef,
35+
identityApi: identityApiRef,
36+
},
37+
factory: ({ discoveryApi, identityApi }) =>
38+
new TechInsightsClient({ discoveryApi, identityApi }),
39+
}),
40+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2026 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { compatWrapper } from '@backstage/core-compat-api';
17+
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
18+
19+
/**
20+
* Entity card extension that displays Tech Insights scorecards for an entity.
21+
*
22+
* @alpha
23+
*/
24+
export const entityTechInsightsScorecardCard = EntityCardBlueprint.make({
25+
name: 'scorecards',
26+
params: {
27+
loader: () =>
28+
import('../components/ScorecardsCard').then(m =>
29+
compatWrapper(<m.ScorecardsCard title="Scorecards" />),
30+
),
31+
},
32+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2026 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { compatWrapper } from '@backstage/core-compat-api';
17+
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
18+
19+
/**
20+
* Entity content extension that displays Tech Insights scorecards for an entity.
21+
*
22+
* @alpha
23+
*/
24+
export const entityTechInsightsScorecardContent = EntityContentBlueprint.make({
25+
name: 'scorecards',
26+
params: {
27+
path: '/tech-insights',
28+
title: 'Scorecards',
29+
loader: () =>
30+
import('../components/ScorecardsContent').then(m =>
31+
compatWrapper(<m.ScorecardsContent title="Scorecards" />),
32+
),
33+
},
34+
});

0 commit comments

Comments
 (0)