Add table component

Signed-off-by: josh <josh.timmons@hashicorp.com>
This commit is contained in:
josh
2023-05-29 19:11:52 -04:00
parent 59102979a5
commit 86497612b6
7 changed files with 143 additions and 40 deletions
@@ -112,6 +112,7 @@ import {
EntityOwnershipCard,
EntityUserProfileCard,
} from '@backstage/plugin-org';
import { EntityNomadContent, isNomadAvailable } from '@backstage/plugin-nomad';
import {
EntityPagerDutyCard,
isPagerDutyAvailable,
@@ -501,6 +502,10 @@ const serviceEntityPage = (
<EntityKubernetesContent />
</EntityLayout.Route>
<EntityLayout.Route path="/nomad" title="Nomad">
<EntityNomadContent />
</EntityLayout.Route>
<EntityLayout.Route path="/pull-requests" title="Pull Requests">
{pullRequestsContent}
</EntityLayout.Route>
+4 -5
View File
@@ -21,16 +21,15 @@ import { MissingAnnotationEmptyState } from '@backstage/core-components';
import { Route, Routes } from 'react-router-dom';
import { GroupListForEntity } from './components/GroupList/GroupListForEntity';
/** @public */
export const NOMAD_NAMESPACE_ANNOTATION = 'nomad.io/namespace';
/** @public */
export const NOMAD_GROUP_ANNOTATION = 'nomad.io/group';
/** @public */
export const NOMAD_TASK_ANNOTATION = 'nomad.io/task';
/** @public */
export const isNomadAvailable = (entity: Entity) =>
Boolean(entity.metadata.annotations?.[NOMAD_GROUP_ANNOTATION]) ||
Boolean(entity.metadata.annotations?.[NOMAD_TASK_ANNOTATION]);
Boolean(entity.metadata.annotations?.[NOMAD_GROUP_ANNOTATION]);
/** @public */
export const EmbeddedRouter = () => {
+48 -22
View File
@@ -25,7 +25,7 @@ export const nomadApiRef = createApiRef<NomadApi>({
/** @public */
export type NomadApi = {
/**
* listAllocations is for listing all allocations matching some part of 'prefix'.
* listAllocations is for listing all allocations matching some part of 'filter'.
*
* See: https://developer.hashicorp.com/nomad/api-docs/allocations#list-allocations
*/
@@ -36,8 +36,8 @@ export type NomadApi = {
/** @public */
export interface ListAllocationsRequest {
namespace?: string;
filter?: string;
namespace: string;
filter: string;
}
/** @public */
@@ -47,45 +47,71 @@ export interface ListAllocationsResponse {
/** @public */
export interface Allocation {
id: string;
clientStatus: string;
createTime: number;
namespace: string;
name: string;
modifyTime: number;
nodeID: string;
taskGroup: string;
ClientStatus: string;
CreateTime: number;
DeploymentStatus: DeploymentStatus;
ID: string;
ModifyTime: number;
Name: string;
Namespace: string;
NodeID: string;
TaskGroup: string;
}
/** @public */
export interface DeploymentStatus {
healthy: boolean;
timestamp: string;
Healthy: boolean;
Timestamp: string;
}
/** @public */
export class FetchError extends Error {
get name(): string {
return this.constructor.name;
}
static async forResponse(resp: Response): Promise<FetchError> {
return new FetchError(
`Request failed with status code ${
resp.status
}.\nReason: ${await resp.text()}`,
);
}
}
/** @public */
export class NomadHttpApi implements NomadApi {
static fromConfig(config: Config) {
return new NomadHttpApi(
config.getOptionalString('nomad.nomadAddr'),
config.getOptionalString('nomad.nomadRegion'),
config.getOptionalString('nomad.nomadNamespace'),
config.getOptionalString('nomad.nomadToken'),
config.getOptionalString('nomad.addr'),
config.getOptionalString('nomad.token'),
);
}
constructor(
private nomadAddr?: string,
private nomadRegion?: string,
private nomadNamespace?: string,
private nomadToken?: string,
private addr: string = 'http://127.0.0.1:4646',
private token?: string,
) {}
// TODO: pagination
async listAllocations(
options: ListAllocationsRequest,
): Promise<ListAllocationsResponse> {
const resp = await fetch(
`${this.addr}/v1/allocations?namespace=${encodeURIComponent(
options.namespace,
)}&filter=${encodeURIComponent(options.filter)}`,
{
method: 'GET',
headers: {
'X-Nomad-Token': this.token || '',
},
},
);
if (!resp.ok) throw await FetchError.forResponse(resp);
return Promise.resolve({
allocations: [],
allocations: await resp.json(),
});
}
}
@@ -14,18 +14,20 @@
* limitations under the License.
*/
import { Table } from '@backstage/core-components';
import { Progress } from '@backstage/core-components';
import React from 'react';
import { useGroupForEntity } from '../../hooks/useGroupForEntity';
import { GroupListTable } from './GroupListTable';
export const GroupListForEntity = () => {
return (
<Table
options={{
paging: false,
toolbar: false,
}}
columns={[]}
data={[]}
/>
);
const { value, loading, error } = useGroupForEntity();
if (loading) {
return <Progress />;
}
if (error || !value) {
return null;
}
return <GroupListTable allocations={value.allocations} />;
};
@@ -0,0 +1,47 @@
/*
* 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 { Table, TableColumn } from '@backstage/core-components';
import React from 'react';
import { Allocation } from '../../api';
const columns: TableColumn[] = [
{
title: 'Allocation ID',
field: 'ID',
},
{
title: 'Name',
field: 'Name',
},
];
export const GroupListTable = ({
allocations,
}: {
allocations: Allocation[];
}) => {
return (
<Table
options={{
paging: false,
toolbar: false,
}}
columns={columns}
data={allocations}
/>
);
};
+24 -1
View File
@@ -15,12 +15,35 @@
*/
import { useEntity } from '@backstage/plugin-catalog-react';
import { NOMAD_GROUP_ANNOTATION } from '../Router';
import { NOMAD_GROUP_ANNOTATION, NOMAD_NAMESPACE_ANNOTATION } from '../Router';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { nomadApiRef } from '../api';
import useAsync from 'react-use/lib/useAsync';
/**
* Get the entity's group and query it from the Nomad API.
*/
export const useGroupForEntity = () => {
const { entity } = useEntity();
const namespace =
entity.metadata.annotations?.[NOMAD_NAMESPACE_ANNOTATION] ?? 'default';
const group = entity.metadata.annotations?.[NOMAD_GROUP_ANNOTATION] ?? '';
const nomadApi = useApi(nomadApiRef);
const errorApi = useApi(errorApiRef);
const response = useAsync(
() =>
nomadApi.listAllocations({
namespace,
filter: `TaskGroup == "${group}"`,
}),
[group],
);
if (response.error) {
errorApi.post(response.error);
}
return response;
};
+2 -1
View File
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { nomadPlugin, NomadPage } from './plugin';
export { nomadPlugin, NomadPage, EntityNomadContent } from './plugin';
export { isNomadAvailable, EmbeddedRouter } from './Router';