Azure Site Plugin Auth permission added
Signed-off-by: Deepankumar <deepan0433@gmail.com>
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
"@backstage/plugin-auth-node": "workspace:^",
|
||||
"@backstage/plugin-azure-devops-backend": "workspace:^",
|
||||
"@backstage/plugin-azure-sites-backend": "workspace:^",
|
||||
"@backstage/plugin-azure-sites-common": "workspace:^",
|
||||
"@backstage/plugin-badges-backend": "workspace:^",
|
||||
"@backstage/plugin-catalog-backend": "workspace:^",
|
||||
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "workspace:^",
|
||||
|
||||
@@ -41,6 +41,7 @@ import healthcheck from './plugins/healthcheck';
|
||||
import { metricsHandler, metricsInit } from './metrics';
|
||||
import auth from './plugins/auth';
|
||||
import azureDevOps from './plugins/azure-devops';
|
||||
import azureSites from './plugins/azure-sites';
|
||||
import catalog from './plugins/catalog';
|
||||
import codeCoverage from './plugins/codecoverage';
|
||||
import entityFeedback from './plugins/entityFeedback';
|
||||
@@ -145,6 +146,7 @@ async function main() {
|
||||
|
||||
const createEnv = makeCreateEnv(config);
|
||||
|
||||
const azureSitesEnv = useHotMemoize(module, () => createEnv('azure-sites'));
|
||||
const healthcheckEnv = useHotMemoize(module, () => createEnv('healthcheck'));
|
||||
const catalogEnv = useHotMemoize(module, () => createEnv('catalog'));
|
||||
const codeCoverageEnv = useHotMemoize(module, () =>
|
||||
@@ -189,6 +191,7 @@ async function main() {
|
||||
apiRouter.use('/tech-insights', await techInsights(techInsightsEnv));
|
||||
apiRouter.use('/auth', await auth(authEnv));
|
||||
apiRouter.use('/azure-devops', await azureDevOps(azureDevOpsEnv));
|
||||
apiRouter.use('/azure-sites', await azureSites(azureSitesEnv));
|
||||
apiRouter.use('/search', await search(searchEnv));
|
||||
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
|
||||
apiRouter.use('/todo', await todo(todoEnv));
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
createRouter,
|
||||
AzureSitesApi,
|
||||
} from '@backstage/plugin-azure-sites-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
return await createRouter({
|
||||
...env,
|
||||
azureSitesApi: AzureSitesApi.fromConfig(env.config),
|
||||
});
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { createRouter } from '@backstage/plugin-permission-backend';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
PolicyDecision,
|
||||
isPermission,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
PermissionPolicy,
|
||||
@@ -30,6 +31,11 @@ import {
|
||||
} from '@backstage/plugin-playlist-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common';
|
||||
import {
|
||||
catalogConditions,
|
||||
createCatalogConditionalDecision,
|
||||
} from '@backstage/plugin-catalog-backend/alpha';
|
||||
|
||||
class ExamplePermissionPolicy implements PermissionPolicy {
|
||||
private playlistPermissionPolicy = new DefaultPlaylistPermissionPolicy();
|
||||
@@ -42,6 +48,15 @@ class ExamplePermissionPolicy implements PermissionPolicy {
|
||||
return this.playlistPermissionPolicy.handle(request, user);
|
||||
}
|
||||
|
||||
if (isPermission(request.permission, azureSitesActionPermission)) {
|
||||
return createCatalogConditionalDecision(
|
||||
request.permission,
|
||||
catalogConditions.isEntityOwner({
|
||||
claims: user?.identity.ownershipEntityRefs ?? [],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
result: AuthorizeResult.ALLOW,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Simple plugin that proxies requests to the Azure Portal API through Azure SDK JavaScript libraries.
|
||||
|
||||
_Inspired by [roadie.io AWS Lambda plugin](https://roadie.io/backstage/plugins/aws-lambda/)_
|
||||
_Inspired by [roadie.io AWS Lamda plugin](https://roadie.io/backstage/plugins/aws-lambda/)_
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -36,11 +36,11 @@ Here's how to get the backend plugin up and running:
|
||||
1. First we need to add the `@backstage/plugin-azure-sites-backend` package to your backend:
|
||||
|
||||
```sh
|
||||
# From your Backstage root directory
|
||||
# From the Backstage root directory
|
||||
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:
|
||||
2. Then we will create a new file named `packages/backend/src/plugins/azure-sites.ts`, and add the following to it:
|
||||
|
||||
```ts
|
||||
import {
|
||||
@@ -56,6 +56,7 @@ Here's how to get the backend plugin up and running:
|
||||
return await createRouter({
|
||||
logger: env.logger,
|
||||
azureSitesApi: AzureSitesApi.fromConfig(env.config),
|
||||
permissions: env.permissions,
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -63,9 +64,9 @@ Here's how to get the backend plugin up and running:
|
||||
3. Next we wire this into the overall backend router, edit `packages/backend/src/index.ts`:
|
||||
|
||||
```ts
|
||||
import azure from './plugins/azure';
|
||||
import azureSites from './plugins/azure-sites';
|
||||
|
||||
// Removed for clarity...
|
||||
// Removed for clairty...
|
||||
|
||||
async function main() {
|
||||
// ...
|
||||
@@ -76,10 +77,34 @@ Here's how to get the backend plugin up and running:
|
||||
|
||||
// ...
|
||||
// Insert this line under the other lines that add their routers to apiRouter in the same way
|
||||
apiRouter.use('/azure-sites', await azure(azureSitesEnv));
|
||||
apiRouter.use('/azure-sites', await azureSites(azureSitesEnv));
|
||||
}
|
||||
```
|
||||
|
||||
4. Now run `yarn start-backend` from the repo root.
|
||||
4. Enable permissions and that the below is just an example policy that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity, edit your `packages/backend/src/plugins/permission.ts`
|
||||
|
||||
5. Finally, open `http://localhost:7007/api/azure-sites/health` in a browser, it should return `{"status":"ok"}`.
|
||||
```diff
|
||||
// packages/backend/src/plugins/permission.ts
|
||||
+ import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common';
|
||||
...
|
||||
class TestPermissionPolicy implements PermissionPolicy {
|
||||
- async handle(): Promise<PolicyDecision> {
|
||||
+ async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise<PolicyDecision> {
|
||||
if (isPermission(request.permission, azureSitesActionPermission)) {
|
||||
return createCatalogConditionalDecision(
|
||||
request.permission,
|
||||
catalogConditions.isEntityOwner({
|
||||
claims: user?.identity.ownershipEntityRefs ?? [],
|
||||
}),
|
||||
);
|
||||
}
|
||||
...
|
||||
return {
|
||||
result: AuthorizeResult.ALLOW,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
5. Now run `yarn start-backend` from the repo root.
|
||||
|
||||
6. Finally, open `http://localhost:7007/api/azure/health` in a browser, it should return `{"status":"ok"}`.
|
||||
|
||||
@@ -36,8 +36,13 @@
|
||||
"@azure/arm-resourcegraph": "^4.2.1",
|
||||
"@azure/identity": "^4.0.0",
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/plugin-auth-node": "workspace:^",
|
||||
"@backstage/plugin-azure-sites-common": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/plugin-permission-node": "workspace:^",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
|
||||
@@ -95,4 +95,15 @@ export class AzureSitesApi {
|
||||
}
|
||||
return { items: items };
|
||||
}
|
||||
|
||||
async validateSite(annotationName: string, siteName: string) {
|
||||
const azureSites = await this.list({
|
||||
name: annotationName,
|
||||
});
|
||||
for (const site of azureSites.items) {
|
||||
if (site.name === siteName) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,19 +19,32 @@ import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { NotAllowedError } from '@backstage/errors';
|
||||
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
|
||||
import {
|
||||
PermissionEvaluator,
|
||||
AuthorizeResult,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
azureSitesActionPermission,
|
||||
AZURE_WEB_SITE_NAME_ANNOTATION,
|
||||
} from '@backstage/plugin-azure-sites-common';
|
||||
|
||||
import { AzureSitesApi } from '../api';
|
||||
|
||||
/** @public */
|
||||
export interface RouterOptions {
|
||||
logger: Logger;
|
||||
azureSitesApi: AzureSitesApi;
|
||||
permissions: PermissionEvaluator;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger, azureSitesApi } = options;
|
||||
const { logger, azureSitesApi, permissions } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
@@ -52,7 +65,33 @@ export async function createRouter(
|
||||
'/:subscription/:resourceGroup/:name/start',
|
||||
async (request, response) => {
|
||||
const { subscription, resourceGroup, name } = request.params;
|
||||
console.log('starting...');
|
||||
const token = getBearerTokenFromAuthorizationHeader(
|
||||
request.header('authorization'),
|
||||
);
|
||||
const entity = request.body.entity;
|
||||
const resourceRef = stringifyEntityRef(entity);
|
||||
|
||||
const annotationName =
|
||||
entity.metadata.annotations?.[AZURE_WEB_SITE_NAME_ANNOTATION];
|
||||
if (await azureSitesApi.validateSite(annotationName, name)) {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
|
||||
const decision = (
|
||||
await permissions.authorize(
|
||||
[{ permission: azureSitesActionPermission, resourceRef }],
|
||||
{
|
||||
token,
|
||||
},
|
||||
)
|
||||
)[0];
|
||||
if (decision.result === AuthorizeResult.DENY) {
|
||||
throw new NotAllowedError('Unauthorized');
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`entity ${entity.metadata.name} - azure site "${name}" is starting...`,
|
||||
);
|
||||
response.json(
|
||||
await azureSitesApi.start({
|
||||
subscription,
|
||||
@@ -66,7 +105,33 @@ export async function createRouter(
|
||||
'/:subscription/:resourceGroup/:name/stop',
|
||||
async (request, response) => {
|
||||
const { subscription, resourceGroup, name } = request.params;
|
||||
console.log('stopping...');
|
||||
const token = getBearerTokenFromAuthorizationHeader(
|
||||
request.header('authorization'),
|
||||
);
|
||||
const entity = request.body.entity;
|
||||
const resourceRef = stringifyEntityRef(entity);
|
||||
|
||||
const annotationName =
|
||||
entity.metadata.annotations?.[AZURE_WEB_SITE_NAME_ANNOTATION];
|
||||
if (await azureSitesApi.validateSite(annotationName, name)) {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
|
||||
const decision = (
|
||||
await permissions.authorize(
|
||||
[{ permission: azureSitesActionPermission, resourceRef }],
|
||||
{
|
||||
token,
|
||||
},
|
||||
)
|
||||
)[0];
|
||||
if (decision.result === AuthorizeResult.DENY) {
|
||||
throw new NotAllowedError('Unauthorized');
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`entity ${entity.metadata.name} - azure site "${name}" is stopping...`,
|
||||
);
|
||||
response.json(
|
||||
await azureSitesApi.stop({
|
||||
subscription,
|
||||
|
||||
@@ -17,11 +17,14 @@
|
||||
import {
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
ServerTokenManager,
|
||||
SingleHostDiscovery,
|
||||
} from '@backstage/backend-common';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
import { AzureSitesApi } from '../api';
|
||||
import { createRouter } from './router';
|
||||
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
@@ -34,9 +37,18 @@ export async function startStandaloneServer(
|
||||
): Promise<Server> {
|
||||
const logger = options.logger.child({ service: 'azure-backend' });
|
||||
const config = await loadBackendConfig({ logger, argv: process.argv });
|
||||
const discovery = SingleHostDiscovery.fromConfig(config);
|
||||
const tokenManager = ServerTokenManager.fromConfig(config, {
|
||||
logger,
|
||||
});
|
||||
const permissions = ServerPermissionClient.fromConfig(config, {
|
||||
discovery,
|
||||
tokenManager,
|
||||
});
|
||||
logger.debug('Starting application server...');
|
||||
const router = await createRouter({
|
||||
logger,
|
||||
permissions,
|
||||
azureSitesApi: AzureSitesApi.fromConfig(config),
|
||||
});
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
},
|
||||
|
||||
@@ -15,3 +15,5 @@
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
export * from './permissions';
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2023 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 { createPermission } from '@backstage/plugin-permission-common';
|
||||
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const azureSitesActionPermission = createPermission({
|
||||
name: 'azure.sites.update',
|
||||
attributes: { action: 'update' },
|
||||
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
});
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const azureSitesPermissions = [azureSitesActionPermission];
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
/** @public */
|
||||
export type AzureSite = {
|
||||
href: string;
|
||||
@@ -45,4 +47,5 @@ export type AzureSiteStartStopRequest = {
|
||||
subscription: string;
|
||||
resourceGroup: string;
|
||||
name: string;
|
||||
entity?: Entity;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const AZURE_WEB_SITE_NAME_ANNOTATION = 'azure.com/microsoft-web-sites';
|
||||
@@ -14,7 +14,7 @@ The following sections will help you get the Azure plugin setup and running
|
||||
|
||||
### Azure Sites Backend
|
||||
|
||||
You need to set up the Azure Sites Backend plugin before you move forward with any of these steps if you haven't already.
|
||||
You need to set up the [Azure Sites Backend plugin](https://github.com/backstage/backstage/tree/master/plugins/azure-sites-backend) before you move forward with any of these steps if you haven't already.
|
||||
|
||||
### Entity Annotation
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-azure-sites-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.61",
|
||||
|
||||
@@ -39,12 +39,16 @@ export class AzureSitesApiBackendClient implements AzureSitesApi {
|
||||
request.subscription
|
||||
}/${request.resourceGroup}/${request.name}/stop`;
|
||||
const { token: accessToken } = await this.identityApi.getCredentials();
|
||||
const entity = request.entity;
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(accessToken && { Authorization: `Bearer ${accessToken}` }),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
entity,
|
||||
}),
|
||||
});
|
||||
}
|
||||
async start(request: AzureSiteStartStopRequest): Promise<void> {
|
||||
@@ -52,12 +56,16 @@ export class AzureSitesApiBackendClient implements AzureSitesApi {
|
||||
request.subscription
|
||||
}/${request.resourceGroup}/${request.name}/start`;
|
||||
const { token: accessToken } = await this.identityApi.getCredentials();
|
||||
const entity = request.entity;
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(accessToken && { Authorization: `Bearer ${accessToken}` }),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
entity,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -17,11 +17,9 @@
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useSites } from '../../hooks/useSites';
|
||||
import {
|
||||
AZURE_WEB_SITE_NAME_ANNOTATION,
|
||||
useServiceEntityAnnotations,
|
||||
} from '../../hooks/useServiceEntityAnnotations';
|
||||
import { ErrorBoundary, ResponseErrorPanel } from '@backstage/core-components';
|
||||
import { useServiceEntityAnnotations } from '../../hooks/useServiceEntityAnnotations';
|
||||
import { AZURE_WEB_SITE_NAME_ANNOTATION } from '@backstage/plugin-azure-sites-common';
|
||||
import {
|
||||
useEntity,
|
||||
MissingAnnotationEmptyState,
|
||||
|
||||
+23
@@ -21,6 +21,11 @@ import {
|
||||
errorApiRef,
|
||||
identityApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
PermissionApi,
|
||||
permissionApiRef,
|
||||
} from '@backstage/plugin-permission-react';
|
||||
import { rest } from 'msw';
|
||||
import {
|
||||
renderInTestApp,
|
||||
@@ -41,15 +46,33 @@ const identityApiMock = (getCredentials: any) => ({
|
||||
});
|
||||
const azureSitesApiMock = {};
|
||||
|
||||
jest.mock('@backstage/plugin-catalog-react', () => ({
|
||||
useEntity: () => {
|
||||
return { loading: false, entity: undefined };
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@backstage/plugin-catalog-react/alpha', () => ({
|
||||
useEntityPermission: () => {
|
||||
return { loading: false, allowed: true };
|
||||
},
|
||||
}));
|
||||
|
||||
const config = {
|
||||
getString: (_: string) => 'https://test-url',
|
||||
};
|
||||
|
||||
const mockAuthorize = jest
|
||||
.fn()
|
||||
.mockImplementation(async () => ({ result: AuthorizeResult.ALLOW }));
|
||||
const permissionApi: Partial<PermissionApi> = { authorize: mockAuthorize };
|
||||
|
||||
const apis: [AnyApiRef, Partial<unknown>][] = [
|
||||
[errorApiRef, errorApiMock],
|
||||
[configApiRef, config],
|
||||
[azureSiteApiRef, azureSitesApiMock],
|
||||
[identityApiRef, identityApiMock],
|
||||
[permissionApiRef, permissionApi],
|
||||
];
|
||||
|
||||
describe('AzureSitesOverviewWidget', () => {
|
||||
|
||||
+16
-5
@@ -27,7 +27,10 @@ import {
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
import { default as MuiAlert } from '@material-ui/lab/Alert';
|
||||
import { AzureSite } from '@backstage/plugin-azure-sites-common';
|
||||
import {
|
||||
AzureSite,
|
||||
azureSitesActionPermission,
|
||||
} from '@backstage/plugin-azure-sites-common';
|
||||
import { Table, TableColumn, Link } from '@backstage/core-components';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import FlashOnIcon from '@material-ui/icons/FlashOn';
|
||||
@@ -40,6 +43,8 @@ import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
import { DateTime } from 'luxon';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { azureSiteApiRef } from '../../api';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { useEntityPermission } from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
type States = 'Waiting' | 'Running' | 'Paused' | 'Failed' | 'Stopped';
|
||||
type Kinds = 'app' | 'functionapp';
|
||||
@@ -117,6 +122,7 @@ const ActionButtons = ({
|
||||
onMenuItemClick: Dispatch<React.SetStateAction<string | null>>;
|
||||
}) => {
|
||||
const azureApi = useApi(azureSiteApiRef);
|
||||
const { entity } = useEntity();
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
@@ -132,6 +138,7 @@ const ActionButtons = ({
|
||||
name: value.name,
|
||||
resourceGroup: value.resourceGroup,
|
||||
subscription: value.subscription,
|
||||
entity: entity,
|
||||
});
|
||||
onMenuItemClick('Starting, this may take some time...');
|
||||
handleClose();
|
||||
@@ -141,11 +148,15 @@ const ActionButtons = ({
|
||||
name: value.name,
|
||||
resourceGroup: value.resourceGroup,
|
||||
subscription: value.subscription,
|
||||
entity: entity,
|
||||
});
|
||||
onMenuItemClick('Stopping, this may take some time...');
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const { loading: loadingPermission, allowed: canDoAction } =
|
||||
useEntityPermission(azureSitesActionPermission);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<IconButton
|
||||
@@ -173,14 +184,14 @@ const ActionButtons = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
{value.state !== 'Running' && (
|
||||
<MenuItem key="start" onClick={start}>
|
||||
{value.state !== 'Running' && !loadingPermission && (
|
||||
<MenuItem key="start" onClick={start} disabled={!canDoAction}>
|
||||
<StartIcon />
|
||||
Start
|
||||
</MenuItem>
|
||||
)}
|
||||
{value.state !== 'Stopped' && (
|
||||
<MenuItem key="stop" onClick={stop}>
|
||||
{value.state !== 'Stopped' && !loadingPermission && (
|
||||
<MenuItem key="stop" onClick={stop} disabled={!canDoAction}>
|
||||
<StopIcon />
|
||||
Stop
|
||||
</MenuItem>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { AZURE_WEB_SITE_NAME_ANNOTATION } from '@backstage/plugin-azure-sites-common';
|
||||
|
||||
export const AZURE_WEB_SITE_NAME_ANNOTATION = 'azure.com/microsoft-web-sites';
|
||||
export const useServiceEntityAnnotations = (entity: Entity) => {
|
||||
const webSiteName =
|
||||
entity?.metadata.annotations?.[AZURE_WEB_SITE_NAME_ANNOTATION] ?? '';
|
||||
|
||||
@@ -4969,9 +4969,14 @@ __metadata:
|
||||
"@azure/arm-resourcegraph": ^4.2.1
|
||||
"@azure/identity": ^4.0.0
|
||||
"@backstage/backend-common": "workspace:^"
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/plugin-auth-node": "workspace:^"
|
||||
"@backstage/plugin-azure-sites-common": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
"@backstage/plugin-permission-node": "workspace:^"
|
||||
"@types/express": ^4.17.6
|
||||
"@types/supertest": ^2.0.8
|
||||
express: ^4.17.1
|
||||
@@ -4986,7 +4991,10 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-azure-sites-common@workspace:plugins/azure-sites-common"
|
||||
dependencies:
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@@ -5001,6 +5009,8 @@ __metadata:
|
||||
"@backstage/dev-utils": "workspace:^"
|
||||
"@backstage/plugin-azure-sites-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-react": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
"@backstage/plugin-permission-react": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@material-ui/core": ^4.12.2
|
||||
"@material-ui/icons": ^4.9.1
|
||||
|
||||
Reference in New Issue
Block a user