Skip to content

Commit 742cee6

Browse files
committed
feat: publisher conformance report
1 parent 0236b35 commit 742cee6

21 files changed

+485
-187
lines changed

apps/insights/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
"bs58": "catalog:",
3333
"clsx": "catalog:",
3434
"cryptocurrency-icons": "catalog:",
35+
"date-fns": "catalog:",
36+
"csv-stringify": "catalog:",
3537
"dnum": "catalog:",
3638
"ioredis": "^5.7.0",
3739
"lightweight-charts": "catalog:",
@@ -46,8 +48,8 @@
4648
"superjson": "catalog:",
4749
"swr": "catalog:",
4850
"zod": "catalog:",
49-
"zod-validation-error": "catalog:",
50-
"zod-search-params": "catalog:"
51+
"zod-search-params": "catalog:",
52+
"zod-validation-error": "catalog:"
5153
},
5254
"devDependencies": {
5355
"@cprussin/eslint-config": "catalog:",

apps/insights/src/app/api/pyth/get-feeds/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,5 @@ export const GET = async (request: NextRequest) => {
3737

3838
const queryParamsSchema = z.object({
3939
cluster: z.enum(CLUSTER_NAMES).transform((value) => toCluster(value)),
40-
excludePriceComponents: z.boolean(),
40+
excludePriceComponents: z.boolean().optional().default(false),
4141
});
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.conformanceReports {
1+
.conformanceReport {
22
display: flex;
33
gap: 0.5rem;
44
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"use client";
2+
3+
import { Download } from "@phosphor-icons/react/dist/ssr/Download";
4+
import { Button } from "@pythnetwork/component-library/Button";
5+
import { Select } from "@pythnetwork/component-library/Select";
6+
import { Skeleton } from "@pythnetwork/component-library/Skeleton";
7+
import { useAlert } from "@pythnetwork/component-library/useAlert";
8+
import { useLogger } from "@pythnetwork/component-library/useLogger";
9+
import { useState } from "react";
10+
11+
import styles from "./conformance-report.module.scss";
12+
import type { Interval } from "./types";
13+
import { INTERVALS } from "./types";
14+
import { useDownloadReportForFeed } from "./use-download-report-for-feed";
15+
import { useDownloadReportForPublisher } from "./use-download-report-for-publisher";
16+
17+
type ConformanceReportProps =
18+
| { isLoading: true }
19+
| {
20+
isLoading?: false | undefined;
21+
symbol?: string;
22+
cluster: "pythnet" | "pythtest-conformance";
23+
publisher?: string;
24+
};
25+
26+
const ConformanceReport = (props: ConformanceReportProps) => {
27+
const [timeframe, setTimeframe] = useState<Interval>(INTERVALS[0]);
28+
const [isGeneratingReport, setIsGeneratingReport] = useState(false);
29+
const { open } = useAlert();
30+
const downloadReportForFeed = useDownloadReportForFeed();
31+
const downloadReportForPublisher = useDownloadReportForPublisher();
32+
const logger = useLogger();
33+
34+
const downloadReport = async () => {
35+
if (props.isLoading) {
36+
return;
37+
}
38+
if (props.symbol && props.publisher) {
39+
await downloadReportForFeed({
40+
symbol: props.symbol,
41+
publisher: props.publisher,
42+
timeframe,
43+
cluster: props.cluster,
44+
});
45+
}
46+
47+
if (props.publisher) {
48+
await downloadReportForPublisher({
49+
publisher: props.publisher,
50+
cluster: props.cluster,
51+
interval: timeframe,
52+
});
53+
}
54+
};
55+
56+
const handleReport = () => {
57+
setIsGeneratingReport(true);
58+
downloadReport()
59+
.catch((error: unknown) => {
60+
open({
61+
title: "Error",
62+
contents: "Error generating conformance report",
63+
});
64+
logger.error(error);
65+
})
66+
.finally(() => {
67+
setIsGeneratingReport(false);
68+
});
69+
};
70+
71+
if (props.isLoading) {
72+
return <Skeleton width={100} />;
73+
}
74+
75+
return (
76+
<div className={styles.conformanceReport}>
77+
<Select
78+
options={INTERVALS.map((interval) => ({ id: interval }))}
79+
placement="bottom end"
80+
selectedKey={timeframe}
81+
onSelectionChange={setTimeframe}
82+
size="sm"
83+
label="Timeframe"
84+
variant="outline"
85+
hideLabel
86+
/>
87+
<Button
88+
variant="outline"
89+
size="sm"
90+
onClick={handleReport}
91+
afterIcon={<Download key="download" />}
92+
isPending={isGeneratingReport}
93+
>
94+
Report
95+
</Button>
96+
</div>
97+
);
98+
};
99+
100+
export default ConformanceReport;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const WEB_API_BASE_URL = "https://web-api.pyth.network";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const INTERVALS = ["24H", "48H", "72H", "1W", "1M"] as const;
2+
export type Interval = (typeof INTERVALS)[number];
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useCallback } from "react";
2+
3+
import { WEB_API_BASE_URL } from "./constants";
4+
import { useDownloadBlob } from "../../hooks/use-download-blob";
5+
6+
const PYTHTEST_CONFORMANCE_REFERENCE_PUBLISHER =
7+
"HUZu4xMSHbxTWbkXR6jkGdjvDPJLjrpSNXSoUFBRgjWs";
8+
9+
export const useDownloadReportForFeed = () => {
10+
const download = useDownloadBlob();
11+
12+
return useCallback(
13+
async ({
14+
symbol,
15+
publisher,
16+
timeframe,
17+
cluster,
18+
}: {
19+
symbol: string;
20+
publisher: string;
21+
timeframe: string;
22+
cluster: string;
23+
}) => {
24+
const url = new URL("/metrics/conformance", WEB_API_BASE_URL);
25+
url.searchParams.set("symbol", symbol);
26+
url.searchParams.set("range", timeframe);
27+
url.searchParams.set("cluster", cluster);
28+
url.searchParams.set("publisher", publisher);
29+
30+
if (cluster === "pythtest-conformance") {
31+
url.searchParams.set(
32+
"pythnet_aggregate_publisher",
33+
PYTHTEST_CONFORMANCE_REFERENCE_PUBLISHER,
34+
);
35+
}
36+
37+
const response = await fetch(url, {
38+
headers: new Headers({
39+
Accept: "application/octet-stream",
40+
}),
41+
});
42+
const blob = await response.blob();
43+
download(
44+
blob,
45+
`${publisher}-${symbol
46+
.split("/")
47+
.join("")}-${timeframe}-${cluster}-conformance-report.tsv`,
48+
);
49+
},
50+
[download],
51+
);
52+
};

0 commit comments

Comments
 (0)