From b31054394cbc1e6ff43b3af15208d039429fdc60 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Tue, 21 Nov 2023 08:51:08 +0100 Subject: [PATCH 01/12] setup log api generate config --- package.json | 2 +- src/store/configs/log-openapi-config.ts | 13 +++++++++++++ src/store/index.ts | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/store/configs/log-openapi-config.ts diff --git a/package.json b/package.json index c37d0d98f..ff417ea2b 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "deps": "npm run deps:license && npm run deps:stale", "deps:license": "node scripts/deps-license-check.js", "deps:stale": "node scripts/deps-stale-check.js", - "apigen": "npx @rtk-query/codegen-openapi ./src/store/configs/*", "apigen:cost": "npx @rtk-query/codegen-openapi ./src/store/configs/cost-openapi-config.ts", + "apigen:log": "npx @rtk-query/codegen-openapi ./src/store/configs/log-openapi-config.ts", "apigen:scan": "npx @rtk-query/codegen-openapi ./src/store/configs/scan-openapi-config.ts" }, "dependencies": { diff --git a/src/store/configs/log-openapi-config.ts b/src/store/configs/log-openapi-config.ts new file mode 100644 index 000000000..e9bed5d6e --- /dev/null +++ b/src/store/configs/log-openapi-config.ts @@ -0,0 +1,13 @@ +import type { ConfigFile } from '@rtk-query/codegen-openapi'; + +const config: ConfigFile = { + schemaFile: + 'https://server-radix-log-api-qa.radix.equinor.com/swagger/doc.json', + apiFile: '../index.ts', + apiImport: 'logApi', + outputFile: '../log-api.ts', + exportName: 'logApi', + hooks: true, +}; + +export default config; diff --git a/src/store/index.ts b/src/store/index.ts index e714c4425..935e7c2a8 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -6,6 +6,12 @@ export const costApi = createApi({ endpoints: () => ({}), }); +export const logApi = createApi({ + reducerPath: 'logApi', + baseQuery: fetchBaseQuery({ baseUrl: '/log-api' }), + endpoints: () => ({}), +}); + export const scanApi = createApi({ reducerPath: 'scanApi', baseQuery: fetchBaseQuery({ baseUrl: '/scan-api' }), From 8b7f5236d464da071c90cc4b9f4711993c48ea84 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 09:23:16 +0100 Subject: [PATCH 02/12] better status messages --- package-lock.json | 6 ++++++ package.json | 1 + .../async-resource/another-async-resource.tsx | 16 +++++++--------- src/store/types.ts | 13 +++++++++++++ 4 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 src/store/types.ts diff --git a/package-lock.json b/package-lock.json index 2e4af156f..8fb5e842e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.6.2", "clsx": "^2.0.0", "date-fns": "^2.30.0", + "http-status-codes": "^2.3.0", "immutability-helper": "^3.1.1", "jdenticon": "^3.2.0", "lodash": "^4.17.21", @@ -5247,6 +5248,11 @@ "node": ">= 6" } }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==" + }, "node_modules/http2-client": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", diff --git a/package.json b/package.json index ff417ea2b..deb2c973d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "axios": "^1.6.2", "clsx": "^2.0.0", "date-fns": "^2.30.0", + "http-status-codes": "^2.3.0", "immutability-helper": "^3.1.1", "jdenticon": "^3.2.0", "lodash": "^4.17.21", diff --git a/src/components/async-resource/another-async-resource.tsx b/src/components/async-resource/another-async-resource.tsx index 3447d973a..edc003efc 100644 --- a/src/components/async-resource/another-async-resource.tsx +++ b/src/components/async-resource/another-async-resource.tsx @@ -1,19 +1,16 @@ import { CircularProgress, Typography } from '@equinor/eds-core-react'; import { SerializedError } from '@reduxjs/toolkit'; -import { BaseQueryFn, FetchBaseQueryError } from '@reduxjs/toolkit/query'; -import { TypedUseQueryHookResult } from '@reduxjs/toolkit/query/react'; +import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; import React, { FunctionComponent, PropsWithChildren, ReactNode } from 'react'; +import { getReasonPhrase } from 'http-status-codes'; import { Alert } from '../alert'; import { externalUrls } from '../../externalUrls'; import { isNullOrUndefined } from '../../utils/object'; +import { FetchQueryResult } from '../../store/types'; type AnotherAsyncStatus = Pick< - TypedUseQueryHookResult< - unknown, - unknown, - BaseQueryFn<[unknown], unknown, FetchBaseQueryError> - >, + FetchQueryResult, 'error' | 'isError' | 'isLoading' >; @@ -34,7 +31,7 @@ const LoadingComponent: FunctionComponent<{ defaultContent ); -export function getErrorData(error: AnotherAsyncStatus['error']): { +export function getErrorData(error: FetchQueryResult['error']): { code?: string | number; message: string; } { @@ -49,6 +46,7 @@ export function getErrorData(error: AnotherAsyncStatus['error']): { errObj.code = err.status; } else if (err.status === 'PARSING_ERROR') { errObj.code = err.originalStatus; + errObj.message = getReasonPhrase(errObj.code); } } @@ -56,7 +54,7 @@ export function getErrorData(error: AnotherAsyncStatus['error']): { } const ErrorPanel: FunctionComponent< - Required> + Required> > = ({ error }) => { const { message, code } = getErrorData(error); diff --git a/src/store/types.ts b/src/store/types.ts new file mode 100644 index 000000000..7bc3837e3 --- /dev/null +++ b/src/store/types.ts @@ -0,0 +1,13 @@ +import type { BaseQueryFn, FetchBaseQueryError } from '@reduxjs/toolkit/query'; +import type { TypedUseQueryHookResult } from '@reduxjs/toolkit/query/react'; + +export type FetchQueryHookResult = TypedUseQueryHookResult< + T, + unknown, + BaseQueryFn<[unknown], unknown, FetchBaseQueryError> +>; + +export type FetchQueryResult = Omit< + FetchQueryHookResult, + 'refetch' +>; From 73738b39c414ccade5233b018f9718495030c5ae Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 09:23:48 +0100 Subject: [PATCH 03/12] generate log api --- src/store/log-api.ts | 341 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 src/store/log-api.ts diff --git a/src/store/log-api.ts b/src/store/log-api.ts new file mode 100644 index 000000000..c52998168 --- /dev/null +++ b/src/store/log-api.ts @@ -0,0 +1,341 @@ +import { logApi as api } from './index'; +const injectedRtkApi = api.injectEndpoints({ + endpoints: (build) => ({ + getComponentInventory: build.query< + GetComponentInventoryApiResponse, + GetComponentInventoryApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/components/${queryArg.componentName}`, + params: { start: queryArg.start, end: queryArg.end }, + }), + }), + getComponentLog: build.query< + GetComponentLogApiResponse, + GetComponentLogApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/components/${queryArg.componentName}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + getComponentContainerLog: build.query< + GetComponentContainerLogApiResponse, + GetComponentContainerLogApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/components/${queryArg.componentName}/replicas/${queryArg.replicaName}/containers/${queryArg.containerId}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + getComponentReplicaLog: build.query< + GetComponentReplicaLogApiResponse, + GetComponentReplicaLogApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/components/${queryArg.componentName}/replicas/${queryArg.replicaName}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + getJobInventory: build.query< + GetJobInventoryApiResponse, + GetJobInventoryApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/jobcomponents/${queryArg.jobComponentName}/jobs/${queryArg.jobName}`, + params: { start: queryArg.start, end: queryArg.end }, + }), + }), + getJobLog: build.query({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/jobcomponents/${queryArg.jobComponentName}/jobs/${queryArg.jobName}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + getJobContainerLog: build.query< + GetJobContainerLogApiResponse, + GetJobContainerLogApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/jobcomponents/${queryArg.jobComponentName}/jobs/${queryArg.jobName}/replicas/${queryArg.replicaName}/containers/${queryArg.containerId}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + getJobReplicaLog: build.query< + GetJobReplicaLogApiResponse, + GetJobReplicaLogApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/environments/${queryArg.envName}/jobcomponents/${queryArg.jobComponentName}/jobs/${queryArg.jobName}/replicas/${queryArg.replicaName}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + getPipelineJobInventory: build.query< + GetPipelineJobInventoryApiResponse, + GetPipelineJobInventoryApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/pipelinejobs/${queryArg.pipelineJobName}`, + params: { start: queryArg.start, end: queryArg.end }, + }), + }), + getPipelineJobContainerLog: build.query< + GetPipelineJobContainerLogApiResponse, + GetPipelineJobContainerLogApiArg + >({ + query: (queryArg) => ({ + url: `/applications/${queryArg.appName}/pipelinejobs/${queryArg.pipelineJobName}/replicas/${queryArg.replicaName}/containers/${queryArg.containerId}/log`, + params: { + tail: queryArg.tail, + start: queryArg.start, + end: queryArg.end, + file: queryArg.file, + }, + }), + }), + }), + overrideExisting: false, +}); +export { injectedRtkApi as logApi }; +export type GetComponentInventoryApiResponse = + /** status 200 OK */ ModelsInventoryResponse; +export type GetComponentInventoryApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Component Name */ + componentName: string; + /** Start time */ + start?: string; + /** End time */ + end?: string; +}; +export type GetComponentLogApiResponse = unknown; +export type GetComponentLogApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Component Name */ + componentName: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type GetComponentContainerLogApiResponse = unknown; +export type GetComponentContainerLogApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Component Name */ + componentName: string; + /** Replica Name */ + replicaName: string; + /** Container ID */ + containerId: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type GetComponentReplicaLogApiResponse = unknown; +export type GetComponentReplicaLogApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Component Name */ + componentName: string; + /** Replica Name */ + replicaName: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type GetJobInventoryApiResponse = + /** status 200 OK */ ModelsInventoryResponse; +export type GetJobInventoryApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Job Component Name */ + jobComponentName: string; + /** Job Name */ + jobName: string; + /** Start time */ + start?: string; + /** End time */ + end?: string; +}; +export type GetJobLogApiResponse = unknown; +export type GetJobLogApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Job Component Name */ + jobComponentName: string; + /** Job Name */ + jobName: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type GetJobContainerLogApiResponse = unknown; +export type GetJobContainerLogApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Job Component Name */ + jobComponentName: string; + /** Job Name */ + jobName: string; + /** Replica Name */ + replicaName: string; + /** Container ID */ + containerId: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type GetJobReplicaLogApiResponse = unknown; +export type GetJobReplicaLogApiArg = { + /** Application Name */ + appName: string; + /** Environment Name */ + envName: string; + /** Job Component Name */ + jobComponentName: string; + /** Job Name */ + jobName: string; + /** Replica Name */ + replicaName: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type GetPipelineJobInventoryApiResponse = + /** status 200 OK */ ModelsInventoryResponse; +export type GetPipelineJobInventoryApiArg = { + /** Application Name */ + appName: string; + /** Pipeline Job Name */ + pipelineJobName: string; + /** Start time */ + start?: string; + /** End time */ + end?: string; +}; +export type GetPipelineJobContainerLogApiResponse = unknown; +export type GetPipelineJobContainerLogApiArg = { + /** Application Name */ + appName: string; + /** Pipeline Job Name */ + pipelineJobName: string; + /** Replica Name */ + replicaName: string; + /** Container ID */ + containerId: string; + /** Number of rows to return from the tail of the log */ + tail?: number; + /** Start time */ + start?: string; + /** End time */ + end?: string; + /** Response as attachment */ + file?: boolean; +}; +export type ModelsContainer = { + creationTimestamp?: string; + id?: string; + lastKnown?: string; + name?: string; +}; +export type ModelsReplica = { + containers?: ModelsContainer[]; + creationTimestamp?: string; + lastKnown?: string; + name?: string; +}; +export type ModelsInventoryResponse = { + replicas?: ModelsReplica[]; +}; +export type ErrorsStatus = { + message?: string; + reason?: string; +}; +export const { + useGetComponentInventoryQuery, + useGetComponentLogQuery, + useGetComponentContainerLogQuery, + useGetComponentReplicaLogQuery, + useGetJobInventoryQuery, + useGetJobLogQuery, + useGetJobContainerLogQuery, + useGetJobReplicaLogQuery, + useGetPipelineJobInventoryQuery, + useGetPipelineJobContainerLogQuery, +} = injectedRtkApi; From 7dbfc0bc595346d93d0638113adaa0100ce3cc67 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 09:25:09 +0100 Subject: [PATCH 04/12] cleanup --- src/components/async-resource/another-async-resource.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/async-resource/another-async-resource.tsx b/src/components/async-resource/another-async-resource.tsx index edc003efc..1b63ad74a 100644 --- a/src/components/async-resource/another-async-resource.tsx +++ b/src/components/async-resource/another-async-resource.tsx @@ -1,8 +1,8 @@ import { CircularProgress, Typography } from '@equinor/eds-core-react'; import { SerializedError } from '@reduxjs/toolkit'; import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; -import React, { FunctionComponent, PropsWithChildren, ReactNode } from 'react'; import { getReasonPhrase } from 'http-status-codes'; +import React, { FunctionComponent, PropsWithChildren, ReactNode } from 'react'; import { Alert } from '../alert'; import { externalUrls } from '../../externalUrls'; From cbbb7276fd8a7f3c601cc6266a8a4f9660f76f82 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 09:38:46 +0100 Subject: [PATCH 05/12] add logApi to store --- src/store/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/store/index.ts b/src/store/index.ts index 935e7c2a8..9fbcf052f 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,5 +1,13 @@ +import { ResponseHandler } from '@reduxjs/toolkit/dist/query/fetchBaseQuery'; import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; +/** Override for text/plain response handler */ +const responseHandler: ResponseHandler = (response) => { + return response.headers.get('content-type').includes('text/plain') + ? response.text() + : response.json(); +}; + export const costApi = createApi({ reducerPath: 'costApi', baseQuery: fetchBaseQuery({ baseUrl: '/cost-api' }), @@ -8,7 +16,7 @@ export const costApi = createApi({ export const logApi = createApi({ reducerPath: 'logApi', - baseQuery: fetchBaseQuery({ baseUrl: '/log-api' }), + baseQuery: fetchBaseQuery({ baseUrl: '/log-api', responseHandler }), endpoints: () => ({}), }); @@ -18,4 +26,4 @@ export const scanApi = createApi({ endpoints: () => ({}), }); -export default [costApi, scanApi]; +export default [costApi, logApi, scanApi]; From a0d12f4ca53ce0e3396aad7622b87c29ad525865 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 10:01:19 +0100 Subject: [PATCH 06/12] update page active component with new log api --- .../component-replica-log-accordion.tsx | 206 +++++++++--------- .../use-get-component-inventory.ts | 19 -- .../use-get-replica-container-log.ts | 20 -- .../use-get-replica-log.ts | 18 -- 4 files changed, 103 insertions(+), 160 deletions(-) delete mode 100644 src/components/page-active-component/use-get-component-inventory.ts delete mode 100644 src/components/page-active-component/use-get-replica-container-log.ts delete mode 100644 src/components/page-active-component/use-get-replica-log.ts diff --git a/src/components/page-active-component/component-replica-log-accordion.tsx b/src/components/page-active-component/component-replica-log-accordion.tsx index eeffe5233..8e5429a57 100644 --- a/src/components/page-active-component/component-replica-log-accordion.tsx +++ b/src/components/page-active-component/component-replica-log-accordion.tsx @@ -17,18 +17,19 @@ import { useState, } from 'react'; -import { useGetComponentInventory } from './use-get-component-inventory'; -import { useGetReplicaContainerLog } from './use-get-replica-container-log'; -import { useGetReplicaLog } from './use-get-replica-log'; - -import AsyncResource from '../async-resource/simple-async-resource'; +import AsyncResource, { + getErrorData, +} from '../async-resource/another-async-resource'; import { errorToast } from '../global-top-nav/styled-toaster'; import { Duration } from '../time/duration'; import { RelativeToNow } from '../time/relative-to-now'; -import { AsyncState } from '../../effects/effect-types'; -import { ContainerModel } from '../../models/log-api/models/container'; -import { ReplicaModel } from '../../models/log-api/models/replica'; -import { RequestState } from '../../state/state-utils/request-states'; +import { + ModelsContainer, + ModelsReplica, + logApi, + useGetComponentInventoryQuery, +} from '../../store/log-api'; +import { FetchQueryResult } from '../../store/types'; import { sortCompareDate, sortDirection } from '../../utils/sort-utils'; import { copyToTextFile, @@ -53,13 +54,12 @@ export interface ComponentReplicaLogAccordionProps extends ComponentNameProps { } const LogDownloadButton: FunctionComponent<{ - status: RequestState; title?: string; disabled?: boolean; onClick: () => void; -}> = ({ status, ...rest }) => ( - ); -function useSaveLog( - { data, status, error }: AsyncState, +function saveLog( + query: Pick, fileName: string, errMsg = 'Failed to download log' ): void { - useEffect(() => { - if (status === RequestState.SUCCESS) { - const extension = !fileName.endsWith('.txt') ? '.txt' : ''; - copyToTextFile(`${fileName}${extension}`, data); - } else if (status === RequestState.FAILURE) { - errorToast(`${errMsg}: ${error}`); - } - }, [data, errMsg, error, fileName, status]); + if (query.isSuccess) { + const extension = !fileName.endsWith('.txt') ? '.txt' : ''; + copyToTextFile(`${fileName}${extension}`, query.data as string); + } else if (query.isError) { + const { code, message } = getErrorData(query.error); + errorToast(`${errMsg}: ${code && `[${code}] `}${message}`); + } } export const ComponentReplicaLogAccordion: FunctionComponent< ComponentReplicaLogAccordionProps > = ({ appName, envName, componentName, title, isExpanded }) => { - const [componentInventory] = useGetComponentInventory( - appName, - envName, - componentName + const inventory = useGetComponentInventoryQuery( + { appName, envName, componentName }, + { skip: !appName || !envName || !componentName } ); - const [sortedData, setSortedData] = useState>([]); + const [sortedData, setSortedData] = useState>([]); const [dateSort, setDateSort] = useState('descending'); const [expandedRows, setExpandedRows] = useState>({}); @@ -101,15 +99,15 @@ export const ComponentReplicaLogAccordion: FunctionComponent< ); useEffect(() => { - if (componentInventory.status === RequestState.SUCCESS) { + if (inventory.isSuccess) { setSortedData( - tableDataSorter(componentInventory.data?.replicas, [ + tableDataSorter(inventory.data?.replicas, [ (x, y) => sortCompareDate(x.creationTimestamp, y.creationTimestamp, dateSort), ]) ); } - }, [componentInventory.data, componentInventory.status, dateSort]); + }, [inventory.data, inventory.isSuccess, dateSort]); return ( @@ -117,17 +115,13 @@ export const ComponentReplicaLogAccordion: FunctionComponent< - {title} ( - {componentInventory.status === RequestState.IN_PROGRESS - ? '…' - : sortedData.length} - ) + {title} ({inventory.isLoading ? '…' : sortedData.length})
- + {sortedData.length > 0 ? ( @@ -148,42 +142,37 @@ export const ComponentReplicaLogAccordion: FunctionComponent< - {sortedData - .map((x) => ({ - replica: x, - expanded: !!expandedRows[x.name], - })) - .map(({ replica, expanded }) => ( - - expandRow(replica.name)} - {...{ appName, envName, componentName }} - /> + {sortedData.map((replica) => ( + + expandRow(replica.name)} + {...{ appName, envName, componentName }} + /> - {expanded && - replica.containers - ?.sort((a, b) => - sortCompareDate( - a.creationTimestamp, - b.creationTimestamp, - 'descending' - ) + {!!expandedRows[replica.name] && + replica.containers + ?.sort((a, b) => + sortCompareDate( + a.creationTimestamp, + b.creationTimestamp, + 'descending' ) - .map((container, i, { length }) => ( - i, - })} - container={container} - replicaName={replica.name} - {...{ appName, envName, componentName }} - /> - ))} - - ))} + ) + .map((container, i, { length }) => ( + i, + })} + container={container} + replicaName={replica.name} + {...{ appName, envName, componentName }} + /> + ))} + + ))}
) : ( @@ -199,7 +188,7 @@ export const ComponentReplicaLogAccordion: FunctionComponent< const ReplicaLogTableRow: FunctionComponent< { - replica: ReplicaModel; + replica: ModelsReplica; isExpanded: boolean; onClick: () => void; } & ComponentNameProps @@ -207,17 +196,15 @@ const ReplicaLogTableRow: FunctionComponent< appName, envName, componentName, - replica: { containers, name, creationTimestamp, lastKnown }, + replica: { containers, creationTimestamp, lastKnown, name }, isExpanded, onClick, }) => { - const [replicaLog, getReplicaLog] = useGetReplicaLog( - appName, - envName, - componentName, - name - ); - useSaveLog(replicaLog, `${appName}_${envName}_${componentName}_${name}`); + const [getLog, { isFetching }] = + logApi.endpoints.getComponentReplicaLog.useLazyQuery(); + + const created = new Date(creationTimestamp); + const ended = new Date(lastKnown); return ( @@ -236,17 +223,26 @@ const ReplicaLogTableRow: FunctionComponent< {containers?.length || 0} - + - {Duration({ start: creationTimestamp, end: lastKnown }) || 'N/A'} + {Duration({ start: created, end: ended }) || 'N/A'} getReplicaLog()} - disabled={replicaLog.status === RequestState.IN_PROGRESS} + onClick={async () => { + const response = await getLog({ + appName, + envName, + componentName, + replicaName: name, + }); + + const fileName = `${appName}_${envName}_${componentName}_${name}`; + saveLog(response, fileName); + }} + disabled={isFetching} /> @@ -257,7 +253,7 @@ const ReplicaContainerTableRow: FunctionComponent< { className?: string; replicaName: string; - container: ContainerModel; + container: ModelsContainer; } & ComponentNameProps > = ({ className, @@ -265,19 +261,13 @@ const ReplicaContainerTableRow: FunctionComponent< componentName, envName, replicaName, - container: { id, creationTimestamp, lastKnown }, + container: { creationTimestamp, id, lastKnown }, }) => { - const [containerLog, getReplicaContainerLog] = useGetReplicaContainerLog( - appName, - envName, - componentName, - replicaName, - id - ); - useSaveLog( - containerLog, - `${appName}_${envName}_${componentName}_${replicaName}_${id}` - ); + const [getLog, { isFetching }] = + logApi.endpoints.getComponentContainerLog.useLazyQuery(); + + const created = new Date(creationTimestamp); + const ended = new Date(lastKnown); return ( @@ -295,17 +285,27 @@ const ReplicaContainerTableRow: FunctionComponent< - + - + getReplicaContainerLog()} - disabled={containerLog.status === RequestState.IN_PROGRESS} + onClick={async () => { + const response = await getLog({ + appName, + envName, + componentName, + replicaName, + containerId: id, + }); + + const fileName = `${appName}_${envName}_${componentName}_${replicaName}_${id}`; + saveLog(response, fileName); + }} + disabled={isFetching} /> diff --git a/src/components/page-active-component/use-get-component-inventory.ts b/src/components/page-active-component/use-get-component-inventory.ts deleted file mode 100644 index 42f1008cf..000000000 --- a/src/components/page-active-component/use-get-component-inventory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useFetchLogJson } from '../../effects'; -import { AsyncLoadingResult } from '../../effects/use-async-loading'; -import { InventoryResponseModel } from '../../models/log-api/models/inventory-response'; -import { InventoryResponseModelNormalizer } from '../../models/log-api/models/inventory-response/normalizer'; - -export function useGetComponentInventory( - appName: string, - envName: string, - componentName: string -): AsyncLoadingResult> { - const encAppName = encodeURIComponent(appName); - const encEnvName = encodeURIComponent(envName); - const encComponentName = encodeURIComponent(componentName); - - return useFetchLogJson( - `/applications/${encAppName}/environments/${encEnvName}/components/${encComponentName}`, - InventoryResponseModelNormalizer - ); -} diff --git a/src/components/page-active-component/use-get-replica-container-log.ts b/src/components/page-active-component/use-get-replica-container-log.ts deleted file mode 100644 index 030b3af8a..000000000 --- a/src/components/page-active-component/use-get-replica-container-log.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useGetLogPlain } from '../../effects'; -import { AsyncRequestResult } from '../../effects/use-async-request'; - -export function useGetReplicaContainerLog( - appName: string, - envName: string, - componentName: string, - replicaName: string, - containerId: string -): AsyncRequestResult { - const encAppName = encodeURIComponent(appName); - const encEnvName = encodeURIComponent(envName); - const encComponentName = encodeURIComponent(componentName); - const encReplicaName = encodeURIComponent(replicaName); - const encContainerId = encodeURIComponent(containerId); - - return useGetLogPlain( - `/applications/${encAppName}/environments/${encEnvName}/components/${encComponentName}/replicas/${encReplicaName}/containers/${encContainerId}/log` - ); -} diff --git a/src/components/page-active-component/use-get-replica-log.ts b/src/components/page-active-component/use-get-replica-log.ts deleted file mode 100644 index 0567a7290..000000000 --- a/src/components/page-active-component/use-get-replica-log.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useGetLogPlain } from '../../effects'; -import { AsyncRequestResult } from '../../effects/use-async-request'; - -export function useGetReplicaLog( - appName: string, - envName: string, - componentName: string, - replicaName: string -): AsyncRequestResult { - const encAppName = encodeURIComponent(appName); - const encEnvName = encodeURIComponent(envName); - const encComponentName = encodeURIComponent(componentName); - const encReplicaName = encodeURIComponent(replicaName); - - return useGetLogPlain( - `/applications/${encAppName}/environments/${encEnvName}/components/${encComponentName}/replicas/${encReplicaName}/log` - ); -} From dcb86d947ca5eede0225c45d4743ca7291cb5dec Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 10:33:37 +0100 Subject: [PATCH 07/12] update page scheduled job with new log api --- .../replica-log-accordion.tsx | 238 +++++++++--------- .../use-get-job-inventory.ts | 32 --- .../use-get-replica-container-log.ts | 22 -- .../page-scheduled-job/use-get-replica-log.ts | 20 -- 4 files changed, 124 insertions(+), 188 deletions(-) delete mode 100644 src/components/page-scheduled-job/use-get-job-inventory.ts delete mode 100644 src/components/page-scheduled-job/use-get-replica-container-log.ts delete mode 100644 src/components/page-scheduled-job/use-get-replica-log.ts diff --git a/src/components/page-scheduled-job/replica-log-accordion.tsx b/src/components/page-scheduled-job/replica-log-accordion.tsx index 8edff40ed..419f29658 100644 --- a/src/components/page-scheduled-job/replica-log-accordion.tsx +++ b/src/components/page-scheduled-job/replica-log-accordion.tsx @@ -17,18 +17,20 @@ import { useState, } from 'react'; -import { useGetJobInventory } from './use-get-job-inventory'; -import { useGetReplicaContainerLog } from './use-get-replica-container-log'; -import { useGetReplicaLog } from './use-get-replica-log'; - -import AsyncResource from '../async-resource/simple-async-resource'; -import { AsyncState } from '../../effects/effect-types'; +import AsyncResource, { + getErrorData, +} from '../async-resource/another-async-resource'; import { errorToast } from '../global-top-nav/styled-toaster'; import { Duration } from '../time/duration'; import { RelativeToNow } from '../time/relative-to-now'; -import { ContainerModel } from '../../models/log-api/models/container'; -import { ReplicaModel } from '../../models/log-api/models/replica'; -import { RequestState } from '../../state/state-utils/request-states'; +import { RawModel } from '../../models/model-types'; +import { + ModelsContainer, + ModelsReplica, + logApi, + useGetJobInventoryQuery, +} from '../../store/log-api'; +import { FetchQueryResult } from '../../store/types'; import { sortCompareDate, sortDirection } from '../../utils/sort-utils'; import { copyToTextFile, @@ -55,13 +57,12 @@ export interface JobReplicaLogAccordionProps extends JobNameProps { } const LogDownloadButton: FunctionComponent<{ - status: RequestState; title?: string; disabled?: boolean; onClick: () => void; -}> = ({ status, ...rest }) => ( - ); -function useSaveLog( - { data, status, error }: AsyncState, +function getTimespan( + span: JobReplicaLogAccordionProps['timeSpan'] +): RawModel { + return { + ...(span && { + start: new Date(span.start).toISOString(), + end: span.end && new Date(span.end.getTime() + 10 * 60000).toISOString(), + }), + }; +} + +function saveLog( + query: Pick, fileName: string, errMsg = 'Failed to download log' ): void { - useEffect(() => { - if (status === RequestState.SUCCESS) { - const extension = !fileName.endsWith('.txt') ? '.txt' : ''; - copyToTextFile(`${fileName}${extension}`, data); - } else if (status === RequestState.FAILURE) { - errorToast(`${errMsg}: ${error}`); - } - }, [data, errMsg, error, fileName, status]); + if (query.isSuccess) { + const extension = !fileName.endsWith('.txt') ? '.txt' : ''; + copyToTextFile(`${fileName}${extension}`, query.data as string); + } else if (query.isError) { + const { code, message } = getErrorData(query.error); + errorToast(`${errMsg}: ${code && `[${code}] `}${message}`); + } } export const JobReplicaLogAccordion: FunctionComponent< @@ -95,15 +106,15 @@ export const JobReplicaLogAccordion: FunctionComponent< timeSpan, isExpanded, }) => { - const [jobInventory] = useGetJobInventory( + const jobInventory = useGetJobInventoryQuery({ appName, envName, jobComponentName, jobName, - timeSpan - ); + ...getTimespan(timeSpan), + }); - const [sortedData, setSortedData] = useState>([]); + const [sortedData, setSortedData] = useState>([]); const [dateSort, setDateSort] = useState('descending'); const [expandedRows, setExpandedRows] = useState>({}); @@ -113,7 +124,7 @@ export const JobReplicaLogAccordion: FunctionComponent< ); useEffect(() => { - if (jobInventory.status === RequestState.SUCCESS) { + if (jobInventory.isSuccess) { setSortedData( tableDataSorter(jobInventory.data?.replicas, [ (x, y) => @@ -121,7 +132,7 @@ export const JobReplicaLogAccordion: FunctionComponent< ]) ); } - }, [jobInventory.data, jobInventory.status, dateSort]); + }, [jobInventory.data, jobInventory.isSuccess, dateSort]); return ( @@ -129,11 +140,7 @@ export const JobReplicaLogAccordion: FunctionComponent< - {title} ( - {jobInventory.status === RequestState.IN_PROGRESS - ? '…' - : sortedData.length} - ) + {title} ({jobInventory.isLoading ? '…' : sortedData.length}) @@ -160,47 +167,42 @@ export const JobReplicaLogAccordion: FunctionComponent< - {sortedData - .map((x) => ({ - replica: x, - expanded: !!expandedRows[x.name], - })) - .map(({ replica, expanded }) => ( - - expandRow(replica.name)} - {...{ appName, envName, jobComponentName, jobName }} - /> + {sortedData.map((replica) => ( + + expandRow(replica.name)} + {...{ appName, envName, jobComponentName, jobName }} + /> - {expanded && - replica.containers - ?.sort((a, b) => - sortCompareDate( - a.creationTimestamp, - b.creationTimestamp, - 'descending' - ) + {!!expandedRows[replica.name] && + replica.containers + ?.sort((a, b) => + sortCompareDate( + a.creationTimestamp, + b.creationTimestamp, + 'descending' ) - .map((container, i, { length }) => ( - i, - })} - container={container} - replicaName={replica.name} - {...{ - appName, - envName, - jobComponentName, - jobName, - }} - /> - ))} - - ))} + ) + .map((container, i, { length }) => ( + i, + })} + container={container} + replicaName={replica.name} + {...{ + appName, + envName, + jobComponentName, + jobName, + }} + /> + ))} + + ))} ) : ( @@ -216,7 +218,7 @@ export const JobReplicaLogAccordion: FunctionComponent< const ReplicaLogTableRow: FunctionComponent< { - replica: ReplicaModel; + replica: ModelsReplica; isExpanded: boolean; onClick: () => void; } & JobNameProps @@ -225,23 +227,15 @@ const ReplicaLogTableRow: FunctionComponent< envName, jobComponentName, jobName, - replica: { containers, name, creationTimestamp, lastKnown }, + replica: { containers, creationTimestamp, lastKnown, name }, isExpanded, onClick, }) => { - const [replicaLog, getReplicaLog] = useGetReplicaLog( - appName, - envName, - jobComponentName, - jobName, - name - ); - useSaveLog( - replicaLog, - `${appName}_${envName}_${jobComponentName}_${jobName}_${smallReplicaName( - name - )}` - ); + const [getLog, { isFetching }] = + logApi.endpoints.getJobReplicaLog.useLazyQuery(); + + const created = new Date(creationTimestamp); + const ended = new Date(lastKnown); return ( @@ -260,17 +254,29 @@ const ReplicaLogTableRow: FunctionComponent< {containers?.length || 0} - + - {Duration({ start: creationTimestamp, end: lastKnown }) || 'N/A'} + {Duration({ start: created, end: ended }) || 'N/A'} getReplicaLog()} - disabled={replicaLog.status === RequestState.IN_PROGRESS} + onClick={async () => { + const response = await getLog({ + appName, + envName, + jobComponentName, + jobName, + replicaName: name, + }); + + const fileName = `${appName}_${envName}_${jobComponentName}_${jobName}_${smallReplicaName( + name + )}`; + saveLog(response, fileName); + }} + disabled={isFetching} /> @@ -281,7 +287,7 @@ const ReplicaContainerTableRow: FunctionComponent< { className?: string; replicaName: string; - container: ContainerModel; + container: ModelsContainer; } & JobNameProps > = ({ className, @@ -290,22 +296,13 @@ const ReplicaContainerTableRow: FunctionComponent< jobName, envName, replicaName, - container: { id, creationTimestamp, lastKnown }, + container: { creationTimestamp, id, lastKnown }, }) => { - const [containerLog, getReplicaContainerLog] = useGetReplicaContainerLog( - appName, - envName, - jobComponentName, - jobName, - replicaName, - id - ); - useSaveLog( - containerLog, - `${appName}_${envName}_${jobComponentName}_${jobName}_${smallReplicaName( - replicaName - )}_${id}` - ); + const [getLog, { isFetching }] = + logApi.endpoints.getJobContainerLog.useLazyQuery(); + + const created = new Date(creationTimestamp); + const ended = new Date(lastKnown); return ( @@ -323,17 +320,30 @@ const ReplicaContainerTableRow: FunctionComponent< - + - + getReplicaContainerLog()} - disabled={containerLog.status === RequestState.IN_PROGRESS} + onClick={async () => { + const response = await getLog({ + appName, + envName, + jobComponentName, + jobName, + replicaName, + containerId: id, + }); + + const fileName = `${appName}_${envName}_${jobComponentName}_${jobName}_${smallReplicaName( + replicaName + )}_${id}`; + saveLog(response, fileName); + }} + disabled={isFetching} /> diff --git a/src/components/page-scheduled-job/use-get-job-inventory.ts b/src/components/page-scheduled-job/use-get-job-inventory.ts deleted file mode 100644 index d53a3b325..000000000 --- a/src/components/page-scheduled-job/use-get-job-inventory.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useFetchLogJson } from '../../effects'; -import { AsyncLoadingResult } from '../../effects/use-async-loading'; -import { InventoryResponseModel } from '../../models/log-api/models/inventory-response'; -import { InventoryResponseModelNormalizer } from '../../models/log-api/models/inventory-response/normalizer'; - -export function useGetJobInventory( - appName: string, - envName: string, - jobComponentName: string, - jobName: string, - interval?: { start: Date; end?: Date } -): AsyncLoadingResult> { - const encAppName = encodeURIComponent(appName); - const encEnvName = encodeURIComponent(envName); - const encJobComponentName = encodeURIComponent(jobComponentName); - const encJobName = encodeURIComponent(jobName); - - const time: Record = { - start: interval && new Date(interval.start), - end: interval?.end && new Date(interval.end.getTime() + 10 * 60000), - }; - const timeInterval = (Object.keys(time) as Array).reduce( - (o, key) => (time[key] ? `${o}${key}=${time[key].toISOString()}&` : o), - '?' - ); - - return useFetchLogJson( - `/applications/${encAppName}/environments/${encEnvName}/jobcomponents/${encJobComponentName}/jobs/${encJobName}` + - timeInterval, - InventoryResponseModelNormalizer - ); -} diff --git a/src/components/page-scheduled-job/use-get-replica-container-log.ts b/src/components/page-scheduled-job/use-get-replica-container-log.ts deleted file mode 100644 index 3d5684486..000000000 --- a/src/components/page-scheduled-job/use-get-replica-container-log.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useGetLogPlain } from '../../effects'; -import { AsyncRequestResult } from '../../effects/use-async-request'; - -export function useGetReplicaContainerLog( - appName: string, - envName: string, - jobComponentName: string, - jobName: string, - replicaName: string, - containerId: string -): AsyncRequestResult { - const encAppName = encodeURIComponent(appName); - const encEnvName = encodeURIComponent(envName); - const encJobComponentName = encodeURIComponent(jobComponentName); - const encJobName = encodeURIComponent(jobName); - const encReplicaName = encodeURIComponent(replicaName); - const encContainerId = encodeURIComponent(containerId); - - return useGetLogPlain( - `/applications/${encAppName}/environments/${encEnvName}/jobcomponents/${encJobComponentName}/jobs/${encJobName}/replicas/${encReplicaName}/containers/${encContainerId}/log` - ); -} diff --git a/src/components/page-scheduled-job/use-get-replica-log.ts b/src/components/page-scheduled-job/use-get-replica-log.ts deleted file mode 100644 index b1c4bca20..000000000 --- a/src/components/page-scheduled-job/use-get-replica-log.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useGetLogPlain } from '../../effects'; -import { AsyncRequestResult } from '../../effects/use-async-request'; - -export function useGetReplicaLog( - appName: string, - envName: string, - jobComponentName: string, - jobName: string, - replicaName: string -): AsyncRequestResult { - const encAppName = encodeURIComponent(appName); - const encEnvName = encodeURIComponent(envName); - const encJobComponentName = encodeURIComponent(jobComponentName); - const encJobName = encodeURIComponent(jobName); - const encReplicaName = encodeURIComponent(replicaName); - - return useGetLogPlain( - `/applications/${encAppName}/environments/${encEnvName}/jobcomponents/${encJobComponentName}/jobs/${encJobName}/replicas/${encReplicaName}/log` - ); -} From ba39575ec9b4addbed0be862658ccbcb129a8b8b Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 10:53:53 +0100 Subject: [PATCH 08/12] update page step with new log api --- src/components/page-step/job-step-logs.tsx | 55 ++++++++++++------- .../use-get-pipeline-container-log.ts | 29 ---------- .../page-step/use-get-pipeline-inventory.ts | 28 ---------- 3 files changed, 35 insertions(+), 77 deletions(-) delete mode 100644 src/components/page-step/use-get-pipeline-container-log.ts delete mode 100644 src/components/page-step/use-get-pipeline-inventory.ts diff --git a/src/components/page-step/job-step-logs.tsx b/src/components/page-step/job-step-logs.tsx index b23e0a9b4..a59b6e30e 100644 --- a/src/components/page-step/job-step-logs.tsx +++ b/src/components/page-step/job-step-logs.tsx @@ -3,18 +3,22 @@ import * as PropTypes from 'prop-types'; import { FunctionComponent, useEffect, useState } from 'react'; import { useGetJobStepFullLogs } from './use-get-job-step-full-logs'; -import { useGetPipelineContainerLog } from './use-get-pipeline-container-log'; -import { useGetPipelineInventory } from './use-get-pipeline-inventory'; import { usePollJobStepLogs } from './use-poll-job-step-logs'; -import AsyncResource from '../async-resource/simple-async-resource'; +import AsyncResource from '../async-resource/another-async-resource'; +import { SimpleAsyncResource } from '../async-resource/simple-async-resource'; import { Log } from '../component/log'; -import { ContainerModel } from '../../models/log-api/models/container'; +import { RawModel } from '../../models/model-types'; import { RequestState } from '../../state/state-utils/request-states'; +import { + ModelsContainer, + useGetPipelineJobContainerLogQuery, + useGetPipelineJobInventoryQuery, +} from '../../store/log-api'; import './style.css'; -type BrandedContainerModel = ContainerModel & { parentId: string }; +type BrandedContainerModel = ModelsContainer & { parentId: string }; export interface StepLogsProps { appName: string; @@ -23,6 +27,17 @@ export interface StepLogsProps { timeSpan?: { start: Date; end?: Date }; } +function getTimespan( + span: StepLogsProps['timeSpan'] +): RawModel { + return { + ...(span && { + start: new Date(span.start).toISOString(), + end: span.end && new Date(span.end.getTime() + 10 * 60000).toISOString(), + }), + }; +} + const NoLog: FunctionComponent = () => ( This replica has no log ); @@ -33,15 +48,15 @@ const HistoricalLog: FunctionComponent = ({ stepName, timeSpan, }) => { - const [{ data, ...state }] = useGetPipelineInventory( + const { data, ...state } = useGetPipelineJobInventoryQuery({ appName, - jobName, - timeSpan - ); + pipelineJobName: jobName, + ...getTimespan(timeSpan), + }); const [container, setContainer] = useState(); useEffect(() => { - if (state.status === RequestState.SUCCESS) { + if (state.isSuccess) { // flattens and return all containers as a flattened array, branded with parents id const flatContainers = data?.replicas.flatMap( ({ name, containers }) => @@ -50,7 +65,7 @@ const HistoricalLog: FunctionComponent = ({ setContainer(flatContainers.find(({ name }) => name === stepName)); } - }, [data?.replicas, state.status, stepName]); + }, [data?.replicas, state.isSuccess, stepName]); return ( @@ -72,18 +87,18 @@ const ContainerLog: FunctionComponent< 'appName' | 'jobName' | 'timeSpan' > > = ({ appName, container: { name, parentId, id }, jobName, timeSpan }) => { - const [{ data, ...state }] = useGetPipelineContainerLog( + const { data, ...state } = useGetPipelineJobContainerLogQuery({ appName, - jobName, - parentId, - id, - timeSpan - ); + pipelineJobName: jobName, + replicaName: parentId, + containerId: id, + ...getTimespan(timeSpan), + }); return ( {data ? ( - + ) : ( )} @@ -126,7 +141,7 @@ export const JobStepLogs: FunctionComponent = ({ const logComponent = pollLogFailedAndNotFound ? ( ) : ( - + {persistLog.data ? ( = ({ ) : ( )} - + ); return ( diff --git a/src/components/page-step/use-get-pipeline-container-log.ts b/src/components/page-step/use-get-pipeline-container-log.ts deleted file mode 100644 index 229b83a8d..000000000 --- a/src/components/page-step/use-get-pipeline-container-log.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useFetchLogPlain } from '../../effects'; -import { AsyncLoadingResult } from '../../effects/use-async-loading'; - -export function useGetPipelineContainerLog( - appName: string, - pipelineJobName: string, - replicaName: string, - containerId: string, - interval?: { start: Date; end?: Date } -): AsyncLoadingResult { - const encAppName = encodeURIComponent(appName); - const encPipelineJobName = encodeURIComponent(pipelineJobName); - const encReplicaName = encodeURIComponent(replicaName); - const encContainerId = encodeURIComponent(containerId); - - const time: Record = { - start: interval && new Date(interval.start), - end: interval?.end && new Date(interval.end.getTime() + 10 * 60000), - }; - const timeInterval = (Object.keys(time) as Array).reduce( - (o, key) => (time[key] ? `${o}${key}=${time[key].toISOString()}&` : o), - '?' - ); - - return useFetchLogPlain( - `/applications/${encAppName}/pipelinejobs/${encPipelineJobName}/replicas/${encReplicaName}/containers/${encContainerId}/log` + - timeInterval - ); -} diff --git a/src/components/page-step/use-get-pipeline-inventory.ts b/src/components/page-step/use-get-pipeline-inventory.ts deleted file mode 100644 index cdb5c1529..000000000 --- a/src/components/page-step/use-get-pipeline-inventory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useFetchLogJson } from '../../effects'; -import { AsyncLoadingResult } from '../../effects/use-async-loading'; -import { InventoryResponseModel } from '../../models/log-api/models/inventory-response'; -import { InventoryResponseModelNormalizer } from '../../models/log-api/models/inventory-response/normalizer'; - -export function useGetPipelineInventory( - appName: string, - pipelineJobName: string, - interval?: { start: Date; end?: Date } -): AsyncLoadingResult> { - const encAppName = encodeURIComponent(appName); - const encPipelineJobName = encodeURIComponent(pipelineJobName); - - const time: Record = { - start: interval && new Date(interval.start), - end: interval?.end && new Date(interval.end.getTime() + 10 * 60000), - }; - const timeInterval = (Object.keys(time) as Array).reduce( - (o, key) => (time[key] ? `${o}${key}=${time[key].toISOString()}&` : o), - '?' - ); - - return useFetchLogJson( - `/applications/${encAppName}/pipelinejobs/${encPipelineJobName}` + - timeInterval, - InventoryResponseModelNormalizer - ); -} From bee68efb61bac5fc943fe34a3ae34718f114f528 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 10:57:18 +0100 Subject: [PATCH 09/12] cleanup old log api effects --- src/api/api-config.ts | 15 --------------- src/effects/index.ts | 17 +---------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/api/api-config.ts b/src/api/api-config.ts index 4a57063d8..97bdbca95 100644 --- a/src/api/api-config.ts +++ b/src/api/api-config.ts @@ -1,9 +1,6 @@ export const dynatraceApiBaseUri = `${ import.meta.env.VITE_DYNATRACE_API_BASE_URI || window.location.host }/uptime-api`; -export const logApiBaseUri = `${ - import.meta.env.VITE_LOG_API_BASE_URI || window.location.host -}/log-api`; export const radixApiBaseUri = `${ import.meta.env.VITE_RADIX_API_BASE_URI || window.location.host }/api/v1`; @@ -34,18 +31,6 @@ export function createDynatraceApiUrl( return createApiUrl(dynatraceApiBaseUri, path, protocol); } -/** - * Create a full URL to the Radix Log API - * @param {string} path Relative path - * @param {string} [protocol] Protocol to use, e.g. 'wss:' - */ -export function createLogApiUrl( - path: string, - protocol: string = window.location.protocol -): string { - return createApiUrl(logApiBaseUri, path, protocol); -} - /** * Create a full URL to the Radix API * @param {string} path Relative path diff --git a/src/effects/index.ts b/src/effects/index.ts index 97a8ff9e7..ae9552802 100644 --- a/src/effects/index.ts +++ b/src/effects/index.ts @@ -2,7 +2,7 @@ import { useAsyncLoading } from './use-async-loading'; import { useAsyncPolling } from './use-async-polling'; import { useAsyncRequest } from './use-async-request'; -import { createLogApiUrl, createRadixApiUrl } from '../api/api-config'; +import { createRadixApiUrl } from '../api/api-config'; import { getJson, getText, @@ -52,13 +52,6 @@ export function useFetchJson( return useFetchJsonBase(createRadixApiUrl(path), responseConverter); } -export function useFetchLogJson( - path: string, - responseConverter?: (responseData: R) => T -) { - return useFetchJsonBase(createLogApiUrl(path), responseConverter); -} - export function useFetchPlain(path: string) { return useAsyncLoading( getText, @@ -66,14 +59,6 @@ export function useFetchPlain(path: string) { ); } -export function useFetchLogPlain(path: string) { - return useAsyncLoading(getText, createLogApiUrl(path)); -} - -export function useGetLogPlain(path: string) { - return useAsyncRequest(getText, createLogApiUrl(path)); -} - export function useGetPlain(path: string) { return useAsyncRequest( getText, From 7345510741bfc00fb001ea875e813d1b923fb522 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 11:01:09 +0100 Subject: [PATCH 10/12] remove old api model --- src/models/log-api/models/container/index.ts | 17 ------- .../log-api/models/container/normalizer.ts | 17 ------- .../log-api/models/container/test-data.ts | 40 ----------------- .../models/inventory-response/index.ts | 17 ------- .../models/inventory-response/normalizer.ts | 17 ------- .../models/inventory-response/test-data.ts | 27 ----------- src/models/log-api/models/replica/index.ts | 23 ---------- .../log-api/models/replica/normalizer.ts | 23 ---------- .../log-api/models/replica/test-data.ts | 45 ------------------- src/models/log-api/test-dependencies.ts | 45 ------------------- src/models/models.test.ts | 5 --- 11 files changed, 276 deletions(-) delete mode 100644 src/models/log-api/models/container/index.ts delete mode 100644 src/models/log-api/models/container/normalizer.ts delete mode 100644 src/models/log-api/models/container/test-data.ts delete mode 100644 src/models/log-api/models/inventory-response/index.ts delete mode 100644 src/models/log-api/models/inventory-response/normalizer.ts delete mode 100644 src/models/log-api/models/inventory-response/test-data.ts delete mode 100644 src/models/log-api/models/replica/index.ts delete mode 100644 src/models/log-api/models/replica/normalizer.ts delete mode 100644 src/models/log-api/models/replica/test-data.ts delete mode 100644 src/models/log-api/test-dependencies.ts diff --git a/src/models/log-api/models/container/index.ts b/src/models/log-api/models/container/index.ts deleted file mode 100644 index 95c2d6e98..000000000 --- a/src/models/log-api/models/container/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as PropTypes from 'prop-types'; - -export type ContainerModel = { - id: string; - name?: string; - creationTimestamp?: Date; - lastKnown?: Date; -}; - -/* PropTypes validation map for ContainerModel */ -export const ContainerModelValidationMap: PropTypes.ValidationMap = - { - id: PropTypes.string.isRequired, - name: PropTypes.string, - creationTimestamp: PropTypes.instanceOf(Date), - lastKnown: PropTypes.instanceOf(Date), - }; diff --git a/src/models/log-api/models/container/normalizer.ts b/src/models/log-api/models/container/normalizer.ts deleted file mode 100644 index 0ec8f0b88..000000000 --- a/src/models/log-api/models/container/normalizer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ContainerModel } from '.'; - -import { ModelNormalizerType } from '../../../model-types'; -import { dateNormalizer, objectNormalizer } from '../../../model-utils'; - -/** - * Create a ContainerModel object - */ -export const ContainerModelNormalizer: ModelNormalizerType< - Readonly -> = (props) => - Object.freeze( - objectNormalizer(props, { - creationTimestamp: dateNormalizer, - lastKnown: dateNormalizer, - }) - ); diff --git a/src/models/log-api/models/container/test-data.ts b/src/models/log-api/models/container/test-data.ts deleted file mode 100644 index a6cb4191f..000000000 --- a/src/models/log-api/models/container/test-data.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ContainerModel } from '.'; - -import { TestDependencyDataType } from '../../../model-types'; - -/* - * TestData array - * - * Note: First object should always be valid - */ -export const testData: TestDependencyDataType = [ - { - __testDescription: 'Valid full object', - id: 'id', - name: 'name', - creationTimestamp: new Date(), - lastKnown: new Date(), - }, - { - __testDescription: 'Valid partial object', - id: 'id', - }, - { - __testDescription: 'Invalid full object', - __testIsInvalidSample: true, - id: { msg: 'error' } as unknown as string, - name: 'name', - creationTimestamp: new Date(), - lastKnown: new Date(), - }, - { - __testDescription: 'Invalid partial object', - __testIsInvalidSample: true, - id: new Date() as unknown as string, - }, - { - __testDescription: 'Invalid empty object', - __testIsInvalidSample: true, - id: undefined, - }, -]; diff --git a/src/models/log-api/models/inventory-response/index.ts b/src/models/log-api/models/inventory-response/index.ts deleted file mode 100644 index d2b5acd46..000000000 --- a/src/models/log-api/models/inventory-response/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as PropTypes from 'prop-types'; - -import { ReplicaModel, ReplicaModelValidationMap } from '../replica'; - -export type InventoryResponseModel = { - replicas: Array; -}; - -/* PropTypes validation map for InventoryResponseModel */ -export const InventoryResponseModelValidationMap: PropTypes.ValidationMap = - { - replicas: PropTypes.arrayOf( - PropTypes.shape( - ReplicaModelValidationMap - ) as PropTypes.Validator - ).isRequired, - }; diff --git a/src/models/log-api/models/inventory-response/normalizer.ts b/src/models/log-api/models/inventory-response/normalizer.ts deleted file mode 100644 index 66ab5af4a..000000000 --- a/src/models/log-api/models/inventory-response/normalizer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { InventoryResponseModel } from '.'; - -import { ReplicaModelNormalizer } from '../replica/normalizer'; -import { ModelNormalizerType } from '../../../model-types'; -import { arrayNormalizer, objectNormalizer } from '../../../model-utils'; - -/** - * Create an InventoryResponseModel object - */ -export const InventoryResponseModelNormalizer: ModelNormalizerType< - Readonly -> = (props) => - Object.freeze( - objectNormalizer(props, { - replicas: (x) => arrayNormalizer(x, ReplicaModelNormalizer), - }) - ); diff --git a/src/models/log-api/models/inventory-response/test-data.ts b/src/models/log-api/models/inventory-response/test-data.ts deleted file mode 100644 index bc6022e21..000000000 --- a/src/models/log-api/models/inventory-response/test-data.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { InventoryResponseModel } from '.'; - -import { ReplicaModel } from '../replica'; -import { testData as ReplicaData } from '../replica/test-data'; -import { TestDependencyDataType } from '../../../model-types'; - -/* - * TestData array - * - * Note: First object should always be valid - */ -export const testData: TestDependencyDataType = [ - { - __testDescription: 'Valid full object', - replicas: [ReplicaData[0]], - }, - { - __testDescription: 'Invalid full object', - __testIsInvalidSample: true, - replicas: {} as unknown as Array, - }, - { - __testDescription: 'Invalid empty object', - __testIsInvalidSample: true, - replicas: undefined, - }, -]; diff --git a/src/models/log-api/models/replica/index.ts b/src/models/log-api/models/replica/index.ts deleted file mode 100644 index dc8dfa46d..000000000 --- a/src/models/log-api/models/replica/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as PropTypes from 'prop-types'; - -import { ContainerModel, ContainerModelValidationMap } from '../container'; - -export type ReplicaModel = { - name: string; - creationTimestamp?: Date; - lastKnown?: Date; - containers: Array; -}; - -/* PropTypes validation map for ReplicaModel */ -export const ReplicaModelValidationMap: PropTypes.ValidationMap = - { - name: PropTypes.string.isRequired, - creationTimestamp: PropTypes.instanceOf(Date), - lastKnown: PropTypes.instanceOf(Date), - containers: PropTypes.arrayOf( - PropTypes.shape( - ContainerModelValidationMap - ) as PropTypes.Validator - ).isRequired, - }; diff --git a/src/models/log-api/models/replica/normalizer.ts b/src/models/log-api/models/replica/normalizer.ts deleted file mode 100644 index 126724adc..000000000 --- a/src/models/log-api/models/replica/normalizer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ReplicaModel } from '.'; - -import { ContainerModelNormalizer } from '../container/normalizer'; -import { ModelNormalizerType } from '../../../model-types'; -import { - arrayNormalizer, - dateNormalizer, - objectNormalizer, -} from '../../../model-utils'; - -/** - * Create a ReplicaModel object - */ -export const ReplicaModelNormalizer: ModelNormalizerType< - Readonly -> = (props) => - Object.freeze( - objectNormalizer(props, { - creationTimestamp: dateNormalizer, - lastKnown: dateNormalizer, - containers: (x) => arrayNormalizer(x, ContainerModelNormalizer), - }) - ); diff --git a/src/models/log-api/models/replica/test-data.ts b/src/models/log-api/models/replica/test-data.ts deleted file mode 100644 index 689e43b27..000000000 --- a/src/models/log-api/models/replica/test-data.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ReplicaModel } from '.'; - -import { ContainerModel } from '../container'; -import { testData as ContainerData } from '../container/test-data'; -import { TestDependencyDataType } from '../../../model-types'; - -/* - * TestData array - * - * Note: First object should always be valid - */ -export const testData: TestDependencyDataType = [ - { - __testDescription: 'Valid full object', - name: 'name', - creationTimestamp: new Date(), - lastKnown: new Date(), - containers: [ContainerData[0]], - }, - { - __testDescription: 'Valid partial object', - name: 'name', - containers: [ContainerData[0]], - }, - { - __testDescription: 'Invalid full object', - __testIsInvalidSample: true, - name: ['name'] as unknown as string, - creationTimestamp: new Date(), - lastKnown: new Date(), - containers: [ContainerData[0]], - }, - { - __testDescription: 'Invalid partial object', - __testIsInvalidSample: true, - name: 'name', - containers: {} as unknown as Array, - }, - { - __testDescription: 'Invalid empty object', - __testIsInvalidSample: true, - name: undefined, - containers: undefined, - }, -]; diff --git a/src/models/log-api/test-dependencies.ts b/src/models/log-api/test-dependencies.ts deleted file mode 100644 index 6748e8c56..000000000 --- a/src/models/log-api/test-dependencies.ts +++ /dev/null @@ -1,45 +0,0 @@ -// It would be great to have these as dynamic imports so we don't repeat so much -// code. Alas, Jest doesn't seem to play ball with async/await AND dynamic -// `it()` declaration‍s ¯\_(ツ)_/¯ - -import { ValidationMap } from 'prop-types'; - -import { ModelNormalizerType, TestDependencyDataType } from '../model-types'; - -import { testData as ContainerData } from './models/container/test-data'; -import { ContainerModelValidationMap } from './models/container'; -import { ContainerModelNormalizer } from './models/container/normalizer'; - -import { testData as InventoryResponseData } from './models/inventory-response/test-data'; -import { InventoryResponseModelValidationMap } from './models/inventory-response'; -import { InventoryResponseModelNormalizer } from './models/inventory-response/normalizer'; - -import { testData as ReplicaData } from './models/replica/test-data'; -import { ReplicaModelValidationMap } from './models/replica'; -import { ReplicaModelNormalizer } from './models/replica/normalizer'; - -interface TestDependencyComponents { - Container: T; - InventoryResponse: T; - Replica: T; -} - -export const testData: TestDependencyComponents = { - Container: ContainerData, - InventoryResponse: InventoryResponseData, - Replica: ReplicaData, -}; - -export const models: TestDependencyComponents< - ValidationMap> -> = { - Container: ContainerModelValidationMap, - InventoryResponse: InventoryResponseModelValidationMap, - Replica: ReplicaModelValidationMap, -}; - -export const normalizers: TestDependencyComponents = { - Container: ContainerModelNormalizer, - InventoryResponse: InventoryResponseModelNormalizer, - Replica: ReplicaModelNormalizer, -}; diff --git a/src/models/models.test.ts b/src/models/models.test.ts index 0865a4ead..5dbd3f03d 100644 --- a/src/models/models.test.ts +++ b/src/models/models.test.ts @@ -3,7 +3,6 @@ import { checkExact } from 'swagger-proptypes'; import { ModelNormalizerType, TestDependencyDataType } from './model-types'; -import * as logApi from './log-api/test-dependencies'; import * as radixApi from './radix-api/test-dependencies'; import * as servicenowApi from './servicenow-api/test-dependencies'; @@ -18,10 +17,6 @@ const dependencyTests: Array<{ id: string; tests: TestSet }> = [ id: 'Radix API', tests: radixApi as TestSet, }, - { - id: 'Log API', - tests: logApi as TestSet, - }, { id: 'ServiceNow API', tests: servicenowApi as TestSet, From fde609aa5d60c41330ce96be6e5a6af31454e865 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 11:07:49 +0100 Subject: [PATCH 11/12] skip queries on missing parameters --- .../replica-log-accordion.tsx | 11 +++----- src/components/page-step/job-step-logs.tsx | 26 ++++++++++--------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/components/page-scheduled-job/replica-log-accordion.tsx b/src/components/page-scheduled-job/replica-log-accordion.tsx index 419f29658..b1f12cbc7 100644 --- a/src/components/page-scheduled-job/replica-log-accordion.tsx +++ b/src/components/page-scheduled-job/replica-log-accordion.tsx @@ -106,13 +106,10 @@ export const JobReplicaLogAccordion: FunctionComponent< timeSpan, isExpanded, }) => { - const jobInventory = useGetJobInventoryQuery({ - appName, - envName, - jobComponentName, - jobName, - ...getTimespan(timeSpan), - }); + const jobInventory = useGetJobInventoryQuery( + { appName, envName, jobComponentName, jobName, ...getTimespan(timeSpan) }, + { skip: !appName || !envName || !jobComponentName || !jobName } + ); const [sortedData, setSortedData] = useState>([]); const [dateSort, setDateSort] = useState('descending'); diff --git a/src/components/page-step/job-step-logs.tsx b/src/components/page-step/job-step-logs.tsx index a59b6e30e..43d92b7be 100644 --- a/src/components/page-step/job-step-logs.tsx +++ b/src/components/page-step/job-step-logs.tsx @@ -48,11 +48,10 @@ const HistoricalLog: FunctionComponent = ({ stepName, timeSpan, }) => { - const { data, ...state } = useGetPipelineJobInventoryQuery({ - appName, - pipelineJobName: jobName, - ...getTimespan(timeSpan), - }); + const { data, ...state } = useGetPipelineJobInventoryQuery( + { appName, pipelineJobName: jobName, ...getTimespan(timeSpan) }, + { skip: !appName || !jobName } + ); const [container, setContainer] = useState(); useEffect(() => { @@ -87,13 +86,16 @@ const ContainerLog: FunctionComponent< 'appName' | 'jobName' | 'timeSpan' > > = ({ appName, container: { name, parentId, id }, jobName, timeSpan }) => { - const { data, ...state } = useGetPipelineJobContainerLogQuery({ - appName, - pipelineJobName: jobName, - replicaName: parentId, - containerId: id, - ...getTimespan(timeSpan), - }); + const { data, ...state } = useGetPipelineJobContainerLogQuery( + { + appName, + pipelineJobName: jobName, + replicaName: parentId, + containerId: id, + ...getTimespan(timeSpan), + }, + { skip: !appName || !jobName || !parentId || !id } + ); return ( From 38237e55df055498d8952742dc3da1826f0d6527 Mon Sep 17 00:00:00 2001 From: Fredrik Hatletvedt Date: Wed, 22 Nov 2023 13:36:44 +0100 Subject: [PATCH 12/12] cleanup --- src/components/page-environment/component-list.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/page-environment/component-list.tsx b/src/components/page-environment/component-list.tsx index f904975ae..c0a30337d 100644 --- a/src/components/page-environment/component-list.tsx +++ b/src/components/page-environment/component-list.tsx @@ -120,7 +120,6 @@ export const ComponentList: FunctionComponent = ({ environment: { name: envName }, components, }) => { - // const [environmentVulnerabilities] = useGetEnvironmentScans(appName, envName); const { data: vulnerabilities, ...vulnerabilitiesState } = useGetEnvironmentVulnerabilitySummaryQuery({ appName, envName }); @@ -189,9 +188,10 @@ export const ComponentList: FunctionComponent = ({ errorContent={ {vulnerabilitiesState.error && - `${(({ code }) => code && `${code}: `)( + (({ code, message }) => + `${code && `${code}: `}${message}`)( getErrorData(vulnerabilitiesState.error) - )} request failed`} + )} } >