Skip to content

Commit f700d26

Browse files
committed
✨ Support for analysis merged flag
Signed-off-by: ibolton336 <[email protected]>
1 parent 61235d2 commit f700d26

File tree

6 files changed

+96
-61
lines changed

6 files changed

+96
-61
lines changed

client/src/app/api/rest.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -322,21 +322,28 @@ export const getApplicationImports = (
322322
.get(`${APP_IMPORT}?importSummary.id=${importSummaryID}&isValid=${isValid}`)
323323
.then((response) => response.data);
324324

325-
export function getTaskById(id: number, format: "json"): Promise<Task>;
326-
export function getTaskById(id: number, format: "yaml"): Promise<string>;
327325
export function getTaskById(
328326
id: number,
329-
format: "json" | "yaml"
327+
format: string,
328+
merged: boolean = false
330329
): Promise<Task | string> {
331-
if (format === "yaml") {
332-
return axios
333-
.get<Task>(`${TASKS}/${id}`, yamlHeaders)
334-
.then((response) => response.data);
335-
} else {
336-
return axios
337-
.get<string>(`${TASKS}/${id}`, jsonHeaders)
338-
.then((response) => response.data);
330+
const headers =
331+
format === "yaml" ? { ...yamlHeaders.headers } : { ...jsonHeaders.headers };
332+
const responseType = format === "yaml" ? "text" : "json";
333+
334+
let url = `${TASKS}/${id}`;
335+
if (merged) {
336+
url += "?merged=1";
339337
}
338+
339+
return axios
340+
.get(url, {
341+
headers: headers,
342+
responseType: responseType,
343+
})
344+
.then((response) => {
345+
return response.data;
346+
});
340347
}
341348

342349
export const getTasks = () =>

client/src/app/components/SimpleDocumentViewer.css

+4
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,7 @@
7474
.simple-task-viewer .language-toggle-group {
7575
--pf-v5-c-toggle-group__button--FontSize: var(--pf-v5-global--FontSize--md);
7676
}
77+
78+
.merged-checkbox {
79+
margin: auto 0.5rem;
80+
}

client/src/app/components/SimpleDocumentViewer.tsx

+42-38
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from "@patternfly/react-code-editor";
77
import {
88
Button,
9+
Checkbox,
910
EmptyState,
1011
EmptyStateIcon,
1112
EmptyStateVariant,
@@ -22,24 +23,17 @@ import CodeIcon from "@patternfly/react-icons/dist/esm/icons/code-icon";
2223
import UndoIcon from "@patternfly/react-icons/dist/esm/icons/undo-icon";
2324

2425
import "./SimpleDocumentViewer.css";
26+
import { useFetchTaskByID } from "@app/queries/tasks";
2527

2628
export { Language } from "@patternfly/react-code-editor";
2729

28-
interface FetchFunction<FetchType> {
29-
/** Fetch a yaml document for the given document */
30-
(documentId: number, format: Language.yaml): Promise<string>;
31-
32-
/** Fetch a JSON document as a `FetchType` object for the given document */
33-
(documentId: number, format: Language.json): Promise<FetchType>;
34-
}
35-
3630
/** The subset of MonacoEditor component functions we want to use. */
3731
type ControlledEditor = {
3832
focus: () => void;
3933
setPosition: (position: object) => void;
4034
};
4135

42-
export interface ISimpleDocumentViewerProps<FetchType> {
36+
export interface ISimpleDocumentViewerProps {
4337
/** The id of the document to display, or `undefined` to display the empty state. */
4438
documentId: number | undefined;
4539

@@ -57,59 +51,60 @@ export interface ISimpleDocumentViewerProps<FetchType> {
5751
* vertical space. Defaults to "450px".
5852
*/
5953
height?: string | "full";
60-
61-
/** Function that will fetch the document to display. */
62-
fetch: FetchFunction<FetchType>;
6354
}
6455

6556
/**
6657
* Fetch and then use the `@patternfly/react-code-editor` to display a document in
6758
* read-only mode with language highlighting applied.
6859
*/
69-
export const SimpleDocumentViewer = <FetchType,>({
60+
export const SimpleDocumentViewer = ({
7061
documentId,
7162
downloadFilename,
7263
language = Language.yaml,
7364
height = "450px",
74-
fetch,
75-
}: ISimpleDocumentViewerProps<FetchType>) => {
65+
}: ISimpleDocumentViewerProps) => {
7666
const editorRef = React.useRef<ControlledEditor>();
77-
78-
const [code, setCode] = React.useState<string | undefined>(undefined);
7967
const [currentLanguage, setCurrentLanguage] = React.useState(language);
68+
const [code, setCode] = React.useState<string>();
69+
const [merged, setMerged] = React.useState(false);
70+
71+
const { task, isFetching, fetchError, refetch } = useFetchTaskByID(
72+
documentId,
73+
currentLanguage === Language.yaml ? "yaml" : "json",
74+
merged
75+
);
76+
77+
const onMergedChange = (checked: boolean) => {
78+
setMerged(checked);
79+
refetch();
80+
};
8081

8182
React.useEffect(() => {
82-
setCode(undefined);
83-
documentId && fetchDocument(documentId);
84-
}, [documentId, currentLanguage]);
83+
if (task) {
84+
const formattedCode =
85+
currentLanguage === Language.yaml
86+
? task.toString()
87+
: JSON.stringify(task, undefined, 2);
8588

86-
const fetchDocument = (documentId: number) => {
87-
if (currentLanguage === Language.yaml) {
88-
fetch(documentId, currentLanguage).then((yaml) => {
89-
setCode(yaml.toString());
90-
focusAndHomePosition();
91-
});
92-
} else {
93-
fetch(documentId, currentLanguage).then((json) => {
94-
setCode(JSON.stringify(json, undefined, 2));
95-
focusAndHomePosition();
96-
});
89+
setCode(formattedCode);
90+
focusAndHomePosition();
9791
}
98-
};
92+
}, [task, currentLanguage]);
9993

10094
const focusAndHomePosition = () => {
10195
if (editorRef.current) {
10296
editorRef.current.focus();
10397
editorRef.current.setPosition({ column: 0, lineNumber: 1 });
10498
}
10599
};
100+
106101
const refreshControl = (
107102
<CodeEditorControl
108103
icon={<UndoIcon />}
109104
aria-label="refresh-task"
110105
tooltipProps={{ content: "Refresh" }}
111106
onClick={() => {
112-
documentId && fetchDocument(documentId);
107+
refetch();
113108
}}
114109
isVisible={code !== ""}
115110
/>
@@ -147,6 +142,15 @@ export const SimpleDocumentViewer = <FetchType,>({
147142
}
148143
customControls={[
149144
refreshControl,
145+
<Checkbox
146+
className="merged-checkbox"
147+
key="merged"
148+
id="merged"
149+
label="Merged"
150+
isChecked={merged}
151+
onChange={(e, checked) => onMergedChange(checked)}
152+
aria-label="Merged Checkbox"
153+
/>,
150154
<div
151155
className={css(
152156
editorStyles.codeEditorTab,
@@ -193,8 +197,8 @@ export const SimpleDocumentViewer = <FetchType,>({
193197
);
194198
};
195199

196-
export interface ISimpleDocumentViewerModalProps<FetchType>
197-
extends ISimpleDocumentViewerProps<FetchType> {
200+
export interface ISimpleDocumentViewerModalProps
201+
extends ISimpleDocumentViewerProps {
198202
/** Simple text content of the modal header. */
199203
title?: string;
200204

@@ -220,14 +224,14 @@ export interface ISimpleDocumentViewerModalProps<FetchType>
220224
* displayed if the `documentId` is set. If `documentId` is `undefined`, the modal is
221225
* closed.
222226
*/
223-
export const SimpleDocumentViewerModal = <FetchType,>({
227+
export const SimpleDocumentViewerModal = ({
224228
title,
225229
documentId,
226230
onClose,
227231
position = "top",
228232
isFullHeight = true,
229233
...rest
230-
}: ISimpleDocumentViewerModalProps<FetchType>) => {
234+
}: ISimpleDocumentViewerModalProps) => {
231235
const isOpen = documentId !== undefined;
232236

233237
return (
@@ -248,7 +252,7 @@ export const SimpleDocumentViewerModal = <FetchType,>({
248252
</Button>,
249253
]}
250254
>
251-
<SimpleDocumentViewer<FetchType>
255+
<SimpleDocumentViewer
252256
documentId={documentId}
253257
height={isFullHeight ? "full" : undefined}
254258
{...rest}

client/src/app/pages/applications/applications-table/applications-table.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,7 @@ import { ImportApplicationsForm } from "../components/import-applications-form";
9999
import { ConditionalRender } from "@app/components/ConditionalRender";
100100
import { NoDataEmptyState } from "@app/components/NoDataEmptyState";
101101
import { ConditionalTooltip } from "@app/components/ConditionalTooltip";
102-
import {
103-
getArchetypeById,
104-
getAssessmentsByItemId,
105-
getTaskById,
106-
} from "@app/api/rest";
102+
import { getArchetypeById, getAssessmentsByItemId } from "@app/api/rest";
107103
import { ApplicationDependenciesForm } from "@app/components/ApplicationDependenciesFormContainer/ApplicationDependenciesForm";
108104
import { useState } from "react";
109105
import { ApplicationAnalysisStatus } from "../components/application-analysis-status";
@@ -631,6 +627,7 @@ export const ApplicationsTable: React.FC = () => {
631627
if (candidateTasks.length === selectedRows.length) return true;
632628
return false;
633629
};
630+
634631
const hasExistingAnalysis = selectedRows.some((app) =>
635632
tasks.some((task) => task.application?.id === app.id)
636633
);
@@ -872,6 +869,10 @@ export const ApplicationsTable: React.FC = () => {
872869
>
873870
<Tbody>
874871
{currentPageItems?.map((application, rowIndex) => {
872+
const hasExistingAnalysis = tasks.some(
873+
(task) => task.application?.id === application.id
874+
);
875+
875876
return (
876877
<Tr
877878
key={application.name}
@@ -1030,7 +1031,7 @@ export const ApplicationsTable: React.FC = () => {
10301031
},
10311032
]
10321033
: []),
1033-
...(analysisReadAccess
1034+
...(analysisReadAccess && hasExistingAnalysis
10341035
? [
10351036
{
10361037
title: t("actions.analysisDetails"),
@@ -1111,9 +1112,8 @@ export const ApplicationsTable: React.FC = () => {
11111112
onClose={() => setSaveApplicationModalState(null)}
11121113
/>
11131114
</Modal>
1114-
<SimpleDocumentViewerModal<Task | string>
1115+
<SimpleDocumentViewerModal
11151116
title={`Analysis details for ${taskToView?.name}`}
1116-
fetch={getTaskById}
11171117
documentId={taskToView?.task}
11181118
onClose={() => setTaskToView(undefined)}
11191119
/>

client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import {
4141
} from "@app/pages/issues/helpers";
4242
import { ApplicationTags } from "../application-tags";
4343
import { COLOR_HEX_VALUES_BY_NAME } from "@app/Constants";
44-
import { getTaskById } from "@app/api/rest";
4544
import { EmptyTextMessage } from "@app/components/EmptyTextMessage";
4645
import { SimpleDocumentViewerModal } from "@app/components/SimpleDocumentViewer";
4746
import { useFetchFacts } from "@app/queries/facts";
@@ -425,9 +424,8 @@ export const ApplicationDetailDrawer: React.FC<
425424
)}
426425
</>
427426
)}
428-
<SimpleDocumentViewerModal<Task | string>
427+
<SimpleDocumentViewerModal
429428
title={`Analysis details for ${application?.name}`}
430-
fetch={getTaskById}
431429
documentId={taskIdToView}
432430
onClose={() => {
433431
setTaskIdToView(undefined);

client/src/app/queries/tasks.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useMutation, useQuery } from "@tanstack/react-query";
22

3-
import { cancelTask, deleteTask, getTasks } from "@app/api/rest";
3+
import { cancelTask, deleteTask, getTaskById, getTasks } from "@app/api/rest";
44

55
interface FetchTasksFilters {
66
addon?: string;
@@ -80,3 +80,25 @@ export const useCancelTaskMutation = (
8080
},
8181
});
8282
};
83+
84+
export const TaskByIDQueryKey = "taskByID";
85+
86+
export const useFetchTaskByID = (
87+
taskId?: number,
88+
format = "json",
89+
merged = false
90+
) => {
91+
console.log("useFetchTaskByID", taskId, format, merged);
92+
const { isLoading, error, data, refetch } = useQuery({
93+
queryKey: [TaskByIDQueryKey, taskId, format, merged],
94+
queryFn: () => (taskId ? getTaskById(taskId, format, merged) : null),
95+
enabled: !!taskId,
96+
});
97+
98+
return {
99+
task: data,
100+
isFetching: isLoading,
101+
fetchError: error,
102+
refetch,
103+
};
104+
};

0 commit comments

Comments
 (0)