add binary query string to /latest_price_feeds (#445)

* add binary query string to /latest_price_feeds

* fix precommit

* fix both rest and ws tests

* bump package version

* fix tests to use the latest schema
This commit is contained in:
Daniel Chew 2023-01-02 17:38:52 +09:00 committed by GitHub
parent cdf99f4c60
commit 378e99b345
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 26 deletions

View File

@ -1,12 +1,12 @@
{ {
"name": "@pythnetwork/pyth-price-service", "name": "@pythnetwork/pyth-price-service",
"version": "2.2.3", "version": "2.2.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@pythnetwork/pyth-price-service", "name": "@pythnetwork/pyth-price-service",
"version": "2.2.3", "version": "2.2.4",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@certusone/wormhole-sdk": "^0.1.4", "@certusone/wormhole-sdk": "^0.1.4",

View File

@ -1,6 +1,6 @@
{ {
"name": "@pythnetwork/pyth-price-service", "name": "@pythnetwork/pyth-price-service",
"version": "2.2.3", "version": "2.2.4",
"description": "Pyth Price Service", "description": "Pyth Price Service",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -1,9 +1,9 @@
import { HexString, PriceFeed, Price } from "@pythnetwork/pyth-sdk-js"; import { HexString, Price, PriceFeed } from "@pythnetwork/pyth-sdk-js";
import { PriceStore, PriceInfo } from "../listen";
import { RestAPI } from "../rest";
import { Express } from "express"; import { Express } from "express";
import request from "supertest";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import request from "supertest";
import { PriceInfo, PriceStore } from "../listen";
import { RestAPI } from "../rest";
let app: Express; let app: Express;
let priceInfoMap: Map<string, PriceInfo>; let priceInfoMap: Map<string, PriceInfo>;
@ -82,6 +82,23 @@ describe("Latest Price Feed Endpoint", () => {
expect(resp.body).toContainEqual(dummyPriceFeed(ids[1]).toJson()); expect(resp.body).toContainEqual(dummyPriceFeed(ids[1]).toJson());
}); });
test("When called with valid ids and binary flag set to true, returns correct price feed with binary vaa", async () => {
const ids = [expandTo64Len("abcd"), expandTo64Len("3456")];
const resp = await request(app)
.get("/api/latest_price_feeds")
.query({ ids, binary: true });
expect(resp.status).toBe(StatusCodes.OK);
expect(resp.body.length).toBe(2);
expect(resp.body).toContainEqual({
...priceInfoMap.get(ids[0])!.priceFeed.toJson(),
vaa: priceInfoMap.get(ids[0])!.vaa.toString("base64"),
});
expect(resp.body).toContainEqual({
...priceInfoMap.get(ids[1])!.priceFeed.toJson(),
vaa: priceInfoMap.get(ids[1])!.vaa.toString("base64"),
});
});
test("When called with some non-existent ids within ids, returns error mentioning non-existent ids", async () => { test("When called with some non-existent ids within ids, returns error mentioning non-existent ids", async () => {
const ids = [ const ids = [
expandTo64Len("ab01"), expandTo64Len("ab01"),

View File

@ -1,4 +1,4 @@
import { HexString, PriceFeed } from "@pythnetwork/pyth-sdk-js"; import { HexString, Price, PriceFeed } from "@pythnetwork/pyth-sdk-js";
import { Server } from "http"; import { Server } from "http";
import { WebSocket, WebSocketServer } from "ws"; import { WebSocket, WebSocketServer } from "ws";
import { sleep } from "../helpers"; import { sleep } from "../helpers";
@ -49,20 +49,20 @@ function dummyPriceInfo(
} }
function dummyPriceFeed(id: string): PriceFeed { function dummyPriceFeed(id: string): PriceFeed {
return PriceFeed.fromJson({ return new PriceFeed({
ema_price: { emaPrice: new Price({
conf: "1", conf: "1",
expo: 2, expo: 2,
price: "3", price: "3",
publish_time: 4, publishTime: 4,
}, }),
id, id,
price: { price: new Price({
conf: "5", conf: "5",
expo: 6, expo: 6,
price: "7", price: "7",
publish_time: 8, publishTime: 8,
}, }),
}); });
} }
@ -273,7 +273,10 @@ describe("Client receives data", () => {
expect(serverMessages[1]).toEqual({ expect(serverMessages[1]).toEqual({
type: "price_update", type: "price_update",
price_feed: priceInfos[0].priceFeed.toJson(), price_feed: {
...priceInfos[0].priceFeed.toJson(),
vaa: priceInfos[0].vaa.toString("base64"),
},
}); });
api.dispatchPriceFeedUpdate(priceInfos[1]); api.dispatchPriceFeedUpdate(priceInfos[1]);

View File

@ -1,15 +1,14 @@
import { HexString } from "@pythnetwork/pyth-sdk-js";
import cors from "cors"; import cors from "cors";
import express, { NextFunction, Request, Response } from "express"; import express, { NextFunction, Request, Response } from "express";
import { Joi, schema, validate, ValidationError } from "express-validation"; import { Joi, schema, validate, ValidationError } from "express-validation";
import { Server } from "http"; import { Server } from "http";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import morgan from "morgan"; import morgan from "morgan";
import responseTime from "response-time"; import { TimestampInSec } from "./helpers";
import { DurationInMs, DurationInSec, TimestampInSec } from "./helpers";
import { PriceStore } from "./listen"; import { PriceStore } from "./listen";
import { logger } from "./logging"; import { logger } from "./logging";
import { PromClient } from "./promClient"; import { PromClient } from "./promClient";
import { HexString } from "@pythnetwork/pyth-sdk-js";
const MORGAN_LOG_FORMAT = const MORGAN_LOG_FORMAT =
':remote-addr - :remote-user ":method :url HTTP/:http-version"' + ':remote-addr - :remote-user ":method :url HTTP/:http-version"' +
@ -120,6 +119,7 @@ export class RestAPI {
.items(Joi.string().regex(/^(0x)?[a-f0-9]{64}$/)) .items(Joi.string().regex(/^(0x)?[a-f0-9]{64}$/))
.required(), .required(),
verbose: Joi.boolean(), verbose: Joi.boolean(),
binary: Joi.boolean(),
}).required(), }).required(),
}; };
app.get( app.get(
@ -129,6 +129,8 @@ export class RestAPI {
const priceIds = req.query.ids as string[]; const priceIds = req.query.ids as string[];
// verbose is optional, default to false // verbose is optional, default to false
const verbose = req.query.verbose === "true"; const verbose = req.query.verbose === "true";
// binary is optional, default to false
const binary = req.query.binary === "true";
const responseJson = []; const responseJson = [];
@ -146,9 +148,9 @@ export class RestAPI {
continue; continue;
} }
if (verbose) { responseJson.push({
responseJson.push({ ...latestPriceInfo.priceFeed.toJson(),
...latestPriceInfo.priceFeed.toJson(), ...(verbose && {
metadata: { metadata: {
emitter_chain: latestPriceInfo.emitterChainId, emitter_chain: latestPriceInfo.emitterChainId,
attestation_time: latestPriceInfo.attestationTime, attestation_time: latestPriceInfo.attestationTime,
@ -156,10 +158,11 @@ export class RestAPI {
price_service_receive_time: price_service_receive_time:
latestPriceInfo.priceServiceReceiveTime, latestPriceInfo.priceServiceReceiveTime,
}, },
}); }),
} else { ...(binary && {
responseJson.push(latestPriceInfo.priceFeed.toJson()); vaa: latestPriceInfo.vaa.toString("base64"),
} }),
});
} }
if (notFoundIds.length > 0) { if (notFoundIds.length > 0) {
@ -175,6 +178,9 @@ export class RestAPI {
endpoints.push( endpoints.push(
"api/latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&..&verbose=true" "api/latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&..&verbose=true"
); );
endpoints.push(
"api/latest_price_feeds?ids[]=<price_feed_id>&ids[]=<price_feed_id_2>&..&verbose=true&binary=true"
);
app.get("/api/price_feed_ids", (req: Request, res: Response) => { app.get("/api/price_feed_ids", (req: Request, res: Response) => {
const availableIds = this.priceFeedVaaInfo.getPriceIds(); const availableIds = this.priceFeedVaaInfo.getPriceIds();