From 285de6291402c4d675b344ed4a38eaf0cf2a8bc7 Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 4 Jun 2023 14:48:59 -0400 Subject: [PATCH] Add job versions card Signed-off-by: josh --- packages/app/src/App.tsx | 2 - .../app/src/components/catalog/EntityPage.tsx | 14 +- plugins/nomad-backend/src/service/router.ts | 78 ++++++---- plugins/nomad/src/Router.tsx | 12 +- plugins/nomad/src/api.ts | 55 +++---- .../EntityNomadAllocationListTable.tsx} | 31 +++- .../EntityNomadJobVersionListCard.tsx | 146 ++++++++++++++++++ .../NomadComponent/NomadComponent.test.tsx | 42 ----- .../NomadComponent/NomadComponent.tsx | 53 ------- .../src/components/NomadComponent/index.ts | 16 -- .../NomadFetchComponent.test.tsx | 40 ----- .../NomadFetchComponent.tsx | 111 ------------- .../{NomadFetchComponent => }/index.ts | 4 +- plugins/nomad/src/index.ts | 9 +- plugins/nomad/src/plugin.ts | 19 --- 15 files changed, 269 insertions(+), 363 deletions(-) rename plugins/nomad/src/components/{AllocationList/AllocationListTable.tsx => EntityNomadAllocationListTable/EntityNomadAllocationListTable.tsx} (87%) create mode 100644 plugins/nomad/src/components/EntityNomadJobVersionListCard/EntityNomadJobVersionListCard.tsx delete mode 100644 plugins/nomad/src/components/NomadComponent/NomadComponent.test.tsx delete mode 100644 plugins/nomad/src/components/NomadComponent/NomadComponent.tsx delete mode 100644 plugins/nomad/src/components/NomadComponent/index.ts delete mode 100644 plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.test.tsx delete mode 100644 plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.tsx rename plugins/nomad/src/components/{NomadFetchComponent => }/index.ts (79%) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 9a9191afc5..df54f37dc6 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -110,7 +110,6 @@ import { ScoreBoardPage } from '@oriflame/backstage-plugin-score-card'; import { StackstormPage } from '@backstage/plugin-stackstorm'; import { PuppetDbPage } from '@backstage/plugin-puppetdb'; import { DevToolsPage } from '@backstage/plugin-devtools'; -import { NomadPage } from '@backstage/plugin-nomad'; import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unprocessed-entities'; @@ -303,7 +302,6 @@ const routes = ( }> {customDevToolsPage} - } /> ); diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index f6950f5c62..043a9fc3aa 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -112,7 +112,10 @@ import { EntityOwnershipCard, EntityUserProfileCard, } from '@backstage/plugin-org'; -import { EntityNomadContent } from '@backstage/plugin-nomad'; +import { + EntityNomadContent, + EntityNomadJobVersionListCard, +} from '@backstage/plugin-nomad'; import { EntityPagerDutyCard, isPagerDutyAvailable, @@ -173,6 +176,7 @@ import { isLinguistAvailable, EntityLinguistCard, } from '@backstage/plugin-linguist'; +import { isNomadJobIDAvailable } from '@backstage/plugin-nomad'; const customEntityFilterKind = ['Component', 'API', 'System']; @@ -443,6 +447,14 @@ const overviewContent = ( + + + + + + + + ); diff --git a/plugins/nomad-backend/src/service/router.ts b/plugins/nomad-backend/src/service/router.ts index 161471f63a..aa863e3493 100644 --- a/plugins/nomad-backend/src/service/router.ts +++ b/plugins/nomad-backend/src/service/router.ts @@ -16,21 +16,32 @@ import { errorHandler, requestLoggingHandler } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; -import express, { RequestHandler } from 'express'; +import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; -const createEndpoint = ( +export interface RouterOptions { + logger: Logger; + config: Config; +} + +export async function createRouter( options: RouterOptions, - endpoint: string, -): RequestHandler => { +): Promise { const { config, logger } = options; // Get Nomad addr and token from config const addr = config.getString('nomad.addr'); const token = config.getOptionalString('nomad.token'); - return async function (req, resp) { + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, resp) => { + resp.json({ status: 'ok' }); + }); + + router.get('/v1/allocations', async (req, resp) => { // Check namespace argument const namespace = (req.query.namespace as string) ?? ''; if (!namespace || namespace === '') { @@ -39,12 +50,7 @@ const createEndpoint = ( // Check filter argument const filter = (req.query.filter as string) ?? ''; - if (!filter || filter === '') { - throw new InputError(`Missing "filter" query parameter`); - } - logger.debug( - `${endpoint} request headers: namespace=${namespace} filter=${filter}`, - ); + logger.debug(`request headers: namespace=${namespace} filter=${filter}`); // Issue the request const allocationsResp = await fetch( @@ -62,27 +68,39 @@ const createEndpoint = ( // Deserialize and return const allocationsBody = await allocationsResp.json(); - logger.debug(`${endpoint} response: ${allocationsBody}`); + logger.debug(`/v1/allocations response: ${allocationsBody}`); resp.json(allocationsBody); - }; -}; - -export interface RouterOptions { - logger: Logger; - config: Config; -} - -export async function createRouter( - options: RouterOptions, -): Promise { - const router = Router(); - router.use(express.json()); - - router.get('/health', (_, resp) => { - resp.json({ status: 'ok' }); }); - router.get('/v1/allocations', createEndpoint(options, '/v1/allocations')); - router.get('/v1/deployments', createEndpoint(options, '/v1/deployments')); + + router.get('/v1/job/:job_id/versions', async (req, resp) => { + // Check namespace argument + const namespace = (req.query.namespace as string) ?? ''; + if (!namespace || namespace === '') { + throw new InputError(`Missing "namespace" query parameter`); + } + + // Get job ID + const jobID = (req.params.job_id as string) ?? ''; + + // Issue the request + const versionsResp = await fetch( + `${addr}/v1/job/${jobID}/versions?namespace=${encodeURIComponent( + namespace, + )}`, + { + method: 'GET', + headers: { + Accept: 'application/json', + 'X-Nomad-Token': token || '', + }, + }, + ); + + // Deserialize and return + const versionsBody = await versionsResp.json(); + logger.debug(`/v1/job/:job_id/versions response: ${versionsBody}`); + resp.json(versionsBody); + }); router.use(requestLoggingHandler()); router.use(errorHandler()); diff --git a/plugins/nomad/src/Router.tsx b/plugins/nomad/src/Router.tsx index 11c79c31d7..dadeda86db 100644 --- a/plugins/nomad/src/Router.tsx +++ b/plugins/nomad/src/Router.tsx @@ -19,7 +19,7 @@ import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; import { MissingAnnotationEmptyState } from '@backstage/core-components'; import { Route, Routes } from 'react-router-dom'; -import { AllocationListTable } from './components/AllocationList/AllocationListTable'; +import { EntityNomadAllocationListTable } from './components/EntityNomadAllocationListTable/EntityNomadAllocationListTable'; /** @public */ export const NOMAD_NAMESPACE_ANNOTATION = 'nomad.io/namespace'; @@ -31,7 +31,11 @@ export const NOMAD_JOB_ID_ANNOTATION = 'nomad.io/job-id'; export const NOMAD_GROUP_ANNOTATION = 'nomad.io/group'; /** @public */ -export const isNomadAvailable = (entity: Entity) => +export const isNomadJobIDAvailable = (entity: Entity) => + Boolean(entity.metadata.annotations?.[NOMAD_JOB_ID_ANNOTATION]); + +/** @public */ +export const isNomadAllocationsAvailable = (entity: Entity) => Boolean(entity.metadata.annotations?.[NOMAD_JOB_ID_ANNOTATION]) || Boolean(entity.metadata.annotations?.[NOMAD_GROUP_ANNOTATION]); @@ -39,7 +43,7 @@ export const isNomadAvailable = (entity: Entity) => export const EmbeddedRouter = () => { const { entity } = useEntity(); - if (!isNomadAvailable(entity)) { + if (!isNomadAllocationsAvailable(entity)) { return ( { return ( - } /> + } /> ); }; diff --git a/plugins/nomad/src/api.ts b/plugins/nomad/src/api.ts index 0cdba4a969..814bba5b6d 100644 --- a/plugins/nomad/src/api.ts +++ b/plugins/nomad/src/api.ts @@ -37,13 +37,13 @@ export type NomadApi = { ) => Promise; /** - * listDeployments is for listing all deployments matching some part of 'filter'. + * listJobVersions is for listing all deployments matching some part of 'filter'. * * See: https://developer.hashicorp.com/nomad/api-docs/deployments#list-deployments */ - listDeployments: ( - options: ListDeploymentsRequest, - ) => Promise; + listJobVersions: ( + options: ListJobVersionsRequest, + ) => Promise; }; /** @public */ @@ -79,37 +79,22 @@ export interface DeploymentStatus { } /** @public */ -export interface ListDeploymentsRequest { +export interface ListJobVersionsRequest { namespace: string; - filter: string; + jobID: string; } /** @public */ -export interface ListDeploymentsResponse { - deployments: Deployment[]; +export interface ListJobVersionsResponse { + versions: Version[]; } /** @public */ -export interface Deployment { +export interface Version { ID: string; - JobID: string; - JobVersion: number; - JobModifyIndex: number; - TaskGroups: { - [taskGroup: string]: { - Promoted: boolean; - DesiredCanaries: number; - DesiredTotal: number; - PlacedAllocs: number; - HealthyAllocs: number; - UnhealthyAllocs: number; - }; - }; - JobSpecModifyIndex: number; - JobCreateIndex: number; - Status: string; - CreateIndex: number; - ModifyIndex: number; + SubmitTime: number; + Stable: boolean; + Version: number; } /** @public */ @@ -154,20 +139,22 @@ export class NomadHttpApi implements NomadApi { } // TODO: pagination - async listDeployments( - options: ListDeploymentsRequest, - ): Promise { + async listJobVersions( + options: ListJobVersionsRequest, + ): Promise { const apiUrl = await this.discoveryApi.getBaseUrl('nomad'); const resp = await this.fetchApi.fetch( - `${apiUrl}/v1/deployments?namespace=${encodeURIComponent( - options.namespace, - )}&filter=${encodeURIComponent(options.filter)}`, + `${apiUrl}/v1/job/${ + options.jobID + }/versions?namespace=${encodeURIComponent(options.namespace)}`, ); if (!resp.ok) throw await FetchError.forResponse(resp); + const respJson = await resp.json(); + return Promise.resolve({ - deployments: await resp.json(), + versions: respJson.Versions, }); } } diff --git a/plugins/nomad/src/components/AllocationList/AllocationListTable.tsx b/plugins/nomad/src/components/EntityNomadAllocationListTable/EntityNomadAllocationListTable.tsx similarity index 87% rename from plugins/nomad/src/components/AllocationList/AllocationListTable.tsx rename to plugins/nomad/src/components/EntityNomadAllocationListTable/EntityNomadAllocationListTable.tsx index 9130eb4007..d07563dd6b 100644 --- a/plugins/nomad/src/components/AllocationList/AllocationListTable.tsx +++ b/plugins/nomad/src/components/EntityNomadAllocationListTable/EntityNomadAllocationListTable.tsx @@ -16,6 +16,7 @@ import { Link, + MissingAnnotationEmptyState, ResponseErrorPanel, StatusError, StatusOK, @@ -32,6 +33,7 @@ import { NOMAD_GROUP_ANNOTATION, NOMAD_JOB_ID_ANNOTATION, NOMAD_NAMESPACE_ANNOTATION, + isNomadAllocationsAvailable, } from '../../Router'; import useAsync from 'react-use/lib/useAsync'; @@ -92,13 +94,14 @@ const columns: TableColumn[] = [ ]; /** - * AllocationListTable is roughly based off Nomad's Allocations tab's view. + * EntityNomadAllocationListTable is roughly based off Nomad's Allocations tab's view. */ -export const AllocationListTable = () => { +export const EntityNomadAllocationListTable = () => { // Wait on entity const { entity } = useEntity(); // Get ref to the backend API + const [init, setInit] = useState(true); const configApi = useApi(configApiRef); const nomadApi = useApi(nomadApiRef); const nomadAddr = configApi.getString('nomad.addr'); @@ -107,6 +110,13 @@ export const AllocationListTable = () => { const [allocations, setAllocations] = useState([]); const [err, setErr] = useState(); + // Check that attributes are available + if (!isNomadAllocationsAvailable(entity)) { + ; + } + // Get plugin attributes const namespace = entity.metadata.annotations?.[NOMAD_NAMESPACE_ANNOTATION] ?? 'default'; @@ -137,10 +147,8 @@ export const AllocationListTable = () => { .sort(({ ClientStatus: a }, { ClientStatus: b }) => { if (a === 'running' || b !== 'running') { return -1; - } else if (a === b || a !== 'running') { - return 0; } - return 1; + return 0; }); setAllocations(results.map(row => ({ ...row, id: row.ID, nomadAddr }))); @@ -148,14 +156,21 @@ export const AllocationListTable = () => { } catch (e) { setAllocations([]); setErr(e); - return; } }; // Start querying for allocations every 5s useAsync(async () => { - query(); - return () => clearInterval(setInterval(query, 5000)); + if (init) { + setInit(false); + query(); + } + + const interval = setTimeout(() => { + query(); + }, 5_000); + + return () => clearTimeout(interval); }, [allocations, entity]); // Store a ref to a potential error diff --git a/plugins/nomad/src/components/EntityNomadJobVersionListCard/EntityNomadJobVersionListCard.tsx b/plugins/nomad/src/components/EntityNomadJobVersionListCard/EntityNomadJobVersionListCard.tsx new file mode 100644 index 0000000000..909fd7a236 --- /dev/null +++ b/plugins/nomad/src/components/EntityNomadJobVersionListCard/EntityNomadJobVersionListCard.tsx @@ -0,0 +1,146 @@ +/* + * Copyright 2020 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 { + InfoCard, + MissingAnnotationEmptyState, + ResponseErrorPanel, + Table, + TableColumn, +} from '@backstage/core-components'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import React, { useEffect, useState } from 'react'; +import { Version, nomadApiRef } from '../../api'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import { + NOMAD_JOB_ID_ANNOTATION, + NOMAD_NAMESPACE_ANNOTATION, + isNomadJobIDAvailable, +} from '../../Router'; +import OpenInNewIcon from '@material-ui/icons/OpenInNew'; +import { Chip } from '@material-ui/core'; + +type rowType = Version & { nomadAddr: string }; + +const columns: TableColumn[] = [ + { + title: 'Version', + field: 'Version', + render: row => row.Version, + }, + { + title: 'Stable', + field: 'Stable', + render: row => , + }, + { + title: 'Submitted', + field: 'SubmitTime', + render: row => new Date(row.SubmitTime / 1000000).toLocaleString(), + }, +]; + +/** + * EntityNomadJobVersionListCard is roughly based on the Nomad UI's versions tab. + */ +export const EntityNomadJobVersionListCard = () => { + // Wait on entity + const { entity } = useEntity(); + + // Get ref to the backend API + const configApi = useApi(configApiRef); + const nomadApi = useApi(nomadApiRef); + const nomadAddr = configApi.getString('nomad.addr'); + + // Store results of calling API + const [init, setInit] = useState(true); + const [versions, setVersions] = useState([]); + const [err, setErr] = useState(); + + // Get plugin attributes + const namespace = + entity.metadata.annotations?.[NOMAD_NAMESPACE_ANNOTATION] ?? 'default'; + const jobID = entity.metadata.annotations?.[NOMAD_JOB_ID_ANNOTATION] ?? ''; + + // Start querying for allocations every 10s + useEffect(() => { + // Create a query to update allocations + const query = async () => { + try { + // Make call to nomad-backend + const resp = await nomadApi.listJobVersions({ namespace, jobID }); + + setVersions( + resp.versions.map(row => ({ ...row, id: row.ID, nomadAddr })), + ); + setErr(undefined); + } catch (e) { + setVersions([]); + setErr(e); + } + }; + + if (init) { + setInit(false); + query(); + } + + const interval = setTimeout(() => { + query(); + }, 10_000); + + return () => clearTimeout(interval); + }, [init, jobID, namespace, nomadAddr, nomadApi]); + + // Store a ref to a potential error + if (err) { + return ; + } + + // Check that job ID is set + if (!isNomadJobIDAvailable(entity)) { + return ( + + + + ); + } + + return ( + + title="Job Versions" + actions={[ + { + icon: () => , + tooltip: 'Open Job Versions Tab in Nomad', + isFreeAction: true, + onClick: () => window.open(`${nomadAddr}/ui/jobs/${jobID}/versions`), + }, + ]} + options={{ + search: false, + padding: 'dense', + sorting: true, + draggable: false, + paging: false, + debounceInterval: 500, + filterCellStyle: { padding: '0 16px 0 20px' }, + }} + columns={columns} + data={versions} + /> + ); +}; diff --git a/plugins/nomad/src/components/NomadComponent/NomadComponent.test.tsx b/plugins/nomad/src/components/NomadComponent/NomadComponent.test.tsx deleted file mode 100644 index 31b7319fd6..0000000000 --- a/plugins/nomad/src/components/NomadComponent/NomadComponent.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 { NomadComponent } from './NomadComponent'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { screen } from '@testing-library/react'; -import { - setupRequestMockHandlers, - renderInTestApp, -} from '@backstage/test-utils'; - -describe('NomadComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - setupRequestMockHandlers(server); - - // setup mock response - beforeEach(() => { - server.use( - rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), - ); - }); - - it('should render', async () => { - await renderInTestApp(); - expect(screen.getByText('Welcome to nomad!')).toBeInTheDocument(); - }); -}); diff --git a/plugins/nomad/src/components/NomadComponent/NomadComponent.tsx b/plugins/nomad/src/components/NomadComponent/NomadComponent.tsx deleted file mode 100644 index cd7334d7eb..0000000000 --- a/plugins/nomad/src/components/NomadComponent/NomadComponent.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - Content, - ContentHeader, - HeaderLabel, - SupportButton, -} from '@backstage/core-components'; -import { NomadFetchComponent } from '../NomadFetchComponent'; - -export const NomadComponent = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - -
-); diff --git a/plugins/nomad/src/components/NomadComponent/index.ts b/plugins/nomad/src/components/NomadComponent/index.ts deleted file mode 100644 index aeecd0d9e6..0000000000 --- a/plugins/nomad/src/components/NomadComponent/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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 { NomadComponent } from './NomadComponent'; diff --git a/plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.test.tsx b/plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.test.tsx deleted file mode 100644 index 48c27dce05..0000000000 --- a/plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 { render, screen } from '@testing-library/react'; -import { NomadFetchComponent } from './NomadFetchComponent'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { setupRequestMockHandlers } from '@backstage/test-utils'; - -describe('NomadFetchComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - setupRequestMockHandlers(server); - - // setup mock response - beforeEach(() => { - server.use( - rest.get('https://randomuser.me/*', (_, res, ctx) => - res(ctx.status(200), ctx.delay(2000), ctx.json({})), - ), - ); - }); - it('should render', async () => { - await render(); - expect(await screen.findByTestId('progress')).toBeInTheDocument(); - }); -}); diff --git a/plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.tsx b/plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.tsx deleted file mode 100644 index 0cd3061a3e..0000000000 --- a/plugins/nomad/src/components/NomadFetchComponent/NomadFetchComponent.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 { makeStyles } from '@material-ui/core/styles'; -import { - Table, - TableColumn, - Progress, - ResponseErrorPanel, -} from '@backstage/core-components'; -import { fetchApiRef, useApi } from '@backstage/core-plugin-api'; -import useAsync from 'react-use/lib/useAsync'; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable = ({ users }: DenseTableProps) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map(user => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( - - ); -}; - -export const NomadFetchComponent = () => { - const { fetch } = useApi(fetchApiRef); - const { value, loading, error } = useAsync(async (): Promise => { - const response = await fetch('https://randomuser.me/api/?results=20'); - const data = await response.json(); - return data.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return ; - } - - return ; -}; diff --git a/plugins/nomad/src/components/NomadFetchComponent/index.ts b/plugins/nomad/src/components/index.ts similarity index 79% rename from plugins/nomad/src/components/NomadFetchComponent/index.ts rename to plugins/nomad/src/components/index.ts index 8e30a21fd1..d113bf73d9 100644 --- a/plugins/nomad/src/components/NomadFetchComponent/index.ts +++ b/plugins/nomad/src/components/index.ts @@ -13,4 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { NomadFetchComponent } from './NomadFetchComponent'; +import { EntityNomadJobVersionListCard } from './EntityNomadJobVersionListCard/EntityNomadJobVersionListCard'; + +export { EntityNomadJobVersionListCard }; diff --git a/plugins/nomad/src/index.ts b/plugins/nomad/src/index.ts index 59ae5efaee..601c133b52 100644 --- a/plugins/nomad/src/index.ts +++ b/plugins/nomad/src/index.ts @@ -13,5 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { nomadPlugin, NomadPage, EntityNomadContent } from './plugin'; -export { isNomadAvailable, EmbeddedRouter } from './Router'; +export { nomadPlugin } from './plugin'; +export { + isNomadAllocationsAvailable, + isNomadJobIDAvailable, + EmbeddedRouter, +} from './Router'; +export { EntityNomadJobVersionListCard } from './components'; diff --git a/plugins/nomad/src/plugin.ts b/plugins/nomad/src/plugin.ts index 1b45e803b4..65181724d5 100644 --- a/plugins/nomad/src/plugin.ts +++ b/plugins/nomad/src/plugin.ts @@ -44,22 +44,3 @@ export const nomadPlugin = createPlugin({ entityContent: entityContentRouteRef, }, }); - -/** @public */ -export const NomadPage = nomadPlugin.provide( - createRoutableExtension({ - name: 'NomadPage', - component: () => - import('./components/NomadComponent').then(m => m.NomadComponent), - mountPoint: rootRouteRef, - }), -); - -/** @public */ -export const EntityNomadContent = nomadPlugin.provide( - createRoutableExtension({ - name: 'EntityNomadContent', - component: () => import('./Router').then(m => m.EmbeddedRouter), - mountPoint: entityContentRouteRef, - }), -);