Skip to content

Commit

Permalink
chore: move validation requests to frontend service
Browse files Browse the repository at this point in the history
  • Loading branch information
MSzalowski committed Aug 1, 2024
1 parent 227e4ec commit 0d210b5
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 18 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ changes.

### Fixed

-
- Fix displaying Governance Action & DRep metadatas

### Changed

- Change link to propose a governace action docs [Issue 1132](https://github.com/IntersectMBO/govtool/issues/1132)
- Change link to docs regarding DReps [Issue 1130](https://github.com/IntersectMBO/govtool/issues/1130)
- Change link to view governance actions docs [Issue 1131](https://github.com/IntersectMBO/govtool/issues/1131)
- Make all the frontend build arguments mandatory [Issue 1642](https://github.com/IntersectMBO/govtool/issues/1642), [Issue 1643](https://github.com/IntersectMBO/govtool/issues/1643)
- Remove usage of metadata validation service in Haskell Backend
- Breaking! Remove usage of metadata validation service in Haskell Backend

## [sancho-v1.0.11](https://github.com/IntersectMBO/govtool/releases/tag/sancho-v1.0.11) 2024-07-30

Expand Down
113 changes: 113 additions & 0 deletions govtool/backend/sql/list-proposals-my.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
WITH LatestDrepDistr AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY hash_id ORDER BY epoch_no DESC) AS rn
FROM
drep_distr
),
EpochUtils AS (
SELECT
(Max(end_time) - Min(end_time)) / (Max(NO) - Min(NO)) AS epoch_duration,
Max(NO) AS last_epoch_no,
Max(end_time) AS last_epoch_end_time
FROM
epoch
),
always_no_confidence_voting_power AS (
SELECT
coalesce((
SELECT
amount
FROM drep_hash
LEFT JOIN drep_distr ON drep_hash.id = drep_distr.hash_id
WHERE
drep_hash.view = 'drep_always_no_confidence' ORDER BY epoch_no DESC LIMIT 1), 0) AS amount
),
always_abstain_voting_power AS (
SELECT
coalesce((
SELECT
amount
FROM drep_hash
LEFT JOIN drep_distr ON drep_hash.id = drep_distr.hash_id
WHERE
drep_hash.view = 'drep_always_abstain' ORDER BY epoch_no DESC LIMIT 1), 0) AS amount
)
SELECT
gov_action_proposal.id AS proposal_id,
encode(creator_tx.hash, 'hex') AS tx_hash,
gov_action_proposal.index,
gov_action_proposal.type::text,
CASE
WHEN gov_action_proposal.type = 'TreasuryWithdrawals' THEN
json_build_object('Reward Address', stake_address.view, 'Amount', treasury_withdrawal.amount)
WHEN gov_action_proposal.type::text = 'InfoAction' THEN
json_build_object()
ELSE
NULL
END AS description,
epoch_utils.last_epoch_end_time + epoch_utils.epoch_duration * (gov_action_proposal.expiration - epoch_utils.last_epoch_no) AS expiry_date,
gov_action_proposal.expiration AS expiry_epoch_no,
creator_block.time AS created_date,
creator_block.epoch_no AS created_epoch_no,
voting_anchor.url,
encode(voting_anchor.data_hash, 'hex') AS metadata_hash,
off_chain_vote_gov_action_data.title,
off_chain_vote_gov_action_data.abstract,
off_chain_vote_gov_action_data.motivation,
off_chain_vote_gov_action_data.rationale,
coalesce(Sum(ldd.amount) FILTER (WHERE voting_procedure.vote::text = 'Yes'), 0) +
CASE
WHEN gov_action_proposal.type = 'NoConfidence' THEN always_no_confidence_voting_power.amount
ELSE 0
END AS yes_votes,
coalesce(Sum(ldd.amount) FILTER (WHERE voting_procedure.vote::text = 'No'), 0) +
CASE
WHEN gov_action_proposal.type = 'NoConfidence' THEN 0
ELSE always_no_confidence_voting_power.amount
END AS no_votes,
coalesce(Sum(ldd.amount) FILTER (WHERE voting_procedure.vote::text = 'Abstain'), 0) + always_abstain_voting_power.amount AS abstain_votes
FROM
gov_action_proposal
LEFT JOIN treasury_withdrawal ON gov_action_proposal.id = treasury_withdrawal.gov_action_proposal_id
LEFT JOIN stake_address ON stake_address.id = treasury_withdrawal.stake_address_id
CROSS JOIN EpochUtils AS epoch_utils
CROSS JOIN always_no_confidence_voting_power
CROSS JOIN always_abstain_voting_power
CROSS JOIN off_chain_vote_gov_action_data ON off_chain_vote_gov_action_data.off_chain_vote_data_id = off_chain_vote_data.id
JOIN tx AS creator_tx ON creator_tx.id = gov_action_proposal.tx_id
JOIN block AS creator_block ON creator_block.id = creator_tx.block_id
LEFT JOIN voting_anchor ON voting_anchor.id = gov_action_proposal.voting_anchor_id
LEFT JOIN off_chain_vote_data ON off_chain_vote_data.voting_anchor_id = voting_anchor.id
LEFT JOIN voting_procedure ON voting_procedure.gov_action_proposal_id = gov_action_proposal.id
LEFT JOIN LatestDrepDistr ldd ON ldd.hash_id = voting_procedure.drep_voter AND ldd.rn = 1
WHERE (NOT ?
OR (concat(encode(creator_tx.hash, 'hex'), '#', gov_action_proposal.index) IN ?))
AND gov_action_proposal.expiration > (
SELECT
Max(NO)
FROM
epoch)
AND gov_action_proposal.ratified_epoch IS NULL
AND gov_action_proposal.enacted_epoch IS NULL
AND gov_action_proposal.expired_epoch IS NULL
AND gov_action_proposal.dropped_epoch IS NULL
GROUP BY
(gov_action_proposal.id,
stake_address.view,
treasury_withdrawal.amount,
creator_block.epoch_no,
off_chain_vote_gov_action_data.title,
off_chain_vote_gov_action_data.abstract,
off_chain_vote_gov_action_data.motivation,
off_chain_vote_gov_action_data.rationale,
gov_action_proposal.index,
creator_tx.hash,
creator_block.time,
epoch_utils.epoch_duration,
epoch_utils.last_epoch_no,
epoch_utils.last_epoch_end_time,
voting_anchor.url,
voting_anchor.data_hash,
always_no_confidence_voting_power.amount,
always_abstain_voting_power.amount)
1 change: 1 addition & 0 deletions govtool/frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
VITE_APP_ENV="development"
VITE_BASE_URL=""
VITE_METADATA_API_URL=""
VITE_NETWORK_FLAG=0
VITE_SENTRY_DSN=""
VITE_GTM_ID=""
Expand Down
2 changes: 1 addition & 1 deletion govtool/frontend/src/pages/DRepDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export const DRepDetails = ({ isConnected }: DRepDetailsProps) => {
<MoreInfoLink label={email} navTo={email} isEmail />
</DRepDetailsInfoItem>
)}
{references.length > 0 && !metadataStatus && (
{references?.length > 0 && !metadataStatus && (
<DRepDetailsInfoItem label={t("moreInformation")}>
<Box
alignItems="flex-start"
Expand Down
45 changes: 42 additions & 3 deletions govtool/frontend/src/services/requests/getDRepList.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { InfinityDRepData, DRepStatus, DRepListSort } from "@models";
import {
type InfinityDRepData,
type DRepStatus,
type DRepListSort,
MetadataStandard,
} from "@models";
import { API } from "../API";
import { postValidate } from "./metadataValidation";

export type GetDRepListArguments = {
filters?: string[];
Expand All @@ -18,7 +24,7 @@ export const getDRepList = async ({
searchPhrase = "",
status = [],
}: GetDRepListArguments): Promise<InfinityDRepData> => {
const response = await API.get("/drep/list", {
const response = await API.get<InfinityDRepData>("/drep/list", {
params: {
page,
pageSize,
Expand All @@ -28,5 +34,38 @@ export const getDRepList = async ({
...(status.length && { status }),
},
});
return response.data;

const validatedResponse = {
...response.data,
elements: await Promise.all(
response.data.elements.map(async (drep) => {
if (drep.metadataStatus || drep.metadataValid) {
return {
...drep,
metadataStatus: drep.metadataStatus,
metadataValid: drep.metadataValid,
};
}
if (drep.url && drep.metadataHash) {
const validationResponse = await postValidate({
url: drep.url,
hash: drep.metadataHash,
standard: MetadataStandard.CIPQQQ,
});
return {
...drep,
bio: validationResponse.metadata?.bio,
dRepName: validationResponse.metadata?.dRepName,
email: validationResponse.metadata?.email,
references: validationResponse.metadata?.references,
metadataStatus: validationResponse.status || null,
metadataValid: validationResponse.valid,
};
}
return drep;
}),
),
};

return validatedResponse;
};
38 changes: 36 additions & 2 deletions govtool/frontend/src/services/requests/getDRepVotes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { VotedProposal } from "@models";
import { MetadataStandard, VotedProposal } from "@models";
import { API } from "../API";
import { postValidate } from "./metadataValidation";

type GetDRepVotesParams = {
type?: string[];
Expand All @@ -18,5 +19,38 @@ export const getDRepVotes = async ({

const { data } = await API.get<VotedProposal[]>(urlBase, { params });

return data;
const validatedData = await Promise.all(
data.map(async (votedProposal) => {
if (
votedProposal.proposal.metadataStatus ||
votedProposal.proposal.metadataValid
) {
return votedProposal;
}

if (votedProposal.proposal.url && votedProposal.proposal.metadataHash) {
const validationResponse = await postValidate({
url: votedProposal.proposal.url,
hash: votedProposal.proposal.metadataHash,
standard: MetadataStandard.CIP108,
});

return {
...votedProposal,
proposal: {
...votedProposal.proposal,
abstract: validationResponse.metadata?.abstract,
motivation: validationResponse.metadata?.motivation,
title: validationResponse.metadata?.title,
rationale: validationResponse.metadata?.rationale,
references: validationResponse.metadata?.references,
metadataStatus: validationResponse.status || null,
metadataValid: validationResponse.valid,
},
};
}
}),
);

return validatedData;
};
27 changes: 25 additions & 2 deletions govtool/frontend/src/services/requests/getProposal.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
import { ProposalData } from "@/models";
import { API } from "../API";
import { postValidate } from "./metadataValidation";

export const getProposal = async (proposalId: string, drepId?: string) => {
const encodedHash = encodeURIComponent(proposalId);

const { data } = await API.get(
const { data } = await API.get<ProposalData>(
`/proposal/get/${encodedHash}?drepId=${drepId}`,
);

return data;
if (data.metadataStatus || data.metadataValid) {
return data;
}
if (!data.url || !data.metadataHash) {
return data;
}

const validationResponse = await postValidate({
url: data.url,
hash: data.metadataHash,
});

return {
...data,
title: validationResponse.metadata?.title,
abstract: validationResponse.metadata?.abstract,
motivation: validationResponse.metadata?.motivation,
rationale: validationResponse.metadata?.rationale,
references: validationResponse.metadata?.references,
metadataStatus: validationResponse.status || null,
metadataValid: validationResponse.valid,
};
};
42 changes: 39 additions & 3 deletions govtool/frontend/src/services/requests/getProposals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { InfinityProposals } from "@models";
import { InfinityProposals, MetadataStandard } from "@models";

import { postValidate } from "./metadataValidation";
import { API } from "../API";

export type GetProposalsArguments = {
Expand All @@ -19,7 +21,7 @@ export const getProposals = async ({
searchPhrase = "",
sorting = "",
}: GetProposalsArguments): Promise<InfinityProposals> => {
const response = await API.get("/proposal/list", {
const response = await API.get<InfinityProposals>("/proposal/list", {
params: {
page,
pageSize,
Expand All @@ -29,5 +31,39 @@ export const getProposals = async ({
...(dRepID && { drepId: dRepID }),
},
});
return response.data;

const validatedResponse = {
...response.data,
elements: await Promise.all(
response.data.elements.map(async (proposal) => {
if (proposal.metadataStatus || proposal.metadataValid) {
return {
...proposal,
metadataStatus: proposal.metadataStatus,
metadataValid: proposal.metadataValid,
};
}
if (proposal.url && proposal.metadataHash) {
const validationResponse = await postValidate({
url: proposal.url,
hash: proposal.metadataHash,
standard: MetadataStandard.CIP108,
});
return {
...proposal,
abstract: validationResponse.metadata?.abstract,
motivation: validationResponse.metadata?.motivation,
title: validationResponse.metadata?.title,
rationale: validationResponse.metadata?.rationale,
references: validationResponse.metadata?.references,
metadataStatus: validationResponse.status || null,
metadataValid: validationResponse.valid,
};
}

return proposal;
}),
),
};
return validatedResponse;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ const TIMEOUT_IN_SECONDS = 30 * 1000; // 1000 ms is 1 s then its 30 s

const METADATA_API = axios.create({
// API address of the metadata-validation service is different from the main API
baseURL: import.meta.env?.VITE_BASE_URL?.replace(
"api",
"metadata-validation",
),
baseURL: import.meta.env?.VITE_METADATA_API_URL,
timeout: TIMEOUT_IN_SECONDS,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export const validateMetadataStandard = async (
try {
await cipStandardSchema[standard]?.validateAsync(data);
} catch (error) {
Logger.error(LoggerMessage.METADATA_VALIDATION_ERROR, error);
Logger.error(
LoggerMessage.METADATA_VALIDATION_ERROR,
MetadataValidationStatus.INCORRECT_FORMAT,
);
throw MetadataValidationStatus.INCORRECT_FORMAT;
}
};

0 comments on commit 0d210b5

Please sign in to comment.