Skip to content

Commit

Permalink
✨ Support for analysis merged flag
Browse files Browse the repository at this point in the history
Signed-off-by: ibolton336 <[email protected]>
  • Loading branch information
ibolton336 committed Dec 15, 2023
1 parent 61235d2 commit 25ec233
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 61 deletions.
29 changes: 18 additions & 11 deletions client/src/app/api/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,21 +322,28 @@ export const getApplicationImports = (
.get(`${APP_IMPORT}?importSummary.id=${importSummaryID}&isValid=${isValid}`)
.then((response) => response.data);

export function getTaskById(id: number, format: "json"): Promise<Task>;
export function getTaskById(id: number, format: "yaml"): Promise<string>;
export function getTaskById(
id: number,
format: "json" | "yaml"
format: string,
merged: boolean = false
): Promise<Task | string> {
if (format === "yaml") {
return axios
.get<Task>(`${TASKS}/${id}`, yamlHeaders)
.then((response) => response.data);
} else {
return axios
.get<string>(`${TASKS}/${id}`, jsonHeaders)
.then((response) => response.data);
const headers =
format === "yaml" ? { ...yamlHeaders.headers } : { ...jsonHeaders.headers };
const responseType = format === "yaml" ? "text" : "json";

let url = `${TASKS}/${id}`;
if (merged) {
url += "?merged=1";
}

return axios
.get<string | Task>(url, {
headers: headers,
responseType: responseType,
})
.then((response) => {
return response.data;
});
}

export const getTasks = () =>
Expand Down
4 changes: 4 additions & 0 deletions client/src/app/components/SimpleDocumentViewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@
.simple-task-viewer .language-toggle-group {
--pf-v5-c-toggle-group__button--FontSize: var(--pf-v5-global--FontSize--md);
}

.merged-checkbox {
margin: auto 0.5rem;
}
80 changes: 42 additions & 38 deletions client/src/app/components/SimpleDocumentViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from "@patternfly/react-code-editor";
import {
Button,
Checkbox,
EmptyState,
EmptyStateIcon,
EmptyStateVariant,
Expand All @@ -22,24 +23,17 @@ import CodeIcon from "@patternfly/react-icons/dist/esm/icons/code-icon";
import UndoIcon from "@patternfly/react-icons/dist/esm/icons/undo-icon";

import "./SimpleDocumentViewer.css";
import { useFetchTaskByID } from "@app/queries/tasks";

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

interface FetchFunction<FetchType> {
/** Fetch a yaml document for the given document */
(documentId: number, format: Language.yaml): Promise<string>;

/** Fetch a JSON document as a `FetchType` object for the given document */
(documentId: number, format: Language.json): Promise<FetchType>;
}

/** The subset of MonacoEditor component functions we want to use. */
type ControlledEditor = {
focus: () => void;
setPosition: (position: object) => void;
};

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

Expand All @@ -57,59 +51,60 @@ export interface ISimpleDocumentViewerProps<FetchType> {
* vertical space. Defaults to "450px".
*/
height?: string | "full";

/** Function that will fetch the document to display. */
fetch: FetchFunction<FetchType>;
}

/**
* Fetch and then use the `@patternfly/react-code-editor` to display a document in
* read-only mode with language highlighting applied.
*/
export const SimpleDocumentViewer = <FetchType,>({
export const SimpleDocumentViewer = ({
documentId,
downloadFilename,
language = Language.yaml,
height = "450px",
fetch,
}: ISimpleDocumentViewerProps<FetchType>) => {
}: ISimpleDocumentViewerProps) => {
const editorRef = React.useRef<ControlledEditor>();

const [code, setCode] = React.useState<string | undefined>(undefined);
const [currentLanguage, setCurrentLanguage] = React.useState(language);
const [code, setCode] = React.useState<string>();
const [merged, setMerged] = React.useState(false);

const { task, isFetching, fetchError, refetch } = useFetchTaskByID(
documentId,
currentLanguage === Language.yaml ? "yaml" : "json",
merged
);

const onMergedChange = (checked: boolean) => {
setMerged(checked);
refetch();
};

React.useEffect(() => {
setCode(undefined);
documentId && fetchDocument(documentId);
}, [documentId, currentLanguage]);
if (task) {
const formattedCode =
currentLanguage === Language.yaml
? task.toString()
: JSON.stringify(task, undefined, 2);

const fetchDocument = (documentId: number) => {
if (currentLanguage === Language.yaml) {
fetch(documentId, currentLanguage).then((yaml) => {
setCode(yaml.toString());
focusAndHomePosition();
});
} else {
fetch(documentId, currentLanguage).then((json) => {
setCode(JSON.stringify(json, undefined, 2));
focusAndHomePosition();
});
setCode(formattedCode);
focusAndHomePosition();
}
};
}, [task, currentLanguage]);

const focusAndHomePosition = () => {
if (editorRef.current) {
editorRef.current.focus();
editorRef.current.setPosition({ column: 0, lineNumber: 1 });
}
};

const refreshControl = (
<CodeEditorControl
icon={<UndoIcon />}
aria-label="refresh-task"
tooltipProps={{ content: "Refresh" }}
onClick={() => {
documentId && fetchDocument(documentId);
refetch();
}}
isVisible={code !== ""}
/>
Expand Down Expand Up @@ -147,6 +142,15 @@ export const SimpleDocumentViewer = <FetchType,>({
}
customControls={[
refreshControl,
<Checkbox
className="merged-checkbox"
key="merged"
id="merged"
label="Merged"
isChecked={merged}
onChange={(e, checked) => onMergedChange(checked)}
aria-label="Merged Checkbox"
/>,
<div
className={css(
editorStyles.codeEditorTab,
Expand Down Expand Up @@ -193,8 +197,8 @@ export const SimpleDocumentViewer = <FetchType,>({
);
};

export interface ISimpleDocumentViewerModalProps<FetchType>
extends ISimpleDocumentViewerProps<FetchType> {
export interface ISimpleDocumentViewerModalProps
extends ISimpleDocumentViewerProps {
/** Simple text content of the modal header. */
title?: string;

Expand All @@ -220,14 +224,14 @@ export interface ISimpleDocumentViewerModalProps<FetchType>
* displayed if the `documentId` is set. If `documentId` is `undefined`, the modal is
* closed.
*/
export const SimpleDocumentViewerModal = <FetchType,>({
export const SimpleDocumentViewerModal = ({
title,
documentId,
onClose,
position = "top",
isFullHeight = true,
...rest
}: ISimpleDocumentViewerModalProps<FetchType>) => {
}: ISimpleDocumentViewerModalProps) => {
const isOpen = documentId !== undefined;

return (
Expand All @@ -248,7 +252,7 @@ export const SimpleDocumentViewerModal = <FetchType,>({
</Button>,
]}
>
<SimpleDocumentViewer<FetchType>
<SimpleDocumentViewer
documentId={documentId}
height={isFullHeight ? "full" : undefined}
{...rest}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,7 @@ import { ImportApplicationsForm } from "../components/import-applications-form";
import { ConditionalRender } from "@app/components/ConditionalRender";
import { NoDataEmptyState } from "@app/components/NoDataEmptyState";
import { ConditionalTooltip } from "@app/components/ConditionalTooltip";
import {
getArchetypeById,
getAssessmentsByItemId,
getTaskById,
} from "@app/api/rest";
import { getArchetypeById, getAssessmentsByItemId } from "@app/api/rest";
import { ApplicationDependenciesForm } from "@app/components/ApplicationDependenciesFormContainer/ApplicationDependenciesForm";
import { useState } from "react";
import { ApplicationAnalysisStatus } from "../components/application-analysis-status";
Expand Down Expand Up @@ -631,6 +627,7 @@ export const ApplicationsTable: React.FC = () => {
if (candidateTasks.length === selectedRows.length) return true;
return false;
};

const hasExistingAnalysis = selectedRows.some((app) =>
tasks.some((task) => task.application?.id === app.id)
);
Expand Down Expand Up @@ -872,6 +869,10 @@ export const ApplicationsTable: React.FC = () => {
>
<Tbody>
{currentPageItems?.map((application, rowIndex) => {
const hasExistingAnalysis = tasks.some(
(task) => task.application?.id === application.id
);

return (
<Tr
key={application.name}
Expand Down Expand Up @@ -1030,7 +1031,7 @@ export const ApplicationsTable: React.FC = () => {
},
]
: []),
...(analysisReadAccess
...(analysisReadAccess && hasExistingAnalysis
? [
{
title: t("actions.analysisDetails"),
Expand Down Expand Up @@ -1111,9 +1112,8 @@ export const ApplicationsTable: React.FC = () => {
onClose={() => setSaveApplicationModalState(null)}
/>
</Modal>
<SimpleDocumentViewerModal<Task | string>
<SimpleDocumentViewerModal
title={`Analysis details for ${taskToView?.name}`}
fetch={getTaskById}
documentId={taskToView?.task}
onClose={() => setTaskToView(undefined)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {
} from "@app/pages/issues/helpers";
import { ApplicationTags } from "../application-tags";
import { COLOR_HEX_VALUES_BY_NAME } from "@app/Constants";
import { getTaskById } from "@app/api/rest";
import { EmptyTextMessage } from "@app/components/EmptyTextMessage";
import { SimpleDocumentViewerModal } from "@app/components/SimpleDocumentViewer";
import { useFetchFacts } from "@app/queries/facts";
Expand Down Expand Up @@ -425,9 +424,8 @@ export const ApplicationDetailDrawer: React.FC<
)}
</>
)}
<SimpleDocumentViewerModal<Task | string>
<SimpleDocumentViewerModal
title={`Analysis details for ${application?.name}`}
fetch={getTaskById}
documentId={taskIdToView}
onClose={() => {
setTaskIdToView(undefined);
Expand Down
24 changes: 23 additions & 1 deletion client/src/app/queries/tasks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMutation, useQuery } from "@tanstack/react-query";

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

interface FetchTasksFilters {
addon?: string;
Expand Down Expand Up @@ -80,3 +80,25 @@ export const useCancelTaskMutation = (
},
});
};

export const TaskByIDQueryKey = "taskByID";

export const useFetchTaskByID = (
taskId?: number,
format = "json",
merged = false
) => {
console.log("useFetchTaskByID", taskId, format, merged);
const { isLoading, error, data, refetch } = useQuery({
queryKey: [TaskByIDQueryKey, taskId, format, merged],
queryFn: () => (taskId ? getTaskById(taskId, format, merged) : null),
enabled: !!taskId,
});

return {
task: data,
isFetching: isLoading,
fetchError: error,
refetch,
};
};

0 comments on commit 25ec233

Please sign in to comment.