From 2d7b9079354a2353eda04cdee869103ff60a6374 Mon Sep 17 00:00:00 2001 From: Hubert Date: Mon, 31 Mar 2025 00:30:10 +0200 Subject: [PATCH 1/5] Configs table providers config table --- public/locales/en.json | 10 +- .../ControlPlane/ProvidersConfig.tsx | 58 ++++++++- src/components/ControlPlane/ProvidersList.tsx | 13 -- src/index.css | 4 + src/lib/api/types/crossplane/CRDList.ts | 32 +++++ src/lib/api/useApiResource.ts | 117 +++++++++++++++++- src/lib/shared/types.ts | 31 +++++ src/views/ControlPlanes/ControlPlaneView.tsx | 8 +- 8 files changed, 248 insertions(+), 25 deletions(-) delete mode 100644 src/components/ControlPlane/ProvidersList.tsx create mode 100644 src/lib/api/types/crossplane/CRDList.ts create mode 100644 src/lib/shared/types.ts diff --git a/public/locales/en.json b/public/locales/en.json index d92cd02b..597090b5 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -27,6 +27,13 @@ "tableHeaderSynced": "Synced", "tableHeaderReady": "Ready" }, + "ProvidersConfig": { + "headerProviderConfigs": "Provider Configs", + "tableHeaderProvider": "Provider", + "tableHeaderName": "Name", + "tableHeaderCreated": "Created", + "tableHeaderUsage": "Usage" + }, "ControlPlaneListToolbar": { "buttonText": "Workspace" }, @@ -150,9 +157,6 @@ "tableHeaderInstalled": "Installed", "tableHeaderHealthy": "Healthy" }, - "ProvidersConfig": { - "header": "Provider Configs" - }, "validationErrors": { "required": "This field is required!", "properFormatting": "Use A-Z, a-z, 0-9, hyphen (-), and period (.), but note that whitespace (spaces, tabs, etc.) is not allowed for proper compatibility.", diff --git a/src/components/ControlPlane/ProvidersConfig.tsx b/src/components/ControlPlane/ProvidersConfig.tsx index 74646ac5..38e6f834 100644 --- a/src/components/ControlPlane/ProvidersConfig.tsx +++ b/src/components/ControlPlane/ProvidersConfig.tsx @@ -3,23 +3,69 @@ import { useTranslation } from 'react-i18next'; import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, Title } from '@ui5/webcomponents-react'; import '@ui5/webcomponents-icons/dist/sys-enter-2'; import '@ui5/webcomponents-icons/dist/sys-cancel-2'; +import { useProvidersConfigResource } from '../../lib/api/useApiResource'; +import { timeAgo } from '../../utils/i18n/timeAgo'; + +type Rows = { + parent: string; + name: string; + usage: string; + created: string; +}; -//empty table TBD export function ProvidersConfig() { const { t } = useTranslation(); + const rows: Rows[] = []; + + const { + data: providerConfigsList, + isLoading + } = useProvidersConfigResource({ + refreshInterval: 60000, // Resources are quite expensive to fetch, so we refresh every 60 seconds + }); - const columns: AnalyticalTableColumnDefinition[] = []; + if (providerConfigsList) { + providerConfigsList.forEach(provider => { + provider.items.forEach(config => { + rows.push({ + parent: provider.provider, + name: config.metadata.name, + usage: config.metadata.usage ? config.metadata.usage : "0", + created: timeAgo.format(new Date(config.metadata.creationTimestamp)), + }) + }); + }) + } + + const columns: AnalyticalTableColumnDefinition[] = [ + { + Header: t('ProvidersConfig.tableHeaderProvider'), + accessor: 'parent', + }, + { + Header: t('ProvidersConfig.tableHeaderName'), + accessor: 'name', + }, + { + Header: t('ProvidersConfig.tableHeaderUsage'), + accessor: 'usage', + }, + { + Header: t('ProvidersConfig.tableHeaderCreated'), + accessor: 'created', + } + ]; return ( <> - {t('ProvidersConfig.header')} + {t('ProvidersConfig.headerProviderConfigs')} - - - - - ); -} diff --git a/src/index.css b/src/index.css index 892b2824..d9da15ec 100644 --- a/src/index.css +++ b/src/index.css @@ -94,6 +94,10 @@ background-color: #FFC933; } +.crossplane-table-element { + margin-bottom: 25px; +} + .cp-panel-gitops { background-color: #D1EFFF; } diff --git a/src/lib/api/types/crossplane/CRDList.ts b/src/lib/api/types/crossplane/CRDList.ts new file mode 100644 index 00000000..c6ac22ba --- /dev/null +++ b/src/lib/api/types/crossplane/CRDList.ts @@ -0,0 +1,32 @@ +import { Resource } from '../resource'; + +export type CRDResponse = + { + items: [{ + metadata: { + name: string; + creationTimestamp: string; + }; + status: { + conditions: [{ + type: "Ready" | "Synced" | unknown; + status: "True" | "False"; + lastTransitionTime: string; + }] + }; + spec: { + names: { + kind: string; + }, + versions: [{ + name: string; + }], + group: string; + } + }]; + } +; + +export const CRDRequest: Resource = { + path: '/apis/apiextensions.k8s.io/v1/customresourcedefinitions', +}; diff --git a/src/lib/api/useApiResource.ts b/src/lib/api/useApiResource.ts index 83a4ecc1..9c93f31e 100644 --- a/src/lib/api/useApiResource.ts +++ b/src/lib/api/useApiResource.ts @@ -1,4 +1,4 @@ -import { useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; import useSWR, { SWRConfiguration, useSWRConfig } from 'swr'; import { fetchApiServerJson } from './fetch'; import { ApiConfigContext } from '../../components/Shared/k8s'; @@ -7,6 +7,8 @@ import { ApiConfig } from './types/apiConfig'; import { Resource } from './types/resource'; import useSWRMutation, { SWRMutationConfiguration } from 'swr/mutation'; import { MutatorOptions } from 'swr/_internal'; +import { CRDRequest, CRDResponse } from './types/crossplane/CRDList'; +import { ProviderConfigs, ProviderConfigsData, ProviderConfigsDataForRequest } from '../shared/types'; export { useApiResource as default }; @@ -39,6 +41,119 @@ export const useApiResource = ( }; }; +export const useProvidersConfigResource = ( + config?: SWRConfiguration, +) => { + const apiConfig = useContext(ApiConfigContext); + const { data, error, isValidating } = useSWR( + CRDRequest.path === null + ? null //TODO: is null a valid key? + : [CRDRequest.path, apiConfig], + ([path, apiConfig]) => + fetchApiServerJson( + path, + apiConfig, + CRDRequest.jq, + CRDRequest.method, + CRDRequest.body, + ), + config, + ); + + let providerConfigsDataForRequest: ProviderConfigsDataForRequest[] = []; + + const crdWithProviderConfig = data?.items.filter(x => x.spec.names.kind === "ProviderConfig"); + + const providerConfigsData: ProviderConfigsData[] = crdWithProviderConfig?.map((item) => { + return { + provider: item.metadata.name, + name: item.spec.group, + versions: item.spec.versions, + } + }) ?? []; + + if (providerConfigsData.length > 0) { + providerConfigsData.forEach(item => { + item.versions.forEach(version => { + providerConfigsDataForRequest.push({ + provider: item.provider, + url: item.name, + version: version.name + }) + }) + }); + } + + providerConfigsDataForRequest.forEach(async item => { + const data = + await fetchApiServerJson( + `/apis/${item.url ?? ""}/${item.version}/providerconfigs`, + apiConfig, + CRDRequest.jq, + CRDRequest.method, + CRDRequest.body, + ) + if (data) { + providerConfigs.push(data); + } + }) + + const providerConfigs: ProviderConfigs[] = []; + + const fetchProviderConfigs = async () => { + try { + // Create an array of promises for each fetch call + const fetchPromises = providerConfigsDataForRequest.map(async (item) => { + const data = await fetchApiServerJson( + `/apis/${item.url ?? ""}/${item.version}/providerconfigs`, + apiConfig, + CRDRequest.jq, + CRDRequest.method, + CRDRequest.body + ); + data.provider = item.provider + return data; // Return fetched data + }); + + // Wait for all fetch operations to complete + const providerConfigs = await Promise.all(fetchPromises); + + // Filter out any null/undefined values and return the valid data + return providerConfigs.filter(config => config !== null); + } catch (error) { + console.error('Error fetching provider configs:', error); + return []; // Return an empty array in case of error + } + }; + const [configs, setConfigs] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchDataAndUpdateState = async () => { + setIsLoading(true); + try { + const finalData = await fetchProviderConfigs(); + + setConfigs(finalData); + if (finalData.length > 0) { + setIsLoading(false); + } + } catch (err) { + setIsLoading(false); + } + }; + + fetchDataAndUpdateState(); + }, [data]); + + return { + data: configs, + error: error as APIError, + isLoading: isLoading, + isValidating: isValidating, + }; +} + export const useApiResourceMutation = ( resource: Resource, config?: SWRMutationConfiguration, diff --git a/src/lib/shared/types.ts b/src/lib/shared/types.ts new file mode 100644 index 00000000..262cc390 --- /dev/null +++ b/src/lib/shared/types.ts @@ -0,0 +1,31 @@ +export type ProviderConfigsData = { + provider: string, + name: string, + versions: [{ + name: string + }] + } + +export type ProviderConfigsDataForRequest = { + provider: string, + url: string, + version: string + } + +export type ProviderConfigs = { + provider: string, + items: [ + { + kind: string; + metadata: { + provider: string; + name: string; + usage: string; + creationTimestamp: string; + }; + status: { + count: string; + }; + }, + ]; + } \ No newline at end of file diff --git a/src/views/ControlPlanes/ControlPlaneView.tsx b/src/views/ControlPlanes/ControlPlaneView.tsx index 722f68a5..be19fa88 100644 --- a/src/views/ControlPlanes/ControlPlaneView.tsx +++ b/src/views/ControlPlanes/ControlPlaneView.tsx @@ -17,13 +17,15 @@ import { McpContextProvider, WithinManagedControlPlane, } from '../../lib/shared/McpContext.tsx'; -import ProvidersList from '../../components/ControlPlane/ProvidersList.tsx'; import FluxList from '../../components/ControlPlane/FluxList.tsx'; import { ControlPlane as ControlPlaneResource } from '../../lib/api/types/crate/controlPlanes.ts'; import useResource from '../../lib/api/useApiResource.ts'; import MCPHealthPopoverButton from '../../components/ControlPlane/MCPHealthPopoverButton.tsx'; import ComponentList from '../../components/ControlPlane/ComponentList.tsx'; import { useTranslation } from 'react-i18next'; +import { ManagedResources } from '../../components/ControlPlane/ManagedResources.tsx'; +import { Providers } from '../../components/ControlPlane/Providers.tsx'; +import { ProvidersConfig } from '../../components/ControlPlane/ProvidersConfig.tsx'; export default function ControlPlaneView() { const { projectName, workspaceName, controlPlaneName, contextName } = @@ -122,7 +124,9 @@ export default function ControlPlaneView() { } noAnimation > - +
+
+
Date: Mon, 31 Mar 2025 09:41:55 +0200 Subject: [PATCH 2/5] PR changes Moving function to common file --- src/components/ControlPlane/ManagedResources.tsx | 14 +------------- src/components/ControlPlane/Providers.tsx | 15 +++------------ src/components/Shared/ResourceStatusCell.tsx | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 25 deletions(-) create mode 100644 src/components/Shared/ResourceStatusCell.tsx diff --git a/src/components/ControlPlane/ManagedResources.tsx b/src/components/ControlPlane/ManagedResources.tsx index e20314eb..c2fcbf05 100644 --- a/src/components/ControlPlane/ManagedResources.tsx +++ b/src/components/ControlPlane/ManagedResources.tsx @@ -3,7 +3,6 @@ import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, - Icon, Title, } from '@ui5/webcomponents-react'; import useResource from '../../lib/api/useApiResource'; @@ -13,7 +12,7 @@ import IllustratedError from '../Shared/IllustratedError'; import '@ui5/webcomponents-icons/dist/sys-enter-2'; import '@ui5/webcomponents-icons/dist/sys-cancel-2'; import { resourcesInterval } from '../../lib/shared/constants'; -import { StatusCellProps } from '../../lib/shared/interfaces'; +import { ResourceStatusCell } from '../Shared/ResourceStatusCell'; interface CellData { cell: { @@ -140,14 +139,3 @@ export function ManagedResources() { ); } - -function ResourceStatusCell({ value, transitionTime }: StatusCellProps) { - return ( - - ); -} diff --git a/src/components/ControlPlane/Providers.tsx b/src/components/ControlPlane/Providers.tsx index 92ad9287..4d2792a1 100644 --- a/src/components/ControlPlane/Providers.tsx +++ b/src/components/ControlPlane/Providers.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, Icon, Title } from '@ui5/webcomponents-react'; +import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, Title } from '@ui5/webcomponents-react'; import useResource from '../../lib/api/useApiResource'; import IllustratedError from '../Shared/IllustratedError'; import '@ui5/webcomponents-icons/dist/sys-enter-2'; @@ -8,7 +8,7 @@ import '@ui5/webcomponents-icons/dist/sys-cancel-2'; import { ProvidersListRequest } from '../../lib/api/types/crossplane/listProviders'; import { resourcesInterval } from '../../lib/shared/constants'; import { timeAgo } from '../../utils/i18n/timeAgo'; -import { StatusCellProps } from '../../lib/shared/interfaces'; +import { ResourceStatusCell } from '../Shared/ResourceStatusCell'; interface CellData { cell: { @@ -109,13 +109,4 @@ export function Providers() { } ) -} - -function ResourceStatusCell({ value, transitionTime }: StatusCellProps) { - return -} +} \ No newline at end of file diff --git a/src/components/Shared/ResourceStatusCell.tsx b/src/components/Shared/ResourceStatusCell.tsx new file mode 100644 index 00000000..bcec3f4e --- /dev/null +++ b/src/components/Shared/ResourceStatusCell.tsx @@ -0,0 +1,14 @@ +import { Icon } from "@ui5/webcomponents-react"; +import { StatusCellProps } from "../../lib/shared/interfaces"; +import { timeAgo } from "../../utils/i18n/timeAgo"; + +export function ResourceStatusCell({ value, transitionTime }: StatusCellProps) { + return ( + + ); + } \ No newline at end of file From 35c14f2ab7a2f515b2dba5c145f91fb142af55c3 Mon Sep 17 00:00:00 2001 From: Hubert Date: Mon, 31 Mar 2025 10:57:04 +0200 Subject: [PATCH 3/5] lint issues fix --- src/components/ControlPlane/ComponentList.tsx | 2 +- src/components/ControlPlane/Providers.tsx | 75 +++++---- .../ControlPlane/ProvidersConfig.tsx | 119 +++++++------- .../List/ControlPlaneListToolbar.tsx | 31 ++-- src/components/Shared/ResourceStatusCell.tsx | 24 +-- src/lib/api/types/crossplane/CRDList.ts | 36 +++-- src/lib/api/types/crossplane/listProviders.ts | 30 ++-- src/lib/api/useApiResource.ts | 146 +++++++++--------- src/lib/shared/interfaces.ts | 6 +- src/lib/shared/types.ts | 56 +++---- src/views/ControlPlanes/ControlPlaneView.tsx | 12 +- 11 files changed, 295 insertions(+), 242 deletions(-) diff --git a/src/components/ControlPlane/ComponentList.tsx b/src/components/ControlPlane/ComponentList.tsx index a34aa794..60d128e6 100644 --- a/src/components/ControlPlane/ComponentList.tsx +++ b/src/components/ControlPlane/ComponentList.tsx @@ -49,7 +49,7 @@ export default function ComponentList({ mcp }: { mcp: ControlPlaneType }) { columns={componentTableColumns} minRows={0} data={data} - style={{marginLeft: "12px", marginRight: "12px"}} + style={{ marginLeft: '12px', marginRight: '12px' }} /> ); diff --git a/src/components/ControlPlane/Providers.tsx b/src/components/ControlPlane/Providers.tsx index 4d2792a1..06e5aed9 100644 --- a/src/components/ControlPlane/Providers.tsx +++ b/src/components/ControlPlane/Providers.tsx @@ -1,6 +1,10 @@ - import { useTranslation } from 'react-i18next'; -import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, Title } from '@ui5/webcomponents-react'; +import { + AnalyticalTable, + AnalyticalTableColumnDefinition, + AnalyticalTableScaleWidthMode, + Title, +} from '@ui5/webcomponents-react'; import useResource from '../../lib/api/useApiResource'; import IllustratedError from '../Shared/IllustratedError'; import '@ui5/webcomponents-icons/dist/sys-enter-2'; @@ -15,25 +19,29 @@ interface CellData { value: T | null; // null for grouping rows row: { original?: ProvidersRow; // missing for grouping rows - } + }; }; } type ProvidersRow = { - name: string + name: string; version: string; healthy: boolean; healthyTransitionTime: string; installed: boolean; installedTransitionTime: string; created: string; -} +}; export function Providers() { const { t } = useTranslation(); - let {data: providers, error, isLoading} = useResource(ProvidersListRequest, { - refreshInterval: resourcesInterval + const { + data: providers, + error, + isLoading, + } = useResource(ProvidersListRequest, { + refreshInterval: resourcesInterval, }); const columns: AnalyticalTableColumnDefinition[] = [ @@ -48,12 +56,24 @@ export function Providers() { { Header: t('Providers.tableHeaderInstalled'), accessor: 'installed', - Cell: (cellData: CellData) => cellData.cell.row.original?.installed != null ? : null + Cell: (cellData: CellData) => + cellData.cell.row.original?.installed != null ? ( + + ) : null, }, { Header: t('Providers.tableHeaderHealthy'), accessor: 'healthy', - Cell: (cellData: CellData) => cellData.cell.row.original?.installed != null ? : null + Cell: (cellData: CellData) => + cellData.cell.row.original?.installed != null ? ( + + ) : null, }, { Header: t('Providers.tableHeaderCreated'), @@ -63,28 +83,31 @@ export function Providers() { const rows: ProvidersRow[] = providers?.items?.map((item) => { - const installed = item.status.conditions?.find((condition) => condition.type === 'Installed'); - const healthy = item.status.conditions?.find((condition) => condition.type === 'Healthy'); + const installed = item.status.conditions?.find( + (condition) => condition.type === 'Installed', + ); + const healthy = item.status.conditions?.find( + (condition) => condition.type === 'Healthy', + ); return { name: item.metadata.name, created: timeAgo.format(new Date(item.metadata.creationTimestamp)), - installed: installed?.status === "True", - installedTransitionTime: installed?.lastTransitionTime ?? "", - healthy: healthy?.status === "True", - healthyTransitionTime: healthy?.lastTransitionTime ?? "", - version: item.spec.package.match(/\d+(\.\d+)+/g)?.toString() ?? "", - } - }) - ?? []; + installed: installed?.status === 'True', + installedTransitionTime: installed?.lastTransitionTime ?? '', + healthy: healthy?.status === 'True', + healthyTransitionTime: healthy?.lastTransitionTime ?? '', + version: item.spec.package.match(/\d+(\.\d+)+/g)?.toString() ?? '', + }; + }) ?? []; return ( <> - {t('Providers.headerProviders')} + {t('Providers.headerProviders')} - {error && } + {error && } - {!error && + {!error && ( - } + )} - ) -} \ No newline at end of file + ); +} diff --git a/src/components/ControlPlane/ProvidersConfig.tsx b/src/components/ControlPlane/ProvidersConfig.tsx index 38e6f834..edef4cc8 100644 --- a/src/components/ControlPlane/ProvidersConfig.tsx +++ b/src/components/ControlPlane/ProvidersConfig.tsx @@ -1,6 +1,10 @@ - import { useTranslation } from 'react-i18next'; -import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, Title } from '@ui5/webcomponents-react'; +import { + AnalyticalTable, + AnalyticalTableColumnDefinition, + AnalyticalTableScaleWidthMode, + Title, +} from '@ui5/webcomponents-react'; import '@ui5/webcomponents-icons/dist/sys-enter-2'; import '@ui5/webcomponents-icons/dist/sys-cancel-2'; import { useProvidersConfigResource } from '../../lib/api/useApiResource'; @@ -17,70 +21,67 @@ export function ProvidersConfig() { const { t } = useTranslation(); const rows: Rows[] = []; - const { - data: providerConfigsList, - isLoading - } = useProvidersConfigResource({ + const { data: providerConfigsList, isLoading } = useProvidersConfigResource({ refreshInterval: 60000, // Resources are quite expensive to fetch, so we refresh every 60 seconds }); - if (providerConfigsList) { - providerConfigsList.forEach(provider => { - provider.items.forEach(config => { - rows.push({ - parent: provider.provider, - name: config.metadata.name, - usage: config.metadata.usage ? config.metadata.usage : "0", - created: timeAgo.format(new Date(config.metadata.creationTimestamp)), - }) + if (providerConfigsList) { + providerConfigsList.forEach((provider) => { + provider.items.forEach((config) => { + rows.push({ + parent: provider.provider, + name: config.metadata.name, + usage: config.metadata.usage ? config.metadata.usage : '0', + created: timeAgo.format(new Date(config.metadata.creationTimestamp)), }); - }) - } - + }); + }); + } + const columns: AnalyticalTableColumnDefinition[] = [ - { - Header: t('ProvidersConfig.tableHeaderProvider'), - accessor: 'parent', - }, - { - Header: t('ProvidersConfig.tableHeaderName'), - accessor: 'name', - }, - { - Header: t('ProvidersConfig.tableHeaderUsage'), - accessor: 'usage', - }, - { - Header: t('ProvidersConfig.tableHeaderCreated'), - accessor: 'created', - } - ]; + { + Header: t('ProvidersConfig.tableHeaderProvider'), + accessor: 'parent', + }, + { + Header: t('ProvidersConfig.tableHeaderName'), + accessor: 'name', + }, + { + Header: t('ProvidersConfig.tableHeaderUsage'), + accessor: 'usage', + }, + { + Header: t('ProvidersConfig.tableHeaderCreated'), + accessor: 'created', + }, + ]; return ( <> - {t('ProvidersConfig.headerProviderConfigs')} - + {t('ProvidersConfig.headerProviderConfigs')} + - ) + ); } diff --git a/src/components/ControlPlanes/List/ControlPlaneListToolbar.tsx b/src/components/ControlPlanes/List/ControlPlaneListToolbar.tsx index 0b8578c7..56f9c3ed 100644 --- a/src/components/ControlPlanes/List/ControlPlaneListToolbar.tsx +++ b/src/components/ControlPlanes/List/ControlPlaneListToolbar.tsx @@ -1,21 +1,30 @@ -import { Toolbar, ToolbarButton } from "@ui5/webcomponents-react"; -import { useState } from "react"; +import { Toolbar, ToolbarButton } from '@ui5/webcomponents-react'; +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import {CreateWorkspaceDialogContainer} from "../../Dialogs/CreateWorkspaceDialogContainer.tsx"; +import { CreateWorkspaceDialogContainer } from '../../Dialogs/CreateWorkspaceDialogContainer.tsx'; - - -export function ControlPlaneListToolbar({ projectName }: { projectName: string }) { +export function ControlPlaneListToolbar({ + projectName, +}: { + projectName: string; +}) { const [dialogCreateProjectIsOpen, setDialogIsOpen] = useState(false); const { t } = useTranslation(); return ( <> - setDialogIsOpen(true)} /> + setDialogIsOpen(true)} + /> - + - ) - -} \ No newline at end of file + ); +} diff --git a/src/components/Shared/ResourceStatusCell.tsx b/src/components/Shared/ResourceStatusCell.tsx index bcec3f4e..8062a2fd 100644 --- a/src/components/Shared/ResourceStatusCell.tsx +++ b/src/components/Shared/ResourceStatusCell.tsx @@ -1,14 +1,14 @@ -import { Icon } from "@ui5/webcomponents-react"; -import { StatusCellProps } from "../../lib/shared/interfaces"; -import { timeAgo } from "../../utils/i18n/timeAgo"; +import { Icon } from '@ui5/webcomponents-react'; +import { StatusCellProps } from '../../lib/shared/interfaces'; +import { timeAgo } from '../../utils/i18n/timeAgo'; export function ResourceStatusCell({ value, transitionTime }: StatusCellProps) { - return ( - - ); - } \ No newline at end of file + return ( + + ); +} diff --git a/src/lib/api/types/crossplane/CRDList.ts b/src/lib/api/types/crossplane/CRDList.ts index c6ac22ba..eb3b2951 100644 --- a/src/lib/api/types/crossplane/CRDList.ts +++ b/src/lib/api/types/crossplane/CRDList.ts @@ -1,31 +1,35 @@ import { Resource } from '../resource'; -export type CRDResponse = - { - items: [{ +export type CRDResponse = { + items: [ + { metadata: { name: string; creationTimestamp: string; }; status: { - conditions: [{ - type: "Ready" | "Synced" | unknown; - status: "True" | "False"; - lastTransitionTime: string; - }] + conditions: [ + { + type: 'Ready' | 'Synced' | unknown; + status: 'True' | 'False'; + lastTransitionTime: string; + }, + ]; }; spec: { names: { kind: string; - }, - versions: [{ - name: string; - }], + }; + versions: [ + { + name: string; + }, + ]; group: string; - } - }]; - } -; + }; + }, + ]; +}; export const CRDRequest: Resource = { path: '/apis/apiextensions.k8s.io/v1/customresourcedefinitions', diff --git a/src/lib/api/types/crossplane/listProviders.ts b/src/lib/api/types/crossplane/listProviders.ts index ffd92c4b..e6ccb201 100644 --- a/src/lib/api/types/crossplane/listProviders.ts +++ b/src/lib/api/types/crossplane/listProviders.ts @@ -1,7 +1,8 @@ import { Resource } from '../resource'; - export type ProvidersListResponse = { - items: [{ +export type ProvidersListResponse = { + items: [ + { spec: { package: string; }; @@ -11,15 +12,18 @@ import { Resource } from '../resource'; creationTimestamp: string; }; status: { - conditions: [{ - type: "Healthy" | "Installed" | unknown; - status: "True" | "False"; - lastTransitionTime: string; - }] + conditions: [ + { + type: 'Healthy' | 'Installed' | unknown; + status: 'True' | 'False'; + lastTransitionTime: string; + }, + ]; }; - }]; - }; - - export const ProvidersListRequest: Resource = { - path: "/apis/pkg.crossplane.io/v1/providers", - }; \ No newline at end of file + }, + ]; +}; + +export const ProvidersListRequest: Resource = { + path: '/apis/pkg.crossplane.io/v1/providers', +}; diff --git a/src/lib/api/useApiResource.ts b/src/lib/api/useApiResource.ts index 9c93f31e..42ebe70a 100644 --- a/src/lib/api/useApiResource.ts +++ b/src/lib/api/useApiResource.ts @@ -8,7 +8,11 @@ import { Resource } from './types/resource'; import useSWRMutation, { SWRMutationConfiguration } from 'swr/mutation'; import { MutatorOptions } from 'swr/_internal'; import { CRDRequest, CRDResponse } from './types/crossplane/CRDList'; -import { ProviderConfigs, ProviderConfigsData, ProviderConfigsDataForRequest } from '../shared/types'; +import { + ProviderConfigs, + ProviderConfigsData, + ProviderConfigsDataForRequest, +} from '../shared/types'; export { useApiResource as default }; @@ -41,9 +45,7 @@ export const useApiResource = ( }; }; -export const useProvidersConfigResource = ( - config?: SWRConfiguration, -) => { +export const useProvidersConfigResource = (config?: SWRConfiguration) => { const apiConfig = useContext(ApiConfigContext); const { data, error, isValidating } = useSWR( CRDRequest.path === null @@ -60,91 +62,93 @@ export const useProvidersConfigResource = ( config, ); - let providerConfigsDataForRequest: ProviderConfigsDataForRequest[] = []; + const providerConfigsDataForRequest: ProviderConfigsDataForRequest[] = []; - const crdWithProviderConfig = data?.items.filter(x => x.spec.names.kind === "ProviderConfig"); + const crdWithProviderConfig = data?.items.filter( + (x) => x.spec.names.kind === 'ProviderConfig', + ); - const providerConfigsData: ProviderConfigsData[] = crdWithProviderConfig?.map((item) => { + const providerConfigsData: ProviderConfigsData[] = + crdWithProviderConfig?.map((item) => { return { provider: item.metadata.name, name: item.spec.group, versions: item.spec.versions, - } + }; }) ?? []; if (providerConfigsData.length > 0) { - providerConfigsData.forEach(item => { - item.versions.forEach(version => { + providerConfigsData.forEach((item) => { + item.versions.forEach((version) => { providerConfigsDataForRequest.push({ provider: item.provider, url: item.name, - version: version.name - }) - }) + version: version.name, + }); + }); }); } - providerConfigsDataForRequest.forEach(async item => { - const data = - await fetchApiServerJson( - `/apis/${item.url ?? ""}/${item.version}/providerconfigs`, - apiConfig, - CRDRequest.jq, - CRDRequest.method, - CRDRequest.body, - ) - if (data) { - providerConfigs.push(data); - } - }) - - const providerConfigs: ProviderConfigs[] = []; + providerConfigsDataForRequest.forEach(async (item) => { + const data = await fetchApiServerJson( + `/apis/${item.url ?? ''}/${item.version}/providerconfigs`, + apiConfig, + CRDRequest.jq, + CRDRequest.method, + CRDRequest.body, + ); + if (data) { + providerConfigs.push(data); + } + }); + + const providerConfigs: ProviderConfigs[] = []; + + const fetchProviderConfigs = async () => { + try { + // Create an array of promises for each fetch call + const fetchPromises = providerConfigsDataForRequest.map(async (item) => { + const data = await fetchApiServerJson( + `/apis/${item.url ?? ''}/${item.version}/providerconfigs`, + apiConfig, + CRDRequest.jq, + CRDRequest.method, + CRDRequest.body, + ); + data.provider = item.provider; + return data; // Return fetched data + }); + + // Wait for all fetch operations to complete + const providerConfigs = await Promise.all(fetchPromises); + + // Filter out any null/undefined values and return the valid data + return providerConfigs.filter((config) => config !== null); + } catch (error) { + console.error('Error fetching provider configs:', error); + return []; // Return an empty array in case of error + } + }; + const [configs, setConfigs] = useState([]); + const [isLoading, setIsLoading] = useState(true); - const fetchProviderConfigs = async () => { + useEffect(() => { + const fetchDataAndUpdateState = async () => { + setIsLoading(true); try { - // Create an array of promises for each fetch call - const fetchPromises = providerConfigsDataForRequest.map(async (item) => { - const data = await fetchApiServerJson( - `/apis/${item.url ?? ""}/${item.version}/providerconfigs`, - apiConfig, - CRDRequest.jq, - CRDRequest.method, - CRDRequest.body - ); - data.provider = item.provider - return data; // Return fetched data - }); - - // Wait for all fetch operations to complete - const providerConfigs = await Promise.all(fetchPromises); - - // Filter out any null/undefined values and return the valid data - return providerConfigs.filter(config => config !== null); - } catch (error) { - console.error('Error fetching provider configs:', error); - return []; // Return an empty array in case of error + const finalData = await fetchProviderConfigs(); + + setConfigs(finalData); + if (finalData.length > 0) { + setIsLoading(false); + } + } catch (err) { + setIsLoading(false); } }; - const [configs, setConfigs] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - const fetchDataAndUpdateState = async () => { - setIsLoading(true); - try { - const finalData = await fetchProviderConfigs(); - - setConfigs(finalData); - if (finalData.length > 0) { - setIsLoading(false); - } - } catch (err) { - setIsLoading(false); - } - }; - - fetchDataAndUpdateState(); - }, [data]); + + fetchDataAndUpdateState(); + }, [data]); return { data: configs, @@ -152,7 +156,7 @@ export const useProvidersConfigResource = ( isLoading: isLoading, isValidating: isValidating, }; -} +}; export const useApiResourceMutation = ( resource: Resource, diff --git a/src/lib/shared/interfaces.ts b/src/lib/shared/interfaces.ts index 5493b25d..cba82c1e 100644 --- a/src/lib/shared/interfaces.ts +++ b/src/lib/shared/interfaces.ts @@ -1,4 +1,4 @@ export interface StatusCellProps { - value: boolean; - transitionTime: string; -} \ No newline at end of file + value: boolean; + transitionTime: string; +} diff --git a/src/lib/shared/types.ts b/src/lib/shared/types.ts index 262cc390..6b8726c2 100644 --- a/src/lib/shared/types.ts +++ b/src/lib/shared/types.ts @@ -1,31 +1,33 @@ export type ProviderConfigsData = { - provider: string, - name: string, - versions: [{ - name: string - }] - } - + provider: string; + name: string; + versions: [ + { + name: string; + }, + ]; +}; + export type ProviderConfigsDataForRequest = { - provider: string, - url: string, - version: string - } + provider: string; + url: string; + version: string; +}; export type ProviderConfigs = { - provider: string, - items: [ - { - kind: string; - metadata: { - provider: string; - name: string; - usage: string; - creationTimestamp: string; - }; - status: { - count: string; - }; - }, - ]; - } \ No newline at end of file + provider: string; + items: [ + { + kind: string; + metadata: { + provider: string; + name: string; + usage: string; + creationTimestamp: string; + }; + status: { + count: string; + }; + }, + ]; +}; diff --git a/src/views/ControlPlanes/ControlPlaneView.tsx b/src/views/ControlPlanes/ControlPlaneView.tsx index be19fa88..f4e3ded9 100644 --- a/src/views/ControlPlanes/ControlPlaneView.tsx +++ b/src/views/ControlPlanes/ControlPlaneView.tsx @@ -124,9 +124,15 @@ export default function ControlPlaneView() { } noAnimation > -
-
-
+
+ +
+
+ +
+
+ +
Date: Mon, 31 Mar 2025 11:17:27 +0200 Subject: [PATCH 4/5] fixing name display --- src/lib/api/types/crossplane/CRDList.ts | 6 ++++++ src/lib/api/useApiResource.ts | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib/api/types/crossplane/CRDList.ts b/src/lib/api/types/crossplane/CRDList.ts index eb3b2951..9cbd3259 100644 --- a/src/lib/api/types/crossplane/CRDList.ts +++ b/src/lib/api/types/crossplane/CRDList.ts @@ -6,6 +6,12 @@ export type CRDResponse = { metadata: { name: string; creationTimestamp: string; + ownerReferences: [ + { + kind: string; + name: string; + }, + ]; }; status: { conditions: [ diff --git a/src/lib/api/useApiResource.ts b/src/lib/api/useApiResource.ts index 42ebe70a..ac6544c8 100644 --- a/src/lib/api/useApiResource.ts +++ b/src/lib/api/useApiResource.ts @@ -70,8 +70,12 @@ export const useProvidersConfigResource = (config?: SWRConfiguration) => { const providerConfigsData: ProviderConfigsData[] = crdWithProviderConfig?.map((item) => { + const providerName = item.metadata.ownerReferences.find( + (x) => x.kind === 'Provider', + )?.name; + return { - provider: item.metadata.name, + provider: providerName ? providerName : '', name: item.spec.group, versions: item.spec.versions, }; From 1789a22ba73911bb07a933de9e91c5661435c0ff Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 3 Apr 2025 12:48:39 +0200 Subject: [PATCH 5/5] PR changes --- src/lib/api/useApiResource.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/lib/api/useApiResource.ts b/src/lib/api/useApiResource.ts index ac6544c8..262d140c 100644 --- a/src/lib/api/useApiResource.ts +++ b/src/lib/api/useApiResource.ts @@ -93,18 +93,22 @@ export const useProvidersConfigResource = (config?: SWRConfiguration) => { }); } - providerConfigsDataForRequest.forEach(async (item) => { - const data = await fetchApiServerJson( - `/apis/${item.url ?? ''}/${item.version}/providerconfigs`, - apiConfig, - CRDRequest.jq, - CRDRequest.method, - CRDRequest.body, - ); - if (data) { - providerConfigs.push(data); - } - }); + const fetchProviderConfigsData = async () => { + const promises = providerConfigsDataForRequest.map(async (item) => { + const data = await fetchApiServerJson( + `/apis/${item.url ?? ''}/${item.version}/providerconfigs`, + apiConfig, + CRDRequest.jq, + CRDRequest.method, + CRDRequest.body, + ); + if (data) { + providerConfigs.push(data); + } + }); + + await Promise.all(promises); + }; const providerConfigs: ProviderConfigs[] = []; @@ -140,6 +144,7 @@ export const useProvidersConfigResource = (config?: SWRConfiguration) => { const fetchDataAndUpdateState = async () => { setIsLoading(true); try { + await fetchProviderConfigsData(); const finalData = await fetchProviderConfigs(); setConfigs(finalData);