diff --git a/changelog/+checks.fixed.md b/changelog/+checks.fixed.md new file mode 100644 index 0000000000..ba06df4153 --- /dev/null +++ b/changelog/+checks.fixed.md @@ -0,0 +1 @@ +Resolved a problem that caused generator checks to fail when retrying requests \ No newline at end of file diff --git a/frontend/app/src/config/constants.ts b/frontend/app/src/config/constants.ts index 12fe3d07dd..c7b5095f89 100644 --- a/frontend/app/src/config/constants.ts +++ b/frontend/app/src/config/constants.ts @@ -1,3 +1,5 @@ +import { CheckType } from "@/shared/api/graphql/generated/graphql"; + import { RelationshipKind } from "@/entities/nodes/types"; import { PROPOSED_CHANGE_OBJECT } from "@/entities/proposed-changes/constants"; @@ -115,9 +117,10 @@ export const CHECKS_LABEL = { IN_PROGRESS: "In progress", }; -export const VALIDATIONS_ENUM_MAP: { [key: string]: string } = { +export const VALIDATIONS_ENUM_MAP: { [key: string]: CheckType } = { CoreArtifactValidator: "ARTIFACT", CoreDataValidator: "DATA", + CoreGeneratorValidator: "GENERATOR", CoreRepositoryValidator: "REPOSITORY", CoreSchemaValidator: "SCHEMA", CoreUserValidator: "USER", diff --git a/frontend/app/src/entities/diff/api/getCheckDetails.ts b/frontend/app/src/entities/diff/api/get-check-details-from-api.ts similarity index 64% rename from frontend/app/src/entities/diff/api/getCheckDetails.ts rename to frontend/app/src/entities/diff/api/get-check-details-from-api.ts index 9e5fc0ce6c..74a976fa5d 100644 --- a/frontend/app/src/entities/diff/api/getCheckDetails.ts +++ b/frontend/app/src/entities/diff/api/get-check-details-from-api.ts @@ -1,8 +1,14 @@ import { gql } from "@apollo/client"; -export const GET_CHECKS = gql` - query GET_CORE_CHECKS($ids: [ID]!) { - CoreCheck(ids: $ids) { +import { + Get_Check_DetailsQuery, + Get_Check_DetailsQueryVariables, +} from "@/shared/api/graphql/generated/graphql"; +import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; + +const GET_CHECK_DETAILS = gql` + query GET_CHECK_DETAILS($id: ID!) { + CoreCheck(ids: [$id]) { edges { node { id @@ -63,3 +69,12 @@ export const GET_CHECKS = gql` } } `; + +export interface GetCheckDetailsFromApiParams extends Get_Check_DetailsQueryVariables {} + +export const getCheckDetailsFromApi = async (variables: GetCheckDetailsFromApiParams) => { + return graphqlClient.query({ + query: GET_CHECK_DETAILS, + variables, + }); +}; diff --git a/frontend/app/src/entities/diff/api/get-validators-from-api.ts b/frontend/app/src/entities/diff/api/get-validators-from-api.ts new file mode 100644 index 0000000000..0d7daff7ee --- /dev/null +++ b/frontend/app/src/entities/diff/api/get-validators-from-api.ts @@ -0,0 +1,54 @@ +import { gql } from "@apollo/client"; + +import { + Get_Check_DetailsQueryVariables, + Get_Core_ValidatorsQuery, +} from "@/shared/api/graphql/generated/graphql"; +import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; + +const GET_VALIDATORS = gql` + query GET_CORE_VALIDATORS($id: ID!) { + CoreValidator(proposed_change__ids: [$id]) { + edges { + node { + id + display_label + conclusion { + value + } + started_at { + value + } + completed_at { + value + } + state { + value + } + checks { + edges { + node { + conclusion { + value + } + severity { + value + } + } + } + } + __typename + } + } + } + } +`; + +export interface GetValidatorsFromApiParams extends Get_Check_DetailsQueryVariables {} + +export const getValidatorsFromApi = async (variables: GetValidatorsFromApiParams) => { + return graphqlClient.query({ + query: GET_VALIDATORS, + variables, + }); +}; diff --git a/frontend/app/src/entities/diff/api/getValidators.ts b/frontend/app/src/entities/diff/api/getValidators.ts deleted file mode 100644 index a7119838be..0000000000 --- a/frontend/app/src/entities/diff/api/getValidators.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_VALIDATORS = gql` - query GET_CORE_VALIDATORS($ids: [ID]!) { - CoreValidator(proposed_change__ids: $ids) { - edges { - node { - id - display_label - conclusion { - value - } - started_at { - value - } - completed_at { - value - } - state { - value - } - checks { - edges { - node { - conclusion { - value - } - severity { - value - } - } - } - } - __typename - } - } - } - } -`; diff --git a/frontend/app/src/entities/diff/api/run-check-from-api.ts b/frontend/app/src/entities/diff/api/run-check-from-api.ts new file mode 100644 index 0000000000..64c4c2b4c6 --- /dev/null +++ b/frontend/app/src/entities/diff/api/run-check-from-api.ts @@ -0,0 +1,32 @@ +import { gql } from "@apollo/client"; + +import { + Run_CheckMutation, + Run_CheckMutationVariables, +} from "@/shared/api/graphql/generated/graphql"; +import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; + +export const RUN_CHECK = gql` + mutation RUN_CHECK($proposedChangeId: String!, $checkType: CheckType) { + CoreProposedChangeRunCheck ( + data: { + id: $proposedChangeId, + check_type: $checkType + } + ) { + ok + } + } +`; + +export interface RunCheckFromApiParams extends Run_CheckMutationVariables {} + +export const runCheckFromApi = ({ proposedChangeId, checkType }: RunCheckFromApiParams) => { + return graphqlClient.mutate({ + mutation: RUN_CHECK, + variables: { + proposedChangeId, + checkType, + }, + }); +}; diff --git a/frontend/app/src/entities/diff/api/runCheck.ts b/frontend/app/src/entities/diff/api/runCheck.ts deleted file mode 100644 index 465c2dbd24..0000000000 --- a/frontend/app/src/entities/diff/api/runCheck.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Handlebars from "@/shared/libs/handlebars"; - -export const runCheck = Handlebars.compile(` -mutation { - CoreProposedChangeRunCheck ( - data: { - id: "{{id}}", - check_type: {{check_type}} - } - ) { - ok - } -} -`); diff --git a/frontend/app/src/entities/diff/checks/check.tsx b/frontend/app/src/entities/diff/checks/check.tsx index 6d20e1fa14..6c8eddfd7c 100644 --- a/frontend/app/src/entities/diff/checks/check.tsx +++ b/frontend/app/src/entities/diff/checks/check.tsx @@ -1,24 +1,22 @@ import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; -import useQuery from "@/shared/api/graphql/useQuery"; import { InfoButton } from "@/shared/components/buttons/info-button"; import Accordion from "@/shared/components/display/accordion"; import { DateDisplay } from "@/shared/components/display/date-display"; import { CodeViewer } from "@/shared/components/editor/code/code-viewer"; import ErrorScreen from "@/shared/components/errors/error-screen"; -import { Skeleton } from "@/shared/components/skeleton"; +import { LoadingIndicator } from "@/shared/components/loading/loading-indicator"; import { List } from "@/shared/components/table/list"; import { Popover, PopoverContent, PopoverTrigger } from "@/shared/components/ui/popover"; import { Tooltip } from "@/shared/components/ui/tooltip"; import { classNames } from "@/shared/utils/common"; -import { GET_CHECKS } from "@/entities/diff/api/getCheckDetails"; +import { DataIntegrityConflicts } from "@/entities/diff/checks/data-integrity-conflicts"; +import { SchemaIntegrityConflicts } from "@/entities/diff/checks/schema-integrity-conflicts"; +import { useGetCheckDetails } from "@/entities/diff/domain/get-check-details.query"; import { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom"; -import { DataIntegrityConflicts } from "./data-integrity-conflicts"; -import { SchemaIntegrityConflicts } from "./schema-integrity-conflicts"; - type tCheckProps = { id: string; }; @@ -75,9 +73,23 @@ const getCheckBorderColor = (severity?: string) => { export const Check = ({ id }: tCheckProps) => { const schemaKindLabel = useAtomValue(schemaKindLabelState); - const { loading, error, data } = useQuery(GET_CHECKS, { variables: { ids: [id] } }); + const { isPending, error, data: check } = useGetCheckDetails({ checkId: id }); + + if (error) { + return ( +
+ +
+ ); + } + + if (isPending) { + return ; + } - const check = data?.CoreCheck?.edges?.[0]?.node ?? {}; + if (!check) { + return null; + } const { __typename, @@ -92,14 +104,6 @@ export const Check = ({ id }: tCheckProps) => { conflicts, } = check; - if (error) { - return ( -
- -
- ); - } - const columns = [ { name: "type", @@ -133,20 +137,12 @@ export const Check = ({ id }: tCheckProps) => {
- {loading ? ( - - ) : ( - getCheckIcon(conclusion?.value) - )} + {getCheckIcon(conclusion?.value)} - {loading ? : name?.value || display_label} + {name?.value || display_label}
- {loading ? ( - - ) : ( - created_at?.value && - )} + {created_at?.value && } diff --git a/frontend/app/src/entities/diff/checks/checks-summary.tsx b/frontend/app/src/entities/diff/checks/checks-summary.tsx index 39b10f73aa..1dcf4fb34e 100644 --- a/frontend/app/src/entities/diff/checks/checks-summary.tsx +++ b/frontend/app/src/entities/diff/checks/checks-summary.tsx @@ -1,5 +1,3 @@ -import { gql } from "@apollo/client"; -import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; import { useParams } from "react-router"; import { toast } from "react-toastify"; @@ -10,7 +8,7 @@ import { VALIDATIONS_ENUM_MAP, } from "@/config/constants"; -import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; +import { queryClient } from "@/shared/api/rest/client"; import { Button } from "@/shared/components/buttons/button-primitive"; import { Retry } from "@/shared/components/buttons/retry"; import { PieChart } from "@/shared/components/display/pie-chart"; @@ -19,24 +17,25 @@ import { ALERT_TYPES, Alert } from "@/shared/components/ui/alert"; import { classNames } from "@/shared/utils/common"; import { useAuth } from "@/entities/authentication/ui/useAuth"; -import { runCheck } from "@/entities/diff/api/runCheck"; +import { proposedChangeValidatorsKeys } from "@/entities/diff/domain/diff.query-keys"; +import { useRunCheckMutation } from "@/entities/diff/domain/run-check.mutation"; import { getValidatorsStats } from "@/entities/proposed-changes/ui/checks"; import { genericSchemasAtom } from "@/entities/schema/stores/schema.atom"; import { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom"; -type tChecksSummaryProps = { +type ChecksSummaryProps = { validators: any[]; isLoading: boolean; - refetch: Function; }; -export const ChecksSummary = (props: tChecksSummaryProps) => { - const { isLoading, validators, refetch } = props; +export const ChecksSummary = (props: ChecksSummaryProps) => { + const { isLoading, validators } = props; const { proposedChangeId } = useParams(); const schemaKindLabel = useAtomValue(schemaKindLabelState); const schemaList = useAtomValue(genericSchemasAtom); const { isAuthenticated } = useAuth(); + const { mutate, isPending } = useRunCheckMutation(); const schemaData = schemaList.find((s) => s.kind === PROPOSED_CHANGES_VALIDATOR_OBJECT); @@ -49,24 +48,20 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { }, {}); const handleRetry = async (validator: string) => { - const runParams = { - id: proposedChangeId, - check_type: VALIDATIONS_ENUM_MAP[validator], - }; - - const mustationString = runCheck(runParams); - - const mutation = gql` - ${mustationString} - `; - - const result = await graphqlClient.mutate({ mutation }); - - refetch(); - - if (result?.data?.CoreProposedChangeRunCheck?.ok) { - toast(); - } + mutate( + { + proposedChangeId: proposedChangeId!, + checkType: VALIDATIONS_ENUM_MAP[validator]!, + }, + { + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: proposedChangeValidatorsKeys.allWithinProposedChange(proposedChangeId!), + }); + toast(); + }, + } + ); }; const canRetry = (stats: any) => { @@ -93,7 +88,7 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { className="gap-1 hover:bg-neutral-200" > Retry all - +
@@ -103,20 +98,28 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { {Object.entries(validatorsCount).map(([kind, data]: [string, any]) => (
- canRetry(data) && handleRetry(kind)}> + + +
+ + {(schemaKindLabel[kind] ?? kind)?.replace("Validator", "").trim()} + + {canRetry(data) && ( -
+
canRetry(data) && handleRetry(kind)} />
)} - - - - {schemaKindLabel[kind]?.replace("Validator", "").trim()} - +
))} diff --git a/frontend/app/src/entities/diff/checks/checks.tsx b/frontend/app/src/entities/diff/checks/checks.tsx index 3cb0958835..9988da5ceb 100644 --- a/frontend/app/src/entities/diff/checks/checks.tsx +++ b/frontend/app/src/entities/diff/checks/checks.tsx @@ -1,42 +1,39 @@ -import { forwardRef, useImperativeHandle } from "react"; import { useParams } from "react-router"; -import useQuery from "@/shared/api/graphql/useQuery"; import ErrorScreen from "@/shared/components/errors/error-screen"; +import { LoadingIndicator } from "@/shared/components/loading/loading-indicator"; -import { GET_VALIDATORS } from "@/entities/diff/api/getValidators"; +import { ChecksSummary } from "@/entities/diff/checks/checks-summary"; +import { Validator } from "@/entities/diff/checks/validator"; +import { useGetValidatorsQuery } from "@/entities/diff/domain/get-validators.query"; -import { ChecksSummary } from "./checks-summary"; -import { Validator } from "./validator"; - -export const Checks = forwardRef((_, ref) => { +export const Checks = () => { const { proposedChangeId } = useParams(); - const { loading, error, data, refetch } = useQuery(GET_VALIDATORS, { - notifyOnNetworkStatusChange: true, - variables: { - ids: [proposedChangeId], - }, + const { + isPending, + error, + data: validators, + } = useGetValidatorsQuery({ + proposedChangeId: proposedChangeId!, }); - // Provide refetch function to parent - useImperativeHandle(ref, () => ({ refetch })); - - const validators = data?.CoreValidator?.edges?.map((edge: any) => edge.node) ?? []; - if (error) { return ; } + if (isPending) { + return ; + } + return (
- - +
- {validators.map((item: any) => ( + {validators.map((item) => ( ))}
); -}); +}; diff --git a/frontend/app/src/entities/diff/checks/validator-details.tsx b/frontend/app/src/entities/diff/checks/validator-details.tsx index 3af08ae2b0..60d9004226 100644 --- a/frontend/app/src/entities/diff/checks/validator-details.tsx +++ b/frontend/app/src/entities/diff/checks/validator-details.tsx @@ -56,7 +56,6 @@ export const ValidatorDetails = ({ id }: tValidatorDetails) => { {!validator?.checks?.edges?.length && }
- {!!validator?.checks?.edges?.length && }
); diff --git a/frontend/app/src/entities/diff/domain/diff.query-keys.ts b/frontend/app/src/entities/diff/domain/diff.query-keys.ts index ed5e1f733d..94d0bb328e 100644 --- a/frontend/app/src/entities/diff/domain/diff.query-keys.ts +++ b/frontend/app/src/entities/diff/domain/diff.query-keys.ts @@ -9,3 +9,14 @@ export const treeQueryKeys = { export const updateDiffMutationKeys = { all: ["update-diff"] as const, }; + +export const getCheckQueryKeys = { + all: ["checks"] as const, + details: (checkId: string) => [...getCheckQueryKeys.all, checkId] as const, +}; + +export const proposedChangeValidatorsKeys = { + all: ["proposed-change-validators"] as const, + allWithinProposedChange: (proposedChangeId: string) => + [...proposedChangeValidatorsKeys.all, proposedChangeId] as const, +}; diff --git a/frontend/app/src/entities/diff/domain/get-check-details.query.ts b/frontend/app/src/entities/diff/domain/get-check-details.query.ts new file mode 100644 index 0000000000..06b7551951 --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-check-details.query.ts @@ -0,0 +1,13 @@ +import { queryOptions, useQuery } from "@tanstack/react-query"; + +import { getCheckQueryKeys } from "@/entities/diff/domain/diff.query-keys"; +import { GetCheckDetailsParams, getCheckDetails } from "@/entities/diff/domain/get-check-details"; + +export const useGetCheckDetails = (params: GetCheckDetailsParams) => { + return useQuery( + queryOptions({ + queryKey: getCheckQueryKeys.details(params.checkId), + queryFn: () => getCheckDetails(params), + }) + ); +}; diff --git a/frontend/app/src/entities/diff/domain/get-check-details.ts b/frontend/app/src/entities/diff/domain/get-check-details.ts new file mode 100644 index 0000000000..edf6790a4f --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-check-details.ts @@ -0,0 +1,11 @@ +import { getCheckDetailsFromApi } from "@/entities/diff/api/get-check-details-from-api"; + +export type GetCheckDetailsParams = { checkId: string }; + +export const getCheckDetails = async ({ checkId }: GetCheckDetailsParams) => { + const { data, error } = await getCheckDetailsFromApi({ id: checkId }); + + if (error) throw error; + + return data?.CoreCheck?.edges?.[0]?.node ?? null; +}; diff --git a/frontend/app/src/entities/diff/domain/get-validators.query.ts b/frontend/app/src/entities/diff/domain/get-validators.query.ts new file mode 100644 index 0000000000..357b59df7b --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-validators.query.ts @@ -0,0 +1,13 @@ +import { queryOptions, useQuery } from "@tanstack/react-query"; + +import { proposedChangeValidatorsKeys } from "@/entities/diff/domain/diff.query-keys"; +import { GetValidatorsParams, getValidators } from "@/entities/diff/domain/get-validators"; + +export const useGetValidatorsQuery = (params: GetValidatorsParams) => { + return useQuery( + queryOptions({ + queryKey: proposedChangeValidatorsKeys.allWithinProposedChange(params.proposedChangeId), + queryFn: () => getValidators(params), + }) + ); +}; diff --git a/frontend/app/src/entities/diff/domain/get-validators.ts b/frontend/app/src/entities/diff/domain/get-validators.ts new file mode 100644 index 0000000000..0b290fff38 --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-validators.ts @@ -0,0 +1,11 @@ +import { getValidatorsFromApi } from "@/entities/diff/api/get-validators-from-api"; + +export type GetValidatorsParams = { proposedChangeId: string }; + +export const getValidators = async ({ proposedChangeId }: GetValidatorsParams) => { + const { data, error } = await getValidatorsFromApi({ id: proposedChangeId }); + + if (error) throw error; + + return data?.CoreValidator?.edges?.map((edge) => edge.node)?.filter((node) => !!node) ?? []; +}; diff --git a/frontend/app/src/entities/diff/domain/run-check.mutation.ts b/frontend/app/src/entities/diff/domain/run-check.mutation.ts new file mode 100644 index 0000000000..d8cec9f9cc --- /dev/null +++ b/frontend/app/src/entities/diff/domain/run-check.mutation.ts @@ -0,0 +1,9 @@ +import { useMutation } from "@tanstack/react-query"; + +import { runCheck } from "@/entities/diff/domain/run-check"; + +export function useRunCheckMutation() { + return useMutation({ + mutationFn: runCheck, + }); +} diff --git a/frontend/app/src/entities/diff/domain/run-check.ts b/frontend/app/src/entities/diff/domain/run-check.ts new file mode 100644 index 0000000000..67de85928e --- /dev/null +++ b/frontend/app/src/entities/diff/domain/run-check.ts @@ -0,0 +1,13 @@ +import { CheckType } from "@/shared/api/graphql/generated/graphql"; + +import { runCheckFromApi } from "@/entities/diff/api/run-check-from-api"; + +export type RunCheckParams = { + proposedChangeId: string; + checkType: CheckType; +}; +export type RunCheck = (params: RunCheckParams) => Promise; + +export const runCheck: RunCheck = async (params) => { + await runCheckFromApi(params); +}; diff --git a/frontend/app/src/shared/components/display/pie-chart.tsx b/frontend/app/src/shared/components/display/pie-chart.tsx index cc6366d1fe..91151aa03e 100644 --- a/frontend/app/src/shared/components/display/pie-chart.tsx +++ b/frontend/app/src/shared/components/display/pie-chart.tsx @@ -1,8 +1,7 @@ import { Cell, Pie, PieChart as RPieChart, Tooltip } from "recharts"; -type tPieChart = { +type PieChartProps = { data: any[]; - children?: any; onClick?: Function; }; @@ -14,14 +13,14 @@ const renderCustomizedTooltip = (props: any) => { } return ( - +
{data.name}: {data.value} - +
); }; -export const PieChart = (props: tPieChart) => { - const { data, children, onClick } = props; +export const PieChart = (props: PieChartProps) => { + const { data, onClick } = props; const handleClick = () => { if (!onClick) return; @@ -30,30 +29,22 @@ export const PieChart = (props: tPieChart) => { }; return ( -
-
- {children} -
- +
- - {data.map((entry, index) => ( ))} + +
);