Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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/stupid-tips-follow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axis-backstage/plugin-statuspage': patch
---

Use EntityInfoCard as the entity card wrapper
5 changes: 5 additions & 0 deletions .changeset/violet-taxes-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axis-backstage/plugin-statuspage-backend': patch
---

Use native fetch instead of cross-fetch according to ADR14
21 changes: 21 additions & 0 deletions .changeset/warm-spiders-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@axis-backstage/plugin-statuspage': minor
---

Add support for the new frontend system (NFS). The plugin can now be imported from the `./alpha` entry point and registered as a feature in a new-system app.

The plugin provides three extensions out of the box:

- `api:statuspage` — registers the `StatuspageClient`
- `page:statuspage` — mounts the full statuspage at `/statuspage`
- `entity-card:statuspage` — renders the `StatuspageEntityCard` on entity pages

Configure the instance name for the page extension in `app-config.yaml`:

```yaml
app:
extensions:
- page:statuspage:
config:
name: mystatuspageinstance
```
1 change: 1 addition & 0 deletions packages/app-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@axis-backstage/plugin-jira-dashboard": "workspace:^",
"@axis-backstage/plugin-jira-dashboard-common": "workspace:^",
"@axis-backstage/plugin-readme": "workspace:^",
"@axis-backstage/plugin-statuspage": "workspace:^",
"@backstage/app-defaults": "^1.7.7",
"@backstage/catalog-model": "^1.8.0",
"@backstage/cli": "^0.36.1",
Expand Down
1 change: 0 additions & 1 deletion plugins/statuspage-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"@backstage/backend-plugin-api": "^1.9.0",
"@backstage/config": "^1.3.7",
"@types/express": "*",
"cross-fetch": "^4.0.0",
"express": "^4.17.1",
"express-promise-router": "^4.1.0"
},
Expand Down
1 change: 0 additions & 1 deletion plugins/statuspage-backend/src/service/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import fetch from 'cross-fetch';
import type {
Component,
ComponentGroup,
Expand Down
36 changes: 36 additions & 0 deletions plugins/statuspage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,39 @@ The `component_id` could be the id of either a component or a component group. T

<Route path="/statuspage" element={<StatuspagePage name="myid" />} />
```

## New Frontend System

1. First, install the plugin into your app:

```bash
# From your Backstage root directory
yarn --cwd packages/app add @axis-backstage/plugin-statuspage
```

2. If [feature discovery](https://backstage.io/docs/frontend-system/architecture/app/#feature-discovery) is enabled in your app, the plugin will be automatically discovered. If not, add it manually:

```ts
// packages/app/src/App.tsx
import statuspagePlugin from '@axis-backstage/plugin-statuspage/alpha';

const app = createApp({
features: [
// ...
statuspagePlugin,
],
});
```

3. Configure the statuspage instance name for the page extension in `app-config.yaml`:

```yaml
# app-config.yaml
app:
extensions:
- page:statuspage:
config:
name: mystatuspageinstance
```

The `StatuspageEntityCard` is automatically added to entity pages via the `entity-card:statuspage` extension. No additional wiring in `EntityPage.tsx` is needed. The entity annotation setup remains the same — see [Integration with the Catalog](#integration-with-the-catalog) above.
19 changes: 18 additions & 1 deletion plugins/statuspage/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
{
"name": "@axis-backstage/plugin-statuspage",
"version": "0.10.2",
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.tsx",
"./package.json": "./package.json"
},
"main": "src/index.ts",
"types": "src/index.ts",
"typesVersions": {
"*": {
"alpha": [
"src/alpha.tsx"
],
"package.json": [
"package.json"
]
}
},
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
Expand Down Expand Up @@ -34,10 +49,12 @@
"@backstage/catalog-model": "^1.8.0",
"@backstage/core-components": "^0.18.9",
"@backstage/core-plugin-api": "^1.12.5",
"@backstage/frontend-plugin-api": "^0.16.2",
"@backstage/plugin-catalog-react": "^2.1.4",
"@mui/icons-material": "^5.15.7",
"@mui/material": "^5.15.7",
"react-use": "^17.2.4"
"react-use": "^17.2.4",
"zod": "^4.3.6"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0",
Expand Down
167 changes: 167 additions & 0 deletions plugins/statuspage/report-alpha.api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
## API Report File for "@axis-backstage/plugin-statuspage"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).

```ts
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { EntityCardType } from '@backstage/plugin-catalog-react/alpha';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { FilterPredicate } from '@backstage/filter-predicates';
import { IconElement } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/core-plugin-api';
import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api';

// @public
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
{},
{
'api:statuspage': OverridableExtensionDefinition<{
kind: 'api';
name: undefined;
config: {};
configInput: {};
output: ExtensionDataRef<AnyApiFactory, 'core.api.factory', {}>;
inputs: {};
params: <
TApi,
TImpl extends TApi,
TDeps extends { [name in string]: unknown },
>(
params: ApiFactory<TApi, TImpl, TDeps>,
) => ExtensionBlueprintParams<AnyApiFactory>;
}>;
'entity-card:statuspage': OverridableExtensionDefinition<{
kind: 'entity-card';
name: undefined;
config: {
filter: FilterPredicate | undefined;
type: 'content' | 'info' | undefined;
};
configInput: {
filter?: FilterPredicate | undefined;
type?: 'content' | 'info' | undefined;
};
output:
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
(entity: Entity) => boolean,
'catalog.entity-filter-function',
{
optional: true;
}
>
| ExtensionDataRef<
string,
'catalog.entity-filter-expression',
{
optional: true;
}
>
| ExtensionDataRef<
EntityCardType,
'catalog.entity-card-type',
{
optional: true;
}
>;
inputs: {};
params: {
loader: () => Promise<JSX.Element>;
filter?: string | FilterPredicate | ((entity: Entity) => boolean);
type?: EntityCardType;
};
}>;
'page:statuspage': OverridableExtensionDefinition<{
config: {
name: string;
path: string | undefined;
title: string | undefined;
};
configInput: {
name?: string | undefined;
path?: string | undefined | undefined;
title?: string | undefined | undefined;
};
output:
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
IconElement,
'core.icon',
{
optional: true;
}
>,
{
singleton: false;
optional: false;
internal: false;
}
>;
};
kind: 'page';
name: undefined;
params: {
path: string;
title?: string;
icon?: IconElement;
loader?: () => Promise<JSX_2.Element>;
routeRef?: RouteRef_2;
noHeader?: boolean;
};
}>;
}
>;
export default _default;
```
77 changes: 77 additions & 0 deletions plugins/statuspage/src/alpha.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Frontend plugin that allows visualization of component statuses from statuspage.io.
*
* @packageDocumentation
*/

import {
ApiBlueprint,
createFrontendPlugin,
discoveryApiRef,
fetchApiRef,
PageBlueprint,
} from '@backstage/frontend-plugin-api';
import { statuspageApiRef } from './api/StatuspageApi';
import { StatuspageClient } from './api/StatuspageClient';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
import { rootRouteRef } from './routes';
import { z } from 'zod';

const statuspageApi = ApiBlueprint.make({
params: defineParams =>
defineParams({
api: statuspageApiRef,
deps: {
discoveryApi: discoveryApiRef,
fetchApi: fetchApiRef,
},
factory: ({ discoveryApi, fetchApi }) =>
new StatuspageClient({ discoveryApi, fetchApi }),
}),
});

const statuspagePage = PageBlueprint.makeWithOverrides({
configSchema: {
/**
* The name of the statuspage instance as configured in `app-config.yaml`.
*
* @example
* ```yaml
* # app-config.yaml
* app:
* extensions:
* - page:statuspage:
* config:
* name: mystatuspageinstance
* ```
*/
name: z.string().default(''),
},
factory(originalFactory, { config }) {
return originalFactory({
path: '/statuspage',
routeRef: rootRouteRef,
loader: async () =>
import('./components/StatuspageComponent').then(m => (
<m.StatuspageComponent name={config.name} />
)),
});
},
});

const statuspageEntityCard = EntityCardBlueprint.make({
params: {
loader: async () =>
import('./components/StatuspageEntityCard').then(m => (
<m.StatuspageEntityCard />
)),
},
});

export default createFrontendPlugin({
pluginId: 'statuspage',
extensions: [statuspageApi, statuspagePage, statuspageEntityCard],
routes: {
root: rootRouteRef,
},
});
Loading
Loading