diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6faf8440c..44ec42e21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ changes.
- Fix displaying modals to not block signing transactions [Issue 710](https://github.com/IntersectMBO/govtool/issues/710)
- Change style of url button to trim the file name [Issue 655](https://github.com/IntersectMBO/govtool/issues/655)
- Change regex for parsing urls to match urls without protocol [Issue 655](https://github.com/IntersectMBO/govtool/issues/655)
+- Integrate ga displaying metadata validation with the validation service [Issue 712](https://github.com/IntersectMBO/govtool/issues/712)
### Added
diff --git a/govtool/frontend/src/components/molecules/Breadcrumbs.tsx b/govtool/frontend/src/components/molecules/Breadcrumbs.tsx
index c0766d345..83f191034 100644
--- a/govtool/frontend/src/components/molecules/Breadcrumbs.tsx
+++ b/govtool/frontend/src/components/molecules/Breadcrumbs.tsx
@@ -4,12 +4,14 @@ import Divider from "@mui/material/Divider";
import { useScreenDimension } from "@hooks";
import { Typography } from "@atoms";
+import { getMetadataDataMissingStatusTranslation } from "@/utils";
+import { MetadataValidationStatus } from "@/models";
type BreadcrumbsProps = {
elementOne: string;
elementOnePath: To;
elementTwo: string;
- isDataMissing: boolean;
+ isDataMissing: MetadataValidationStatus | boolean;
};
export const Breadcrumbs = ({
@@ -19,7 +21,6 @@ export const Breadcrumbs = ({
isDataMissing,
}: BreadcrumbsProps) => {
const { isMobile } = useScreenDimension();
-
return (
- {isDataMissing || elementTwo}
+ {(isDataMissing !== false &&
+ getMetadataDataMissingStatusTranslation(
+ isDataMissing as MetadataValidationStatus,
+ )) ||
+ elementTwo}
);
diff --git a/govtool/frontend/src/components/molecules/DataMissingInfoBox.tsx b/govtool/frontend/src/components/molecules/DataMissingInfoBox.tsx
index f13da7ec2..1770a6806 100644
--- a/govtool/frontend/src/components/molecules/DataMissingInfoBox.tsx
+++ b/govtool/frontend/src/components/molecules/DataMissingInfoBox.tsx
@@ -2,40 +2,43 @@ import { Box, Link } from "@mui/material";
import { Typography } from "@atoms";
import { useTranslation } from "@hooks";
-import { GAMetedataErrors, openInNewTab } from "@utils";
+import { openInNewTab } from "@utils";
+import { MetadataValidationStatus } from "@/models";
export const DataMissingInfoBox = ({
isDataMissing,
isInProgress,
isSubmitted,
}: {
- isDataMissing: boolean | GAMetedataErrors;
+ isDataMissing: boolean | MetadataValidationStatus;
isInProgress?: boolean;
isSubmitted?: boolean;
}) => {
const { t } = useTranslation();
const gaMetadataErrorMessage = {
- [GAMetedataErrors.DATA_MISSING]: t("errors.gAMetadata.message.dataMissing"),
- [GAMetedataErrors.INCORRECT_FORMAT]: t(
+ [MetadataValidationStatus.URL_NOT_FOUND]: t(
+ "errors.gAMetadata.message.dataMissing",
+ ),
+ [MetadataValidationStatus.INVALID_JSONLD]: t(
"errors.gAMetadata.message.incorrectFormat",
),
- [GAMetedataErrors.NOT_VERIFIABLE]: t(
+ [MetadataValidationStatus.INVALID_HASH]: t(
"errors.gAMetadata.message.notVerifiable",
),
- }[isDataMissing as GAMetedataErrors];
+ }[isDataMissing as MetadataValidationStatus];
const gaMetadataErrorDescription = {
- [GAMetedataErrors.DATA_MISSING]: t(
+ [MetadataValidationStatus.URL_NOT_FOUND]: t(
"errors.gAMetadata.description.dataMissing",
),
- [GAMetedataErrors.INCORRECT_FORMAT]: t(
+ [MetadataValidationStatus.INVALID_JSONLD]: t(
"errors.gAMetadata.description.incorrectFormat",
),
- [GAMetedataErrors.NOT_VERIFIABLE]: t(
+ [MetadataValidationStatus.INVALID_HASH]: t(
"errors.gAMetadata.description.notVerifiable",
),
- }[isDataMissing as GAMetedataErrors];
+ }[isDataMissing as MetadataValidationStatus];
return isDataMissing && !isSubmitted && !isInProgress ? (
@@ -38,11 +39,17 @@ export const GovernanceActionCardHeader = ({
...(isDataMissing && { color: "#9E2323" }),
}}
>
- {isDataMissing || title}
+ {(isDataMissing !== false &&
+ getMetadataDataMissingStatusTranslation(
+ isDataMissing as MetadataValidationStatus,
+ )) ||
+ title}
{isDataMissing && typeof isDataMissing === "string" && (
- {isDataMissing || title}
+ {(isDataMissing !== false &&
+ getMetadataDataMissingStatusTranslation(
+ isDataMissing as MetadataValidationStatus,
+ )) ||
+ title}
diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx
index 689c5b24a..d66a05a1c 100644
--- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx
+++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx
@@ -7,7 +7,7 @@ import {
GovernanceActionDetailsCardVotes,
} from "@molecules";
import { GovernanceActionDetailsCardData } from "@organisms";
-import { GAMetedataErrors } from "@utils";
+import { MetadataValidationStatus } from "@models";
type GovernanceActionDetailsCardProps = {
abstainVotes: number;
@@ -25,7 +25,7 @@ type GovernanceActionDetailsCardProps = {
rationale?: string;
yesVotes: number;
govActionId: string;
- isDataMissing: boolean | GAMetedataErrors;
+ isDataMissing: boolean | MetadataValidationStatus;
isDashboard?: boolean;
isVoter?: boolean;
voteFromEP?: string;
diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx
index 617979d9a..da838262e 100644
--- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx
+++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx
@@ -10,7 +10,8 @@ import {
GovernanceActionDetailsCardOnChainData,
} from "@molecules";
import { useScreenDimension, useTranslation } from "@hooks";
-import { GAMetedataErrors, getProposalTypeNoEmptySpaces } from "@utils";
+import { getProposalTypeNoEmptySpaces } from "@utils";
+import { MetadataValidationStatus } from "@models";
type GovernanceActionDetailsCardDataProps = {
type: string;
@@ -25,7 +26,7 @@ type GovernanceActionDetailsCardDataProps = {
about?: string;
motivation?: string;
rationale?: string;
- isDataMissing: boolean | GAMetedataErrors;
+ isDataMissing: boolean | MetadataValidationStatus;
isOneColumn: boolean;
isDashboard?: boolean;
isInProgress?: boolean;
diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts
index 78ef05249..714251b82 100644
--- a/govtool/frontend/src/i18n/locales/en.ts
+++ b/govtool/frontend/src/i18n/locales/en.ts
@@ -714,6 +714,11 @@ export const en = {
usingUnregisteredStakeKeys:
"Warning, no registered stake keys, using unregistered stake keys",
},
+ dataMissingErrors: {
+ dataMissing: "Data Missing",
+ notVerifiable: "Not Verifiable",
+ incorrectFormat: "Incorrect Format",
+ },
about: "About",
abstain: "Abstain",
addLink: "+ Add link",
diff --git a/govtool/frontend/src/models/api.ts b/govtool/frontend/src/models/api.ts
index 0686601b7..6c61c77c3 100644
--- a/govtool/frontend/src/models/api.ts
+++ b/govtool/frontend/src/models/api.ts
@@ -1,4 +1,4 @@
-import { GAMetedataErrors } from "@utils";
+import { MetadataValidationStatus } from "@models";
export interface VoterInfo {
isRegisteredAsDRep: boolean;
@@ -22,7 +22,7 @@ export interface DRepData {
deposit: number;
votingPower: number;
status: DRepStatus;
- type: 'DRep' | 'SoleVoter';
+ type: "DRep" | "SoleVoter";
}
export type Vote = "yes" | "no" | "abstain";
@@ -70,5 +70,7 @@ export interface VotedProposal {
}
export type VotedProposalToDisplay = {
vote: ProposalVote;
- proposal: ProposalData & { isDataMissing: boolean | GAMetedataErrors };
+ proposal: ProposalData & {
+ isDataMissing: boolean | MetadataValidationStatus;
+ };
};
diff --git a/govtool/frontend/src/models/metadataValidation.ts b/govtool/frontend/src/models/metadataValidation.ts
index 284a8821c..e36e7c226 100644
--- a/govtool/frontend/src/models/metadataValidation.ts
+++ b/govtool/frontend/src/models/metadataValidation.ts
@@ -10,7 +10,12 @@ export type ValidateMetadataResult = {
valid: boolean;
};
+export enum MetadataStandard {
+ CIP108 = "CIP108",
+}
+
export type MetadataValidationDTO = {
url: string;
hash: string;
+ standard?: MetadataStandard;
};
diff --git a/govtool/frontend/src/stories/GovernanceAction.stories.ts b/govtool/frontend/src/stories/GovernanceAction.stories.ts
index fece9f0d6..76f2408f9 100644
--- a/govtool/frontend/src/stories/GovernanceAction.stories.ts
+++ b/govtool/frontend/src/stories/GovernanceAction.stories.ts
@@ -1,7 +1,8 @@
import type { Meta, StoryObj } from "@storybook/react";
import { within, userEvent, waitFor, screen } from "@storybook/testing-library";
import { expect, jest } from "@storybook/jest";
-import { GAMetedataErrors, formatDisplayDate } from "@utils";
+import { formatDisplayDate } from "@utils";
+import { MetadataValidationStatus } from "@models";
import { GovernanceActionCard } from "@/components/molecules";
const meta = {
@@ -74,20 +75,20 @@ export const GovernanceActionCardIsLoading: Story = {
export const GovernanceActionCardDataMissing: Story = {
args: {
...commonArgs,
- isDataMissing: GAMetedataErrors.DATA_MISSING,
+ isDataMissing: MetadataValidationStatus.URL_NOT_FOUND,
},
};
export const GovernanceActionCardIncorectFormat: Story = {
args: {
...commonArgs,
- isDataMissing: GAMetedataErrors.INCORRECT_FORMAT,
+ isDataMissing: MetadataValidationStatus.INVALID_JSONLD,
},
};
export const GovernanceActionCardNotVerifiable: Story = {
args: {
...commonArgs,
- isDataMissing: GAMetedataErrors.NOT_VERIFIABLE,
+ isDataMissing: MetadataValidationStatus.INVALID_HASH,
},
};
diff --git a/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts b/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts
index cb9700c35..bd9a67c4f 100644
--- a/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts
+++ b/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { screen, userEvent, waitFor, within } from "@storybook/testing-library";
import { GovernanceActionDetailsCard } from "@organisms";
import { expect } from "@storybook/jest";
-import { GAMetedataErrors } from "@/utils";
+import { MetadataValidationStatus } from "@models";
const meta = {
title: "Example/GovernanceActionDetailsCard",
@@ -79,20 +79,20 @@ export const GovernanceActionDetailsDrep: Story = {
export const GovernanceActionDetailsDataMissing: Story = {
args: {
...commonArgs,
- isDataMissing: GAMetedataErrors.DATA_MISSING,
+ isDataMissing: MetadataValidationStatus.URL_NOT_FOUND,
},
};
export const GovernanceActionDetailsIncorrectFormat: Story = {
args: {
...commonArgs,
- isDataMissing: GAMetedataErrors.INCORRECT_FORMAT,
+ isDataMissing: MetadataValidationStatus.INVALID_JSONLD,
},
};
export const GovernanceActionDetailsNotVerifiable: Story = {
args: {
...commonArgs,
- isDataMissing: GAMetedataErrors.NOT_VERIFIABLE,
+ isDataMissing: MetadataValidationStatus.INVALID_HASH,
},
};
diff --git a/govtool/frontend/src/types/global.d.ts b/govtool/frontend/src/types/global.d.ts
index 0640e6c20..6204ffc90 100644
--- a/govtool/frontend/src/types/global.d.ts
+++ b/govtool/frontend/src/types/global.d.ts
@@ -1,4 +1,4 @@
-import { GAMetedataErrors } from "@utils";
+import { MetadataValidationStatus } from "@models";
export {};
@@ -40,7 +40,7 @@ declare global {
};
type ActionTypeToDsiplay = ActionType & {
- isDataMissing: boolean | GAMetedataErrors;
+ isDataMissing: boolean | MetadataValidationStatus;
};
interface ActionVotedOnType extends ActionTypeToDsiplay {
diff --git a/govtool/frontend/src/utils/getMetadataDataMissingStatusTranslation.ts b/govtool/frontend/src/utils/getMetadataDataMissingStatusTranslation.ts
new file mode 100644
index 000000000..0a91531a6
--- /dev/null
+++ b/govtool/frontend/src/utils/getMetadataDataMissingStatusTranslation.ts
@@ -0,0 +1,19 @@
+import i18n from "@/i18n";
+import { MetadataValidationStatus } from "@/models";
+
+/**
+ * Retrieves the translation for the given metadata validation status.
+ *
+ * @param status - The metadata validation status.
+ * @returns The translated string corresponding to the status.
+ */
+export const getMetadataDataMissingStatusTranslation = (
+ status: MetadataValidationStatus,
+): string => {
+ const errorKey = {
+ [MetadataValidationStatus.URL_NOT_FOUND]: "dataMissing",
+ [MetadataValidationStatus.INVALID_JSONLD]: "incorrectFormat",
+ [MetadataValidationStatus.INVALID_HASH]: "notVerifiable",
+ }[status] as "dataMissing" | "incorrectFormat" | "notVerifiable";
+ return i18n.t(`dataMissingErrors.${errorKey || "dataMissing"}`);
+};
diff --git a/govtool/frontend/src/utils/index.ts b/govtool/frontend/src/utils/index.ts
index e1069f5c2..2a0eb135e 100644
--- a/govtool/frontend/src/utils/index.ts
+++ b/govtool/frontend/src/utils/index.ts
@@ -14,6 +14,7 @@ export * from "./generateJsonld";
export * from "./getDRepID";
export * from "./getGovActionId";
export * from "./getLengthInBytes";
+export * from "./getMetadataDataMissingStatusTranslation";
export * from "./getProposalTypeLabel";
export * from "./isValidFormat";
export * from "./jsonUtils";
diff --git a/govtool/frontend/src/utils/tests/getMetadataDataMissingStatusTranslation.test.ts b/govtool/frontend/src/utils/tests/getMetadataDataMissingStatusTranslation.test.ts
new file mode 100644
index 000000000..230dedf48
--- /dev/null
+++ b/govtool/frontend/src/utils/tests/getMetadataDataMissingStatusTranslation.test.ts
@@ -0,0 +1,32 @@
+import { MetadataValidationStatus } from "@models";
+import { getMetadataDataMissingStatusTranslation } from "../getMetadataDataMissingStatusTranslation";
+
+describe("getMetadataDataMissingStatusTranslation", () => {
+ it("should return the correct translation for URL_NOT_FOUND status", () => {
+ const translation = getMetadataDataMissingStatusTranslation(
+ MetadataValidationStatus.URL_NOT_FOUND,
+ );
+ expect(translation).toBe("Data Missing");
+ });
+
+ it("should return the correct translation for INVALID_JSONLD status", () => {
+ const translation = getMetadataDataMissingStatusTranslation(
+ MetadataValidationStatus.INVALID_JSONLD,
+ );
+ expect(translation).toBe("Incorrect Format");
+ });
+
+ it("should return the correct translation for INVALID_HASH status", () => {
+ const translation = getMetadataDataMissingStatusTranslation(
+ MetadataValidationStatus.INVALID_HASH,
+ );
+ expect(translation).toBe("Not Verifiable");
+ });
+
+ it("should return the default translation for unknown status", () => {
+ const translation = getMetadataDataMissingStatusTranslation(
+ "UNKNOWN_STATUS" as MetadataValidationStatus,
+ );
+ expect(translation).toBe("Data Missing");
+ });
+});
diff --git a/govtool/frontend/src/utils/validateMetadataHash.ts b/govtool/frontend/src/utils/validateMetadataHash.ts
index af8ece41d..d9eb0e1e8 100644
--- a/govtool/frontend/src/utils/validateMetadataHash.ts
+++ b/govtool/frontend/src/utils/validateMetadataHash.ts
@@ -1,15 +1,6 @@
-import * as blake from "blakejs";
+import { postValidate } from "@services";
-import { API } from "@services";
-import { sharedGovernanceActionFields } from "@consts";
-
-import { URL_REGEX, areObjectsTheSame, canonizeJSON } from ".";
-
-export enum GAMetedataErrors {
- DATA_MISSING = "Data Missing",
- NOT_VERIFIABLE = "Data Not Verifiable",
- INCORRECT_FORMAT = "Data Formatted Incorrectly",
-}
+import { MetadataStandard, MetadataValidationStatus } from "@/models";
export const checkIsMissingGAMetadata = async ({
url,
@@ -17,45 +8,18 @@ export const checkIsMissingGAMetadata = async ({
}: {
url: string;
hash: string;
-}): Promise => {
- if (!url?.match(URL_REGEX)) {
- return GAMetedataErrors.DATA_MISSING;
- }
-
- let gaMetadata;
- try {
- const { data } = await API.get(url);
- gaMetadata = data;
- } catch (e) {
- return GAMetedataErrors.DATA_MISSING;
- }
- const JSONBody = gaMetadata?.body;
-
- if (!JSONBody) {
- return GAMetedataErrors.DATA_MISSING;
- }
-
- const govtoolFields = {
- ...sharedGovernanceActionFields,
- references: [],
- };
-
- if (!areObjectsTheSame(JSONBody, govtoolFields)) {
- return GAMetedataErrors.INCORRECT_FORMAT;
- }
-
- let canonizedGAMetadata;
+}): Promise => {
try {
- canonizedGAMetadata = await canonizeJSON(gaMetadata);
+ const { status } = await postValidate({
+ url,
+ hash,
+ standard: MetadataStandard.CIP108,
+ });
+ if (status) {
+ return status;
+ }
+ return false;
} catch (error) {
- return GAMetedataErrors.INCORRECT_FORMAT;
+ return MetadataValidationStatus.URL_NOT_FOUND;
}
-
- const gaHash = blake.blake2bHex(canonizedGAMetadata, undefined, 32);
-
- if (gaHash !== hash) {
- return GAMetedataErrors.NOT_VERIFIABLE;
- }
-
- return false;
};