From f9b1ec31f1b8ce5173ce672d9faa5caa0f80b491 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 10:19:39 +0100 Subject: [PATCH 01/23] Some initial code Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/.eslintrc.js | 1 + plugins/octopus-deploy/README.md | 11 ++ plugins/octopus-deploy/dev/index.tsx | 12 ++ plugins/octopus-deploy/package.json | 52 ++++++++ plugins/octopus-deploy/src/api/index.ts | 88 +++++++++++++ .../EntityPageOctopusDeploy.tsx | 15 +++ .../EntityPageOctopusDeploy/index.ts | 1 + .../components/ReleaseTable/ReleaseTable.tsx | 123 ++++++++++++++++++ .../src/components/ReleaseTable/index.ts | 1 + plugins/octopus-deploy/src/constants.ts | 1 + .../octopus-deploy/src/hooks/useReleases.ts | 26 ++++ plugins/octopus-deploy/src/index.ts | 7 + plugins/octopus-deploy/src/plugin.test.ts | 7 + plugins/octopus-deploy/src/plugin.ts | 49 +++++++ plugins/octopus-deploy/src/routes.ts | 5 + plugins/octopus-deploy/src/setupTests.ts | 2 + .../src/utils/getAnnotationFromEntity.ts | 14 ++ 17 files changed, 415 insertions(+) create mode 100644 plugins/octopus-deploy/.eslintrc.js create mode 100644 plugins/octopus-deploy/README.md create mode 100644 plugins/octopus-deploy/dev/index.tsx create mode 100644 plugins/octopus-deploy/package.json create mode 100644 plugins/octopus-deploy/src/api/index.ts create mode 100644 plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx create mode 100644 plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts create mode 100644 plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx create mode 100644 plugins/octopus-deploy/src/components/ReleaseTable/index.ts create mode 100644 plugins/octopus-deploy/src/constants.ts create mode 100644 plugins/octopus-deploy/src/hooks/useReleases.ts create mode 100644 plugins/octopus-deploy/src/index.ts create mode 100644 plugins/octopus-deploy/src/plugin.test.ts create mode 100644 plugins/octopus-deploy/src/plugin.ts create mode 100644 plugins/octopus-deploy/src/routes.ts create mode 100644 plugins/octopus-deploy/src/setupTests.ts create mode 100644 plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts diff --git a/plugins/octopus-deploy/.eslintrc.js b/plugins/octopus-deploy/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/octopus-deploy/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/octopus-deploy/README.md b/plugins/octopus-deploy/README.md new file mode 100644 index 0000000000..26f8021283 --- /dev/null +++ b/plugins/octopus-deploy/README.md @@ -0,0 +1,11 @@ +# Octopus Deploy Plugin + +Welcome to the octopus-deploy plugin! + +## Features + +- Display the deployment status of the most recent releases for a project in Octopus Deploy straight from the Backstage catalog + +## Getting started + + diff --git a/plugins/octopus-deploy/dev/index.tsx b/plugins/octopus-deploy/dev/index.tsx new file mode 100644 index 0000000000..503cbe6d12 --- /dev/null +++ b/plugins/octopus-deploy/dev/index.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { createDevApp } from '@backstage/dev-utils'; +import { octopusDeployPlugin, OctopusDeployPage } from '../src/plugin'; + +createDevApp() + .registerPlugin(octopusDeployPlugin) + .addPage({ + element: , + title: 'Root Page', + path: '/octopus-deploy' + }) + .render(); diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json new file mode 100644 index 0000000000..8486ed1a32 --- /dev/null +++ b/plugins/octopus-deploy/package.json @@ -0,0 +1,52 @@ +{ + "name": "@backstage/plugin-octopus-deploy", + "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.12.3", + "@backstage/core-plugin-api": "^1.3.0", + "@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.22.1", + "@backstage/core-app-api": "^1.4.0", + "@backstage/dev-utils": "^1.0.11", + "@backstage/test-utils": "^1.2.4", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/node": "*", + "msw": "^0.49.0", + "cross-fetch": "^3.1.5" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/octopus-deploy/src/api/index.ts b/plugins/octopus-deploy/src/api/index.ts new file mode 100644 index 0000000000..b066f700db --- /dev/null +++ b/plugins/octopus-deploy/src/api/index.ts @@ -0,0 +1,88 @@ +import { createApiRef, DiscoveryApi, IdentityApi } from "@backstage/core-plugin-api"; + +export type OctopusProgression = { + Environments: OctopusEnvironment[]; + Releases: OctopusReleaseProgression[]; +} + +export type OctopusEnvironment = { + Id: string, + Name: string +} + +export type OctopusReleaseProgression = { + Release: OctopusRelease + Deployments: { [key: string]: OctopusDeployment[] } +} + +export type OctopusRelease = { + Id: string + Version: string +} + +export type OctopusDeployment = { + State: string +} + +export const octopusDeployApiRef = createApiRef({ + id: 'plugin.octopusdeploy.service' +}); + +const DEFAULT_PROXY_PATH_BASE = '/octopus-deploy'; + +type Options = { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi + /** + * Path to use for requests via the proxy, defaults to /octopus-deploy + */ + proxyPathBase?: string +} + +export interface OctopusDeployApi { + getReleaseProgression(projectId: string, releaseHistoryCount: number): Promise; +} + +export class OctopusDeployClient implements OctopusDeployApi { + private readonly discoveryApi: DiscoveryApi; + private readonly identityApi: IdentityApi; + private readonly proxyPathBase: string; + + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; + this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE; + } + + async getReleaseProgression(projectId: string, releaseHistoryCount: number): Promise { + const url = await this.getApiUrl(projectId, releaseHistoryCount); + + const { token: idToken } = await this.identityApi.getCredentials(); + const response = await fetch(url, { + headers: idToken ? { Authorization: `Bearer ${idToken}` } : {}, + }); + + let responseJson; + + try { + responseJson = await response.json(); + } catch (e) { + responseJson = { releases: [] }; + } + + if (response.status !== 200) { + throw new Error( + `Error communicating with Octopus Deploy: ${ + responseJson?.error?.title || response.statusText + }`, + ); + } + + return responseJson; + } + + private async getApiUrl(projectId: string, releaseHistoryCount: number) { + const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); + return `${proxyUrl}${this.proxyPathBase}/projects/${projectId}/progression?releaseHistoryCount=${releaseHistoryCount}`; + } +} \ No newline at end of file diff --git a/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx new file mode 100644 index 0000000000..d9b6a001c2 --- /dev/null +++ b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx @@ -0,0 +1,15 @@ +import { useEntity } from '@backstage/plugin-catalog-react'; +import { useReleases } from '../../hooks/useReleases'; +import { getAnnotationFromEntity } from '../../utils/getAnnotationFromEntity'; +import React from 'react'; +import { ReleaseTable } from '../ReleaseTable'; + +export const EntityPageOctopusDeploy = (props: { defaultLimit?: number }) => { + const { entity } = useEntity(); + + const projectId = getAnnotationFromEntity(entity); + + const { environments, releases, loading, error } = useReleases(projectId, props.defaultLimit ?? 3); + + return ; +} \ No newline at end of file diff --git a/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts new file mode 100644 index 0000000000..ceed04684b --- /dev/null +++ b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts @@ -0,0 +1 @@ +export { EntityPageOctopusDeploy } from './EntityPageOctopusDeploy'; \ No newline at end of file diff --git a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx new file mode 100644 index 0000000000..21322057d3 --- /dev/null +++ b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx @@ -0,0 +1,123 @@ +import { Box, Typography } from "@material-ui/core"; +import { + OctopusEnvironment, + OctopusReleaseProgression +} from '../../api'; + +import React from 'react'; +import { ResponseErrorPanel, StatusAborted, StatusError, StatusOK, StatusPending, StatusRunning, StatusWarning, Table, TableColumn } from "@backstage/core-components"; + +type ReleaseTableProps = { + environments?: OctopusEnvironment[]; + releases?: OctopusReleaseProgression[]; + loading: boolean; + error?: Error +}; + +export const getDeploymentStatusComponent = (state: string | undefined) => { + switch (state) { + case "Success": + return ( + + Success + + ); + case "Queued": + return ( + + Queued + + ); + case "Executing": + return ( + + Executing + + ); + case "Failed": + return ( + + Failed + + ); + case "Cancelling": + return ( + + Cancelling + + ); + case "Canceled": + return ( + + Canceled + + ); + case "TimedOut": + return ( + + Timed Out + + ); + default: + return ( + + Unknown + + ); + } +} + +export const ReleaseTable = ({environments, releases, loading, error}: ReleaseTableProps) => { + if (error) { + return ( +
+ +
+ ); + } + + const columns: TableColumn[] = [ + { + title: 'Version', + field: 'Release.Version', + highlight: false, + width: 'auto' + }, + ...environments?.map(env => ({ + title: env.Name, + width: 'auto', + render: (row: Partial) => { + const deploymentsForEnvironment = row.Deployments[env.Id]; + if (deploymentsForEnvironment) { + return ( + + + {getDeploymentStatusComponent(deploymentsForEnvironment[0].State)} + + + ) + } + } + //field: `Deployments.${env.Id}[0].State` // TODO: We'll probably need to deal with multi-tenant deployments here + })) ?? [] + ] + + return ( + + Octopus Deploy - Releases ({releases ? releases.length : 0}) + + } + data={releases ?? []} + /> + ) +} \ No newline at end of file diff --git a/plugins/octopus-deploy/src/components/ReleaseTable/index.ts b/plugins/octopus-deploy/src/components/ReleaseTable/index.ts new file mode 100644 index 0000000000..bd0b5bb6b1 --- /dev/null +++ b/plugins/octopus-deploy/src/components/ReleaseTable/index.ts @@ -0,0 +1 @@ +export { ReleaseTable } from './ReleaseTable'; \ No newline at end of file diff --git a/plugins/octopus-deploy/src/constants.ts b/plugins/octopus-deploy/src/constants.ts new file mode 100644 index 0000000000..cec3804d9d --- /dev/null +++ b/plugins/octopus-deploy/src/constants.ts @@ -0,0 +1 @@ +export const OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION = 'octopus.com/project-id'; \ No newline at end of file diff --git a/plugins/octopus-deploy/src/hooks/useReleases.ts b/plugins/octopus-deploy/src/hooks/useReleases.ts new file mode 100644 index 0000000000..ce38bfb286 --- /dev/null +++ b/plugins/octopus-deploy/src/hooks/useReleases.ts @@ -0,0 +1,26 @@ +import { useApi } from "@backstage/core-plugin-api"; +import useAsync from "react-use/lib/useAsync"; +import { octopusDeployApiRef, OctopusEnvironment, OctopusReleaseProgression } from "../api"; + +export function useReleases( + projectId: string, + releaseHistoryCount: number +): { + environments?: OctopusEnvironment[], + releases?: OctopusReleaseProgression[] + loading: boolean; + error?: Error; +} { + const api = useApi(octopusDeployApiRef); + + const { value, loading, error } = useAsync(() => { + return api.getReleaseProgression(projectId, releaseHistoryCount); + }, [ api, projectId, releaseHistoryCount ]); + + return { + environments: value?.Environments, + releases: value?.Releases, + loading, + error + }; +} \ No newline at end of file diff --git a/plugins/octopus-deploy/src/index.ts b/plugins/octopus-deploy/src/index.ts new file mode 100644 index 0000000000..9573438e87 --- /dev/null +++ b/plugins/octopus-deploy/src/index.ts @@ -0,0 +1,7 @@ +export { + octopusDeployPlugin, + EntityOctopusDeployContent, + isOctopusDeployAvailable + } from './plugin'; + + export * from './api'; diff --git a/plugins/octopus-deploy/src/plugin.test.ts b/plugins/octopus-deploy/src/plugin.test.ts new file mode 100644 index 0000000000..ec8e3f260d --- /dev/null +++ b/plugins/octopus-deploy/src/plugin.test.ts @@ -0,0 +1,7 @@ +import { octopusDeployPlugin } from './plugin'; + +describe('octopus-deploy', () => { + it('should export plugin', () => { + expect(octopusDeployPlugin).toBeDefined(); + }); +}); diff --git a/plugins/octopus-deploy/src/plugin.ts b/plugins/octopus-deploy/src/plugin.ts new file mode 100644 index 0000000000..c73f4b3aed --- /dev/null +++ b/plugins/octopus-deploy/src/plugin.ts @@ -0,0 +1,49 @@ +import { + OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION +} from './constants'; +import { + octopusDeployEntityContentRouteRef +} from './routes'; + +import { OctopusDeployClient, octopusDeployApiRef } from './api'; + +import { createApiFactory, createPlugin, createRoutableExtension, discoveryApiRef, identityApiRef } from '@backstage/core-plugin-api'; + +import { Entity } from '@backstage/catalog-model'; + +/** @public */ +export const isOctopusDeployAvailable = (entity: Entity) => + Boolean(entity.metadata.annotations?.[OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION]); + +export const octopusDeployPlugin = createPlugin({ + id: 'octopus-deploy', + apis: [ + createApiFactory({ + api: octopusDeployApiRef, + deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, + factory: ({ discoveryApi, identityApi }) => new OctopusDeployClient({ discoveryApi, identityApi }) + }) + ] +}); + +/* +export const OctopusDeployPage = octopusDeployPlugin.provide( + createRoutableExtension({ + name: 'OctopusDeployPage', + component: () => + import('./components/ExampleComponent').then(m => m.ExampleComponent), + mountPoint: rootRouteRef, + }), +); +*/ + +export const EntityOctopusDeployContent = octopusDeployPlugin.provide( + createRoutableExtension({ + name: 'EntityOctopusDeployContent', + component: () => + import('./components/EntityPageOctopusDeploy').then( + m => m.EntityPageOctopusDeploy + ), + mountPoint: octopusDeployEntityContentRouteRef + }) +) \ No newline at end of file diff --git a/plugins/octopus-deploy/src/routes.ts b/plugins/octopus-deploy/src/routes.ts new file mode 100644 index 0000000000..772db37cb6 --- /dev/null +++ b/plugins/octopus-deploy/src/routes.ts @@ -0,0 +1,5 @@ +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const octopusDeployEntityContentRouteRef = createRouteRef({ + id: 'octopus-deploy-entity-content' +}) diff --git a/plugins/octopus-deploy/src/setupTests.ts b/plugins/octopus-deploy/src/setupTests.ts new file mode 100644 index 0000000000..48c09b5346 --- /dev/null +++ b/plugins/octopus-deploy/src/setupTests.ts @@ -0,0 +1,2 @@ +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts b/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts new file mode 100644 index 0000000000..b1a8984163 --- /dev/null +++ b/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts @@ -0,0 +1,14 @@ +import { + OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION +} from '../constants'; + +import { Entity } from '@backstage/catalog-model'; + +export function getAnnotationFromEntity(entity: Entity): string { + const annotation = entity.metadata.annotations?.[OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION]; + if (!annotation) { + throw new Error('Value for annotation octopus.com/project-id was not found'); + } + + return annotation; +} \ No newline at end of file From 5e5df7135614ccc31ee306fbceb744169489ca6b Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 10:20:10 +0100 Subject: [PATCH 02/23] Some initial docs Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/README.md | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/plugins/octopus-deploy/README.md b/plugins/octopus-deploy/README.md index 26f8021283..a789bfabfb 100644 --- a/plugins/octopus-deploy/README.md +++ b/plugins/octopus-deploy/README.md @@ -8,4 +8,55 @@ Welcome to the octopus-deploy plugin! ## Getting started +This plugin (currently) uses the Backstage proxy to securely communicate with the Octopus Deploy API. + +To use it, you will need to generate an [API Key](https://octopus.com/docs/octopus-rest-api/how-to-create-an-api-key) within Octopus Deploy. + +1. Add the following to your app-config.yaml to enable the proxy: + +``` +// app-config.yaml +proxy: + '/octopus-deploy': + target: '' + headers: + X-Octopus-ApiKey: ${OCTOPUS_API_KEY} +``` + +2. Add the following to `EntityPage.tsx` to display Octopus Releases + +``` +// In packages/app/src/components/catalog/EntityPage.tsx +import { + isOctopusDeployAvailable + EntityOctopusDeployContent +} from '@backstage/plugin-octopus-deploy'; + +const cicdContent = ( + + {/* other components... */} + + + + +) +``` + +3. Add `octopus.com/project-id` annotation in catalog descriptor file + +To obtain a projects ID you will have to query the Octopus API. In the future we'll add support for using a projects slug as well. + +``` +// catalog-info.yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + # ... + annotations: + octopus.com/project-id: Projects-102 +spec: + type: service +``` + +All set , you will be able to see the plugin in action! From c3991a51c8a73b3dbf188698a79187c8cf98f7c6 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 10:40:05 +0100 Subject: [PATCH 03/23] Update yarn lock file Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/package.json | 4 ++-- yarn.lock | 34 +++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 8486ed1a32..0a3c37f0cc 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -43,8 +43,8 @@ "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/node": "*", - "msw": "^0.49.0", - "cross-fetch": "^3.1.5" + "cross-fetch": "^3.1.5", + "msw": "^0.49.0" }, "files": [ "dist" diff --git a/yarn.lock b/yarn.lock index 7e4005f2c1..1a9e8bcfdd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3630,7 +3630,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli@workspace:*, @backstage/cli@workspace:^, @backstage/cli@workspace:packages/cli": +"@backstage/cli@^0.22.1, @backstage/cli@workspace:*, @backstage/cli@workspace:^, @backstage/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@backstage/cli@workspace:packages/cli" dependencies: @@ -3830,7 +3830,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-app-api@workspace:^, @backstage/core-app-api@workspace:packages/core-app-api": +"@backstage/core-app-api@^1.4.0, @backstage/core-app-api@workspace:^, @backstage/core-app-api@workspace:packages/core-app-api": version: 0.0.0-use.local resolution: "@backstage/core-app-api@workspace:packages/core-app-api" dependencies: @@ -3990,7 +3990,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/dev-utils@workspace:^, @backstage/dev-utils@workspace:packages/dev-utils": +"@backstage/dev-utils@^1.0.11, @backstage/dev-utils@workspace:^, @backstage/dev-utils@workspace:packages/dev-utils": version: 0.0.0-use.local resolution: "@backstage/dev-utils@workspace:packages/dev-utils" dependencies: @@ -6949,6 +6949,32 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-octopus-deploy@workspace:plugins/octopus-deploy": + version: 0.0.0-use.local + resolution: "@backstage/plugin-octopus-deploy@workspace:plugins/octopus-deploy" + dependencies: + "@backstage/cli": ^0.22.1 + "@backstage/core-app-api": ^1.4.0 + "@backstage/core-components": ^0.12.3 + "@backstage/core-plugin-api": ^1.3.0 + "@backstage/dev-utils": ^1.0.11 + "@backstage/test-utils": ^1.2.4 + "@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/node": "*" + cross-fetch: ^3.1.5 + msw: ^0.49.0 + react-use: ^17.2.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-org-react@workspace:plugins/org-react": version: 0.0.0-use.local resolution: "@backstage/plugin-org-react@workspace:plugins/org-react" @@ -8612,7 +8638,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/test-utils@workspace:^, @backstage/test-utils@workspace:packages/test-utils": +"@backstage/test-utils@^1.2.4, @backstage/test-utils@workspace:^, @backstage/test-utils@workspace:packages/test-utils": version: 0.0.0-use.local resolution: "@backstage/test-utils@workspace:packages/test-utils" dependencies: From b7fff1171f071352a7b2bf986b8143b3647eba65 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 10:41:21 +0100 Subject: [PATCH 04/23] Add changeset Signed-off-by: Jonathan Mezach --- .changeset/honest-nails-bake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/honest-nails-bake.md diff --git a/.changeset/honest-nails-bake.md b/.changeset/honest-nails-bake.md new file mode 100644 index 0000000000..03d5a080c9 --- /dev/null +++ b/.changeset/honest-nails-bake.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-octopus-deploy': minor +--- + +Initial version From e671c20c61515b5268443256cc12fa9c12f23a42 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 10:54:39 +0100 Subject: [PATCH 05/23] Fix errors Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/dev/index.tsx | 22 +- .../components/ReleaseTable/ReleaseTable.tsx | 259 ++++++++++-------- 2 files changed, 164 insertions(+), 117 deletions(-) diff --git a/plugins/octopus-deploy/dev/index.tsx b/plugins/octopus-deploy/dev/index.tsx index 503cbe6d12..d3d6cb3215 100644 --- a/plugins/octopus-deploy/dev/index.tsx +++ b/plugins/octopus-deploy/dev/index.tsx @@ -1,12 +1,28 @@ +/* + * 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 React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { octopusDeployPlugin, OctopusDeployPage } from '../src/plugin'; +import { octopusDeployPlugin } from '../src/plugin'; +import { EntityPageOctopusDeploy } from '../src/components/EntityPageOctopusDeploy'; createDevApp() .registerPlugin(octopusDeployPlugin) .addPage({ - element: , + element: , title: 'Root Page', - path: '/octopus-deploy' + path: '/octopus-deploy', }) .render(); diff --git a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx index 21322057d3..be2a68a687 100644 --- a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx +++ b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx @@ -1,123 +1,154 @@ -import { Box, Typography } from "@material-ui/core"; -import { - OctopusEnvironment, - OctopusReleaseProgression -} from '../../api'; +/* + * 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 { Box, Typography } from '@material-ui/core'; +import { OctopusEnvironment, OctopusReleaseProgression } from '../../api'; import React from 'react'; -import { ResponseErrorPanel, StatusAborted, StatusError, StatusOK, StatusPending, StatusRunning, StatusWarning, Table, TableColumn } from "@backstage/core-components"; +import { + ResponseErrorPanel, + StatusAborted, + StatusError, + StatusOK, + StatusPending, + StatusRunning, + StatusWarning, + Table, + TableColumn, +} from '@backstage/core-components'; type ReleaseTableProps = { - environments?: OctopusEnvironment[]; - releases?: OctopusReleaseProgression[]; - loading: boolean; - error?: Error + environments?: OctopusEnvironment[]; + releases?: OctopusReleaseProgression[]; + loading: boolean; + error?: Error; }; export const getDeploymentStatusComponent = (state: string | undefined) => { - switch (state) { - case "Success": - return ( - - Success - - ); - case "Queued": - return ( - - Queued - - ); - case "Executing": - return ( - - Executing - - ); - case "Failed": - return ( - - Failed - - ); - case "Cancelling": - return ( - - Cancelling - - ); - case "Canceled": - return ( - - Canceled - - ); - case "TimedOut": - return ( - - Timed Out - - ); - default: - return ( - - Unknown - - ); - } -} - -export const ReleaseTable = ({environments, releases, loading, error}: ReleaseTableProps) => { - if (error) { - return ( -
- -
- ); - } - - const columns: TableColumn[] = [ - { - title: 'Version', - field: 'Release.Version', - highlight: false, - width: 'auto' - }, - ...environments?.map(env => ({ - title: env.Name, - width: 'auto', - render: (row: Partial) => { - const deploymentsForEnvironment = row.Deployments[env.Id]; - if (deploymentsForEnvironment) { - return ( - - - {getDeploymentStatusComponent(deploymentsForEnvironment[0].State)} - - - ) - } - } - //field: `Deployments.${env.Id}[0].State` // TODO: We'll probably need to deal with multi-tenant deployments here - })) ?? [] - ] + switch (state) { + case 'Success': + return ( + + Success + + ); + case 'Queued': + return ( + + Queued + + ); + case 'Executing': + return ( + + Executing + + ); + case 'Failed': + return ( + + Failed + + ); + case 'Cancelling': + return ( + + Cancelling + + ); + case 'Canceled': + return ( + + Canceled + + ); + case 'TimedOut': + return ( + + Timed Out + + ); + default: + return ( + + Unknown + + ); + } +}; +export const ReleaseTable = ({ + environments, + releases, + loading, + error, +}: ReleaseTableProps) => { + if (error) { return ( -
- Octopus Deploy - Releases ({releases ? releases.length : 0}) - - } - data={releases ?? []} - /> - ) -} \ No newline at end of file +
+ +
+ ); + } + + const columns: TableColumn[] = [ + { + title: 'Version', + field: 'Release.Version', + highlight: false, + width: 'auto', + }, + ...(environments?.map(env => ({ + title: env.Name, + width: 'auto', + render: (row: Partial) => { + const deploymentsForEnvironment = row.Deployments + ? row.Deployments[env.Id] + : null; + if (deploymentsForEnvironment) { + return ( + + + {getDeploymentStatusComponent( + deploymentsForEnvironment[0].State, + )} + + + ); + } + return ; + }, + })) ?? []), + ]; + + return ( +
+ Octopus Deploy - Releases ({releases ? releases.length : 0}) + + } + data={releases ?? []} + /> + ); +}; From 09237c3f1441783df68b1c590ad9f5918a025352 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 10:58:19 +0100 Subject: [PATCH 06/23] Use workspace range Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 0a3c37f0cc..059235caf3 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -23,9 +23,9 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.12.3", - "@backstage/core-plugin-api": "^1.3.0", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -35,10 +35,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.22.1", - "@backstage/core-app-api": "^1.4.0", - "@backstage/dev-utils": "^1.0.11", - "@backstage/test-utils": "^1.2.4", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", From 318e1fa26bcf7fcb30ea41b3b8fab26b150fae5e Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 11:59:38 +0100 Subject: [PATCH 07/23] Update yarn lock file Signed-off-by: Jonathan Mezach --- yarn.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1a9e8bcfdd..c732c8c21d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3630,7 +3630,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli@^0.22.1, @backstage/cli@workspace:*, @backstage/cli@workspace:^, @backstage/cli@workspace:packages/cli": +"@backstage/cli@workspace:*, @backstage/cli@workspace:^, @backstage/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@backstage/cli@workspace:packages/cli" dependencies: @@ -3830,7 +3830,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-app-api@^1.4.0, @backstage/core-app-api@workspace:^, @backstage/core-app-api@workspace:packages/core-app-api": +"@backstage/core-app-api@workspace:^, @backstage/core-app-api@workspace:packages/core-app-api": version: 0.0.0-use.local resolution: "@backstage/core-app-api@workspace:packages/core-app-api" dependencies: @@ -3990,7 +3990,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/dev-utils@^1.0.11, @backstage/dev-utils@workspace:^, @backstage/dev-utils@workspace:packages/dev-utils": +"@backstage/dev-utils@workspace:^, @backstage/dev-utils@workspace:packages/dev-utils": version: 0.0.0-use.local resolution: "@backstage/dev-utils@workspace:packages/dev-utils" dependencies: @@ -6953,13 +6953,13 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-octopus-deploy@workspace:plugins/octopus-deploy" dependencies: - "@backstage/cli": ^0.22.1 - "@backstage/core-app-api": ^1.4.0 - "@backstage/core-components": ^0.12.3 - "@backstage/core-plugin-api": ^1.3.0 - "@backstage/dev-utils": ^1.0.11 - "@backstage/test-utils": ^1.2.4 - "@backstage/theme": ^0.2.16 + "@backstage/cli": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" "@material-ui/core": ^4.9.13 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.57 @@ -8638,7 +8638,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/test-utils@^1.2.4, @backstage/test-utils@workspace:^, @backstage/test-utils@workspace:packages/test-utils": +"@backstage/test-utils@workspace:^, @backstage/test-utils@workspace:packages/test-utils": version: 0.0.0-use.local resolution: "@backstage/test-utils@workspace:packages/test-utils" dependencies: From 3a4e7fbb1646178d3ff4097639395ec318c461aa Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 14:05:13 +0100 Subject: [PATCH 08/23] Pretty most files Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/src/api/index.ts | 147 ++++++++++-------- .../EntityPageOctopusDeploy.tsx | 35 ++++- .../EntityPageOctopusDeploy/index.ts | 17 +- .../src/components/ReleaseTable/index.ts | 17 +- plugins/octopus-deploy/src/constants.ts | 17 +- .../octopus-deploy/src/hooks/useReleases.ts | 61 +++++--- plugins/octopus-deploy/src/plugin.ts | 46 ++++-- plugins/octopus-deploy/src/routes.ts | 19 ++- .../src/utils/getAnnotationFromEntity.ts | 36 +++-- 9 files changed, 279 insertions(+), 116 deletions(-) diff --git a/plugins/octopus-deploy/src/api/index.ts b/plugins/octopus-deploy/src/api/index.ts index b066f700db..f89e429f64 100644 --- a/plugins/octopus-deploy/src/api/index.ts +++ b/plugins/octopus-deploy/src/api/index.ts @@ -1,88 +1,113 @@ -import { createApiRef, DiscoveryApi, IdentityApi } from "@backstage/core-plugin-api"; +/* + * 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 { + createApiRef, + DiscoveryApi, + IdentityApi, +} from '@backstage/core-plugin-api'; export type OctopusProgression = { - Environments: OctopusEnvironment[]; - Releases: OctopusReleaseProgression[]; -} + Environments: OctopusEnvironment[]; + Releases: OctopusReleaseProgression[]; +}; export type OctopusEnvironment = { - Id: string, - Name: string -} + Id: string; + Name: string; +}; export type OctopusReleaseProgression = { - Release: OctopusRelease - Deployments: { [key: string]: OctopusDeployment[] } -} + Release: OctopusRelease; + Deployments: { [key: string]: OctopusDeployment[] }; +}; export type OctopusRelease = { - Id: string - Version: string -} + Id: string; + Version: string; +}; export type OctopusDeployment = { - State: string -} + State: string; +}; export const octopusDeployApiRef = createApiRef({ - id: 'plugin.octopusdeploy.service' + id: 'plugin.octopusdeploy.service', }); const DEFAULT_PROXY_PATH_BASE = '/octopus-deploy'; type Options = { - discoveryApi: DiscoveryApi; - identityApi: IdentityApi - /** - * Path to use for requests via the proxy, defaults to /octopus-deploy - */ - proxyPathBase?: string -} + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + /** + * Path to use for requests via the proxy, defaults to /octopus-deploy + */ + proxyPathBase?: string; +}; export interface OctopusDeployApi { - getReleaseProgression(projectId: string, releaseHistoryCount: number): Promise; + getReleaseProgression( + projectId: string, + releaseHistoryCount: number, + ): Promise; } export class OctopusDeployClient implements OctopusDeployApi { - private readonly discoveryApi: DiscoveryApi; - private readonly identityApi: IdentityApi; - private readonly proxyPathBase: string; + private readonly discoveryApi: DiscoveryApi; + private readonly identityApi: IdentityApi; + private readonly proxyPathBase: string; - constructor(options: Options) { - this.discoveryApi = options.discoveryApi; - this.identityApi = options.identityApi; - this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE; + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; + this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE; + } + + async getReleaseProgression( + projectId: string, + releaseHistoryCount: number, + ): Promise { + const url = await this.getApiUrl(projectId, releaseHistoryCount); + + const { token: idToken } = await this.identityApi.getCredentials(); + const response = await fetch(url, { + headers: idToken ? { Authorization: `Bearer ${idToken}` } : {}, + }); + + let responseJson; + + try { + responseJson = await response.json(); + } catch (e) { + responseJson = { releases: [] }; } - async getReleaseProgression(projectId: string, releaseHistoryCount: number): Promise { - const url = await this.getApiUrl(projectId, releaseHistoryCount); - - const { token: idToken } = await this.identityApi.getCredentials(); - const response = await fetch(url, { - headers: idToken ? { Authorization: `Bearer ${idToken}` } : {}, - }); - - let responseJson; - - try { - responseJson = await response.json(); - } catch (e) { - responseJson = { releases: [] }; - } - - if (response.status !== 200) { - throw new Error( - `Error communicating with Octopus Deploy: ${ - responseJson?.error?.title || response.statusText - }`, - ); - } - - return responseJson; + if (response.status !== 200) { + throw new Error( + `Error communicating with Octopus Deploy: ${ + responseJson?.error?.title || response.statusText + }`, + ); } - private async getApiUrl(projectId: string, releaseHistoryCount: number) { - const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); - return `${proxyUrl}${this.proxyPathBase}/projects/${projectId}/progression?releaseHistoryCount=${releaseHistoryCount}`; - } -} \ No newline at end of file + return responseJson; + } + + private async getApiUrl(projectId: string, releaseHistoryCount: number) { + const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); + return `${proxyUrl}${this.proxyPathBase}/projects/${projectId}/progression?releaseHistoryCount=${releaseHistoryCount}`; + } +} diff --git a/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx index d9b6a001c2..de28edb9e0 100644 --- a/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx +++ b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/EntityPageOctopusDeploy.tsx @@ -1,3 +1,18 @@ +/* + * 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 { useEntity } from '@backstage/plugin-catalog-react'; import { useReleases } from '../../hooks/useReleases'; import { getAnnotationFromEntity } from '../../utils/getAnnotationFromEntity'; @@ -5,11 +20,21 @@ import React from 'react'; import { ReleaseTable } from '../ReleaseTable'; export const EntityPageOctopusDeploy = (props: { defaultLimit?: number }) => { - const { entity } = useEntity(); + const { entity } = useEntity(); - const projectId = getAnnotationFromEntity(entity); + const projectId = getAnnotationFromEntity(entity); - const { environments, releases, loading, error } = useReleases(projectId, props.defaultLimit ?? 3); + const { environments, releases, loading, error } = useReleases( + projectId, + props.defaultLimit ?? 3, + ); - return ; -} \ No newline at end of file + return ( + + ); +}; diff --git a/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts index ceed04684b..e168735130 100644 --- a/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts +++ b/plugins/octopus-deploy/src/components/EntityPageOctopusDeploy/index.ts @@ -1 +1,16 @@ -export { EntityPageOctopusDeploy } from './EntityPageOctopusDeploy'; \ No newline at end of file +/* + * 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. + */ +export { EntityPageOctopusDeploy } from './EntityPageOctopusDeploy'; diff --git a/plugins/octopus-deploy/src/components/ReleaseTable/index.ts b/plugins/octopus-deploy/src/components/ReleaseTable/index.ts index bd0b5bb6b1..d0c679a48e 100644 --- a/plugins/octopus-deploy/src/components/ReleaseTable/index.ts +++ b/plugins/octopus-deploy/src/components/ReleaseTable/index.ts @@ -1 +1,16 @@ -export { ReleaseTable } from './ReleaseTable'; \ No newline at end of file +/* + * 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. + */ +export { ReleaseTable } from './ReleaseTable'; diff --git a/plugins/octopus-deploy/src/constants.ts b/plugins/octopus-deploy/src/constants.ts index cec3804d9d..391dacb42c 100644 --- a/plugins/octopus-deploy/src/constants.ts +++ b/plugins/octopus-deploy/src/constants.ts @@ -1 +1,16 @@ -export const OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION = 'octopus.com/project-id'; \ No newline at end of file +/* + * 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. + */ +export const OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION = 'octopus.com/project-id'; diff --git a/plugins/octopus-deploy/src/hooks/useReleases.ts b/plugins/octopus-deploy/src/hooks/useReleases.ts index ce38bfb286..742ce78dca 100644 --- a/plugins/octopus-deploy/src/hooks/useReleases.ts +++ b/plugins/octopus-deploy/src/hooks/useReleases.ts @@ -1,26 +1,45 @@ -import { useApi } from "@backstage/core-plugin-api"; -import useAsync from "react-use/lib/useAsync"; -import { octopusDeployApiRef, OctopusEnvironment, OctopusReleaseProgression } from "../api"; +/* + * 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 { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { + octopusDeployApiRef, + OctopusEnvironment, + OctopusReleaseProgression, +} from '../api'; export function useReleases( - projectId: string, - releaseHistoryCount: number + projectId: string, + releaseHistoryCount: number, ): { - environments?: OctopusEnvironment[], - releases?: OctopusReleaseProgression[] - loading: boolean; - error?: Error; + environments?: OctopusEnvironment[]; + releases?: OctopusReleaseProgression[]; + loading: boolean; + error?: Error; } { - const api = useApi(octopusDeployApiRef); - - const { value, loading, error } = useAsync(() => { - return api.getReleaseProgression(projectId, releaseHistoryCount); - }, [ api, projectId, releaseHistoryCount ]); + const api = useApi(octopusDeployApiRef); - return { - environments: value?.Environments, - releases: value?.Releases, - loading, - error - }; -} \ No newline at end of file + const { value, loading, error } = useAsync(() => { + return api.getReleaseProgression(projectId, releaseHistoryCount); + }, [api, projectId, releaseHistoryCount]); + + return { + environments: value?.Environments, + releases: value?.Releases, + loading, + error, + }; +} diff --git a/plugins/octopus-deploy/src/plugin.ts b/plugins/octopus-deploy/src/plugin.ts index c73f4b3aed..1634da2a9a 100644 --- a/plugins/octopus-deploy/src/plugin.ts +++ b/plugins/octopus-deploy/src/plugin.ts @@ -1,13 +1,30 @@ -import { - OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION -} from './constants'; -import { - octopusDeployEntityContentRouteRef -} from './routes'; +/* + * 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 { OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION } from './constants'; +import { octopusDeployEntityContentRouteRef } from './routes'; import { OctopusDeployClient, octopusDeployApiRef } from './api'; -import { createApiFactory, createPlugin, createRoutableExtension, discoveryApiRef, identityApiRef } from '@backstage/core-plugin-api'; +import { + createApiFactory, + createPlugin, + createRoutableExtension, + discoveryApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; @@ -21,9 +38,10 @@ export const octopusDeployPlugin = createPlugin({ createApiFactory({ api: octopusDeployApiRef, deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, - factory: ({ discoveryApi, identityApi }) => new OctopusDeployClient({ discoveryApi, identityApi }) - }) - ] + factory: ({ discoveryApi, identityApi }) => + new OctopusDeployClient({ discoveryApi, identityApi }), + }), + ], }); /* @@ -42,8 +60,8 @@ export const EntityOctopusDeployContent = octopusDeployPlugin.provide( name: 'EntityOctopusDeployContent', component: () => import('./components/EntityPageOctopusDeploy').then( - m => m.EntityPageOctopusDeploy + m => m.EntityPageOctopusDeploy, ), - mountPoint: octopusDeployEntityContentRouteRef - }) -) \ No newline at end of file + mountPoint: octopusDeployEntityContentRouteRef, + }), +); diff --git a/plugins/octopus-deploy/src/routes.ts b/plugins/octopus-deploy/src/routes.ts index 772db37cb6..824b6ae113 100644 --- a/plugins/octopus-deploy/src/routes.ts +++ b/plugins/octopus-deploy/src/routes.ts @@ -1,5 +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. + */ import { createRouteRef } from '@backstage/core-plugin-api'; export const octopusDeployEntityContentRouteRef = createRouteRef({ - id: 'octopus-deploy-entity-content' -}) + id: 'octopus-deploy-entity-content', +}); diff --git a/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts b/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts index b1a8984163..96ac4d19f7 100644 --- a/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts +++ b/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts @@ -1,14 +1,30 @@ -import { - OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION -} from '../constants'; +/* + * 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 { OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION } from '../constants'; import { Entity } from '@backstage/catalog-model'; export function getAnnotationFromEntity(entity: Entity): string { - const annotation = entity.metadata.annotations?.[OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION]; - if (!annotation) { - throw new Error('Value for annotation octopus.com/project-id was not found'); - } - - return annotation; -} \ No newline at end of file + const annotation = + entity.metadata.annotations?.[OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION]; + if (!annotation) { + throw new Error( + 'Value for annotation octopus.com/project-id was not found', + ); + } + + return annotation; +} From fe5646e63f3740c53093215109496c283634e633 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 15:25:52 +0100 Subject: [PATCH 09/23] More prettying Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/README.md | 1 - plugins/octopus-deploy/src/index.ts | 27 +++++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/plugins/octopus-deploy/README.md b/plugins/octopus-deploy/README.md index a789bfabfb..abc0559896 100644 --- a/plugins/octopus-deploy/README.md +++ b/plugins/octopus-deploy/README.md @@ -59,4 +59,3 @@ spec: ``` All set , you will be able to see the plugin in action! - diff --git a/plugins/octopus-deploy/src/index.ts b/plugins/octopus-deploy/src/index.ts index 9573438e87..f3696853df 100644 --- a/plugins/octopus-deploy/src/index.ts +++ b/plugins/octopus-deploy/src/index.ts @@ -1,7 +1,22 @@ -export { - octopusDeployPlugin, - EntityOctopusDeployContent, - isOctopusDeployAvailable - } from './plugin'; +/* + * 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. + */ +export { + octopusDeployPlugin, + EntityOctopusDeployContent, + isOctopusDeployAvailable, +} from './plugin'; - export * from './api'; +export * from './api'; From d85c4ae7c06f1daa4ec8f3ea43b5f4fdd5a3a8d0 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Wed, 15 Feb 2023 15:27:09 +0100 Subject: [PATCH 10/23] Add notice to test files Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/src/plugin.test.ts | 15 +++++++++++++++ plugins/octopus-deploy/src/setupTests.ts | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/plugins/octopus-deploy/src/plugin.test.ts b/plugins/octopus-deploy/src/plugin.test.ts index ec8e3f260d..a18e74f689 100644 --- a/plugins/octopus-deploy/src/plugin.test.ts +++ b/plugins/octopus-deploy/src/plugin.test.ts @@ -1,3 +1,18 @@ +/* + * 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 { octopusDeployPlugin } from './plugin'; describe('octopus-deploy', () => { diff --git a/plugins/octopus-deploy/src/setupTests.ts b/plugins/octopus-deploy/src/setupTests.ts index 48c09b5346..73dd8dce47 100644 --- a/plugins/octopus-deploy/src/setupTests.ts +++ b/plugins/octopus-deploy/src/setupTests.ts @@ -1,2 +1,17 @@ +/* + * 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 '@testing-library/jest-dom'; import 'cross-fetch/polyfill'; From 350ee5dcb6a6b9037fdb06f26c5f58bda0d6e787 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Thu, 16 Feb 2023 08:26:47 +0100 Subject: [PATCH 11/23] Add an api-report file Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/api-report.md | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 plugins/octopus-deploy/api-report.md diff --git a/plugins/octopus-deploy/api-report.md b/plugins/octopus-deploy/api-report.md new file mode 100644 index 0000000000..84d65804ca --- /dev/null +++ b/plugins/octopus-deploy/api-report.md @@ -0,0 +1,100 @@ +## API Report File for "@backstage/plugin-octopus-deploy" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ApiRef } from '@backstage/core-plugin-api'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; +import { IdentityApi } from '@backstage/core-plugin-api'; + +// Warning: (ae-missing-release-tag) "EntityOctopusDeployContent" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const EntityOctopusDeployContent: (props: { + defaultLimit?: number | undefined; +}) => JSX.Element; + +// @public (undocumented) +export const isOctopusDeployAvailable: (entity: Entity) => boolean; + +// Warning: (ae-missing-release-tag) "OctopusDeployApi" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface OctopusDeployApi { + // (undocumented) + getReleaseProgression( + projectId: string, + releaseHistoryCount: number, + ): Promise; +} + +// Warning: (ae-missing-release-tag) "octopusDeployApiRef" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const octopusDeployApiRef: ApiRef; + +// Warning: (ae-missing-release-tag) "OctopusDeployClient" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class OctopusDeployClient implements OctopusDeployApi { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts + constructor(options: Options); + // (undocumented) + getReleaseProgression( + projectId: string, + releaseHistoryCount: number, + ): Promise; +} + +// Warning: (ae-missing-release-tag) "OctopusDeployment" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type OctopusDeployment = { + State: string; +}; + +// Warning: (ae-missing-release-tag) "octopusDeployPlugin" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const octopusDeployPlugin: BackstagePlugin<{}, {}, {}>; + +// Warning: (ae-missing-release-tag) "OctopusEnvironment" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type OctopusEnvironment = { + Id: string; + Name: string; +}; + +// Warning: (ae-missing-release-tag) "OctopusProgression" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type OctopusProgression = { + Environments: OctopusEnvironment[]; + Releases: OctopusReleaseProgression[]; +}; + +// Warning: (ae-missing-release-tag) "OctopusRelease" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type OctopusRelease = { + Id: string; + Version: string; +}; + +// Warning: (ae-missing-release-tag) "OctopusReleaseProgression" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type OctopusReleaseProgression = { + Release: OctopusRelease; + Deployments: { + [key: string]: OctopusDeployment[]; + }; +}; + +// (No @packageDocumentation comment for this package) +``` From 4faab54915fae7cd414f1610440e40911c7854d3 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Thu, 16 Feb 2023 08:48:56 +0100 Subject: [PATCH 12/23] Fix API report Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/api-report.md | 28 +++++++------------------ plugins/octopus-deploy/src/api/index.ts | 11 +++++++++- plugins/octopus-deploy/src/plugin.ts | 2 ++ 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/plugins/octopus-deploy/api-report.md b/plugins/octopus-deploy/api-report.md index 84d65804ca..563e7ebea4 100644 --- a/plugins/octopus-deploy/api-report.md +++ b/plugins/octopus-deploy/api-report.md @@ -11,8 +11,6 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { IdentityApi } from '@backstage/core-plugin-api'; -// Warning: (ae-missing-release-tag) "EntityOctopusDeployContent" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const EntityOctopusDeployContent: (props: { defaultLimit?: number | undefined; @@ -21,8 +19,6 @@ export const EntityOctopusDeployContent: (props: { // @public (undocumented) export const isOctopusDeployAvailable: (entity: Entity) => boolean; -// Warning: (ae-missing-release-tag) "OctopusDeployApi" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface OctopusDeployApi { // (undocumented) @@ -32,16 +28,11 @@ export interface OctopusDeployApi { ): Promise; } -// Warning: (ae-missing-release-tag) "octopusDeployApiRef" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const octopusDeployApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "OctopusDeployClient" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export class OctopusDeployClient implements OctopusDeployApi { - // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options); // (undocumented) getReleaseProgression( @@ -50,44 +41,32 @@ export class OctopusDeployClient implements OctopusDeployApi { ): Promise; } -// Warning: (ae-missing-release-tag) "OctopusDeployment" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type OctopusDeployment = { State: string; }; -// Warning: (ae-missing-release-tag) "octopusDeployPlugin" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const octopusDeployPlugin: BackstagePlugin<{}, {}, {}>; -// Warning: (ae-missing-release-tag) "OctopusEnvironment" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type OctopusEnvironment = { Id: string; Name: string; }; -// Warning: (ae-missing-release-tag) "OctopusProgression" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type OctopusProgression = { Environments: OctopusEnvironment[]; Releases: OctopusReleaseProgression[]; }; -// Warning: (ae-missing-release-tag) "OctopusRelease" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type OctopusRelease = { Id: string; Version: string; }; -// Warning: (ae-missing-release-tag) "OctopusReleaseProgression" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type OctopusReleaseProgression = { Release: OctopusRelease; @@ -96,5 +75,12 @@ export type OctopusReleaseProgression = { }; }; +// @public (undocumented) +export type Options = { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + proxyPathBase?: string; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/octopus-deploy/src/api/index.ts b/plugins/octopus-deploy/src/api/index.ts index f89e429f64..dcdd881c10 100644 --- a/plugins/octopus-deploy/src/api/index.ts +++ b/plugins/octopus-deploy/src/api/index.ts @@ -19,37 +19,44 @@ import { IdentityApi, } from '@backstage/core-plugin-api'; +/** @public */ export type OctopusProgression = { Environments: OctopusEnvironment[]; Releases: OctopusReleaseProgression[]; }; +/** @public */ export type OctopusEnvironment = { Id: string; Name: string; }; +/** @public */ export type OctopusReleaseProgression = { Release: OctopusRelease; Deployments: { [key: string]: OctopusDeployment[] }; }; +/** @public */ export type OctopusRelease = { Id: string; Version: string; }; +/** @public */ export type OctopusDeployment = { State: string; }; +/** @public */ export const octopusDeployApiRef = createApiRef({ id: 'plugin.octopusdeploy.service', }); const DEFAULT_PROXY_PATH_BASE = '/octopus-deploy'; -type Options = { +/** @public */ +export type Options = { discoveryApi: DiscoveryApi; identityApi: IdentityApi; /** @@ -58,6 +65,7 @@ type Options = { proxyPathBase?: string; }; +/** @public */ export interface OctopusDeployApi { getReleaseProgression( projectId: string, @@ -65,6 +73,7 @@ export interface OctopusDeployApi { ): Promise; } +/** @public */ export class OctopusDeployClient implements OctopusDeployApi { private readonly discoveryApi: DiscoveryApi; private readonly identityApi: IdentityApi; diff --git a/plugins/octopus-deploy/src/plugin.ts b/plugins/octopus-deploy/src/plugin.ts index 1634da2a9a..15bb32c9bd 100644 --- a/plugins/octopus-deploy/src/plugin.ts +++ b/plugins/octopus-deploy/src/plugin.ts @@ -32,6 +32,7 @@ import { Entity } from '@backstage/catalog-model'; export const isOctopusDeployAvailable = (entity: Entity) => Boolean(entity.metadata.annotations?.[OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION]); +/** @public */ export const octopusDeployPlugin = createPlugin({ id: 'octopus-deploy', apis: [ @@ -55,6 +56,7 @@ export const OctopusDeployPage = octopusDeployPlugin.provide( ); */ +/** @public */ export const EntityOctopusDeployContent = octopusDeployPlugin.provide( createRoutableExtension({ name: 'EntityOctopusDeployContent', From 9d00cd45586e8fc6afe1e4a41c9722bd6c03573d Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 15:32:06 +0100 Subject: [PATCH 13/23] Set version to 0.0.0 for now Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 059235caf3..8334b14b00 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-octopus-deploy", - "version": "0.1.0", + "version": "0.0.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", From 95d60d60d4e4f654dc7d2450c9fe4c66d2af485f Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 15:41:10 +0100 Subject: [PATCH 14/23] Don't trust user input Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/src/api/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/octopus-deploy/src/api/index.ts b/plugins/octopus-deploy/src/api/index.ts index dcdd881c10..8a29415dcc 100644 --- a/plugins/octopus-deploy/src/api/index.ts +++ b/plugins/octopus-deploy/src/api/index.ts @@ -117,6 +117,11 @@ export class OctopusDeployClient implements OctopusDeployApi { private async getApiUrl(projectId: string, releaseHistoryCount: number) { const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); - return `${proxyUrl}${this.proxyPathBase}/projects/${projectId}/progression?releaseHistoryCount=${releaseHistoryCount}`; + const queryParameters = new URLSearchParams({ + releaseHistoryCount: releaseHistoryCount.toString(), + }); + return `${proxyUrl}${this.proxyPathBase}/projects/${encodeURIComponent( + projectId, + )}/progression?releaseHistoryCount=${queryParameters}`; } } From 4335910a6186e08fc86a7c56c81f1aa79c495be1 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 15:41:56 +0100 Subject: [PATCH 15/23] Remove unnecessary div Signed-off-by: Jonathan Mezach --- .../src/components/ReleaseTable/ReleaseTable.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx index be2a68a687..dfda291e1a 100644 --- a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx +++ b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx @@ -96,11 +96,7 @@ export const ReleaseTable = ({ error, }: ReleaseTableProps) => { if (error) { - return ( -
- -
- ); + return ; } const columns: TableColumn[] = [ From bf072ab2e890867e8fcabd3562f9a1cd915c7b69 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 15:42:31 +0100 Subject: [PATCH 16/23] Expose annotation constant in API Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/api-report.md | 3 +++ plugins/octopus-deploy/src/constants.ts | 2 ++ plugins/octopus-deploy/src/index.ts | 2 ++ 3 files changed, 7 insertions(+) diff --git a/plugins/octopus-deploy/api-report.md b/plugins/octopus-deploy/api-report.md index 563e7ebea4..48487c46c9 100644 --- a/plugins/octopus-deploy/api-report.md +++ b/plugins/octopus-deploy/api-report.md @@ -19,6 +19,9 @@ export const EntityOctopusDeployContent: (props: { // @public (undocumented) export const isOctopusDeployAvailable: (entity: Entity) => boolean; +// @public (undocumented) +export const OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION = 'octopus.com/project-id'; + // @public (undocumented) export interface OctopusDeployApi { // (undocumented) diff --git a/plugins/octopus-deploy/src/constants.ts b/plugins/octopus-deploy/src/constants.ts index 391dacb42c..3bdc48252f 100644 --- a/plugins/octopus-deploy/src/constants.ts +++ b/plugins/octopus-deploy/src/constants.ts @@ -13,4 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/** @public */ export const OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION = 'octopus.com/project-id'; diff --git a/plugins/octopus-deploy/src/index.ts b/plugins/octopus-deploy/src/index.ts index f3696853df..e3264efaf0 100644 --- a/plugins/octopus-deploy/src/index.ts +++ b/plugins/octopus-deploy/src/index.ts @@ -20,3 +20,5 @@ export { } from './plugin'; export * from './api'; + +export { OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION } from './constants'; From 2f5f5a742dc581c857f71922e8796c5761f97968 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 15:43:26 +0100 Subject: [PATCH 17/23] Remove commented out code Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/src/plugin.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/plugins/octopus-deploy/src/plugin.ts b/plugins/octopus-deploy/src/plugin.ts index 15bb32c9bd..f2ca924032 100644 --- a/plugins/octopus-deploy/src/plugin.ts +++ b/plugins/octopus-deploy/src/plugin.ts @@ -45,17 +45,6 @@ export const octopusDeployPlugin = createPlugin({ ], }); -/* -export const OctopusDeployPage = octopusDeployPlugin.provide( - createRoutableExtension({ - name: 'OctopusDeployPage', - component: () => - import('./components/ExampleComponent').then(m => m.ExampleComponent), - mountPoint: rootRouteRef, - }), -); -*/ - /** @public */ export const EntityOctopusDeployContent = octopusDeployPlugin.provide( createRoutableExtension({ From 0741521aa9480747fb620784c463b5538c12dd58 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 16:03:17 +0100 Subject: [PATCH 18/23] Use constant instead of hardcoded value Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts b/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts index 96ac4d19f7..e59a32235f 100644 --- a/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts +++ b/plugins/octopus-deploy/src/utils/getAnnotationFromEntity.ts @@ -22,7 +22,7 @@ export function getAnnotationFromEntity(entity: Entity): string { entity.metadata.annotations?.[OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION]; if (!annotation) { throw new Error( - 'Value for annotation octopus.com/project-id was not found', + `Value for annotation ${OCTOPUS_DEPLOY_PROJECT_ID_ANNOTATION} was not found`, ); } From ec60e69df4514ee63d4a0f8779584d2f4be587b9 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 17:01:46 +0100 Subject: [PATCH 19/23] Use the fetchApi Fix an issue previously introduced Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/src/api/index.ts | 15 ++++++--------- plugins/octopus-deploy/src/plugin.ts | 8 ++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/plugins/octopus-deploy/src/api/index.ts b/plugins/octopus-deploy/src/api/index.ts index 8a29415dcc..c1a2711b09 100644 --- a/plugins/octopus-deploy/src/api/index.ts +++ b/plugins/octopus-deploy/src/api/index.ts @@ -16,7 +16,7 @@ import { createApiRef, DiscoveryApi, - IdentityApi, + FetchApi, } from '@backstage/core-plugin-api'; /** @public */ @@ -58,7 +58,7 @@ const DEFAULT_PROXY_PATH_BASE = '/octopus-deploy'; /** @public */ export type Options = { discoveryApi: DiscoveryApi; - identityApi: IdentityApi; + fetchApi: FetchApi; /** * Path to use for requests via the proxy, defaults to /octopus-deploy */ @@ -76,12 +76,12 @@ export interface OctopusDeployApi { /** @public */ export class OctopusDeployClient implements OctopusDeployApi { private readonly discoveryApi: DiscoveryApi; - private readonly identityApi: IdentityApi; + private readonly fetchApi: FetchApi; private readonly proxyPathBase: string; constructor(options: Options) { this.discoveryApi = options.discoveryApi; - this.identityApi = options.identityApi; + this.fetchApi = options.fetchApi; this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE; } @@ -91,10 +91,7 @@ export class OctopusDeployClient implements OctopusDeployApi { ): Promise { const url = await this.getApiUrl(projectId, releaseHistoryCount); - const { token: idToken } = await this.identityApi.getCredentials(); - const response = await fetch(url, { - headers: idToken ? { Authorization: `Bearer ${idToken}` } : {}, - }); + const response = await this.fetchApi.fetch(url); let responseJson; @@ -122,6 +119,6 @@ export class OctopusDeployClient implements OctopusDeployApi { }); return `${proxyUrl}${this.proxyPathBase}/projects/${encodeURIComponent( projectId, - )}/progression?releaseHistoryCount=${queryParameters}`; + )}/progression?${queryParameters}`; } } diff --git a/plugins/octopus-deploy/src/plugin.ts b/plugins/octopus-deploy/src/plugin.ts index f2ca924032..9365f93bdb 100644 --- a/plugins/octopus-deploy/src/plugin.ts +++ b/plugins/octopus-deploy/src/plugin.ts @@ -23,7 +23,7 @@ import { createPlugin, createRoutableExtension, discoveryApiRef, - identityApiRef, + fetchApiRef, } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; @@ -38,9 +38,9 @@ export const octopusDeployPlugin = createPlugin({ apis: [ createApiFactory({ api: octopusDeployApiRef, - deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, - factory: ({ discoveryApi, identityApi }) => - new OctopusDeployClient({ discoveryApi, identityApi }), + deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef }, + factory: ({ discoveryApi, fetchApi }) => + new OctopusDeployClient({ discoveryApi, fetchApi }), }), ], }); From d85821f0a8127aae2c6261212aeb81f6aaacec09 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Fri, 17 Feb 2023 17:15:27 +0100 Subject: [PATCH 20/23] Removed options type Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/api-report.md | 15 ++++++--------- plugins/octopus-deploy/src/api/index.ts | 16 +++++----------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/plugins/octopus-deploy/api-report.md b/plugins/octopus-deploy/api-report.md index 48487c46c9..82512ebcef 100644 --- a/plugins/octopus-deploy/api-report.md +++ b/plugins/octopus-deploy/api-report.md @@ -9,7 +9,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import { IdentityApi } from '@backstage/core-plugin-api'; +import { FetchApi } from '@backstage/core-plugin-api'; // @public (undocumented) export const EntityOctopusDeployContent: (props: { @@ -36,7 +36,11 @@ export const octopusDeployApiRef: ApiRef; // @public (undocumented) export class OctopusDeployClient implements OctopusDeployApi { - constructor(options: Options); + constructor(options: { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; + proxyPathBase?: string; + }); // (undocumented) getReleaseProgression( projectId: string, @@ -78,12 +82,5 @@ export type OctopusReleaseProgression = { }; }; -// @public (undocumented) -export type Options = { - discoveryApi: DiscoveryApi; - identityApi: IdentityApi; - proxyPathBase?: string; -}; - // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/octopus-deploy/src/api/index.ts b/plugins/octopus-deploy/src/api/index.ts index c1a2711b09..9cba49d6c0 100644 --- a/plugins/octopus-deploy/src/api/index.ts +++ b/plugins/octopus-deploy/src/api/index.ts @@ -55,16 +55,6 @@ export const octopusDeployApiRef = createApiRef({ const DEFAULT_PROXY_PATH_BASE = '/octopus-deploy'; -/** @public */ -export type Options = { - discoveryApi: DiscoveryApi; - fetchApi: FetchApi; - /** - * Path to use for requests via the proxy, defaults to /octopus-deploy - */ - proxyPathBase?: string; -}; - /** @public */ export interface OctopusDeployApi { getReleaseProgression( @@ -79,7 +69,11 @@ export class OctopusDeployClient implements OctopusDeployApi { private readonly fetchApi: FetchApi; private readonly proxyPathBase: string; - constructor(options: Options) { + constructor(options: { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; + proxyPathBase?: string; + }) { this.discoveryApi = options.discoveryApi; this.fetchApi = options.fetchApi; this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE; From 2bba72864becddd08d92a9d9e6fe1f6b34b5534b Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Mon, 20 Feb 2023 10:58:57 +0100 Subject: [PATCH 21/23] Add missing dependency Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 8334b14b00..d35a178f5e 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -25,6 +25,7 @@ "dependencies": { "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", diff --git a/yarn.lock b/yarn.lock index c732c8c21d..96b7078aaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6958,6 +6958,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.9.13 From 007fa56c0c1e0c858f9b8433ce5eed00f7facb93 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Mon, 20 Feb 2023 12:54:39 +0100 Subject: [PATCH 22/23] Add another missing dependency Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index d35a178f5e..047f325316 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -23,6 +23,7 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/catalog-model": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 96b7078aaa..60f6b8a9e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6953,6 +6953,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-octopus-deploy@workspace:plugins/octopus-deploy" dependencies: + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" From 99b3eff86e53db8f5b2705d8d596c09e1b136043 Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Tue, 21 Feb 2023 15:10:25 +0100 Subject: [PATCH 23/23] Remove private tag from package.json Signed-off-by: Jonathan Mezach --- plugins/octopus-deploy/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 047f325316..3a8e3213ea 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -4,7 +4,6 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.esm.js",