Skip to content
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
89fc632
fix const
pa-lem Oct 16, 2025
2117f24
add api function
pa-lem Oct 16, 2025
d82183d
add domain
pa-lem Oct 16, 2025
4e07916
add query
pa-lem Oct 16, 2025
2ede2bf
update domain
pa-lem Oct 16, 2025
495bf89
use fsd in checks
pa-lem Oct 16, 2025
c5bf78d
Merge branch 'stable' of github.com:opsmill/infrahub into ple-fix-ret…
pa-lem Oct 20, 2025
8b14885
add check api
pa-lem Oct 20, 2025
38a5477
update keys
pa-lem Oct 20, 2025
ece8beb
add domain
pa-lem Oct 20, 2025
b5e4b6d
add mutation
pa-lem Oct 20, 2025
a917c43
update query
pa-lem Oct 20, 2025
8027eea
cleanup useless style
pa-lem Oct 20, 2025
cb05e69
remove refetch
pa-lem Oct 20, 2025
d400a05
update mutation
pa-lem Oct 20, 2025
5defc71
remove unused file
pa-lem Oct 21, 2025
9846acd
remove unused file
pa-lem Oct 21, 2025
6702994
Merge branch 'stable' of github.com:opsmill/infrahub into ple-fix-ret…
pa-lem Oct 21, 2025
3a93cf2
add api
pa-lem Oct 21, 2025
8387421
update query keys
pa-lem Oct 21, 2025
670016c
add domain
pa-lem Oct 21, 2025
04b2ff9
add query
pa-lem Oct 21, 2025
faa1302
update component
pa-lem Oct 21, 2025
c64569c
remove file
pa-lem Oct 21, 2025
6a5ee2b
add changelog
pa-lem Oct 22, 2025
eead83b
add errors check
pa-lem Oct 22, 2025
aff1d28
Merge branch 'stable' of github.com:opsmill/infrahub into ple-fix-ret…
pa-lem Oct 22, 2025
c641f71
fix type
pa-lem Oct 22, 2025
f897859
use as const
pa-lem Oct 22, 2025
6d05267
update type
pa-lem Oct 22, 2025
b10b133
update ui for retry
pa-lem Oct 22, 2025
6b4c274
update retry all ui
pa-lem Oct 22, 2025
3ed3581
ensure kind
pa-lem Oct 22, 2025
53d073c
revert tooltip
pa-lem Oct 22, 2025
d479645
improve TS
bilalabbad Oct 23, 2025
55415c9
rephrase changelog
pa-lem Oct 23, 2025
ac4182d
Merge branch 'ple-fix-retry-checks-IFC-1023' of github.com:opsmill/in…
pa-lem Oct 23, 2025
9b5ec2a
Merge branch 'stable' into ple-fix-retry-checks-IFC-1023
pa-lem Oct 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/+checks.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix check retry request and loading state
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changelog is not by user oriented. We write it for them

