+plugin-azure-functions

+plugin-azure-functions-backend

Signed-off-by: Wesley <wpattison08@gmail.com>
This commit is contained in:
Wesley
2022-09-11 11:33:24 +02:00
parent f1e42b71e1
commit e12c08354e
38 changed files with 1975 additions and 10 deletions
+2
View File
@@ -52,6 +52,8 @@
"@backstage/plugin-azure-devops": "0.1.24",
"@backstage/plugin-azure-devops-backend": "0.3.14",
"@backstage/plugin-azure-devops-common": "0.2.4",
"@backstage/plugin-azure-functions": "0.1.0",
"@backstage/plugin-azure-functions-backend": "0.1.0",
"@backstage/plugin-badges": "0.2.32",
"@backstage/plugin-badges-backend": "0.1.29",
"@backstage/plugin-bazaar": "0.1.23",
@@ -0,0 +1,9 @@
---
title: Azure Functions
author: FRISS
authorUrl: https://friss.com
category: Infrastructure
description: View Azure Functions 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'
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="18.24 21.46 64 64" width="64" height="64"><path d="M81.837 53.85c.585-.585.487-1.7 0-2.307l-3.087-3.087-13.8-13.386c-.585-.585-1.495-.585-2.2 0-.585.585-.812 1.7 0 2.307l14.5 14.198c.585.585.585 1.7 0 2.307L62.44 68.568c-.585.585-.585 1.7 0 2.307.585.585 1.7.487 2.2 0l13.8-13.68zm-63.194 0c-.585-.585-.487-1.7 0-2.307l3.087-3.087 13.8-13.386c.585-.585 1.495-.585 2.2 0 .585.585.812 1.7 0 2.307l-14.2 14.2c-.585.585-.585 1.7 0 2.307l14.5 14.686c.585.585.585 1.7 0 2.307-.585.585-1.7.487-2.2 0L21.73 57.4z" fill="#3999c6"/><path d="M52.823 43.875L65.82 23.96H46.325L35.83 53.56l12.8.097-10.007 29.307L66.24 43.878z" fill="#fcd116"/><path d="M66.242 43.875h-13.42L65.82 23.96H55.617L44.83 48.554l12.8.097-19.007 34.3z" opacity=".3" fill="#ff8c00"/></svg>

After

Width:  |  Height:  |  Size: 801 B

