From 9c3455e1b2d0bd4ff803fea02aa5174cad5bd15e Mon Sep 17 00:00:00 2001 From: Aleksandr Kraiz Date: Sun, 22 May 2022 23:15:30 +0400 Subject: [PATCH] Multiple PriceFeed subscriptions --- README.md | 37 ++++++++-------- package.json | 2 +- .../PriceFeed/ws/PriceFeedSubscription.ts | 37 +++++++++------- src/services/PriceFeed/ws/index.ts | 42 ++++++++++++------- 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 2297ad9..90f9a32 100644 --- a/README.md +++ b/README.md @@ -347,33 +347,30 @@ orionUnit.orionAggregator.ws.unsubscribe("btasabu"); ## Price Feed Websocket Stream -> :warning: **Currently supported only one subscription per subscription type** - ```ts -orionUnit.priceFeed.ws.subscribe( - "allTickers", - (tickers) => { +const allTickersSubscription = orionUnit.priceFeed.ws.subscribe("allTickers", { + callback: (tickers) => { console.log(tickers); }, - undefined -); -orionUnit.priceFeed.ws.unsubscribe("allTickers"); +}); +allTickersSubscription.unsubscribe(); +orionUnit.priceFeed.ws.unsubscribe("allTickers", allTickersSubscription.id); // Also you can unsubscribe like this -orionUnit.priceFeed.ws.subscribe( - "ticker", - (ticker) => { +const tickerSubscription = orionUnit.priceFeed.ws.subscribe("ticker", { + callback: (ticker) => { console.log(tricker); }, - "ORN-USDT" -); -orionUnit.priceFeed.ws.unsubscribe("ticker"); + payload: "ORN-USDT", +}); +tickerSubscription.subscription(); +orionUnit.priceFeed.ws.unsubscribe("ticker", tickerSubscription.id); -orionUnit.priceFeed.ws.subscribe( - "lastPrice", - ({ pair, price }) => { +const lastPriceSubscription = orionUnit.priceFeed.ws.subscribe("lastPrice", { + callback: ({ pair, price }) => { console.log(`Price: ${price}`); }, - "ORN-USDT" -); -orionUnit.priceFeed.ws.unsubscribe("lastPrice"); + payload: "ORN-USDT", +}); +lastPriceSubscription.unsubscribe(); +orionUnit.priceFeed.ws.unsubscribe("lastPrice", lastPriceSubscription.id); ``` diff --git a/package.json b/package.json index 803b504..43c5ed1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.9.0", + "version": "0.10.0", "description": "Orion Protocol SDK", "main": "./lib/esm/index.js", "module": "./lib/esm/index.js", diff --git a/src/services/PriceFeed/ws/PriceFeedSubscription.ts b/src/services/PriceFeed/ws/PriceFeedSubscription.ts index 4e34390..15154b1 100644 --- a/src/services/PriceFeed/ws/PriceFeedSubscription.ts +++ b/src/services/PriceFeed/ws/PriceFeedSubscription.ts @@ -38,7 +38,7 @@ export const subscriptions = { payload: false as const, }, [priceFeedSubscriptions.TICKER]: { - schema: z.tuple([z.number(), tickerInfoSchema]), + schema: z.tuple([z.number(), tickerInfoSchema]).transform(([, tickerInfo]) => tickerInfo), payload: true as const, }, [priceFeedSubscriptions.LAST_PRICE]: { @@ -47,18 +47,24 @@ export const subscriptions = { }, }; -export type SubscriptionType = keyof typeof subscriptions +export type SubscriptionType = keyof typeof subscriptions; +export type Subscription< + T extends SubscriptionType, + Schema = z.infer +> = typeof subscriptions[T] extends { payload: true } + ? { + callback: (data: Schema) => void, + payload: string, +} : { + callback: (data: Schema) => void, +} -export type Payload = typeof subscriptions[T] extends { payload: true } ? string : undefined; - -export type ResponseSchemaType = typeof subscriptions[T]['schema']; - -export default class PriceFeedSubscription { +export default class PriceFeedSubscription { public readonly id: string; - private readonly callback: (data: z.infer>) => void; + private readonly callback: Subscription['callback']; - private readonly payload: Payload; + private readonly payload?: string; private ws?: WebSocket; @@ -66,19 +72,20 @@ export default class PriceFeedSubscription { private heartbeatInterval?: ReturnType; - private readonly type: S; + readonly type: T; constructor( - type: S, + type: T, url: string, - callback: (data: z.infer>) => void, - payload: Payload, + params: Subscription, ) { this.id = uuidv4(); this.url = url; this.type = type; - this.payload = payload; - this.callback = callback; + if ('payload' in params) { + this.payload = params.payload; + } + this.callback = params.callback; this.init(); } diff --git a/src/services/PriceFeed/ws/index.ts b/src/services/PriceFeed/ws/index.ts index 41e8c28..4cbaec2 100644 --- a/src/services/PriceFeed/ws/index.ts +++ b/src/services/PriceFeed/ws/index.ts @@ -1,11 +1,15 @@ -import { z } from 'zod'; -import PriceFeedSubscription, { Payload, ResponseSchemaType, SubscriptionType } from './PriceFeedSubscription'; +import PriceFeedSubscription, { SubscriptionType, Subscription } from './PriceFeedSubscription'; export * as schemas from './schemas'; export class PriceFeedWS { private subscriptions: Partial<{ - [S in SubscriptionType]: PriceFeedSubscription - }> = { }; + [K in SubscriptionType]: Partial< + Record< + string, + PriceFeedSubscription + > + >; + }> = {}; private url: string; @@ -15,23 +19,29 @@ export class PriceFeedWS { subscribe( type: S, - callback: (data: z.infer>) => void, - payload: Payload, + params: Subscription, ) { - if (this.subscriptions[type]) throw new Error(`Subscription already exists for '${type}'. Please unsubscribe first.`); + const sub = new PriceFeedSubscription( + type, + this.url, + params, + ); this.subscriptions = { ...this.subscriptions, - [type]: new PriceFeedSubscription( - type, - this.url, - callback, - payload, - ), + [type]: { + ...this.subscriptions[type], + [sub.id]: sub, + }, + }; + return { + type: sub.type, + id: sub.id, + unsubscribe: () => this.unsubscribe(sub.type, sub.id), }; } - unsubscribe(type: S) { - this.subscriptions[type]?.kill(); - delete this.subscriptions[type]; + unsubscribe(subType: SubscriptionType, subId: string) { + this.subscriptions[subType]?.[subId]?.kill(); + delete this.subscriptions[subType]?.[subId]; } }