1 change: 1 addition & 0 deletions frontend/app/src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const CHECKS_LABEL = {
export const VALIDATIONS_ENUM_MAP: { [key: string]: string } = {
CoreArtifactValidator: "ARTIFACT",
CoreDataValidator: "DATA",
CoreGeneratorValidator: "GENERATOR",
CoreRepositoryValidator: "REPOSITORY",
CoreSchemaValidator: "SCHEMA",
CoreUserValidator: "USER",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { gql } from "@apollo/client";

export const GET_CHECKS = gql`
query GET_CORE_CHECKS($ids: [ID]!) {
CoreCheck(ids: $ids) {
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";

const GET_CHECK_DETAILS = gql`
query GET_CHECK_DETAILS($id: ID!) {
CoreCheck(ids: [$id]) {
edges {
node {
id
Expand Down Expand Up @@ -63,3 +65,16 @@ export const GET_CHECKS = gql`
}
}
`;

export interface GetCheckDetailsFromApiParams {
checkId: string;
}

export const getCheckDetailsFromApi = async ({ checkId }: GetCheckDetailsFromApiParams) => {
return graphqlClient.query({
query: GET_CHECK_DETAILS,
variables: {
id: checkId,
},
});
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { gql } from "@apollo/client";

export const GET_VALIDATORS = gql`
query GET_CORE_VALIDATORS($ids: [ID]!) {
CoreValidator(proposed_change__ids: $ids) {
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
Expand Down Expand Up @@ -37,3 +39,16 @@ export const GET_VALIDATORS = gql`
}
}
`;

export interface GetValidatorsFromApiParams {
proposedChangeId: string;
}

export const getValidatorsFromApi = async ({ proposedChangeId }: GetValidatorsFromApiParams) => {
return graphqlClient.query({
query: GET_VALIDATORS,
variables: {
id: proposedChangeId,
},
});
};
32 changes: 32 additions & 0 deletions frontend/app/src/entities/diff/api/run-check-from-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { gql } from "@apollo/client";

import { CheckType } 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 type UpdateCheckFromApiParams = {
proposedChangeId: string;
checkType: CheckType;
};

export const runCheckFromApi = ({ proposedChangeId, checkType }: UpdateCheckFromApiParams) => {
return graphqlClient.mutate({
mutation: RUN_CHECK,
variables: {
proposedChangeId,
checkType,
},
});
};
14 changes: 0 additions & 14 deletions frontend/app/src/entities/diff/api/runCheck.ts

This file was deleted.

41 changes: 17 additions & 24 deletions frontend/app/src/entities/diff/checks/check.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
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 { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom";

import { useGetCheckDetailsQuery } from "../domain/get-check-details.query";
import { DataIntegrityConflicts } from "./data-integrity-conflicts";
import { SchemaIntegrityConflicts } from "./schema-integrity-conflicts";

Expand Down Expand Up @@ -75,9 +74,19 @@ 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 } = useGetCheckDetailsQuery({ checkId: id });

const check = data?.CoreCheck?.edges?.[0]?.node ?? {};
if (error) {
return (
<div className={"flex flex-col rounded-md border-l-4 bg-white p-2"}>
<ErrorScreen message="Something went wrong when fetching the check details" />
</div>
);
}

if (isPending) {
return <LoadingIndicator />;
}

const {
__typename,
Expand All @@ -92,14 +101,6 @@ export const Check = ({ id }: tCheckProps) => {
conflicts,
} = check;

if (error) {
return (
<div className={"flex flex-col rounded-md border-l-4 bg-white p-2"}>
<ErrorScreen message="Something went wrong when fetching the check details" />
</div>
);
}

const columns = [
{
name: "type",
Expand Down Expand Up @@ -133,20 +134,12 @@ export const Check = ({ id }: tCheckProps) => {
<div className="mb-2 flex">
<div className="flex flex-1 flex-col">
<div className="flex items-center">
{loading ? (
<Skeleton className="mr-2 h-3 w-3 rounded-sm" />
) : (
getCheckIcon(conclusion?.value)
)}
{getCheckIcon(conclusion?.value)}

{loading ? <Skeleton className="h-3 w-40" /> : name?.value || display_label}
{name?.value || display_label}

<div className="flex flex-1 items-center justify-end">
{loading ? (
<Skeleton className="h-3 w-24" />
) : (
created_at?.value && <DateDisplay date={created_at?.value} />
)}
{created_at?.value && <DateDisplay date={created_at?.value} />}

<Popover>
<PopoverTrigger asChild>
Expand Down
74 changes: 39 additions & 35 deletions frontend/app/src/entities/diff/checks/checks-summary.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand All @@ -19,24 +17,26 @@ 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 { 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 = {
import { proposedChangeValidatorsKeys } from "../domain/diff.query-keys";

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);

Expand All @@ -49,24 +49,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(<Alert type={ALERT_TYPES.SUCCESS} message="Checks are running" />);
}
mutate(
{
proposedChangeId: proposedChangeId!,
checkType: VALIDATIONS_ENUM_MAP[validator]!,
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: proposedChangeValidatorsKeys.allWithinProposedChange(proposedChangeId!),
});
toast(<Alert type={ALERT_TYPES.SUCCESS} message="Checks are running" />);
},
}
);
};

const canRetry = (stats: any) => {
Expand All @@ -93,7 +89,7 @@ export const ChecksSummary = (props: tChecksSummaryProps) => {
className="gap-1 hover:bg-neutral-200"
>
Retry all
<Icon icon="mdi:reload" className={classNames(isLoading && "animate-spin")} />
<Retry isLoading={isPending || isLoading} isDisabled={isPending || isLoading} />
</Button>
</div>

Expand All @@ -103,20 +99,28 @@ export const ChecksSummary = (props: tChecksSummaryProps) => {
{Object.entries(validatorsCount).map(([kind, data]: [string, any]) => (
<div key={kind} className="flex items-center justify-center gap-2 p-2">
<div className={"group relative flex flex-col items-center"}>
<PieChart data={data} onClick={() => canRetry(data) && handleRetry(kind)}>
<PieChart data={data} />

<div className="flex h-6 items-center justify-center">
<span
className={classNames(
"text-xs",
canRetry(data) && "absolute text-xs group-hover:invisible"
)}
>
{(schemaKindLabel[kind] ?? kind)?.replace("Validator", "").trim()}
</span>

{canRetry(data) && (
<div className="invisible absolute cursor-pointer group-hover:visible">
<div className="invisible absolute group-hover:visible">
<Retry
isLoading={isLoading || !!data.inProgress}
isLoading={isPending || isLoading || !!data.inProgress}
isDisabled={!canRetry(data)}
onClick={() => canRetry(data) && handleRetry(kind)}
/>
</div>
)}
</PieChart>

<span className="text-xs">
{schemaKindLabel[kind]?.replace("Validator", "").trim()}
</span>
</div>
</div>
</div>
))}
Expand Down
38 changes: 18 additions & 20 deletions frontend/app/src/entities/diff/checks/checks.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,40 @@
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 <ErrorScreen message="Something went wrong when fetching the checks list." />;
}

if (isPending) {
return <LoadingIndicator />;
}

return (
<div className="grow bg-stone-100 text-sm">
<ChecksSummary isLoading={loading} validators={validators} refetch={refetch} />
<ChecksSummary isLoading={isPending} validators={validators} />

<div className="space-y-2 p-4 pt-0">
{validators.map((item: any) => (
{validators?.map((item: any) => (
<Validator key={item.id} validator={item} />
))}
</div>
</div>
);
});
};
Loading