diff --git a/plugins/azure-functions-backend/src/service/router.ts b/plugins/azure-functions-backend/src/service/router.ts index 03f4b30f42..9d23a1294a 100644 --- a/plugins/azure-functions-backend/src/service/router.ts +++ b/plugins/azure-functions-backend/src/service/router.ts @@ -38,8 +38,8 @@ export async function createRouter( logger.info('PONG!'); response.send({ status: 'ok' }); }); - router.get('/get', async (request, response) => { - response.json(await azureWebManagementApi.list({ functionName: request.query.functionName!.toString() })) + router.post('/list', async (request, response) => { + response.json(await azureWebManagementApi.list({ functionName: request.body.functionName!.toString() })) }); router.use(errorHandler()); return router; diff --git a/plugins/azure-functions/src/api/AzureFunctionsBackendClient.ts b/plugins/azure-functions/src/api/AzureFunctionsBackendClient.ts index e7b5506c44..056528c946 100644 --- a/plugins/azure-functions/src/api/AzureFunctionsBackendClient.ts +++ b/plugins/azure-functions/src/api/AzureFunctionsBackendClient.ts @@ -36,14 +36,15 @@ export class AzureFunctionsBackendClient implements AzureFunctionsApi { functionName: string; }): Promise { try { - const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/get?functionName=${functionName}`; + const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/list`; const { token: idToken } = await this.identityApi.getCredentials(); const response = await fetch(url, { - method: 'GET', + method: 'POST', headers: { 'Content-Type': 'application/json', ...(idToken && { Authorization: `Bearer ${idToken}` }), - } + }, + body: JSON.stringify({ functionName: functionName }) }); return await response.json(); } catch (e: any) { diff --git a/plugins/azure-functions/src/components/AzureFunctionsOverviewComponent/AzureFunctionsOverview.tsx b/plugins/azure-functions/src/components/AzureFunctionsOverviewComponent/AzureFunctionsOverview.tsx new file mode 100644 index 0000000000..2198940c31 --- /dev/null +++ b/plugins/azure-functions/src/components/AzureFunctionsOverviewComponent/AzureFunctionsOverview.tsx @@ -0,0 +1,56 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { useFunctions } from '../../hooks/useFunctions'; +import { + AZURE_FUNCTIONS_ANNOTATION, + useServiceEntityAnnotations, +} from '../../hooks/useServiceEntityAnnotations'; +import { MissingAnnotationEmptyState } from '@backstage/core-components'; +import ErrorBoundary from '../ErrorBoundary'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { OverviewTable } from '../OverviewTableComponent/OverviewTable'; + +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 ( + <> + ); +}; + +export const AzureFunctionsOverviewWidget = () => { + const { entity } = useEntity(); + + if (!isAzureFunctionsAvailable(entity)) { + return (); + } + + return ( + + + + ) +}; diff --git a/plugins/azure-functions/src/components/AzureFunctionsOverview/AzureFunctionsOverview.test.tsx b/plugins/azure-functions/src/components/OverviewTableComponent/OverviewTable.test.tsx similarity index 73% rename from plugins/azure-functions/src/components/AzureFunctionsOverview/AzureFunctionsOverview.test.tsx rename to plugins/azure-functions/src/components/OverviewTableComponent/OverviewTable.test.tsx index aef10a113e..7a3ae48d7d 100644 --- a/plugins/azure-functions/src/components/AzureFunctionsOverview/AzureFunctionsOverview.test.tsx +++ b/plugins/azure-functions/src/components/OverviewTableComponent/OverviewTable.test.tsx @@ -27,13 +27,12 @@ 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 '../..'; +import { azureFunctionsApiRef } from '../..'; +import { OverviewTable } from './OverviewTable'; const errorApiMock = { post: jest.fn(), error$: jest.fn() }; const identityApiMock = (getCredentials: any) => ({ @@ -61,9 +60,11 @@ describe('AzureFunctionsOverviewWidget', () => { 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)), + rest.post( + '/list', + (_, res, ctx) => { + res(ctx.json(functionResponseMock)); + }, ), ); }); @@ -71,20 +72,19 @@ describe('AzureFunctionsOverviewWidget', () => { it('should display an overview table with the data from the requests', async () => { const rendered = render( - - - + , ); + expect( - await rendered.findByText(functionResponseMock.name), + await rendered.findByText(functionResponseMock.functionName), ).toBeInTheDocument(); expect( - await rendered.findByText(functionResponseMock.properties.state), + await rendered.findByText(functionResponseMock.state), ).toBeInTheDocument(); expect( await rendered.findByText( - new Date(functionResponseMock.properties.lastModifiedTimeUtc).toUTCString(), + new Date(functionResponseMock.lastModifiedDate).toUTCString(), ), ).toBeInTheDocument(); }); diff --git a/plugins/azure-functions/src/components/AzureFunctionsOverview/AzureFunctionsOverview.tsx b/plugins/azure-functions/src/components/OverviewTableComponent/OverviewTable.tsx similarity index 68% rename from plugins/azure-functions/src/components/AzureFunctionsOverview/AzureFunctionsOverview.tsx rename to plugins/azure-functions/src/components/OverviewTableComponent/OverviewTable.tsx index 1a12086fee..8ecb2a3ac9 100644 --- a/plugins/azure-functions/src/components/AzureFunctionsOverview/AzureFunctionsOverview.tsx +++ b/plugins/azure-functions/src/components/OverviewTableComponent/OverviewTable.tsx @@ -21,17 +21,9 @@ import { 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 { Table, TableColumn } from '@backstage/core-components'; import FlashOnIcon from '@material-ui/icons/FlashOn' -import ErrorBoundary from '../ErrorBoundary'; -import { useEntity } from '@backstage/plugin-catalog-react'; type States = 'Waiting' | 'Running' | 'Paused' | 'Failed'; @@ -59,8 +51,6 @@ const State = ({ value }: { value: States }) => { ); }; - - type FunctionTableProps = { data: FunctionsData[]; loading: boolean; @@ -95,7 +85,7 @@ const DEFAULT_COLUMNS: TableColumn[] = [ }, ]; -const OverviewComponent = ({ data, loading }: FunctionTableProps) => { +export const OverviewTable = ({ data, loading }: FunctionTableProps) => { const columns: TableColumn[] = [...DEFAULT_COLUMNS]; const tableStyle = { minWidth: '0', @@ -123,32 +113,3 @@ const OverviewComponent = ({ data, loading }: FunctionTableProps) => { ); }; - -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 ( - <> - ); -}; - -export const AzureFunctionsOverviewWidget = () => { - const { entity } = useEntity(); - - if (!isAzureFunctionsAvailable(entity)) { - return (); - } - - return ( - - - - ) -}; diff --git a/plugins/azure-functions/src/index.ts b/plugins/azure-functions/src/index.ts index 4d359dc0a2..7b091198ee 100644 --- a/plugins/azure-functions/src/index.ts +++ b/plugins/azure-functions/src/index.ts @@ -16,4 +16,4 @@ export * from './plugin'; export * from './api'; -export * from './components/AzureFunctionsOverview/AzureFunctionsOverview'; \ No newline at end of file +export * from './components/AzureFunctionsOverviewComponent/AzureFunctionsOverview'; \ No newline at end of file diff --git a/plugins/azure-functions/src/mocks/mocks.ts b/plugins/azure-functions/src/mocks/mocks.ts index 053fe27b54..9e601fda11 100644 --- a/plugins/azure-functions/src/mocks/mocks.ts +++ b/plugins/azure-functions/src/mocks/mocks.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { FunctionsData } from "../api"; + export const entityMock = { metadata: { namespace: 'default', @@ -35,225 +37,13 @@ export const entityMock = { }; // 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", +export const functionResponseMock: FunctionsData = { + functionName: "func-mock", 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 - } + state: "Running", + href: "https://mockurl.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/rg_mock-WestEuropewebspace/sites/func-mock", + logstreamHref: "https://mockurl.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/rg_mock-WestEuropewebspace/sites/func-mock/logStream", + usageState: "Normal", + lastModifiedDate: new Date("2022-09-02T11:09:58.9033333"), + containerSize: 100 }; diff --git a/plugins/azure-functions/src/plugin.ts b/plugins/azure-functions/src/plugin.ts index b0945f8c5d..b3e905e6af 100644 --- a/plugins/azure-functions/src/plugin.ts +++ b/plugins/azure-functions/src/plugin.ts @@ -51,7 +51,7 @@ export const EntityAzureFunctionsOverviewCard = azureFunctionsPlugin.provide( name: 'EntityAzureFunctionsOverviewCard', component: { lazy: () => - import('./components/AzureFunctionsOverview/AzureFunctionsOverview').then( + import('./components/AzureFunctionsOverviewComponent/AzureFunctionsOverview').then( m => m.AzureFunctionsOverviewWidget, ), },