diff --git a/.changeset/tasty-colts-hug.md b/.changeset/tasty-colts-hug.md
index d5ee029aaf..e56d48a441 100644
--- a/.changeset/tasty-colts-hug.md
+++ b/.changeset/tasty-colts-hug.md
@@ -1,7 +1,7 @@
---
-'@backstage/plugin-azure-functions': minor
-'@backstage/plugin-azure-functions-backend': minor
-'@backstage/plugin-azure-functions-common': minor
+'@backstage/plugin-azure-sites': minor
+'@backstage/plugin-azure-sites-backend': minor
+'@backstage/plugin-azure-sites-common': minor
---
-Basic Azure Functions support for a given entity. View the current status of the function app, quickly jump to Function's Overview page, or Log Stream page.
+Azure Sites (Apps & Functions) support for a given entity. View the current status of the site, quickly jump to site's Overview page, or Log Stream page.
diff --git a/microsite/data/plugins/azure-functions.yaml b/microsite/data/plugins/azure-sites.yaml
similarity index 66%
rename from microsite/data/plugins/azure-functions.yaml
rename to microsite/data/plugins/azure-sites.yaml
index bc5b003726..edfba635fc 100644
--- a/microsite/data/plugins/azure-functions.yaml
+++ b/microsite/data/plugins/azure-sites.yaml
@@ -4,6 +4,6 @@ author: FRISS
authorUrl: https://friss.com
category: Infrastructure
description: View related Azure information for your components in Backstage.
-documentation: https://github.com/backstage/backstage/tree/master/plugins/azure-functions
-iconUrl: img/azurefunctions-icon.svg
-npmPackageName: '@backstage/plugin-azure-functions'
+documentation: https://github.com/backstage/backstage/tree/master/plugins/azure-sites
+iconUrl: img/azure-icon.svg
+npmPackageName: '@backstage/plugin-azure-sites'
diff --git a/packages/app/package.json b/packages/app/package.json
index d3360518ec..88c6407ba3 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -19,7 +19,7 @@
"@backstage/plugin-apache-airflow": "workspace:^",
"@backstage/plugin-api-docs": "workspace:^",
"@backstage/plugin-azure-devops": "workspace:^",
- "@backstage/plugin-azure-functions": "workspace:^",
+ "@backstage/plugin-azure-sites": "workspace:^",
"@backstage/plugin-badges": "workspace:^",
"@backstage/plugin-catalog-common": "workspace:^",
"@backstage/plugin-catalog-graph": "workspace:^",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 468731be33..3a5dc6fbe7 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -36,7 +36,7 @@
"@backstage/plugin-auth-backend": "workspace:^",
"@backstage/plugin-auth-node": "workspace:^",
"@backstage/plugin-azure-devops-backend": "workspace:^",
- "@backstage/plugin-azure-functions-backend": "workspace:^",
+ "@backstage/plugin-azure-sites-backend": "workspace:^",
"@backstage/plugin-badges-backend": "workspace:^",
"@backstage/plugin-catalog-backend": "workspace:^",
"@backstage/plugin-code-coverage-backend": "workspace:^",
diff --git a/plugins/azure-functions/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx b/plugins/azure-functions/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx
deleted file mode 100644
index c26fb6bc55..0000000000
--- a/plugins/azure-functions/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2022 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React from 'react';
-import { Box, Card, Link, LinearProgress } from '@material-ui/core';
-import { AzureSite } from '@backstage/plugin-azure-functions-common';
-import { Table, TableColumn } from '@backstage/core-components';
-import FlashOnIcon from '@material-ui/icons/FlashOn';
-import { DateTime } from 'luxon';
-
-type States = 'Waiting' | 'Running' | 'Paused' | 'Failed';
-
-const State = ({ value }: { value: States }) => {
- const colorMap = {
- Waiting: '#dcbc21',
- Running: 'green',
- Paused: 'black',
- Failed: 'red',
- };
- return (
-
-
- {value}
-
- );
-};
-
-type TableProps = {
- data: AzureSite[];
- loading: boolean;
-};
-
-const DEFAULT_COLUMNS: TableColumn[] = [
- {
- title: 'name',
- highlight: true,
- render: (func: AzureSite) => {
- return (
-
- {func.name}
-
- );
- },
- },
- {
- title: 'kind',
- render: (func: AzureSite) => func.kind ?? 'unknown',
- },
- {
- title: 'location',
- render: (func: AzureSite) => func.location ?? 'unknown',
- },
- {
- title: 'status',
- render: (func: AzureSite) => ,
- },
- {
- title: 'last modified',
- render: (func: AzureSite) =>
- DateTime.fromISO(func.lastModifiedDate).toLocaleString(
- DateTime.DATETIME_MED,
- ),
- },
- {
- title: 'logs',
- align: 'right',
- render: (func: AzureSite) => {
- return (
-
- View Logs
-
- );
- },
- },
-];
-
-/** @public */
-export const AzureSitesOverviewTable = ({ data, loading }: TableProps) => {
- const columns: TableColumn[] = [...DEFAULT_COLUMNS];
- const tableStyle = {
- minWidth: '0',
- width: '100%',
- };
-
- return (
-
-
-
-
- Azure Sites
-
- }
- options={{ paging: true, search: false, pageSize: 10 }}
- data={data}
- emptyContent={}
- isLoading={loading}
- columns={columns}
- />
-
- );
-};
diff --git a/plugins/azure-functions-backend/.eslintrc.js b/plugins/azure-sites-backend/.eslintrc.js
similarity index 100%
rename from plugins/azure-functions-backend/.eslintrc.js
rename to plugins/azure-sites-backend/.eslintrc.js
diff --git a/plugins/azure-functions-backend/README.md b/plugins/azure-sites-backend/README.md
similarity index 92%
rename from plugins/azure-functions-backend/README.md
rename to plugins/azure-sites-backend/README.md
index e220cca00f..b655f1fb64 100644
--- a/plugins/azure-functions-backend/README.md
+++ b/plugins/azure-sites-backend/README.md
@@ -33,11 +33,11 @@ Configuration Details:
Here's how to get the backend plugin up and running:
-1. First we need to add the `@backstage/plugin-azure-functions-backend` package to your backend:
+1. First we need to add the `@backstage/plugin-azure-sites-backend` package to your backend:
```sh
# From the Backstage root directory
- yarn add --cwd packages/backend @backstage/plugin-azure-functions-backend
+ yarn add --cwd packages/backend @backstage/plugin-azure-sites-backend
```
2. Then we will create a new file named `packages/backend/src/plugins/azure.ts`, and add the following to it:
@@ -46,7 +46,7 @@ Here's how to get the backend plugin up and running:
import {
createRouter,
AzureSitesApi,
- } from '@backstage/plugin-azure-functions-backend';
+ } from '@backstage/plugin-azure-sites-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
diff --git a/plugins/azure-functions-backend/api-report.md b/plugins/azure-sites-backend/api-report.md
similarity index 75%
rename from plugins/azure-functions-backend/api-report.md
rename to plugins/azure-sites-backend/api-report.md
index b8d8871595..5c868ad507 100644
--- a/plugins/azure-functions-backend/api-report.md
+++ b/plugins/azure-sites-backend/api-report.md
@@ -1,10 +1,11 @@
-## API Report File for "@backstage/plugin-azure-functions-backend"
+## API Report File for "@backstage/plugin-azure-sites-backend"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
-import { AzureSiteListRequest } from '@backstage/plugin-azure-functions-common';
-import { AzureSiteListResponse } from '@backstage/plugin-azure-functions-common';
+import { AzureSiteListRequest } from '@backstage/plugin-azure-sites-common';
+import { AzureSiteListResponse } from '@backstage/plugin-azure-sites-common';
+import { AzureSiteStartStopRequest } from '@backstage/plugin-azure-sites-common';
import { Config } from '@backstage/config';
import express from 'express';
import { Logger } from 'winston';
@@ -39,6 +40,10 @@ export class AzureSitesApi {
static fromConfig(config: Config): AzureSitesApi;
// (undocumented)
list(request: AzureSiteListRequest): Promise;
+ // (undocumented)
+ start(request: AzureSiteStartStopRequest): Promise;
+ // (undocumented)
+ stop(request: AzureSiteStartStopRequest): Promise;
}
// @public (undocumented)
diff --git a/plugins/azure-functions-backend/package.json b/plugins/azure-sites-backend/package.json
similarity index 86%
rename from plugins/azure-functions-backend/package.json
rename to plugins/azure-sites-backend/package.json
index 99e134e53e..c1f801bb71 100644
--- a/plugins/azure-functions-backend/package.json
+++ b/plugins/azure-sites-backend/package.json
@@ -1,5 +1,5 @@
{
- "name": "@backstage/plugin-azure-functions-backend",
+ "name": "@backstage/plugin-azure-sites-backend",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
@@ -17,7 +17,7 @@
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
- "directory": "plugins/azure-functions-backend"
+ "directory": "plugins/azure-sites-backend"
},
"keywords": [
"backstage",
@@ -33,11 +33,12 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
+ "@azure/arm-appservice": "^13.0.1",
"@azure/arm-resourcegraph": "^4.2.1",
"@azure/identity": "^2.1.0",
"@backstage/backend-common": "workspace:^",
"@backstage/config": "workspace:^",
- "@backstage/plugin-azure-functions-common": "workspace:^",
+ "@backstage/plugin-azure-sites-common": "workspace:^",
"@types/express": "^4.17.6",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
diff --git a/plugins/azure-functions-backend/schema.d.ts b/plugins/azure-sites-backend/schema.d.ts
similarity index 100%
rename from plugins/azure-functions-backend/schema.d.ts
rename to plugins/azure-sites-backend/schema.d.ts
diff --git a/plugins/azure-functions-backend/src/api/AzureSitesApi.ts b/plugins/azure-sites-backend/src/api/AzureSitesApi.ts
similarity index 68%
rename from plugins/azure-functions-backend/src/api/AzureSitesApi.ts
rename to plugins/azure-sites-backend/src/api/AzureSitesApi.ts
index e00c49e4d3..70dc9bba69 100644
--- a/plugins/azure-functions-backend/src/api/AzureSitesApi.ts
+++ b/plugins/azure-sites-backend/src/api/AzureSitesApi.ts
@@ -20,11 +20,13 @@ import {
ClientSecretCredential,
} from '@azure/identity';
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
+import { WebSiteManagementClient } from '@azure/arm-appservice';
import {
AzureSite,
AzureSiteListRequest,
AzureSiteListResponse,
-} from '@backstage/plugin-azure-functions-common';
+ AzureSiteStartStopRequest,
+} from '@backstage/plugin-azure-sites-common';
import { AzureFunctionsConfig } from '../config';
/** @public */
@@ -34,22 +36,41 @@ export class AzureSitesApi {
private readonly client: ResourceGraphClient;
constructor(private readonly config: AzureFunctionsConfig) {
- const creds =
- config.clientId && config.clientSecret
- ? new ClientSecretCredential(
- config.tenantId,
- config.clientId,
- config.clientSecret,
- )
- : new DefaultAzureCredential({ tenantId: config.tenantId });
+ const creds = this.getCredentials(config);
this.client = new ResourceGraphClient(creds);
}
+ private getCredentials(config: AzureFunctionsConfig) {
+ return config.clientId && config.clientSecret
+ ? new ClientSecretCredential(
+ config.tenantId,
+ config.clientId,
+ config.clientSecret,
+ )
+ : new DefaultAzureCredential({ tenantId: config.tenantId });
+ }
+
static fromConfig(config: Config): AzureSitesApi {
return new AzureSitesApi(AzureFunctionsConfig.fromConfig(config));
}
+ async start(request: AzureSiteStartStopRequest): Promise {
+ const client = new WebSiteManagementClient(
+ this.getCredentials(this.config),
+ request.subscription,
+ );
+ await client.webApps.start(request.resourceGroup, request.name);
+ }
+
+ async stop(request: AzureSiteStartStopRequest): Promise {
+ const client = new WebSiteManagementClient(
+ this.getCredentials(this.config),
+ request.subscription,
+ );
+ await client.webApps.stop(request.resourceGroup, request.name);
+ }
+
async list(request: AzureSiteListRequest): Promise {
const items: AzureSite[] = [];
try {
@@ -66,6 +87,7 @@ export class AzureSitesApi {
name: v.name!,
kind: v.kind!,
resourceGroup: v.resourceGroup!,
+ subscription: v.id!.match(/\w{8}\-\w{4}\-\w{4}\-\w{4}\-\w{12}/)[0],
location: v.location!,
lastModifiedDate: v.properties.lastModifiedTimeUtc!,
usageState: v.properties.usageState!,
diff --git a/plugins/azure-functions-backend/src/api/index.ts b/plugins/azure-sites-backend/src/api/index.ts
similarity index 100%
rename from plugins/azure-functions-backend/src/api/index.ts
rename to plugins/azure-sites-backend/src/api/index.ts
diff --git a/plugins/azure-functions-backend/src/config.ts b/plugins/azure-sites-backend/src/config.ts
similarity index 100%
rename from plugins/azure-functions-backend/src/config.ts
rename to plugins/azure-sites-backend/src/config.ts
diff --git a/plugins/azure-functions-backend/src/index.ts b/plugins/azure-sites-backend/src/index.ts
similarity index 100%
rename from plugins/azure-functions-backend/src/index.ts
rename to plugins/azure-sites-backend/src/index.ts
diff --git a/plugins/azure-functions-backend/src/run.ts b/plugins/azure-sites-backend/src/run.ts
similarity index 100%
rename from plugins/azure-functions-backend/src/run.ts
rename to plugins/azure-sites-backend/src/run.ts
diff --git a/plugins/azure-functions-backend/src/service/router.ts b/plugins/azure-sites-backend/src/service/router.ts
similarity index 67%
rename from plugins/azure-functions-backend/src/service/router.ts
rename to plugins/azure-sites-backend/src/service/router.ts
index 4e4cb36630..bea9a15396 100644
--- a/plugins/azure-functions-backend/src/service/router.ts
+++ b/plugins/azure-sites-backend/src/service/router.ts
@@ -48,6 +48,34 @@ export async function createRouter(
}),
);
});
+ router.post(
+ '/:subscription/:resourceGroup/:name/start',
+ async (request, response) => {
+ const { subscription, resourceGroup, name } = request.params;
+ console.log('starting...');
+ response.json(
+ await azureSitesApi.start({
+ subscription,
+ resourceGroup,
+ name,
+ }),
+ );
+ },
+ );
+ router.post(
+ '/:subscription/:resourceGroup/:name/stop',
+ async (request, response) => {
+ const { subscription, resourceGroup, name } = request.params;
+ console.log('stopping...');
+ response.json(
+ await azureSitesApi.stop({
+ subscription,
+ resourceGroup,
+ name,
+ }),
+ );
+ },
+ );
router.use(errorHandler());
return router;
}
diff --git a/plugins/azure-functions-backend/src/service/standaloneServer.ts b/plugins/azure-sites-backend/src/service/standaloneServer.ts
similarity index 100%
rename from plugins/azure-functions-backend/src/service/standaloneServer.ts
rename to plugins/azure-sites-backend/src/service/standaloneServer.ts
diff --git a/plugins/azure-functions-backend/src/setupTests.ts b/plugins/azure-sites-backend/src/setupTests.ts
similarity index 100%
rename from plugins/azure-functions-backend/src/setupTests.ts
rename to plugins/azure-sites-backend/src/setupTests.ts
diff --git a/plugins/azure-functions-common/.eslintrc.js b/plugins/azure-sites-common/.eslintrc.js
similarity index 100%
rename from plugins/azure-functions-common/.eslintrc.js
rename to plugins/azure-sites-common/.eslintrc.js
diff --git a/plugins/azure-functions-common/api-report.md b/plugins/azure-sites-common/api-report.md
similarity index 73%
rename from plugins/azure-functions-common/api-report.md
rename to plugins/azure-sites-common/api-report.md
index 12fed0f08c..64b4275085 100644
--- a/plugins/azure-functions-common/api-report.md
+++ b/plugins/azure-sites-common/api-report.md
@@ -1,4 +1,4 @@
-## API Report File for "@backstage/plugin-azure-functions-common"
+## API Report File for "@backstage/plugin-azure-sites-common"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
@@ -11,6 +11,7 @@ export type AzureSite = {
kind: string;
location: string;
resourceGroup: string;
+ subscription: string;
state: string;
usageState: string;
containerSize: number;
@@ -28,5 +29,12 @@ export type AzureSiteListResponse = {
items: AzureSite[];
};
+// @public (undocumented)
+export type AzureSiteStartStopRequest = {
+ subscription: string;
+ resourceGroup: string;
+ name: string;
+};
+
// (No @packageDocumentation comment for this package)
```
diff --git a/plugins/azure-functions-common/package.json b/plugins/azure-sites-common/package.json
similarity index 89%
rename from plugins/azure-functions-common/package.json
rename to plugins/azure-sites-common/package.json
index 3faa3260ae..eb0093bf65 100644
--- a/plugins/azure-functions-common/package.json
+++ b/plugins/azure-sites-common/package.json
@@ -1,5 +1,5 @@
{
- "name": "@backstage/plugin-azure-functions-common",
+ "name": "@backstage/plugin-azure-sites-common",
"description": "Common functionalities for the azure plugin",
"version": "0.0.0",
"main": "src/index.ts",
@@ -18,7 +18,7 @@
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
- "directory": "plugins/azure-functions-common"
+ "directory": "plugins/azure-sites-common"
},
"keywords": [
"backstage"
diff --git a/plugins/azure-functions-common/src/index.ts b/plugins/azure-sites-common/src/index.ts
similarity index 100%
rename from plugins/azure-functions-common/src/index.ts
rename to plugins/azure-sites-common/src/index.ts
diff --git a/plugins/azure-functions-common/src/types.ts b/plugins/azure-sites-common/src/types.ts
similarity index 87%
rename from plugins/azure-functions-common/src/types.ts
rename to plugins/azure-sites-common/src/types.ts
index 5364b43421..7c8be7ce24 100644
--- a/plugins/azure-functions-common/src/types.ts
+++ b/plugins/azure-sites-common/src/types.ts
@@ -22,6 +22,7 @@ export type AzureSite = {
kind: string;
location: string;
resourceGroup: string;
+ subscription: string;
state: string;
usageState: string;
containerSize: number;
@@ -38,3 +39,10 @@ export type AzureSiteListRequest = {
export type AzureSiteListResponse = {
items: AzureSite[];
};
+
+/** @public */
+export type AzureSiteStartStopRequest = {
+ subscription: string;
+ resourceGroup: string;
+ name: string;
+};
diff --git a/plugins/azure-functions/.eslintrc.js b/plugins/azure-sites/.eslintrc.js
similarity index 100%
rename from plugins/azure-functions/.eslintrc.js
rename to plugins/azure-sites/.eslintrc.js
diff --git a/plugins/azure-functions/README.md b/plugins/azure-sites/README.md
similarity index 94%
rename from plugins/azure-functions/README.md
rename to plugins/azure-sites/README.md
index ed7e18c3f0..2f69518078 100644
--- a/plugins/azure-functions/README.md
+++ b/plugins/azure-sites/README.md
@@ -47,14 +47,14 @@ azure.com/microsoft-web-sites: func-testapp
1. Install the plugin in the `packages/app` directory
```sh
-yarn add @backstage/plugin-azure-functions
+yarn add @backstage/plugin-azure-sites
```
2. Add widget component to your Backstage instance:
```ts
// In packages/app/src/components/catalog/EntityPage.tsx
-import { EntityAzureSitesOverviewWidget, isAzureWebSiteNameAvailable } from '@backstage/plugin-azure-functions';
+import { EntityAzureSitesOverviewWidget, isAzureWebSiteNameAvailable } from '@backstage/plugin-azure-sites';
...
diff --git a/plugins/azure-functions/api-report.md b/plugins/azure-sites/api-report.md
similarity index 72%
rename from plugins/azure-functions/api-report.md
rename to plugins/azure-sites/api-report.md
index bed1c36c4d..2e9b81376a 100644
--- a/plugins/azure-functions/api-report.md
+++ b/plugins/azure-sites/api-report.md
@@ -1,4 +1,4 @@
-## API Report File for "@backstage/plugin-azure-functions"
+## API Report File for "@backstage/plugin-azure-sites"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
@@ -6,8 +6,9 @@
///
import { ApiRef } from '@backstage/core-plugin-api';
-import { AzureSiteListRequest } from '@backstage/plugin-azure-functions-common';
-import { AzureSiteListResponse } from '@backstage/plugin-azure-functions-common';
+import { AzureSiteListRequest } from '@backstage/plugin-azure-sites-common';
+import { AzureSiteListResponse } from '@backstage/plugin-azure-sites-common';
+import { AzureSiteStartStopRequest } from '@backstage/plugin-azure-sites-common';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
@@ -20,6 +21,8 @@ export const azureSiteApiRef: ApiRef;
// @public (undocumented)
export type AzureSitesApi = {
list: (request: AzureSiteListRequest) => Promise;
+ start: (request: AzureSiteStartStopRequest) => Promise;
+ stop: (request: AzureSiteStartStopRequest) => Promise;
};
// @public (undocumented)
@@ -30,6 +33,10 @@ export class AzureSitesApiBackendClient implements AzureSitesApi {
});
// (undocumented)
list(request: AzureSiteListRequest): Promise;
+ // (undocumented)
+ start(request: AzureSiteStartStopRequest): Promise;
+ // (undocumented)
+ stop(request: AzureSiteStartStopRequest): Promise;
}
// @public (undocumented)
diff --git a/plugins/azure-functions/dev/index.tsx b/plugins/azure-sites/dev/index.tsx
similarity index 100%
rename from plugins/azure-functions/dev/index.tsx
rename to plugins/azure-sites/dev/index.tsx
diff --git a/plugins/azure-functions/docs/functions-table.png b/plugins/azure-sites/docs/functions-table.png
similarity index 100%
rename from plugins/azure-functions/docs/functions-table.png
rename to plugins/azure-sites/docs/functions-table.png
diff --git a/plugins/azure-functions/package.json b/plugins/azure-sites/package.json
similarity index 91%
rename from plugins/azure-functions/package.json
rename to plugins/azure-sites/package.json
index 84999a1665..8b9ee9f25d 100644
--- a/plugins/azure-functions/package.json
+++ b/plugins/azure-sites/package.json
@@ -1,5 +1,5 @@
{
- "name": "@backstage/plugin-azure-functions",
+ "name": "@backstage/plugin-azure-sites",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
@@ -17,7 +17,7 @@
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
- "directory": "plugins/azure-functions"
+ "directory": "plugins/azure-sites"
},
"keywords": [
"backstage",
@@ -36,7 +36,7 @@
"@backstage/catalog-model": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
- "@backstage/plugin-azure-functions-common": "workspace:^",
+ "@backstage/plugin-azure-sites-common": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
"@backstage/theme": "workspace:^",
"@material-ui/core": "^4.12.2",
diff --git a/plugins/azure-functions/src/api/AzureSitesApi.ts b/plugins/azure-sites/src/api/AzureSitesApi.ts
similarity index 79%
rename from plugins/azure-functions/src/api/AzureSitesApi.ts
rename to plugins/azure-sites/src/api/AzureSitesApi.ts
index 5ff921a419..87a3b46a8d 100644
--- a/plugins/azure-functions/src/api/AzureSitesApi.ts
+++ b/plugins/azure-sites/src/api/AzureSitesApi.ts
@@ -18,14 +18,17 @@ import { createApiRef } from '@backstage/core-plugin-api';
import {
AzureSiteListRequest,
AzureSiteListResponse,
-} from '@backstage/plugin-azure-functions-common';
+ AzureSiteStartStopRequest,
+} from '@backstage/plugin-azure-sites-common';
/** @public */
export const azureSiteApiRef = createApiRef({
- id: 'plugin.azure-functions.service',
+ id: 'plugin.azure-sites.service',
});
/** @public */
export type AzureSitesApi = {
list: (request: AzureSiteListRequest) => Promise;
+ start: (request: AzureSiteStartStopRequest) => Promise;
+ stop: (request: AzureSiteStartStopRequest) => Promise;
};
diff --git a/plugins/azure-functions/src/api/AzureSitesApiBackendClient.ts b/plugins/azure-sites/src/api/AzureSitesApiBackendClient.ts
similarity index 54%
rename from plugins/azure-functions/src/api/AzureSitesApiBackendClient.ts
rename to plugins/azure-sites/src/api/AzureSitesApiBackendClient.ts
index a19cf270e5..5b1da009b5 100644
--- a/plugins/azure-functions/src/api/AzureSitesApiBackendClient.ts
+++ b/plugins/azure-sites/src/api/AzureSitesApiBackendClient.ts
@@ -18,7 +18,8 @@ import { AzureSitesApi } from './AzureSitesApi';
import {
AzureSiteListRequest,
AzureSiteListResponse,
-} from '@backstage/plugin-azure-functions-common';
+ AzureSiteStartStopRequest,
+} from '@backstage/plugin-azure-sites-common';
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
/** @public */
@@ -33,11 +34,46 @@ export class AzureSitesApiBackendClient implements AzureSitesApi {
this.identityApi = options.identityApi;
}
+ async stop(request: AzureSiteStartStopRequest): Promise {
+ try {
+ const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/${
+ request.subscription
+ }/${request.resourceGroup}/${request.name}/stop`;
+ const { token: accessToken } = await this.identityApi.getCredentials();
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ ...(accessToken && { Authorization: `Bearer ${accessToken}` }),
+ },
+ });
+ } catch (e: any) {
+ throw new Error(e);
+ }
+ }
+ async start(request: AzureSiteStartStopRequest): Promise {
+ try {
+ const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/${
+ request.subscription
+ }/${request.resourceGroup}/${request.name}/start`;
+ const { token: accessToken } = await this.identityApi.getCredentials();
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ ...(accessToken && { Authorization: `Bearer ${accessToken}` }),
+ },
+ });
+ } catch (e: any) {
+ throw new Error(e);
+ }
+ }
+
async list(request: AzureSiteListRequest): Promise {
try {
- const url = `${await this.discoveryApi.getBaseUrl(
- 'azure-functions',
- )}/list/${request.name}`;
+ const url = `${await this.discoveryApi.getBaseUrl('azure-sites')}/list/${
+ request.name
+ }`;
const { token: accessToken } = await this.identityApi.getCredentials();
const response = await fetch(url, {
method: 'GET',
diff --git a/plugins/azure-functions/src/api/index.ts b/plugins/azure-sites/src/api/index.ts
similarity index 100%
rename from plugins/azure-functions/src/api/index.ts
rename to plugins/azure-sites/src/api/index.ts
diff --git a/plugins/azure-functions/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx
similarity index 100%
rename from plugins/azure-functions/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx
rename to plugins/azure-sites/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx
diff --git a/plugins/azure-functions/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx
similarity index 96%
rename from plugins/azure-functions/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx
rename to plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx
index 936a697dcf..23476b5c3e 100644
--- a/plugins/azure-functions/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx
+++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx
@@ -73,7 +73,7 @@ describe('AzureSitesOverviewWidget', () => {
);
expect(await rendered.findByText(siteMock.name)).toBeInTheDocument();
- expect(await rendered.findByText(siteMock.kind)).toBeInTheDocument();
+ expect(await rendered.findByText(siteMock.location)).toBeInTheDocument();
expect(await rendered.findByText(siteMock.state)).toBeInTheDocument();
expect(
await rendered.findByText(
diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx
new file mode 100644
index 0000000000..3f9895a226
--- /dev/null
+++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { Dispatch, useEffect, useState } from 'react';
+import {
+ Box,
+ Card,
+ IconButton,
+ LinearProgress,
+ Link,
+ Menu,
+ MenuItem,
+ Snackbar,
+ Tooltip,
+} from '@material-ui/core';
+import { default as MuiAlert } from '@material-ui/lab/Alert';
+import { AzureSite } from '@backstage/plugin-azure-sites-common';
+import { Table, TableColumn } from '@backstage/core-components';
+import FlashOnIcon from '@material-ui/icons/FlashOn';
+import PublicIcon from '@material-ui/icons/Public';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import StartIcon from '@material-ui/icons/PlayArrow';
+import StopIcon from '@material-ui/icons/Stop';
+import OpenInNewIcon from '@material-ui/icons/OpenInNew';
+import { DateTime } from 'luxon';
+import { useApi } from '@backstage/core-plugin-api';
+import { azureSiteApiRef } from '../../api';
+
+type States = 'Waiting' | 'Running' | 'Paused' | 'Failed' | 'Stopped';
+type Kinds = 'app' | 'functionapp';
+
+const State = ({ value }: { value: States }) => {
+ const colorMap = {
+ Waiting: '#dcbc21',
+ Running: 'green',
+ Paused: 'black',
+ Failed: 'red',
+ Stopped: 'black',
+ };
+ return (
+
+
+ {value}
+
+ );
+};
+
+const Kind = ({ value }: { value: Kinds }) => {
+ const iconMap = {
+ app: ,
+ functionapp: ,
+ };
+ return (
+
+ {iconMap[value]}
+
+ );
+};
+
+type TableProps = {
+ data: AzureSite[];
+ loading: boolean;
+};
+
+const ActionButtons = ({
+ value,
+ onMenuItemClick,
+}: {
+ value: AzureSite;
+ onMenuItemClick: Dispatch>;
+}) => {
+ const azureApi = useApi(azureSiteApiRef);
+
+ const [anchorEl, setAnchorEl] = useState(null);
+ const open = Boolean(anchorEl);
+
+ const handleOpen = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+ const start = () => {
+ azureApi.start({
+ name: value.name,
+ resourceGroup: value.resourceGroup,
+ subscription: value.subscription,
+ });
+ onMenuItemClick('Starting, this may take some time...');
+ handleClose();
+ };
+ const stop = () => {
+ azureApi.stop({
+ name: value.name,
+ resourceGroup: value.resourceGroup,
+ subscription: value.subscription,
+ });
+ onMenuItemClick('Stopping, this may take some time...');
+ handleClose();
+ };
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+/** @public */
+export const AzureSitesOverviewTable = ({ data, loading }: TableProps) => {
+ const [snackbarMessage, setSnackbarMessage] = useState(null);
+ const [isSnackbarOpen, setSnackbarOpen] = useState(false);
+
+ const onSnackbarClose = () => {
+ setSnackbarMessage(null);
+ };
+
+ useEffect(() => {
+ setSnackbarOpen(!!snackbarMessage);
+ }, [snackbarMessage]);
+
+ const columns: TableColumn[] = [
+ {
+ width: '25px',
+ field: 'kind',
+ render: (func: AzureSite) => ,
+ },
+ {
+ title: 'name',
+ field: 'name',
+ highlight: true,
+ render: (func: AzureSite) => {
+ return (
+
+ {func.name}
+
+ );
+ },
+ },
+ {
+ title: 'location',
+ field: 'location',
+ render: (func: AzureSite) => func.location ?? 'unknown',
+ },
+ {
+ title: 'status',
+ field: 'status',
+ render: (func: AzureSite) => ,
+ },
+ {
+ title: 'last modified',
+ field: 'lastModifiedDate',
+ render: (func: AzureSite) =>
+ DateTime.fromISO(func.lastModifiedDate).toLocaleString(
+ DateTime.DATETIME_MED,
+ ),
+ },
+ {
+ title: 'actions',
+ align: 'right',
+ sorting: false,
+ field: 'actions',
+ render: (func: AzureSite) => (
+
+ ),
+ },
+ ];
+
+ const tableStyle = {
+ minWidth: '0',
+ width: '100%',
+ };
+
+ return (
+
+
+
+
+ Azure Sites
+
+ }
+ options={{ paging: true, search: false, pageSize: 10 }}
+ data={data}
+ emptyContent={}
+ isLoading={loading}
+ columns={columns}
+ />
+
+
+ {snackbarMessage}
+
+
+
+ );
+};
diff --git a/plugins/azure-functions/src/hooks/useServiceEntityAnnotations.ts b/plugins/azure-sites/src/hooks/useServiceEntityAnnotations.ts
similarity index 100%
rename from plugins/azure-functions/src/hooks/useServiceEntityAnnotations.ts
rename to plugins/azure-sites/src/hooks/useServiceEntityAnnotations.ts
diff --git a/plugins/azure-functions/src/hooks/useSites.ts b/plugins/azure-sites/src/hooks/useSites.ts
similarity index 89%
rename from plugins/azure-functions/src/hooks/useSites.ts
rename to plugins/azure-sites/src/hooks/useSites.ts
index f321fca3e1..fb3215c008 100644
--- a/plugins/azure-functions/src/hooks/useSites.ts
+++ b/plugins/azure-sites/src/hooks/useSites.ts
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+import useInterval from 'react-use/lib/useInterval';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
-import { AzureSiteListResponse } from '@backstage/plugin-azure-functions-common';
+import { AzureSiteListResponse } from '@backstage/plugin-azure-sites-common';
import { azureSiteApiRef } from '../api';
import { useCallback } from 'react';
+const POLLING_INTERVAL = 15000;
+
export function useSites({ name }: { name: string }) {
const azureApi = useApi(azureSiteApiRef);
const errorApi = useApi(errorApiRef);
@@ -56,6 +59,8 @@ export function useSites({ name }: { name: string }) {
}
}, []);
+ useInterval(() => retry(), POLLING_INTERVAL);
+
return [
{
loading,
diff --git a/plugins/azure-functions/src/index.ts b/plugins/azure-sites/src/index.ts
similarity index 100%
rename from plugins/azure-functions/src/index.ts
rename to plugins/azure-sites/src/index.ts
diff --git a/plugins/azure-functions/src/mocks/mocks.ts b/plugins/azure-sites/src/mocks/mocks.ts
similarity index 93%
rename from plugins/azure-functions/src/mocks/mocks.ts
rename to plugins/azure-sites/src/mocks/mocks.ts
index 134bd32c5d..2bc3b8b9fe 100644
--- a/plugins/azure-functions/src/mocks/mocks.ts
+++ b/plugins/azure-sites/src/mocks/mocks.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { AzureSite } from '@backstage/plugin-azure-functions-common';
+import { AzureSite } from '@backstage/plugin-azure-sites-common';
export const entityMock = {
metadata: {
@@ -40,6 +40,7 @@ export const siteMock: AzureSite = {
name: 'func-mock',
kind: 'functionapp',
resourceGroup: 'mock-resourcegroup',
+ subscription: '00000000-0000-0000-0000-000000000000',
tags: {
isMock: true,
},
diff --git a/plugins/azure-functions/src/plugin.test.ts b/plugins/azure-sites/src/plugin.test.ts
similarity index 100%
rename from plugins/azure-functions/src/plugin.test.ts
rename to plugins/azure-sites/src/plugin.test.ts
diff --git a/plugins/azure-functions/src/plugin.ts b/plugins/azure-sites/src/plugin.ts
similarity index 100%
rename from plugins/azure-functions/src/plugin.ts
rename to plugins/azure-sites/src/plugin.ts
diff --git a/plugins/azure-functions/src/routes.ts b/plugins/azure-sites/src/routes.ts
similarity index 96%
rename from plugins/azure-functions/src/routes.ts
rename to plugins/azure-sites/src/routes.ts
index 61234c4da5..1b10ad8dde 100644
--- a/plugins/azure-functions/src/routes.ts
+++ b/plugins/azure-sites/src/routes.ts
@@ -17,5 +17,5 @@
import { createRouteRef } from '@backstage/core-plugin-api';
export const rootRouteRef = createRouteRef({
- id: 'azure-functions',
+ id: 'azure-sites',
});
diff --git a/plugins/azure-functions/src/setupTests.ts b/plugins/azure-sites/src/setupTests.ts
similarity index 100%
rename from plugins/azure-functions/src/setupTests.ts
rename to plugins/azure-sites/src/setupTests.ts
diff --git a/yarn.lock b/yarn.lock
index 456fd8d8ec..15a08e5092 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -339,6 +339,21 @@ __metadata:
languageName: node
linkType: hard
+"@azure/arm-appservice@npm:^13.0.1":
+ version: 13.0.2
+ resolution: "@azure/arm-appservice@npm:13.0.2"
+ dependencies:
+ "@azure/abort-controller": ^1.0.0
+ "@azure/core-auth": ^1.3.0
+ "@azure/core-client": ^1.5.0
+ "@azure/core-lro": ^2.2.0
+ "@azure/core-paging": ^1.2.0
+ "@azure/core-rest-pipeline": ^1.8.0
+ tslib: ^2.2.0
+ checksum: 6d604c492a70a6cdd4d3ad3f634a74228dc081ed4f49128873e76bfa5e65ec8cce18b7b19319fd2b1b4edbd390dbd66c9ac48646f0ed80cae09cd2f1d05fb279
+ languageName: node
+ linkType: hard
+
"@azure/arm-resourcegraph@npm:^4.2.1":
version: 4.2.1
resolution: "@azure/arm-resourcegraph@npm:4.2.1"
@@ -358,7 +373,7 @@ __metadata:
languageName: node
linkType: hard
-"@azure/core-auth@npm:^1.1.4":
+"@azure/core-auth@npm:^1.1.4, @azure/core-auth@npm:^1.4.0":
version: 1.4.0
resolution: "@azure/core-auth@npm:1.4.0"
dependencies:
@@ -393,6 +408,21 @@ __metadata:
languageName: node
linkType: hard
+"@azure/core-client@npm:^1.5.0":
+ version: 1.6.1
+ resolution: "@azure/core-client@npm:1.6.1"
+ dependencies:
+ "@azure/abort-controller": ^1.0.0
+ "@azure/core-auth": ^1.4.0
+ "@azure/core-rest-pipeline": ^1.9.1
+ "@azure/core-tracing": ^1.0.0
+ "@azure/core-util": ^1.0.0
+ "@azure/logger": ^1.0.0
+ tslib: ^2.2.0
+ checksum: 400890a9b5f0c8801ff92005c3cba7bb5124634e321735406731bef33688a79f23d28b49fccdfab90814dade41a13f3d3cb99f80151a2bff17628416e811afb6
+ languageName: node
+ linkType: hard
+
"@azure/core-http@npm:^2.0.0":
version: 2.2.4
resolution: "@azure/core-http@npm:2.2.4"
@@ -437,6 +467,15 @@ __metadata:
languageName: node
linkType: hard
+"@azure/core-paging@npm:^1.2.0":
+ version: 1.3.0
+ resolution: "@azure/core-paging@npm:1.3.0"
+ dependencies:
+ tslib: ^2.2.0
+ checksum: 3fbf3d6474e2346f7c3ea8344a41bf41e053789efbbd29df78365e9c9ca66e143da3e5407d8c9dd5ddc82a65680185cd6f0ec851c48635776d18a6a605a98e78
+ languageName: node
+ linkType: hard
+
"@azure/core-rest-pipeline@npm:^1.1.0, @azure/core-rest-pipeline@npm:^1.5.0":
version: 1.5.0
resolution: "@azure/core-rest-pipeline@npm:1.5.0"
@@ -454,6 +493,24 @@ __metadata:
languageName: node
linkType: hard
+"@azure/core-rest-pipeline@npm:^1.8.0, @azure/core-rest-pipeline@npm:^1.9.1":
+ version: 1.9.2
+ resolution: "@azure/core-rest-pipeline@npm:1.9.2"
+ dependencies:
+ "@azure/abort-controller": ^1.0.0
+ "@azure/core-auth": ^1.4.0
+ "@azure/core-tracing": ^1.0.1
+ "@azure/core-util": ^1.0.0
+ "@azure/logger": ^1.0.0
+ form-data: ^4.0.0
+ http-proxy-agent: ^5.0.0
+ https-proxy-agent: ^5.0.0
+ tslib: ^2.2.0
+ uuid: ^8.3.0
+ checksum: cdfdaf8bdb0778f40ecfeb8b5da0b4f8643d641b75bb5869485afd38f7c3127ab088ac2fe2980df03d28fb6715928e04560343ee6416246aebe75443c4dd2906
+ languageName: node
+ linkType: hard
+
"@azure/core-tracing@npm:1.0.0-preview.13":
version: 1.0.0-preview.13
resolution: "@azure/core-tracing@npm:1.0.0-preview.13"
@@ -464,7 +521,7 @@ __metadata:
languageName: node
linkType: hard
-"@azure/core-tracing@npm:^1.0.0":
+"@azure/core-tracing@npm:^1.0.0, @azure/core-tracing@npm:^1.0.1":
version: 1.0.1
resolution: "@azure/core-tracing@npm:1.0.1"
dependencies:
@@ -4339,16 +4396,17 @@ __metadata:
languageName: unknown
linkType: soft
-"@backstage/plugin-azure-functions-backend@workspace:^, @backstage/plugin-azure-functions-backend@workspace:plugins/azure-functions-backend":
+"@backstage/plugin-azure-sites-backend@workspace:^, @backstage/plugin-azure-sites-backend@workspace:plugins/azure-sites-backend":
version: 0.0.0-use.local
- resolution: "@backstage/plugin-azure-functions-backend@workspace:plugins/azure-functions-backend"
+ resolution: "@backstage/plugin-azure-sites-backend@workspace:plugins/azure-sites-backend"
dependencies:
+ "@azure/arm-appservice": ^13.0.1
"@azure/arm-resourcegraph": ^4.2.1
"@azure/identity": ^2.1.0
"@backstage/backend-common": "workspace:^"
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
- "@backstage/plugin-azure-functions-common": "workspace:^"
+ "@backstage/plugin-azure-sites-common": "workspace:^"
"@types/express": ^4.17.6
"@types/supertest": ^2.0.8
express: ^4.17.1
@@ -4360,17 +4418,17 @@ __metadata:
languageName: unknown
linkType: soft
-"@backstage/plugin-azure-functions-common@workspace:^, @backstage/plugin-azure-functions-common@workspace:plugins/azure-functions-common":
+"@backstage/plugin-azure-sites-common@workspace:^, @backstage/plugin-azure-sites-common@workspace:plugins/azure-sites-common":
version: 0.0.0-use.local
- resolution: "@backstage/plugin-azure-functions-common@workspace:plugins/azure-functions-common"
+ resolution: "@backstage/plugin-azure-sites-common@workspace:plugins/azure-sites-common"
dependencies:
"@backstage/cli": "workspace:^"
languageName: unknown
linkType: soft
-"@backstage/plugin-azure-functions@workspace:^, @backstage/plugin-azure-functions@workspace:plugins/azure-functions":
+"@backstage/plugin-azure-sites@workspace:^, @backstage/plugin-azure-sites@workspace:plugins/azure-sites":
version: 0.0.0-use.local
- resolution: "@backstage/plugin-azure-functions@workspace:plugins/azure-functions"
+ resolution: "@backstage/plugin-azure-sites@workspace:plugins/azure-sites"
dependencies:
"@backstage/catalog-model": "workspace:^"
"@backstage/cli": "workspace:^"
@@ -4378,7 +4436,7 @@ __metadata:
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/dev-utils": "workspace:^"
- "@backstage/plugin-azure-functions-common": "workspace:^"
+ "@backstage/plugin-azure-sites-common": "workspace:^"
"@backstage/plugin-catalog-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
@@ -22099,7 +22157,7 @@ __metadata:
"@backstage/plugin-apache-airflow": "workspace:^"
"@backstage/plugin-api-docs": "workspace:^"
"@backstage/plugin-azure-devops": "workspace:^"
- "@backstage/plugin-azure-functions": "workspace:^"
+ "@backstage/plugin-azure-sites": "workspace:^"
"@backstage/plugin-badges": "workspace:^"
"@backstage/plugin-catalog-common": "workspace:^"
"@backstage/plugin-catalog-graph": "workspace:^"
@@ -22205,7 +22263,7 @@ __metadata:
"@backstage/plugin-auth-backend": "workspace:^"
"@backstage/plugin-auth-node": "workspace:^"
"@backstage/plugin-azure-devops-backend": "workspace:^"
- "@backstage/plugin-azure-functions-backend": "workspace:^"
+ "@backstage/plugin-azure-sites-backend": "workspace:^"
"@backstage/plugin-badges-backend": "workspace:^"
"@backstage/plugin-catalog-backend": "workspace:^"
"@backstage/plugin-code-coverage-backend": "workspace:^"