+1
View File
@@ -19,6 +19,7 @@
"@backstage/plugin-apache-airflow": "^0.2.2-next.2",
"@backstage/plugin-api-docs": "^0.8.9-next.2",
"@backstage/plugin-azure-devops": "^0.2.0-next.2",
"@backstage/plugin-azure-functions": "^0.1.0",
"@backstage/plugin-badges": "^0.2.33-next.2",
"@backstage/plugin-catalog-common": "^1.0.6-next.0",
"@backstage/plugin-catalog-graph": "^0.2.21-next.1",
+1
View File
@@ -36,6 +36,7 @@
"@backstage/plugin-auth-backend": "^0.16.0-next.2",
"@backstage/plugin-auth-node": "^0.2.5-next.2",
"@backstage/plugin-azure-devops-backend": "^0.3.15-next.1",
"@backstage/plugin-azure-functions-backend": "^0.1.0",
"@backstage/plugin-badges-backend": "^0.1.30-next.0",
"@backstage/plugin-catalog-backend": "^1.4.0-next.2",
"@backstage/plugin-code-coverage-backend": "^0.2.2-next.1",
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+82
View File
@@ -0,0 +1,82 @@
# Azure Functions Backend
Simple plugin that proxies requests to the Azure Portal API through Azure SDK JavaScript libraries.
*Inspired by [roadie.io AWS Lamda plugin](https://roadie.io/backstage/plugins/aws-lambda/)*
## Setup
The following sections will help you get the Azure Functions Backend plugin setup and running.
### Configuration
The Azure Functions plugin requires the following YAML to be added to your app-config.yaml:
```yaml
azureFunctions:
domain:
tenantId:
clientId:
clientSecret:
allowedSubscriptions:
- id:
name:
```
Configuration Details:
- `domain` can be found by visiting the [Directories + Subscriptions settings page](https://portal.azure.com/#settings/directory). Alternatively you can inspect the [Azure home](https://portal.azure.com/#home) URL - `https://portal.azure.com/#@<Your_Domain>/`.
- `tenantId` can be found by visiting [Azure Directory Overview page](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade).
- `clientId` and `clientSecret` can be the same values you used for [Azure DevOps Backend](https://github.com/backstage/backstage/tree/master/plugins/azure-devops-backend) or [Azure Integration](https://backstage.io/docs/integrations/azure/org#app-registration) as long as this App Registration has permissions to read your function apps.
- `allowedSubscriptions` is an array of `id` and `name` that will be used to iterate over and look for the specified functions' app. `id` and `name` can be found the [Subscriptions page](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade).
### Integrating
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:
```sh
# From the Backstage root directory
cd packages/backend
yarn add @backstage/plugin-azure-functions-backend
```
2. Then we will create a new file named `packages/backend/src/plugins/azure-functions.ts`, and add the following to it:
```ts
import { createRouter, AzureWebManagementApi } from '@backstage/plugin-azure-functions-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
azureWebManagementApi: AzureWebManagementApi.fromConfig(env.config)
});
}
```
3. Next we wire this into the overall backend router, edit `packages/backend/src/index.ts`:
```ts
import azureFunctions from './plugins/azure-functions';
// Removed for clairty...
async function main() {
// ...
// Add this line under the other lines that follow the useHotMemoize pattern
const azureFunctionsEnv = useHotMemoize(module, () => createEnv('azureFunctions'));
// ...
// Insert this line under the other lines that add their routers to apiRouter in the same way
apiRouter.use('/azure-functions', await azureFunctions(azureFunctionsEnv));
}
```
4. Now run `yarn start-backend` from the repo root.
5. Finally, open `http://localhost:7007/api/azure-functions/health` in a browser, it should return `{"status":"ok"}`.
@@ -0,0 +1,46 @@
{
"name": "@backstage/plugin-azure-functions-backend",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "backend-plugin"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@azure/arm-appservice": "^13.0.0",
"@azure/identity": "^2.1.0",
"@backstage/backend-common": "^0.15.0",
"@backstage/config": "^1.0.1",
"@types/express": "*",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"node-fetch": "^2.6.7",
"winston": "^3.2.1",
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.18.0",
"@types/supertest": "^2.0.8",
"msw": "^0.44.0",
"supertest": "^4.0.2"
},
"files": [
"dist"
]
}
+19
View File
@@ -0,0 +1,19 @@
export interface Config {
azureFunctions: {
/** @visibility backend */
tenantId: string;
/** @visibility backend */
clientId: string;
/** @visibility secret */
clientSecret: string;
/** @visibility backend */
domain: string;
/** @visibility backend */
allowedSubscriptions: [{
/** @visibility backend */
name: string;
/** @visibility backend */
id: string;
}]
};
}
@@ -0,0 +1,85 @@
/*
* 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 { Config } from '@backstage/config';
import { ClientSecretCredential } from '@azure/identity';
import { WebSiteManagementClient } from '@azure/arm-appservice';
import { AzureFunctionsAllowedSubscriptionsConfig, FunctionsData } from './types';
export class AzureFunctionsConfig {
constructor(public readonly tenantId: string, public readonly clientId: string, public readonly clientSecret: string, public readonly domain: string, public readonly allowedSubscriptions: AzureFunctionsAllowedSubscriptionsConfig[]) { }
static fromConfig(config: Config): AzureFunctionsConfig {
const azfConfig = config.getConfig('azureFunctions');
return new AzureFunctionsConfig(
azfConfig.getString('tenantId'),
azfConfig.getString('clientId'),
azfConfig.getString('clientSecret'),
azfConfig.getString('domain'),
azfConfig.getConfigArray('allowedSubscriptions').map<AzureFunctionsAllowedSubscriptionsConfig>(as => ({ id: as.getString('id'), name: as.getString('name') }))
)
}
}
export class AzureWebManagementApi {
private readonly baseHref = (domain: string) => `https://portal.azure.com/#@${domain}/resource`;
private readonly clients: WebSiteManagementClient[] = [];
constructor(private readonly config: AzureFunctionsConfig) {
const creds = new ClientSecretCredential(config.tenantId, config.clientId, config.clientSecret);
for (const subscription of config.allowedSubscriptions) {
if (!this.clients.some(c => c.subscriptionId == subscription.id)) {
this.clients.push(new WebSiteManagementClient(creds, subscription.id));
}
}
}
static fromConfig(config: Config): AzureWebManagementApi {
return new AzureWebManagementApi(AzureFunctionsConfig.fromConfig(config));
}
async list({
functionName,
}: {
functionName: string;
}): Promise<FunctionsData[]> {
const results = [];
for (const client of this.clients) {
try {
for await (const webApp of client.webApps.list()) {
if (!webApp.name!.startsWith(functionName)) {
continue;
}
const v = webApp!;
results.push({
href: `${this.baseHref(this.config.domain)}${v.id!}`,
logstreamHref: `${this.baseHref(this.config.domain)}${v.id!}/logStream`,
functionName: v.name!,
location: v.location!,
lastModifiedDate: v.lastModifiedTimeUtc!,
usageState: v.usageState!,
state: v.state!,
containerSize: v.containerSize!
})
}
} catch (ex) {
console.log(ex);
}
}
return results;
}
}
@@ -0,0 +1,18 @@
/*
* 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.
*/
export { AzureWebManagementApi, AzureFunctionsConfig } from './AzureWebManagementApi'
export * from './types'
@@ -0,0 +1,15 @@
export interface AzureFunctionsAllowedSubscriptionsConfig {
name: string;
id: string;
}
export type FunctionsData = {
href: string;
logstreamHref: string;
functionName: string;
location: string;
state: string;
usageState: string;
containerSize: number;
lastModifiedDate: Date;
};
@@ -0,0 +1,18 @@
/*
* 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.
*/
export * from './service/router';
export * from './api';
@@ -0,0 +1,33 @@
/*
* 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 { getRootLogger } from '@backstage/backend-common';
import yn from 'yn';
import { startStandaloneServer } from './service/standaloneServer';
const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007;
const enableCors = yn(process.env.PLUGIN_CORS, { default: false });
const logger = getRootLogger();
startStandaloneServer({ port, enableCors, logger }).catch(err => {
logger.error(err);
process.exit(1);
});
process.on('SIGINT', () => {
logger.info('CTRL+C pressed; exiting.');
process.exit(0);
});
@@ -0,0 +1,46 @@
/*
* 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 { errorHandler } from '@backstage/backend-common';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { AzureWebManagementApi } from '../api';
export interface RouterOptions {
logger: Logger;
azureWebManagementApi: AzureWebManagementApi;
}
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { logger, azureWebManagementApi } = options;
const router = Router();
router.use(express.json());
router.get('/health', (_, response) => {
logger.info('PONG!');
response.send({ status: 'ok' });
});
router.get('/get', async (request, response) => {
response.json(await azureWebManagementApi.list({ functionName: request.query['functionName']!.toString() }))
});
router.use(errorHandler());
return router;
}
@@ -0,0 +1,53 @@
/*
* 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 { createServiceBuilder, loadBackendConfig } from '@backstage/backend-common';
import { Server } from 'http';
import { Logger } from 'winston';
import { AzureWebManagementApi } from '../api';
import { createRouter } from './router';
export interface ServerOptions {
port: number;
enableCors: boolean;
logger: Logger;
}
export async function startStandaloneServer(
options: ServerOptions,
): Promise<Server> {
const logger = options.logger.child({ service: 'azure-functions-backend' });
const config = await loadBackendConfig({ logger, argv: process.argv })
logger.debug('Starting application server...');
const router = await createRouter({
logger,
azureWebManagementApi: AzureWebManagementApi.fromConfig(config)
});
let service = createServiceBuilder(module)
.setPort(options.port)
.addRouter('/azure-functions-backend', router);
if (options.enableCors) {
service = service.enableCors({ origin: 'http://localhost:3000' });
}
return await service.start().catch(err => {
logger.error(err);
process.exit(1);
});
}
module.hot?.accept();
@@ -0,0 +1,17 @@
/*
* 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.
*/
export {};
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+74
View File
@@ -0,0 +1,74 @@
# Azure Functions Plugin
![preview of Azure Functions table](docs/functions-table.png)
*Inspired by [roadie.io AWS Lamda plugin](https://roadie.io/backstage/plugins/aws-lambda/)*
## Features
- Azure Functions overview table
## Plugin Setup
The following sections will help you get the Azure Functions plugin setup and running
### Azure Functions Backend
You need to set up the Azure Functions backend plugin before you move forward with any of these steps if you haven't already.
### Entity Annotation
To be able to use the Azure Functions plugin you need to add the following annotation to any entities you want to use it with:
```yaml
portal.azure.com/functions-name: <function-name>
```
`<function-name>` can be an exact or partial name for the functions' app. When using a partial name, it's important that the value here matches the **start** of the functions name.
Example of Partial Matching:
Let's say you have a number of functions apps, spread out over different regions (and possibly different subscriptions), and they follow a naming convention:
```
func-testapp-eu
func-testapp-ca
func-testapp-us
```
The annotation you will use to have the three functions' app appear in the overview table would look like this:
```yaml
portal.azure.com/functions-name: func-testapp
```
### Install the component
1. Install the plugin in the `packages/app` directory
```sh
yarn add @backstage/plugin-azure-functions
```
2. Add widget component to your Backstage instance:
```ts
// In packages/app/src/components/catalog/EntityPage.tsx
import { EntityAzureFunctionsOverviewCard, isAzureFunctionsAvailable } from '@backstage/plugin-azure-functions';
...
const serviceEntityPage = (
<EntityLayout>
//...
<EntityLayout.Route if={e => Boolean(isAzureFunctionsAvailable(e))} path="/azure-functions" title="Azure Functions">
<EntityAzureFunctionsOverviewCard />
</EntityLayout.Route>
//...
</EntityLayout>
);
```
## Roadmap
- [ ] Metrics
+6
View File
@@ -0,0 +1,6 @@
import { createDevApp } from '@backstage/dev-utils';
import { azureFunctionsPlugin } from '../src/plugin';
createDevApp()
.registerPlugin(azureFunctionsPlugin)
.render();
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+53
View File
@@ -0,0 +1,53 @@
{
"name": "@backstage/plugin-azure-functions",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "^0.11.0",
"@backstage/core-plugin-api": "^1.0.5",
"@backstage/theme": "^0.2.16",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.18.0",
"@backstage/core-app-api": "^1.0.5",
"@backstage/dev-utils": "^1.0.4",
"@backstage/test-utils": "^1.1.3",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"@types/jest": "*",
"@types/node": "*",
"cross-fetch": "^3.1.5",
"msw": "^0.44.0"
},
"files": [
"dist"
]
}
@@ -0,0 +1,30 @@
/*
* 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 { createApiRef } from '@backstage/core-plugin-api';
import { FunctionsData } from './types';
export const azureFunctionsApiRef = createApiRef<AzureFunctionsApi>({
id: 'plugin.azurefunctions.service',
});
export type AzureFunctionsApi = {
list: ({
functionName,
}: {
functionName: string;
}) => Promise<FunctionsData[]>;
};
@@ -0,0 +1,53 @@
/*
* 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 { AzureFunctionsApi } from './AzureFunctionsApi';
import { FunctionsData } from './types';
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
export class AzureFunctionsBackendClient implements AzureFunctionsApi {
private readonly identityApi: IdentityApi;
private readonly discoveryApi: DiscoveryApi;
constructor(options: {
discoveryApi: DiscoveryApi;
identityApi: IdentityApi;
}) {
this.discoveryApi = options.discoveryApi;
this.identityApi = options.identityApi;
}
async list({
functionName,
}: {
functionName: string;
}): Promise<FunctionsData[]> {
try {
const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/get?functionName=${functionName}`;
const { token: idToken } = await this.identityApi.getCredentials();
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(idToken && { Authorization: `Bearer ${idToken}` }),
}
});
return await response.json();
} catch (e: any) {
throw new Error('MissingAzureBackendException')
}
}
}
+19
View File
@@ -0,0 +1,19 @@
/*
* 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.
*/
export * from './AzureFunctionsApi';
export * from './AzureFunctionsBackendClient';
export * from './types';
+10
View File
@@ -0,0 +1,10 @@
export type FunctionsData = {
href: string;
logstreamHref: string;
functionName: string;
location: string;
state: string;
usageState: string;
containerSize: number;
lastModifiedDate: Date;
};
@@ -0,0 +1,91 @@
/*
* 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 { render } from '@testing-library/react';
import {
AnyApiRef,
configApiRef,
errorApiRef,
identityApiRef,
} from '@backstage/core-plugin-api';
import { rest } from 'msw';
import {
setupRequestMockHandlers,
TestApiProvider,
} from '@backstage/test-utils';
import { EntityProvider } from '@backstage/plugin-catalog-react';
import { setupServer } from 'msw/node';
import {
entityMock,
functionResponseMock,
} from '../../mocks/mocks';
import { azureFunctionsApiRef, AzureFunctionsOverviewWidget } from '../..';
const errorApiMock = { post: jest.fn(), error$: jest.fn() };
const identityApiMock = (getCredentials: any) => ({
signOut: jest.fn(),
getProfileInfo: jest.fn(),
getBackstageIdentity: jest.fn(),
getCredentials,
});
const azureFunctionsApiMock = {};
const config = {
getString: (_: string) => 'https://test-url',
};
const apis: [AnyApiRef, Partial<unknown>][] = [
[errorApiRef, errorApiMock],
[configApiRef, config],
[azureFunctionsApiRef, azureFunctionsApiMock],
[identityApiRef, identityApiMock],
];
describe('AzureFunctionsOverviewWidget', () => {
const worker = setupServer();
setupRequestMockHandlers(worker);
beforeEach(() => {
worker.use(
rest.get(
'https://portal.azure.com/#@test/resource/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mock_rg/providers/Microsoft.Web/sites/func-mock',
(_, res, ctx) => res(ctx.json(functionResponseMock)),
),
);
});
it('should display an overview table with the data from the requests', async () => {
const rendered = render(
<TestApiProvider apis={apis}>
<EntityProvider entity={entityMock}>
<AzureFunctionsOverviewWidget />
</EntityProvider>
</TestApiProvider>,
);
expect(
await rendered.findByText(functionResponseMock.name),
).toBeInTheDocument();
expect(
await rendered.findByText(functionResponseMock.properties.state),
).toBeInTheDocument();
expect(
await rendered.findByText(
new Date(functionResponseMock.properties.lastModifiedTimeUtc).toUTCString(),
),
).toBeInTheDocument();
});
});
@@ -0,0 +1,154 @@
/*
* 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 { Entity } from '@backstage/catalog-model';
import { useFunctions } from '../../hooks/useFunctions';
import { FunctionsData } from '../../api/types';
import {
AZURE_FUNCTIONS_ANNOTATION,
useServiceEntityAnnotations,
} from '../../hooks/useServiceEntityAnnotations';
import { MissingAnnotationEmptyState, Table, TableColumn } from '@backstage/core-components';
import { FlashOn } from '@material-ui/icons'
import ErrorBoundary from '../ErrorBoundary';
import { useEntity } from '@backstage/plugin-catalog-react';
type States = 'Waiting' | 'Running' | 'Paused' | 'Failed';
const State = ({ value }: { value: States }) => {
const colorMap = {
Waiting: '#dcbc21',
Running: 'green',
Paused: 'black',
Failed: 'red',
};
return (
<Box display="flex" alignItems="center">
<span
style={{
display: 'block',
width: '8px',
height: '8px',
borderRadius: '50%',
backgroundColor: colorMap[value],
marginRight: '5px',
}}
/>
{value}
</Box>
);
};
type FunctionTableProps = {
data: FunctionsData[];
loading: boolean;
};
const DEFAULT_COLUMNS: TableColumn<FunctionsData>[] = [
{
title: 'name',
highlight: true,
render: (func: FunctionsData) => {
return (<Link href={func.href} target="_blank">{func.functionName}</Link>)
},
},
{
title: 'location',
render: (func: FunctionsData) => func.location ?? 'unknown',
},
{
title: 'status',
render: (func: FunctionsData) => <State value={func.state as States} />,
},
{
title: 'last modified',
render: (func: FunctionsData) => new Date(func.lastModifiedDate).toUTCString(),
},
{
title: 'logs',
align: 'right',
render: (func: FunctionsData) => {
return (<Link href={func.logstreamHref} target="_blank">View Logs</Link>)
},
},
];
const OverviewComponent = ({ data, loading }: FunctionTableProps) => {
const columns: TableColumn<FunctionsData>[] = [...DEFAULT_COLUMNS];
const tableStyle = {
minWidth: '0',
width: '100%',
};
return (
<Card style={tableStyle}>
<Table
title={
<Box display="flex" alignItems="center">
<FlashOn style={{ fontSize: 30 }} />
<Box mr={1} />
Azure Functions
</Box>
}
options={{ paging: true, search: false, pageSize: 10 }}
data={data}
emptyContent={
<LinearProgress />
}
isLoading={loading}
columns={columns}
/>
</Card>
);
};
export const isAzureFunctionsAvailable = (entity: Entity) =>
entity?.metadata.annotations?.[AZURE_FUNCTIONS_ANNOTATION];
const AzureFunctionsOverview = ({ entity }: { entity: Entity }) => {
const { functionsName } = useServiceEntityAnnotations(entity);
const [functionsData] = useFunctions({
functionsName
});
return (
<>{<OverviewComponent data={functionsData.data ?? []} loading={functionsData.loading} />}</>
);
};
export const AzureFunctionsOverviewWidget = () => {
const { entity } = useEntity();
if (!isAzureFunctionsAvailable(entity)) {
return (<MissingAnnotationEmptyState annotation={AZURE_FUNCTIONS_ANNOTATION} />);
}
return (
<ErrorBoundary>
<AzureFunctionsOverview entity={entity} />
</ErrorBoundary>
)
};
@@ -0,0 +1,58 @@
/*
* 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, { Component } from 'react';
import Alert from '@material-ui/lab/Alert';
interface Props {}
interface MyProps {}
interface MyState {
hasError: boolean;
}
export default class ErrorBoundary extends Component<MyProps, MyState> {
static getDerivedStateFromError() {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<Alert severity="error">
Something went wrong. Please make sure that you installed:
<strong>
<a
href="https://github.com/backstage/backstage/tree/master/plugins/azure-functions-backend"
target="_blank"
rel="noopener noreferrer"
>
@backstage/plugin-azure-functions-backend
</a>
</strong>
</Alert>
);
}
return this.props.children;
}
}
@@ -0,0 +1,71 @@
/*
* 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 { useAsyncRetry } from 'react-use';
import {
useApi,
errorApiRef,
} from '@backstage/core-plugin-api';
import { FunctionsData } from '../api/types';
import { azureFunctionsApiRef } from '../api';
import { useCallback } from 'react';
export function useFunctions({
functionsName
}: {
functionsName: string;
}) {
const azureFunctionsApi = useApi(azureFunctionsApiRef);
const errorApi = useApi(errorApiRef);
const list = useCallback(
async () => {
return await azureFunctionsApi.list({
functionName: functionsName,
});
},
[functionsName], // eslint-disable-line react-hooks/exhaustive-deps
);
const {
loading,
value: data,
error,
retry,
} = useAsyncRetry<FunctionsData[] | null>(async () => {
try {
const azureFunction = await list();
return azureFunction;
} catch (e) {
if (e instanceof Error) {
if (e?.message === 'MissingAzureBackendException') {
errorApi.post(new Error('Please add azure-functions-backend plugin'));
return null;
}
errorApi.post(e);
}
return null;
}
}, []);
return [
{
loading,
data,
error,
retry,
},
] as const;
}
@@ -0,0 +1,30 @@
/*
* 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 { Entity } from '@backstage/catalog-model';
export const AZURE_FUNCTIONS_ANNOTATION = 'portal.azure.com/functions-name';
export const useServiceEntityAnnotations = (entity: Entity) => {
const functionsName =
entity?.metadata.annotations?.[AZURE_FUNCTIONS_ANNOTATION] ?? '';
const projectName =
entity?.metadata.annotations?.['dev.azure.com/project-repo'] ?? '';
return {
projectName,
functionsName
};
};
+3
View File
@@ -0,0 +1,3 @@
export * from './plugin';
export * from './api';
export * from './components/AzureFunctionsOverview/AzureFunctionsOverview';
+243
View File
@@ -0,0 +1,243 @@
export const entityMock = {
metadata: {
namespace: 'default',
annotations: {
'portal.azure.com/functions-name': 'func-mock',
},
name: 'sample-azure-function-service',
description:
'A service for testing Backstage functionality.',
uid: 'c009b513-d053-4b3f-9429-8433a145e943',
},
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
spec: {
type: 'service',
owner: 'dev_group@example.com',
lifecycle: 'experimental',
},
};
// https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.Web/sites/{{functionsName}}?api-version=2022-03-01
export const functionResponseMock = {
id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg_mock/providers/Microsoft.Web/sites/func-mock",
name: "func-mock",
type: "Microsoft.Web/sites",
kind: "functionapp",
location: "West Europe",
tags: {},
properties: {
name: "func-mock",
state: "Running",
hostNames: [
"func-mock.azurewebsites.net"
],
webSpace: "rg_mock-WestEuropewebspace",
selfLink: "https://mockurl.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/rg_mock-WestEuropewebspace/sites/func-mock",
repositorySiteName: "func-mock",
owner: null,
usageState: "Normal",
enabled: true,
adminEnabled: true,
enabledHostNames: [
"func-mock.azurewebsites.net",
"func-mock.scm.azurewebsites.net"
],
siteProperties: {
metadata: null,
properties: [
{
name: "LinuxFxVersion",
value: ""
},
{
name: "WindowsFxVersion",
value: null
}
],
appSettings: null
},
availabilityState: "Normal",
sslCertificates: null,
csrs: [],
cers: null,
siteMode: null,
hostNameSslStates: [
{
name: "func-mock.azurewebsites.net",
sslState: "Disabled",
ipBasedSslResult: null,
virtualIP: null,
thumbprint: null,
toUpdate: null,
toUpdateIpBasedSsl: null,
ipBasedSslState: "NotConfigured",
hostType: "Standard"
},
{
name: "func-mock.scm.azurewebsites.net",
sslState: "Disabled",
ipBasedSslResult: null,
virtualIP: null,
thumbprint: null,
toUpdate: null,
toUpdateIpBasedSsl: null,
ipBasedSslState: "NotConfigured",
hostType: "Repository"
}
],
computeMode: null,
serverFarm: null,
serverFarmId: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg_mock/providers/Microsoft.Web/serverfarms/plan-mock",
reserved: false,
isXenon: false,
hyperV: false,
lastModifiedTimeUtc: "2022-09-02T11:09:58.9033333",
storageRecoveryDefaultState: "Running",
contentAvailabilityState: "Normal",
runtimeAvailabilityState: "Normal",
secretsCollection: [],
vnetRouteAllEnabled: false,
containerAllocationSubnet: null,
useContainerLocalhostBindings: null,
vnetImagePullEnabled: false,
vnetContentShareEnabled: false,
siteConfig: {
numberOfWorkers: 1,
defaultDocuments: null,
netFrameworkVersion: null,
phpVersion: null,
pythonVersion: null,
nodeVersion: null,
powerShellVersion: null,
linuxFxVersion: "",
windowsFxVersion: null,
requestTracingEnabled: null,
remoteDebuggingEnabled: null,
remoteDebuggingVersion: null,
httpLoggingEnabled: null,
azureMonitorLogCategories: null,
acrUseManagedIdentityCreds: false,
acrUserManagedIdentityID: null,
logsDirectorySizeLimit: null,
detailedErrorLoggingEnabled: null,
publishingUsername: null,
publishingPassword: null,
appSettings: null,
metadata: null,
connectionStrings: null,
machineKey: null,
handlerMappings: null,
documentRoot: null,
scmType: null,
use32BitWorkerProcess: null,
webSocketsEnabled: null,
alwaysOn: true,
javaVersion: null,
javaContainer: null,
javaContainerVersion: null,
appCommandLine: null,
managedPipelineMode: null,
virtualApplications: null,
winAuthAdminState: null,
winAuthTenantState: null,
customAppPoolIdentityAdminState: null,
customAppPoolIdentityTenantState: null,
runtimeADUser: null,
runtimeADUserPassword: null,
loadBalancing: null,
routingRules: null,
experiments: null,
limits: null,
autoHealEnabled: null,
autoHealRules: null,
tracingOptions: null,
vnetName: null,
vnetRouteAllEnabled: null,
vnetPrivatePortsCount: null,
publicNetworkAccess: null,
cors: null,
push: null,
apiDefinition: null,
apiManagementConfig: null,
autoSwapSlotName: null,
localMySqlEnabled: null,
managedServiceIdentityId: null,
xManagedServiceIdentityId: null,
keyVaultReferenceIdentity: null,
ipSecurityRestrictions: null,
ipSecurityRestrictionsDefaultAction: null,
scmIpSecurityRestrictions: null,
scmIpSecurityRestrictionsDefaultAction: null,
scmIpSecurityRestrictionsUseMain: null,
http20Enabled: false,
minTlsVersion: null,
minTlsCipherSuite: null,
supportedTlsCipherSuites: null,
scmMinTlsVersion: null,
ftpsState: null,
preWarmedInstanceCount: null,
functionAppScaleLimit: 0,
elasticWebAppScaleLimit: null,
healthCheckPath: null,
fileChangeAuditEnabled: null,
functionsRuntimeScaleMonitoringEnabled: null,
websiteTimeZone: null,
minimumElasticInstanceCount: 1,
azureStorageAccounts: null,
http20ProxyFlag: null,
sitePort: null,
antivirusScanEnabled: null,
storageType: null
},
deploymentId: "func-mock",
slotName: null,
trafficManagerHostNames: null,
sku: "Standard",
scmSiteAlsoStopped: false,
targetSwapSlot: null,
hostingEnvironment: null,
hostingEnvironmentProfile: null,
clientAffinityEnabled: false,
clientCertEnabled: false,
clientCertMode: "Required",
clientCertExclusionPaths: null,
hostNamesDisabled: false,
domainVerificationIdentifiers: null,
customDomainVerificationId: "",
kind: "functionapp",
inboundIpAddress: "",
possibleInboundIpAddresses: "",
ftpUsername: "func-mock\\$func-mock",
ftpsHostName: "ftps://mockurl.ftp.azurewebsites.windows.net/site/wwwroot",
outboundIpAddresses: "",
possibleOutboundIpAddresses: "",
containerSize: 1536,
dailyMemoryTimeQuota: 0,
suspendedTill: null,
siteDisabledReason: 0,
functionExecutionUnitsCache: null,
maxNumberOfWorkers: null,
homeStamp: "mockurl",
cloningInfo: null,
hostingEnvironmentId: null,
tags: {},
resourceGroup: "rg_mock",
defaultHostName: "func-mock.azurewebsites.net",
slotSwapStatus: null,
httpsOnly: false,
redundancyMode: "None",
inProgressOperationId: null,
geoDistributions: null,
privateEndpointConnections: [],
publicNetworkAccess: null,
buildVersion: null,
targetBuildVersion: null,
migrationState: null,
eligibleLogCategories: "FunctionAppLogs",
storageAccountRequired: false,
virtualNetworkSubnetId: null,
keyVaultReferenceIdentity: "SystemAssigned",
privateLinkIdentifiers: null
}
};
@@ -0,0 +1,7 @@
import { azureFunctionsPlugin } from './plugin';
describe('azure-functions', () => {
it('should export plugin', () => {
expect(azureFunctionsPlugin).toBeDefined();
});
});
+43
View File
@@ -0,0 +1,43 @@
import {
createApiFactory,
createComponentExtension,
createPlugin,
createRouteRef,
discoveryApiRef,
identityApiRef
} from '@backstage/core-plugin-api';
import { azureFunctionsApiRef, AzureFunctionsBackendClient } from './api';
export const entityContentRouteRef = createRouteRef({
id: 'Azure Functions Entity Content',
});
export const azureFunctionsPlugin = createPlugin({
id: 'azure-functions',
apis: [
createApiFactory({
api: azureFunctionsApiRef,
deps: {
discoveryApi: discoveryApiRef,
identityApi: identityApiRef,
},
factory: ({ discoveryApi, identityApi }) =>
new AzureFunctionsBackendClient({ discoveryApi, identityApi }),
})
],
routes: {
entityContent: entityContentRouteRef,
},
});
export const EntityAzureFunctionsOverviewCard = azureFunctionsPlugin.provide(
createComponentExtension({
name: 'EntityAzureFunctionsOverviewCard',
component: {
lazy: () =>
import('./components/AzureFunctionsOverview/AzureFunctionsOverview').then(
m => m.AzureFunctionsOverviewWidget,
),
},
}),
);
+5
View File
@@ -0,0 +1,5 @@
import { createRouteRef } from '@backstage/core-plugin-api';
export const rootRouteRef = createRouteRef({
id: 'azure-functions',
});
@@ -0,0 +1,2 @@
import '@testing-library/jest-dom';
import 'cross-fetch/polyfill';
+575 -10
View File
@@ -337,6 +337,21 @@ __metadata:
languageName: node
linkType: hard
"@azure/arm-appservice@npm:^13.0.0":
version: 13.0.0
resolution: "@azure/arm-appservice@npm:13.0.0"
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: 0a21b4d462ac0ba5b46561ef90559bfc2b1f5a2a8ea551ae13f95205b4aafee5b5bd5d03f52fced7687af8d5f37d5451b48b4edc8acabf977b534af1b2790dcd
languageName: node
linkType: hard
"@azure/core-asynciterator-polyfill@npm:^1.0.0":
version: 1.0.0
resolution: "@azure/core-asynciterator-polyfill@npm:1.0.0"
@@ -354,6 +369,16 @@ __metadata:
languageName: node
linkType: hard
"@azure/core-auth@npm:^1.4.0":
version: 1.4.0
resolution: "@azure/core-auth@npm:1.4.0"
dependencies:
"@azure/abort-controller": ^1.0.0
tslib: ^2.2.0
checksum: 1c76c296fe911ad39fc780b033c25a92c41c5a15f011b816d42c13584f627a4dd153dfb4334900ec93eb5b006e14bdda37e8412a7697c5a9636a0abaccffad39
languageName: node
linkType: hard
"@azure/core-client@npm:^1.4.0":
version: 1.5.0
resolution: "@azure/core-client@npm:1.5.0"
@@ -369,6 +394,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"
@@ -413,6 +453,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"
@@ -430,6 +479,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"
@@ -440,7 +507,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:
@@ -2839,6 +2906,24 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/app-defaults@npm:^1.0.5":
version: 1.0.5
resolution: "@backstage/app-defaults@npm:1.0.5"
dependencies:
"@backstage/core-app-api": ^1.0.5
"@backstage/core-components": ^0.11.0
"@backstage/core-plugin-api": ^1.0.5
"@backstage/plugin-permission-react": ^0.4.4
"@backstage/theme": ^0.2.16
"@material-ui/core": ^4.12.2
"@material-ui/icons": ^4.9.1
react-router-dom: 6.0.0-beta.0
peerDependencies:
react: ^16.13.1 || ^17.0.0
checksum: c070eaa21dbbe2d0cb46d0c6fbcce623b305d2a715a0b126166b227d0f669c1fa0375573e21ce57c468367e5a3782764fb3b7738c71e77a9d5bab70ea7e3e280
languageName: node
linkType: hard
"@backstage/backend-app-api@^0.2.1-next.0, @backstage/backend-app-api@^0.2.1-next.1, @backstage/backend-app-api@workspace:packages/backend-app-api":
version: 0.0.0-use.local
resolution: "@backstage/backend-app-api@workspace:packages/backend-app-api"
@@ -2941,6 +3026,66 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/backend-common@npm:^0.15.0":
version: 0.15.0
resolution: "@backstage/backend-common@npm:0.15.0"
dependencies:
"@backstage/cli-common": ^0.1.9
"@backstage/config": ^1.0.1
"@backstage/config-loader": ^1.1.3
"@backstage/errors": ^1.1.0
"@backstage/integration": ^1.3.0
"@backstage/types": ^1.0.0
"@google-cloud/storage": ^6.0.0
"@keyv/redis": ^2.2.3
"@manypkg/get-packages": ^1.1.3
"@octokit/rest": ^19.0.3
"@types/cors": ^2.8.6
"@types/dockerode": ^3.3.0
"@types/express": ^4.17.6
"@types/luxon": ^3.0.0
"@types/webpack-env": ^1.15.2
archiver: ^5.0.2
aws-sdk: ^2.840.0
base64-stream: ^1.0.0
compression: ^1.7.4
concat-stream: ^2.0.0
cors: ^2.8.5
dockerode: ^3.3.1
express: ^4.17.1
express-promise-router: ^4.1.0
fs-extra: 10.1.0
git-url-parse: ^12.0.0
helmet: ^5.0.2
isomorphic-git: ^1.8.0
jose: ^4.6.0
keyv: ^4.0.3
keyv-memcache: ^1.2.5
knex: ^2.0.0
lodash: ^4.17.21
logform: ^2.3.2
luxon: ^3.0.0
minimatch: ^5.0.0
minimist: ^1.2.5
morgan: ^1.10.0
node-abort-controller: ^3.0.1
node-fetch: ^2.6.7
raw-body: ^2.4.1
selfsigned: ^2.0.0
stoppable: ^1.1.0
tar: ^6.1.2
winston: ^3.2.1
yauzl: ^2.10.0
yn: ^4.0.0
peerDependencies:
pg-connection-string: ^2.3.0
peerDependenciesMeta:
pg-connection-string:
optional: true
checksum: 331e774247f79d4a041069b604a289694a920b43e579e5b4437480460ddf027a6e7106e1acc0e09b9d6c6414a2324eaea5d5d40972055e138409cbe05c2f66bd
languageName: node
linkType: hard
"@backstage/backend-defaults@^0.1.1-next.0, @backstage/backend-defaults@workspace:packages/backend-defaults":
version: 0.0.0-use.local
resolution: "@backstage/backend-defaults@workspace:packages/backend-defaults"
@@ -3200,6 +3345,114 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/cli@npm:^0.18.0":
version: 0.18.1
resolution: "@backstage/cli@npm:0.18.1"
dependencies:
"@backstage/cli-common": ^0.1.9
"@backstage/config": ^1.0.1
"@backstage/config-loader": ^1.1.3
"@backstage/errors": ^1.1.0
"@backstage/release-manifests": ^0.0.5
"@backstage/types": ^1.0.0
"@hot-loader/react-dom-v16": "npm:@hot-loader/react-dom@^16.0.2"
"@hot-loader/react-dom-v17": "npm:@hot-loader/react-dom@^17.0.2"
"@manypkg/get-packages": ^1.1.3
"@octokit/request": ^6.0.0
"@rollup/plugin-commonjs": ^22.0.0
"@rollup/plugin-json": ^4.1.0
"@rollup/plugin-node-resolve": ^13.0.6
"@rollup/plugin-yaml": ^3.1.0
"@spotify/eslint-config-base": ^14.0.0
"@spotify/eslint-config-react": ^14.0.0
"@spotify/eslint-config-typescript": ^14.0.0
"@sucrase/jest-plugin": ^2.1.1
"@sucrase/webpack-loader": ^2.0.0
"@svgr/plugin-jsx": 6.3.x
"@svgr/plugin-svgo": 6.3.x
"@svgr/rollup": 6.3.x
"@svgr/webpack": 6.3.x
"@types/webpack-env": ^1.15.2
"@typescript-eslint/eslint-plugin": ^5.9.0
"@typescript-eslint/parser": ^5.9.0
"@yarnpkg/lockfile": ^1.1.0
"@yarnpkg/parsers": ^3.0.0-rc.4
bfj: ^7.0.2
buffer: ^6.0.3
chalk: ^4.0.0
chokidar: ^3.3.1
commander: ^9.1.0
css-loader: ^6.5.1
diff: ^5.0.0
esbuild: ^0.14.10
esbuild-loader: ^2.18.0
eslint: ^8.6.0
eslint-config-prettier: ^8.3.0
eslint-formatter-friendly: ^7.0.0
eslint-plugin-deprecation: ^1.3.2
eslint-plugin-import: ^2.25.4
eslint-plugin-jest: ^26.1.2
eslint-plugin-jsx-a11y: ^6.5.1
eslint-plugin-monorepo: ^0.3.2
eslint-plugin-react: ^7.28.0
eslint-plugin-react-hooks: ^4.3.0
eslint-webpack-plugin: ^3.1.1
express: ^4.17.1
fork-ts-checker-webpack-plugin: ^7.0.0-alpha.8
fs-extra: 10.1.0
glob: ^7.1.7
global-agent: ^3.0.0
handlebars: ^4.7.3
html-webpack-plugin: ^5.3.1
inquirer: ^8.2.0
jest: ^27.5.1
jest-css-modules: ^2.1.0
jest-runtime: ^27.5.1
jest-transform-yaml: ^1.0.0
json-schema: ^0.4.0
lodash: ^4.17.21
mini-css-extract-plugin: ^2.4.2
minimatch: 5.1.0
node-fetch: ^2.6.7
node-libs-browser: ^2.2.1
npm-packlist: ^5.0.0
ora: ^5.3.0
postcss: ^8.1.0
process: ^0.11.10
react-dev-utils: ^12.0.0-next.60
react-hot-loader: ^4.13.0
recursive-readdir: ^2.2.2
replace-in-file: ^6.0.0
rollup: ^2.60.2
rollup-plugin-dts: ^4.0.1
rollup-plugin-esbuild: ^4.7.2
rollup-plugin-postcss: ^4.0.0
rollup-pluginutils: ^2.8.2
run-script-webpack-plugin: ^0.1.0
semver: ^7.3.2
style-loader: ^3.3.1
sucrase: ^3.20.2
tar: ^6.1.2
terser-webpack-plugin: ^5.1.3
util: ^0.12.3
webpack: ^5.66.0
webpack-dev-server: ^4.7.3
webpack-node-externals: ^3.0.0
yaml: ^2.0.0
yml-loader: ^2.1.0
yn: ^4.0.0
zod: ^3.11.6
peerDependencies:
"@microsoft/api-extractor": ^7.21.2
peerDependenciesMeta:
"@microsoft/api-extractor":
optional: true
bin:
backstage-cli: bin/backstage-cli
checksum: e3e0a14a87be6add3adcc9d3a3f6d34064315e4a420a2ab97401f5074a0cce96c80908ffd9687db281581b5a459c8900b2683ae686ade38b24df22535296cc3f
languageName: node
linkType: hard
"@backstage/codemods@workspace:*, @backstage/codemods@workspace:packages/codemods":
version: 0.0.0-use.local
resolution: "@backstage/codemods@workspace:packages/codemods"
@@ -3247,6 +3500,29 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/config-loader@npm:^1.1.3":
version: 1.1.3
resolution: "@backstage/config-loader@npm:1.1.3"
dependencies:
"@backstage/cli-common": ^0.1.9
"@backstage/config": ^1.0.1
"@backstage/errors": ^1.1.0
"@backstage/types": ^1.0.0
"@types/json-schema": ^7.0.6
ajv: ^8.10.0
chokidar: ^3.5.2
fs-extra: 10.1.0
json-schema: ^0.4.0
json-schema-merge-allof: ^0.8.1
json-schema-traverse: ^1.0.0
node-fetch: ^2.6.7
typescript-json-schema: ^0.54.0
yaml: ^2.0.0
yup: ^0.32.9
checksum: b7467887a94977509ea1a95227521142318ea8c13f45c37959ed792668abf33e30377a79931f46891628389513c1459824fe6cb53a2dc8d5e6cb7abf57162371
languageName: node
linkType: hard
"@backstage/config@^1.0.0, @backstage/config@^1.0.1, @backstage/config@workspace:packages/config":
version: 0.0.0-use.local
resolution: "@backstage/config@workspace:packages/config"
@@ -3293,6 +3569,27 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/core-app-api@npm:^1.0.5":
version: 1.0.5
resolution: "@backstage/core-app-api@npm:1.0.5"
dependencies:
"@backstage/config": ^1.0.1
"@backstage/core-plugin-api": ^1.0.5
"@backstage/types": ^1.0.0
"@backstage/version-bridge": ^1.0.1
"@types/prop-types": ^15.7.3
prop-types: ^15.7.2
react-router-dom: 6.0.0-beta.0
react-use: ^17.2.4
zen-observable: ^0.8.15
zod: ^3.11.6
peerDependencies:
"@types/react": ^16.13.1 || ^17.0.0
react: ^16.13.1 || ^17.0.0
checksum: e5cb3ff2c7aefc19406f2895eeba5a9cf3f332b14ebfe4b5c304cc4e84d76f80c027d76ffa7fc2c4b6f856e8c534f689a1fe726f54fdf0d36017d909c1727aca
languageName: node
linkType: hard
"@backstage/core-components@^0.11.1-next.1, @backstage/core-components@^0.11.1-next.2, @backstage/core-components@workspace:packages/core-components":
version: 0.0.0-use.local
resolution: "@backstage/core-components@workspace:packages/core-components"
@@ -3518,6 +3815,37 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/dev-utils@npm:^1.0.4":
version: 1.0.5
resolution: "@backstage/dev-utils@npm:1.0.5"
dependencies:
"@backstage/app-defaults": ^1.0.5
"@backstage/catalog-model": ^1.1.0
"@backstage/core-app-api": ^1.0.5
"@backstage/core-components": ^0.11.0
"@backstage/core-plugin-api": ^1.0.5
"@backstage/integration-react": ^1.1.3
"@backstage/plugin-catalog-react": ^1.1.3
"@backstage/test-utils": ^1.1.3
"@backstage/theme": ^0.2.16
"@material-ui/core": ^4.12.2
"@material-ui/icons": ^4.9.1
"@testing-library/jest-dom": ^5.10.1
"@testing-library/react": ^12.1.3
"@testing-library/user-event": ^14.0.0
react-hot-loader: ^4.13.0
react-router: 6.0.0-beta.0
react-router-dom: 6.0.0-beta.0
react-use: ^17.2.4
zen-observable: ^0.8.15
peerDependencies:
"@types/react": ^16.13.1 || ^17.0.0
react: ^16.13.1 || ^17.0.0
react-dom: ^16.13.1 || ^17.0.0
checksum: 3d8787c91d38d9573b6084ce069c5e7fea55207b259fd9edd629ea9e2970bdc3858f680a1a637877e326af657134dfc5340545eca1b6d67fdcc2d3f703f4f665
languageName: node
linkType: hard
"@backstage/errors@1.1.0, @backstage/errors@^1.0.0, @backstage/errors@^1.1.0, @backstage/errors@^1.1.0-next.0, @backstage/errors@workspace:packages/errors":
version: 0.0.0-use.local
resolution: "@backstage/errors@workspace:packages/errors"
@@ -3556,7 +3884,7 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/integration-react@npm:^1.0.0":
"@backstage/integration-react@npm:^1.0.0, @backstage/integration-react@npm:^1.1.3":
version: 1.1.3
resolution: "@backstage/integration-react@npm:1.1.3"
dependencies:
@@ -4074,6 +4402,54 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/plugin-azure-functions-backend@workspace:plugins/azure-functions-backend":
version: 0.0.0-use.local
resolution: "@backstage/plugin-azure-functions-backend@workspace:plugins/azure-functions-backend"
dependencies:
"@azure/arm-appservice": ^13.0.0
"@azure/identity": ^2.1.0
"@backstage/backend-common": ^0.15.0
"@backstage/cli": ^0.18.0
"@backstage/config": ^1.0.1
"@types/express": "*"
"@types/supertest": ^2.0.8
express: ^4.17.1
express-promise-router: ^4.1.0
msw: ^0.44.0
node-fetch: ^2.6.7
supertest: ^4.0.2
winston: ^3.2.1
yn: ^4.0.0
languageName: unknown
linkType: soft
"@backstage/plugin-azure-functions@workspace:plugins/azure-functions":
version: 0.0.0-use.local
resolution: "@backstage/plugin-azure-functions@workspace:plugins/azure-functions"
dependencies:
"@backstage/cli": ^0.18.0
"@backstage/core-app-api": ^1.0.5
"@backstage/core-components": ^0.11.0
"@backstage/core-plugin-api": ^1.0.5
"@backstage/dev-utils": ^1.0.4
"@backstage/test-utils": ^1.1.3
"@backstage/theme": ^0.2.16
"@material-ui/core": ^4.9.13
"@material-ui/icons": ^4.9.1
"@material-ui/lab": 4.0.0-alpha.57
"@testing-library/jest-dom": ^5.10.1
"@testing-library/react": ^12.1.3
"@testing-library/user-event": ^14.0.0
"@types/jest": "*"
"@types/node": "*"
cross-fetch: ^3.1.5
msw: ^0.44.0
react-use: ^17.2.4
peerDependencies:
react: ^16.13.1 || ^17.0.0
languageName: unknown
linkType: soft
"@backstage/plugin-badges-backend@^0.1.30-next.0, @backstage/plugin-badges-backend@workspace:plugins/badges-backend":
version: 0.0.0-use.local
resolution: "@backstage/plugin-badges-backend@workspace:plugins/badges-backend"
@@ -7400,6 +7776,15 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/release-manifests@npm:^0.0.5":
version: 0.0.5
resolution: "@backstage/release-manifests@npm:0.0.5"
dependencies:
cross-fetch: ^3.1.5
checksum: 6e7a37d11a24fdbd99e45354c1703e12be691da37df27c752b3f1422c5d75ec89e45cc4808e9a2f98a37a525f4076428ec3cba2e9cdd579f4383b7f1ccdd564b
languageName: node
linkType: hard
"@backstage/test-utils@^1.2.0-next.1, @backstage/test-utils@^1.2.0-next.2, @backstage/test-utils@workspace:packages/test-utils":
version: 0.0.0-use.local
resolution: "@backstage/test-utils@workspace:packages/test-utils"
@@ -7430,6 +7815,33 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/test-utils@npm:^1.1.3":
version: 1.1.3
resolution: "@backstage/test-utils@npm:1.1.3"
dependencies:
"@backstage/config": ^1.0.1
"@backstage/core-app-api": ^1.0.5
"@backstage/core-plugin-api": ^1.0.5
"@backstage/plugin-permission-common": ^0.6.3
"@backstage/plugin-permission-react": ^0.4.4
"@backstage/theme": ^0.2.16
"@backstage/types": ^1.0.0
"@material-ui/core": ^4.12.2
"@material-ui/icons": ^4.11.2
"@testing-library/jest-dom": ^5.10.1
"@testing-library/react": ^12.1.3
"@testing-library/user-event": ^14.0.0
cross-fetch: ^3.1.5
react-router: 6.0.0-beta.0
react-router-dom: 6.0.0-beta.0
zen-observable: ^0.8.15
peerDependencies:
"@types/react": ^16.13.1 || ^17.0.0
react: ^16.13.1 || ^17.0.0
checksum: c83feac65473848691abc7d8d40fd5e0f6e179387266810fc36c806d336c640255aada09f17f6568b6bb4a48bc030eb8810195edbaf6eff5c75207daf7e7adc5
languageName: node
linkType: hard
"@backstage/theme@^0.2.15, @backstage/theme@^0.2.16, @backstage/theme@^0.2.6, @backstage/theme@^0.2.7, @backstage/theme@^0.2.9, @backstage/theme@workspace:packages/theme":
version: 0.0.0-use.local
resolution: "@backstage/theme@workspace:packages/theme"
@@ -9021,6 +9433,33 @@ __metadata:
languageName: node
linkType: hard
"@hot-loader/react-dom-v16@npm:@hot-loader/react-dom@^16.0.2":
version: 16.14.0
resolution: "@hot-loader/react-dom@npm:16.14.0"
dependencies:
loose-envify: ^1.1.0
object-assign: ^4.1.1
prop-types: ^15.6.2
scheduler: ^0.19.1
peerDependencies:
react: ^16.14.0
checksum: 9362ef1dfcea97eb833983c3cfcd3bb813ef5cd7dc3f91ad4927fabbaf05db2b81751aaa98e5c233004081771ca5d5478bd648a9a969e08eb9cf1c6a1ee0de00
languageName: node
linkType: hard
"@hot-loader/react-dom-v17@npm:@hot-loader/react-dom@^17.0.2":
version: 17.0.2
resolution: "@hot-loader/react-dom@npm:17.0.2"
dependencies:
loose-envify: ^1.1.0
object-assign: ^4.1.1
scheduler: ^0.20.2
peerDependencies:
react: 17.0.2
checksum: 180de3211aff1d60fd9d2a9130ba7977e2e356b1ecd2e7c50a98e6507bb7873f0523b44fa63142f0ffdc629d2933994f4add86e1f19288941f9d223bd28f5e48
languageName: node
linkType: hard
"@humanwhocodes/config-array@npm:^0.10.4":
version: 0.10.4
resolution: "@humanwhocodes/config-array@npm:0.10.4"
@@ -19016,7 +19455,7 @@ __metadata:
languageName: node
linkType: hard
"component-emitter@npm:^1.2.1, component-emitter@npm:^1.3.0, component-emitter@npm:~1.3.0":
"component-emitter@npm:^1.2.0, component-emitter@npm:^1.2.1, component-emitter@npm:^1.3.0, component-emitter@npm:~1.3.0":
version: 1.3.0
resolution: "component-emitter@npm:1.3.0"
checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b
@@ -19402,7 +19841,7 @@ __metadata:
languageName: node
linkType: hard
"cookiejar@npm:^2.1.3":
"cookiejar@npm:^2.1.0, cookiejar@npm:^2.1.3":
version: 2.1.3
resolution: "cookiejar@npm:2.1.3"
checksum: 88259983ebc52ceb23cdacfa48762b6a518a57872eff1c7ed01d214fff5cf492e2660d7d5c04700a28f1787a76811df39e8639f8e17670b3cf94ecd86e161f07
@@ -21997,6 +22436,23 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-jest@npm:^26.1.2":
version: 26.9.0
resolution: "eslint-plugin-jest@npm:26.9.0"
dependencies:
"@typescript-eslint/utils": ^5.10.0
peerDependencies:
"@typescript-eslint/eslint-plugin": ^5.0.0
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta:
"@typescript-eslint/eslint-plugin":
optional: true
jest:
optional: true
checksum: 6d5fd5c95368f1ca2640389aeb7ce703d6202493c3ec6bdedb4eaca37233710508b0c75829e727765a16fd27029a466d34202bc7f2811c752038ccbbce224400
languageName: node
linkType: hard
"eslint-plugin-jest@npm:^27.0.0":
version: 27.0.2
resolution: "eslint-plugin-jest@npm:27.0.2"
@@ -23431,7 +23887,7 @@ __metadata:
languageName: node
linkType: hard
"form-data@npm:^2.3.2, form-data@npm:^2.5.0":
"form-data@npm:^2.3.1, form-data@npm:^2.3.2, form-data@npm:^2.5.0":
version: 2.5.1
resolution: "form-data@npm:2.5.1"
dependencies:
@@ -23502,6 +23958,13 @@ __metadata:
languageName: node
linkType: hard
"formidable@npm:^1.2.0":
version: 1.2.6
resolution: "formidable@npm:1.2.6"
checksum: 2b68ed07ba88302b9c63f8eda94f19a460cef6017bfda48348f09f41d2a36660c9353137991618e0e4c3db115b41e4b8f6fa63bc973b7a7c91dec66acdd02a56
languageName: node
linkType: hard
"formidable@npm:^2.0.1":
version: 2.0.1
resolution: "formidable@npm:2.0.1"
@@ -24196,7 +24659,7 @@ __metadata:
languageName: node
linkType: hard
"global@npm:~4.4.0":
"global@npm:^4.3.0, global@npm:~4.4.0":
version: 4.4.0
resolution: "global@npm:4.4.0"
dependencies:
@@ -24886,6 +25349,13 @@ __metadata:
languageName: node
linkType: hard
"helmet@npm:^5.0.2":
version: 5.1.1
resolution: "helmet@npm:5.1.1"
checksum: b72ba26cc431804ad3b8ecdc18db95409a492cbb7a7e825efc27fc502b9433fec39fc083f2aad4fe7ed1a89a4287560b59f4435f9689eebbae6a2b61a1ec1b7d
languageName: node
linkType: hard
"helmet@npm:^6.0.0":
version: 6.0.0
resolution: "helmet@npm:6.0.0"
@@ -29763,7 +30233,7 @@ __metadata:
languageName: node
linkType: hard
"methods@npm:^1.0.0, methods@npm:^1.1.2, methods@npm:~1.1.2":
"methods@npm:^1.0.0, methods@npm:^1.1.1, methods@npm:^1.1.2, methods@npm:~1.1.2":
version: 1.1.2
resolution: "methods@npm:1.1.2"
checksum: 0917ff4041fa8e2f2fda5425a955fe16ca411591fbd123c0d722fcf02b73971ed6f764d85f0a6f547ce49ee0221ce2c19a5fa692157931cecb422984f1dcd13a
@@ -30173,7 +30643,7 @@ __metadata:
languageName: node
linkType: hard
"mime@npm:1.6.0, mime@npm:^1.3.4":
"mime@npm:1.6.0, mime@npm:^1.3.4, mime@npm:^1.4.1":
version: 1.6.0
resolution: "mime@npm:1.6.0"
bin:
@@ -30619,6 +31089,41 @@ __metadata:
languageName: node
linkType: hard
"msw@npm:^0.44.0":
version: 0.44.2
resolution: "msw@npm:0.44.2"
dependencies:
"@mswjs/cookies": ^0.2.2
"@mswjs/interceptors": ^0.17.2
"@open-draft/until": ^1.0.3
"@types/cookie": ^0.4.1
"@types/js-levenshtein": ^1.1.1
chalk: 4.1.1
chokidar: ^3.4.2
cookie: ^0.4.2
graphql: ^16.3.0
headers-polyfill: ^3.0.4
inquirer: ^8.2.0
is-node-process: ^1.0.1
js-levenshtein: ^1.1.6
node-fetch: ^2.6.7
outvariant: ^1.3.0
path-to-regexp: ^6.2.0
statuses: ^2.0.0
strict-event-emitter: ^0.2.0
type-fest: ^1.2.2
yargs: ^17.3.1
peerDependencies:
typescript: ">= 4.2.x <= 4.7.x"
peerDependenciesMeta:
typescript:
optional: true
bin:
msw: cli/index.js
checksum: 739d536ee09d1c0a2cbc9dbe917f167c42115a6548f99780850ce9a63a5e7c377cf9b5f1a3b497e85ce7304025ab22966e997fe73adbd8d2ab968b8685f15a24
languageName: node
linkType: hard
"msw@npm:^0.47.0":
version: 0.47.0
resolution: "msw@npm:0.47.0"
@@ -33718,7 +34223,7 @@ __metadata:
languageName: node
linkType: hard
"prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.7, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
"prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.7, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@@ -34025,6 +34530,15 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:^6.5.1":
version: 6.11.0
resolution: "qs@npm:6.11.0"
dependencies:
side-channel: ^1.0.4
checksum: 6e1f29dd5385f7488ec74ac7b6c92f4d09a90408882d0c208414a34dd33badc1a621019d4c799a3df15ab9b1d0292f97c1dd71dc7c045e69f81a8064e5af7297
languageName: node
linkType: hard
"qs@npm:~6.5.2":
version: 6.5.2
resolution: "qs@npm:6.5.2"
@@ -34405,6 +34919,29 @@ __metadata:
languageName: node
linkType: hard
"react-hot-loader@npm:^4.13.0":
version: 4.13.0
resolution: "react-hot-loader@npm:4.13.0"
dependencies:
fast-levenshtein: ^2.0.6
global: ^4.3.0
hoist-non-react-statics: ^3.3.0
loader-utils: ^1.1.0
prop-types: ^15.6.1
react-lifecycles-compat: ^3.0.4
shallowequal: ^1.1.0
source-map: ^0.7.3
peerDependencies:
"@types/react": "^15.0.0 || ^16.0.0 || ^17.0.0 "
react: "^15.0.0 || ^16.0.0 || ^17.0.0 "
react-dom: "^15.0.0 || ^16.0.0 || ^17.0.0 "
peerDependenciesMeta:
"@types/react":
optional: true
checksum: effdbf4644ce912ae20ad94be62083970c74b26a59fe24ed0024cf73190a5b3edf59650cb693bdd7b70791df8ab8530de273d73b895c4831a91da8a76683e3a3
languageName: node
linkType: hard
"react-icons@npm:^4.4.0":
version: 4.4.0
resolution: "react-icons@npm:4.4.0"
@@ -37867,6 +38404,24 @@ __metadata:
languageName: node
linkType: hard
"superagent@npm:^3.8.3":
version: 3.8.3
resolution: "superagent@npm:3.8.3"
dependencies:
component-emitter: ^1.2.0
cookiejar: ^2.1.0
debug: ^3.1.0
extend: ^3.0.0
form-data: ^2.3.1
formidable: ^1.2.0
methods: ^1.1.1
mime: ^1.4.1
qs: ^6.5.1
readable-stream: ^2.3.5
checksum: b13d0303259d76c9180bd40d97d9f0713760f5ced1aef089bdb2fcdf69cfaef89004cd6e986416d59bd9a2f0f9933d72521b5171fa26f89b781a2c3460c516fe
languageName: node
linkType: hard
"superagent@npm:^8.0.0":
version: 8.0.0
resolution: "superagent@npm:8.0.0"
@@ -37886,6 +38441,16 @@ __metadata:
languageName: node
linkType: hard
"supertest@npm:^4.0.2":
version: 4.0.2
resolution: "supertest@npm:4.0.2"
dependencies:
methods: ^1.1.2
superagent: ^3.8.3
checksum: ec848088c5d0f6743a0829331c274f67ab4c2e5e6ba724f2fdf4965a73afa22d5091448323e28ae5b69137e42fe3ac67235dfc5b0a0acda3c088bdd193313319
languageName: node
linkType: hard
"supertest@npm:^6.1.3, supertest@npm:^6.1.6, supertest@npm:^6.2.4":
version: 6.2.4
resolution: "supertest@npm:6.2.4"
@@ -40196,7 +40761,7 @@ __metadata:
languageName: node
linkType: hard
"webpack@npm:^5, webpack@npm:^5.70.0":
"webpack@npm:^5, webpack@npm:^5.66.0, webpack@npm:^5.70.0":
version: 5.74.0
resolution: "webpack@npm:5.74.0"
dependencies: