diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml index bf1d30c45..2bf75ada6 100644 --- a/.github/workflows/lighthouse.yml +++ b/.github/workflows/lighthouse.yml @@ -36,23 +36,23 @@ jobs: - name: Run build and lighthouse task working-directory: ./govtool/frontend run: | - npm install - VITE_BASE_URL=https://staging.govtool.byron.network/api npm run build - lhci collect + npm install + VITE_BASE_URL=https://staging.govtool.byron.network/ npm run build + lhci collect - name: Evaluate reports if: github.repository_owner != 'IntersectMBO' run: | - lhci assert --preset "lighthouse:recommended" + lhci assert --preset "lighthouse:recommended" - name: Publish reports working-directory: ./govtool/frontend if: github.repository_owner == 'IntersectMBO' run: | - lhci assert --preset lighthouse:recommended || echo "LightHouse Assertion error ignored ..." - lhci upload --githubAppToken="${{ secrets.LHCI_GITHUB_APP_TOKEN }}" --token="${{ secrets.LHCI_SERVER_TOKEN }}" --serverBaseUrl=https://lighthouse.cardanoapi.io --ignoreDuplicateBuildFailure - curl -X POST https://ligththouse.cardanoapi.io/api/metrics/build-reports \ - -d "@./lighthouseci/$(ls ./.lighthouseci |grep 'lhr.*\.json' | head -n 1)" \ - -H "commit-hash: $(git rev-parse HEAD)" \ - -H "secret-token: ${{ secrets.METRICS_SERVER_SECRET_TOKEN }}" \ - -H 'Content-Type: application/json' || echo "Metric Upload error ignored ..." + lhci assert --preset lighthouse:recommended || echo "LightHouse Assertion error ignored ..." + lhci upload --githubAppToken="${{ secrets.LHCI_GITHUB_APP_TOKEN }}" --token="${{ secrets.LHCI_SERVER_TOKEN }}" --serverBaseUrl=https://lighthouse.cardanoapi.io --ignoreDuplicateBuildFailure + curl -X POST https://ligththouse.cardanoapi.io/api/metrics/build-reports \ + -d "@./lighthouseci/$(ls ./.lighthouseci |grep 'lhr.*\.json' | head -n 1)" \ + -H "commit-hash: $(git rev-parse HEAD)" \ + -H "secret-token: ${{ secrets.METRICS_SERVER_SECRET_TOKEN }}" \ + -H 'Content-Type: application/json' || echo "Metric Upload error ignored ..." diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index eb17b9e46..3d58e2468 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -3,12 +3,12 @@ name: Backend Test on: push: paths: - - .github/workflows/test_backend.yml + - .github/workflows/test_backend.yml # - govtool/backend # - tests/govtool-backend schedule: - - cron: '0 0 * * *' + - cron: "0 0 * * *" workflow_dispatch: inputs: deployment: @@ -24,26 +24,26 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: 3.11.4 - cache: 'pip' + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11.4 + cache: "pip" - - name: Run Backend Test - working-directory: tests/govtool-backend - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pytest -v --github-report - env: - BASE_URL: https://${{inputs.deployment || 'staging.govtool.byron.network/api' }} - METRICS_URL: https://metrics.cardanoapi.io - METRICS_API_SECRET: "${{ secrets.METRICS_SERVER_SECRET_TOKEN }}" + - name: Run Backend Test + working-directory: tests/govtool-backend + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pytest -v --github-report + env: + BASE_URL: https://${{inputs.deployment || 'staging.govtool.byron.network/' }} + METRICS_URL: https://metrics.cardanoapi.io + METRICS_API_SECRET: "${{ secrets.METRICS_SERVER_SECRET_TOKEN }}" - # - uses: schemathesis/action@v1 - # with: - # schema: "http://localhost:8080/swagger.json" + # - uses: schemathesis/action@v1 + # with: + # schema: "http://localhost:8080/swagger.json" diff --git a/CHANGELOG.md b/CHANGELOG.md index 94d503c25..274801639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ changes. - Fix endless spinner on a dashboard [Issue 539](https://github.com/IntersectMBO/govtool/issues/539) - Update frontend package readme to reflect recent changes [Issue 543](https://github.com/IntersectMBO/govtool/issues/543) - Change input selection strategy to 3 (random) [Issue 575](https://github.com/IntersectMBO/govtool/issues/575) +- Integrate frontend with metadata validation service [Issue 617](https://github.com/IntersectMBO/govtool/issues/617) ### Added diff --git a/govtool/frontend/Makefile b/govtool/frontend/Makefile index 24f0702dc..f8dd5c348 100644 --- a/govtool/frontend/Makefile +++ b/govtool/frontend/Makefile @@ -15,7 +15,7 @@ build-frontend: docker-login if [[ "$(cardano_network)" = "mainnet" ]]; then NETWORK_FLAG=1; else NETWORK_FLAG=0; fi; \ $(call check_image_on_ecr,frontend,$(frontend_image_tag)) || \ $(docker) build --tag "$(repo_url)/frontend:$(frontend_image_tag)" \ - --build-arg VITE_BASE_URL="https://$(domain)/api" \ + --build-arg VITE_BASE_URL="https://$(domain)" \ --build-arg VITE_GTM_ID="$${GTM_ID}" \ --build-arg VITE_NETWORK_FLAG="$$NETWORK_FLAG" \ --build-arg VITE_SENTRY_DSN="$${SENTRY_DSN}" \ diff --git a/govtool/frontend/src/consts/externalDataModalConfig.ts b/govtool/frontend/src/consts/externalDataModalConfig.ts index 1508004f4..132b20103 100644 --- a/govtool/frontend/src/consts/externalDataModalConfig.ts +++ b/govtool/frontend/src/consts/externalDataModalConfig.ts @@ -1,5 +1,6 @@ import { ModalState } from "@/context"; import I18n from "@/i18n"; +import { MetadataValidationStatus } from "@/models"; export enum MetadataHashValidationErrors { INVALID_URL = "Invalid URL", @@ -29,13 +30,12 @@ const urlCannotBeFound = { }; export const storageInformationErrorModals: Record< - MetadataHashValidationErrors, + MetadataValidationStatus, ModalState< typeof externalDataDoesntMatchModal | typeof urlCannotBeFound >["state"] > = { - [MetadataHashValidationErrors.INVALID_URL]: urlCannotBeFound, - [MetadataHashValidationErrors.FETCH_ERROR]: urlCannotBeFound, - [MetadataHashValidationErrors.INVALID_JSON]: externalDataDoesntMatchModal, - [MetadataHashValidationErrors.INVALID_HASH]: externalDataDoesntMatchModal, + [MetadataValidationStatus.URL_NOT_FOUND]: urlCannotBeFound, + [MetadataValidationStatus.INVALID_JSONLD]: externalDataDoesntMatchModal, + [MetadataValidationStatus.INVALID_HASH]: externalDataDoesntMatchModal, }; diff --git a/govtool/frontend/src/consts/queryKeys.ts b/govtool/frontend/src/consts/queryKeys.ts index 66a12b0b1..9a1e3c887 100644 --- a/govtool/frontend/src/consts/queryKeys.ts +++ b/govtool/frontend/src/consts/queryKeys.ts @@ -10,3 +10,7 @@ export const QUERY_KEYS = { useGetDRepInfoKey: "useGetDRepInfoKey", useGetVoteContextFromFile: "useGetVoteContextFromFile", }; + +export const MUTATION_KEYS = { + postValidateKey: "postValidateKey", +}; diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts index e8ff4f660..24ed8a26b 100644 --- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts +++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts @@ -10,22 +10,19 @@ import { CIP_100, CIP_108, GOVERNANCE_ACTION_CONTEXT, - MetadataHashValidationErrors, PATHS, storageInformationErrorModals, } from "@consts"; import { useCardano, useModal } from "@context"; -import { - canonizeJSON, - downloadJson, - generateJsonld, - validateMetadataHash, -} from "@utils"; +import { canonizeJSON, downloadJson, generateJsonld } from "@utils"; +import { MetadataValidationStatus } from "@models"; import { GovernanceActionFieldSchemas, GovernanceActionType, } from "@/types/governanceAction"; +import { useValidateMutation } from "../mutations"; + export type CreateGovernanceActionValues = { links?: { link: string }[]; storeData?: boolean; @@ -48,6 +45,7 @@ export const useCreateGovernanceActionForm = ( buildTreasuryGovernanceAction, buildSignSubmitConwayCertTx, } = useCardano(); + const { validateMetadata } = useValidateMutation(); const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const [hash, setHash] = useState(null); @@ -120,22 +118,27 @@ export const useCreateGovernanceActionForm = ( }, [govActionType, json]); const validateHash = useCallback( - async (storingUrl: string, localHash: string | null) => { + async (url: string, localHash: string | null) => { try { if (!localHash) { - throw new Error(MetadataHashValidationErrors.INVALID_HASH); + throw new Error(MetadataValidationStatus.INVALID_HASH); } - await validateMetadataHash(storingUrl, localHash); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { + const result = await validateMetadata({ url, hash: localHash }); + + if (result.status) { + throw result.status; + } + } catch (error) { if ( - Object.values(MetadataHashValidationErrors).includes(error.message) + Object.values(MetadataValidationStatus).includes( + error as MetadataValidationStatus, + ) ) { openModal({ type: "statusModal", state: { ...storageInformationErrorModals[ - error.message as MetadataHashValidationErrors + error as MetadataValidationStatus ], onSubmit: backToForm, onCancel: backToDashboard, @@ -214,6 +217,7 @@ export const useCreateGovernanceActionForm = ( setIsLoading(true); await validateHash(data.storingURL, hash); + const govActionBuilder = await buildTransaction(data); await buildSignSubmitConwayCertTx({ govActionBuilder, diff --git a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts index 89795269c..60a3e6bd0 100644 --- a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts +++ b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts @@ -15,12 +15,10 @@ import { storageInformationErrorModals, } from "@consts"; import { useCardano, useModal } from "@context"; -import { - canonizeJSON, - downloadJson, - generateJsonld, - validateMetadataHash, -} from "@utils"; +import { canonizeJSON, downloadJson, generateJsonld } from "@utils"; +import { MetadataValidationStatus } from "@models"; + +import { useValidateMutation } from "../mutations"; export type EditDRepInfoValues = { bio?: string; @@ -43,6 +41,7 @@ export const defaultEditDRepInfoValues: EditDRepInfoValues = { export const useEditDRepInfoForm = ( setStep?: Dispatch>, ) => { + const { validateMetadata } = useValidateMutation(); const { t } = useTranslation(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); @@ -113,21 +112,23 @@ export const useEditDRepInfoForm = ( }; const validateHash = useCallback( - async (storingUrl: string) => { + async (url: string) => { try { if (!hash) throw new Error(MetadataHashValidationErrors.INVALID_HASH); - await validateMetadataHash(storingUrl, hash); + const result = await validateMetadata({ url, hash }); + + if (result.status) { + throw result.status; + } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if ( - Object.values(MetadataHashValidationErrors).includes(error.message) - ) { + if (Object.values(MetadataValidationStatus).includes(error)) { openModal({ type: "statusModal", state: { ...storageInformationErrorModals[ - error.message as MetadataHashValidationErrors + error as MetadataValidationStatus ], onSubmit: backToForm, onCancel: backToDashboard, diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index 22c2aa4cd..d7f502f43 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -10,18 +10,15 @@ import { CIP_100, CIP_QQQ, DREP_CONTEXT, - MetadataHashValidationErrors, PATHS, storageInformationErrorModals, } from "@consts"; import { useCardano, useModal } from "@context"; -import { - canonizeJSON, - downloadJson, - generateJsonld, - validateMetadataHash, -} from "@utils"; +import { MetadataValidationStatus } from "@models"; +import { canonizeJSON, downloadJson, generateJsonld } from "@utils"; + import { useGetVoterInfo } from ".."; +import { useValidateMutation } from "../mutations"; export type RegisterAsDRepValues = { bio?: string; @@ -44,6 +41,7 @@ export const defaultRegisterAsDRepValues: RegisterAsDRepValues = { export const useRegisterAsdRepForm = ( setStep?: Dispatch>, ) => { + const { validateMetadata } = useValidateMutation(); const { t } = useTranslation(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); @@ -116,21 +114,27 @@ export const useRegisterAsdRepForm = ( }; const validateHash = useCallback( - async (storingUrl: string) => { + async (url: string) => { try { - if (!hash) throw new Error(MetadataHashValidationErrors.INVALID_HASH); + if (!hash) throw new Error(MetadataValidationStatus.INVALID_HASH); - await validateMetadataHash(storingUrl, hash); + const result = await validateMetadata({ url, hash }); + + if (result.status) { + throw result.status; + } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { if ( - Object.values(MetadataHashValidationErrors).includes(error.message) + Object.values(MetadataValidationStatus).includes( + error as MetadataValidationStatus, + ) ) { openModal({ type: "statusModal", state: { ...storageInformationErrorModals[ - error.message as MetadataHashValidationErrors + error as MetadataValidationStatus ], onSubmit: backToForm, onCancel: backToDashboard, diff --git a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx index 27cfccede..b14f1722f 100644 --- a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx +++ b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx @@ -2,20 +2,14 @@ import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { NodeObject } from "jsonld"; import { useFormContext } from "react-hook-form"; import { blake2bHex } from "blakejs"; - -import { - CIP_108, - MetadataHashValidationErrors, - VOTE_TEST_CONTEXT, -} from "@consts"; -import { - canonizeJSON, - downloadJson, - generateJsonld, - validateMetadataHash, -} from "@utils"; import { captureException } from "@sentry/react"; +import { CIP_108, VOTE_TEST_CONTEXT } from "@consts"; +import { canonizeJSON, downloadJson, generateJsonld } from "@utils"; +import { MetadataValidationStatus } from "@models"; + +import { useValidateMutation } from "../mutations"; + export type VoteContextFormValues = { voteContextText: string; terms?: boolean; @@ -27,6 +21,7 @@ export const useVoteContextForm = ( setStep?: Dispatch>, setErrorMessage?: Dispatch>, ) => { + const { validateMetadata } = useValidateMutation(); const [hash, setHash] = useState(null); const [json, setJson] = useState(null); @@ -71,18 +66,19 @@ export const useVoteContextForm = ( }, [json]); const validateHash = useCallback( - async (storingUrl: string, localHash: string | null) => { + async (url: string, localHash: string | null) => { try { if (!localHash) { - throw new Error(MetadataHashValidationErrors.INVALID_HASH); + throw new Error(MetadataValidationStatus.INVALID_HASH); + } + const result = await validateMetadata({ url, hash: localHash }); + if (result.status) { + throw result.status; } - await validateMetadataHash(storingUrl, localHash); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if ( - Object.values(MetadataHashValidationErrors).includes(error.message) - ) { - if (setErrorMessage) setErrorMessage(error.message); + if (Object.values(MetadataValidationStatus).includes(error)) { + if (setErrorMessage) setErrorMessage(error); if (setStep) setStep(4); } throw error; diff --git a/govtool/frontend/src/hooks/mutations/index.ts b/govtool/frontend/src/hooks/mutations/index.ts new file mode 100644 index 000000000..c70ff462e --- /dev/null +++ b/govtool/frontend/src/hooks/mutations/index.ts @@ -0,0 +1 @@ +export * from "./metadataValidation"; diff --git a/govtool/frontend/src/hooks/mutations/metadataValidation/index.ts b/govtool/frontend/src/hooks/mutations/metadataValidation/index.ts new file mode 100644 index 000000000..61abbe804 --- /dev/null +++ b/govtool/frontend/src/hooks/mutations/metadataValidation/index.ts @@ -0,0 +1 @@ +export * from "./useValidateMutation"; diff --git a/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts b/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts new file mode 100644 index 000000000..9363ca7f1 --- /dev/null +++ b/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts @@ -0,0 +1,18 @@ +import { useMutation } from "react-query"; + +import { postValidate } from "@services"; +import { MUTATION_KEYS } from "@consts"; +import { MetadataValidationDTO } from "@models"; + +export const useValidateMutation = () => { + const { data, isLoading, mutateAsync } = useMutation({ + mutationFn: (body: MetadataValidationDTO) => postValidate(body), + mutationKey: [MUTATION_KEYS.postValidateKey], + }); + + return { + validateMetadata: mutateAsync, + validationStatus: data, + isValidating: isLoading, + }; +}; diff --git a/govtool/frontend/src/models/index.ts b/govtool/frontend/src/models/index.ts index 331bf6b43..46b6a8e5d 100644 --- a/govtool/frontend/src/models/index.ts +++ b/govtool/frontend/src/models/index.ts @@ -1,3 +1,4 @@ export * from "./api"; export * from "./snackbar"; export * from "./wallet"; +export * from "./metadataValidation"; diff --git a/govtool/frontend/src/models/metadataValidation.ts b/govtool/frontend/src/models/metadataValidation.ts new file mode 100644 index 000000000..284a8821c --- /dev/null +++ b/govtool/frontend/src/models/metadataValidation.ts @@ -0,0 +1,16 @@ +// TODO: Should be taken from @govtool/metadata-validation +export enum MetadataValidationStatus { + URL_NOT_FOUND = "URL_NOT_FOUND", + INVALID_JSONLD = "INVALID_JSONLD", + INVALID_HASH = "INVALID_HASH", +} + +export type ValidateMetadataResult = { + status?: MetadataValidationStatus; + valid: boolean; +}; + +export type MetadataValidationDTO = { + url: string; + hash: string; +}; diff --git a/govtool/frontend/src/services/API.ts b/govtool/frontend/src/services/API.ts index 8ac9341f2..1815ecbc2 100644 --- a/govtool/frontend/src/services/API.ts +++ b/govtool/frontend/src/services/API.ts @@ -1,13 +1,18 @@ import axios from "axios"; +import { NavigateFunction } from "react-router-dom"; import { PATHS } from "@consts"; -import { NavigateFunction } from "react-router-dom"; const TIMEOUT_IN_SECONDS = 30 * 1000; // 1000 ms is 1 s then its 10 s const BASE_URL = import.meta.env.VITE_BASE_URL; export const API = axios.create({ - baseURL: BASE_URL, + baseURL: `${BASE_URL}api`, + timeout: TIMEOUT_IN_SECONDS, +}); + +export const METADATA_VALIDATION_API = axios.create({ + baseURL: `${BASE_URL}metadata-validation`, timeout: TIMEOUT_IN_SECONDS, }); diff --git a/govtool/frontend/src/services/requests/index.ts b/govtool/frontend/src/services/requests/index.ts index 747289515..3121e4c5c 100644 --- a/govtool/frontend/src/services/requests/index.ts +++ b/govtool/frontend/src/services/requests/index.ts @@ -17,3 +17,4 @@ export * from "./postDRepRegister"; export * from "./postDRepRemoveVote"; export * from "./postDRepRetire"; export * from "./postDRepVote"; +export * from "./metadataValidation"; diff --git a/govtool/frontend/src/services/requests/metadataValidation/index.ts b/govtool/frontend/src/services/requests/metadataValidation/index.ts new file mode 100644 index 000000000..3eb5cea95 --- /dev/null +++ b/govtool/frontend/src/services/requests/metadataValidation/index.ts @@ -0,0 +1 @@ +export * from "./postValidate"; diff --git a/govtool/frontend/src/services/requests/metadataValidation/postValidate.ts b/govtool/frontend/src/services/requests/metadataValidation/postValidate.ts new file mode 100644 index 000000000..4dd95281b --- /dev/null +++ b/govtool/frontend/src/services/requests/metadataValidation/postValidate.ts @@ -0,0 +1,11 @@ +import type { MetadataValidationDTO, ValidateMetadataResult } from "@models"; +import { METADATA_VALIDATION_API } from "../../API"; + +export const postValidate = async (body: MetadataValidationDTO) => { + const response = await METADATA_VALIDATION_API.post( + `/validate`, + body, + ); + + return response.data; +}; diff --git a/govtool/frontend/src/utils/tests/validateMetadataHash.test.ts b/govtool/frontend/src/utils/tests/validateMetadataHash.test.ts deleted file mode 100644 index b6bd163c7..000000000 --- a/govtool/frontend/src/utils/tests/validateMetadataHash.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { vi } from "vitest"; -import { API } from "@/services"; -import { validateMetadataHash } from ".."; - -describe("validateMetadataHash", () => { - const mockHash = "abcdef1234567890"; - - it("should throw an error for invalid URL", async () => { - const invalidURL = "invalid-url"; - await expect(validateMetadataHash(invalidURL, mockHash)).rejects.toThrow( - "Invalid URL", - ); - }); - - it("should throw an error for invalid JSON", async () => { - const invalidJSONURL = "https://example.com/invalid-json"; - vi.spyOn(API, "get").mockRejectedValueOnce(new Error("Invalid JSON")); - await expect( - validateMetadataHash(invalidJSONURL, mockHash), - ).rejects.toThrow("Invalid JSON"); - }); - - // TODO: Provide tests for invalid hash -}); diff --git a/govtool/frontend/src/utils/validateMetadataHash.ts b/govtool/frontend/src/utils/validateMetadataHash.ts index 4c9f640eb..af8ece41d 100644 --- a/govtool/frontend/src/utils/validateMetadataHash.ts +++ b/govtool/frontend/src/utils/validateMetadataHash.ts @@ -1,11 +1,7 @@ import * as blake from "blakejs"; -import { isAxiosError } from "axios"; import { API } from "@services"; -import { - sharedGovernanceActionFields, - MetadataHashValidationErrors, -} from "@consts"; +import { sharedGovernanceActionFields } from "@consts"; import { URL_REGEX, areObjectsTheSame, canonizeJSON } from "."; @@ -15,54 +11,6 @@ export enum GAMetedataErrors { INCORRECT_FORMAT = "Data Formatted Incorrectly", } -/** - * Validates the metadata hash by fetching the metadata from the given URL, - * canonizing it, and comparing the hash with the provided hash. - * - * @param storingURL - The URL where the metadata is stored. - * @param hash - The hash to compare with the calculated hash of the metadata. - * @returns A promise that resolves to `true` if the metadata hash is valid, - * or rejects with an error message if validation fails. - */ -export const validateMetadataHash = async ( - storingURL: string, - hash: string, -) => { - try { - if (!storingURL.match(URL_REGEX)) { - throw new Error(MetadataHashValidationErrors.INVALID_URL); - } - - const { data: userMetadataJSON } = await API.get(storingURL); - - let canonizedUserMetadata; - try { - canonizedUserMetadata = await canonizeJSON(userMetadataJSON); - } catch (error) { - throw new Error(MetadataHashValidationErrors.INVALID_JSON); - } - if (!canonizedUserMetadata) { - throw new Error(MetadataHashValidationErrors.INVALID_JSON); - } - - const hashedUserMetadata = blake.blake2bHex( - canonizedUserMetadata, - undefined, - 32, - ); - - if (hashedUserMetadata !== hash) { - throw new Error(MetadataHashValidationErrors.INVALID_HASH); - } - return true; - } catch (error) { - if (isAxiosError(error)) { - throw new Error(MetadataHashValidationErrors.FETCH_ERROR); - } - throw error; - } -}; - export const checkIsMissingGAMetadata = async ({ url, hash, diff --git a/govtool/metadata-validation/src/app.controller.ts b/govtool/metadata-validation/src/app.controller.ts index 0bcf57fe9..6b248a789 100644 --- a/govtool/metadata-validation/src/app.controller.ts +++ b/govtool/metadata-validation/src/app.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Body } from '@nestjs/common'; +import { Controller, Body, Post } from '@nestjs/common'; import { AppService } from './app.service'; import { ValidateMetadataDTO } from './dto/validateMetadata.dto'; import { ValidateMetadataResult } from './types/validateMetadata'; @@ -7,7 +7,7 @@ import { ValidateMetadataResult } from './types/validateMetadata'; export class AppController { constructor(private readonly appService: AppService) {} - @Get('validate') + @Post('validate') validateMetadata( @Body() validateMetadataDto: ValidateMetadataDTO, ): Promise {