From 7e53b8300979898c9a1909b723b9af45c7a68def Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 11:24:37 +0800 Subject: [PATCH 1/7] feat: support new-communication-protocol --- packages/apollo-links/package.json | 1 + packages/apollo-links/src/core/responseLink.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/apollo-links/package.json b/packages/apollo-links/package.json index 00415f7c..4f4f2b08 100644 --- a/packages/apollo-links/package.json +++ b/packages/apollo-links/package.json @@ -15,6 +15,7 @@ "buffer": "^6.0.3", "cross-fetch": "^4.0.0", "ethers": "^5.6.8", + "js-base64": "^3.7.5", "jwt-decode": "^3.1.2", "lru-cache": "^10.0.1" }, diff --git a/packages/apollo-links/src/core/responseLink.ts b/packages/apollo-links/src/core/responseLink.ts index 4115d8aa..f8b95053 100644 --- a/packages/apollo-links/src/core/responseLink.ts +++ b/packages/apollo-links/src/core/responseLink.ts @@ -5,6 +5,7 @@ import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apoll import { Logger } from '../utils/logger'; import { POST } from '../utils/query'; import { ChannelState, OrderType } from '../types'; +import { Base64 } from 'js-base64'; export type ResponseLinkOptions = { authUrl: string; @@ -42,7 +43,13 @@ export class ResponseLink extends ApolloLink { const subscription = forward(operation).subscribe({ next: (response: FetchResult> & { state: ChannelState }) => { if (!response.errors && type === OrderType.flexPlan) { - void this.syncChannelState(response.state); + const responseHeaders = operation.getContext().response.headers; + const channelState = responseHeaders.get('X-Channel-State') + ? (JSON.parse( + Base64.decode(responseHeaders.get('X-Channel-State')).toString() + ) as ChannelState) + : response.state; + void this.syncChannelState(channelState); } observer.next(response); From 6c320da02ad3ee01be677343263ac4fb71da7640 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 11:25:22 +0800 Subject: [PATCH 2/7] new version --- packages/apollo-links/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-links/package.json b/packages/apollo-links/package.json index 4f4f2b08..d917699a 100644 --- a/packages/apollo-links/package.json +++ b/packages/apollo-links/package.json @@ -1,6 +1,6 @@ { "name": "@subql/apollo-links", - "version": "1.0.9-0", + "version": "1.1.0", "description": "SubQuery Network - graphql links", "main": "dist/index.js", "author": "SubQuery Pte Limited", From 13b70f0b41310bd71c5558b60faaf3b258185d08 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 11:28:00 +0800 Subject: [PATCH 3/7] new version --- packages/apollo-links/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/apollo-links/CHANGELOG.md b/packages/apollo-links/CHANGELOG.md index ff6ae1fd..618a579b 100644 --- a/packages/apollo-links/CHANGELOG.md +++ b/packages/apollo-links/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.1.0] - 2023-10-13 + +- Support new communication protocol + ## [1.0.8] - 2023-09-07 - Enable base64 encoding for payg signature @@ -78,7 +82,8 @@ Breaking change for `dictHttpLink` and `deploymentHttpLink`, use `const { link } - Add Authlink for Apollo client -[unreleased]: https://github.com/subquery/network-clients/compare/v1.0.8...HEAD +[unreleased]: https://github.com/subquery/network-clients/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/subquery/network-clients/compare/v1.0.8...v1.1.0 [1.0.8]: https://github.com/subquery/network-clients/compare/v1.0.4...v1.0.8 [1.0.4]: https://github.com/subquery/network-clients/compare/v1.0.2...v1.0.4 [1.0.2]: https://github.com/subquery/network-clients/compare/v1.0.0...v1.0.2 From 8c9bddcee0cc776a2bf30e8b82e5fb0937a9f5a4 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 11:28:36 +0800 Subject: [PATCH 4/7] chore: lock --- yarn.lock | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/yarn.lock b/yarn.lock index 113da227..ad3cc337 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3085,6 +3085,7 @@ __metadata: buffer: ^6.0.3 cross-fetch: ^4.0.0 ethers: ^5.6.8 + js-base64: ^3.7.5 jwt-decode: ^3.1.2 lru-cache: ^10.0.1 pino: ^8.14.1 @@ -9411,6 +9412,13 @@ __metadata: languageName: node linkType: hard +"js-base64@npm:^3.7.5": + version: 3.7.5 + resolution: "js-base64@npm:3.7.5" + checksum: 67a78c8b1c47b73f1c6fba1957e9fe6fd9dc78ac93ac46cc2e43472dcb9cf150d126fb0e593192e88e0497354fa634d17d255add7cc6ee3c7b4d29870faa8e18 + languageName: node + linkType: hard + "js-cookie@npm:^2.x.x": version: 2.2.1 resolution: "js-cookie@npm:2.2.1" From df89c71f392abf13473b0f92f8c039f90606afa7 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 13:43:26 +0800 Subject: [PATCH 5/7] fix: testcase --- packages/apollo-links/package.json | 2 +- .../apollo-links/src/core/responseLink.ts | 9 ++- test/authLink.test.ts | 81 ++++++++++--------- 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/packages/apollo-links/package.json b/packages/apollo-links/package.json index d917699a..be50ea87 100644 --- a/packages/apollo-links/package.json +++ b/packages/apollo-links/package.json @@ -2,7 +2,7 @@ "name": "@subql/apollo-links", "version": "1.1.0", "description": "SubQuery Network - graphql links", - "main": "dist/index.js", + "main": "src/index.ts", "author": "SubQuery Pte Limited", "license": "Apache-2.0", "scripts": { diff --git a/packages/apollo-links/src/core/responseLink.ts b/packages/apollo-links/src/core/responseLink.ts index f8b95053..26bfa606 100644 --- a/packages/apollo-links/src/core/responseLink.ts +++ b/packages/apollo-links/src/core/responseLink.ts @@ -27,8 +27,13 @@ export class ResponseLink extends ApolloLink { async syncChannelState(state: ChannelState): Promise { try { const stateUrl = new URL('/channel/state', this.options.authUrl); - await POST(stateUrl.toString(), state); - this.logger?.debug(`syncChannelState succeed`); + const res = await POST<{ consumerSign: string }>(stateUrl.toString(), state); + + if (res.consumerSign) { + this.logger?.debug(`syncChannelState succeed`); + } else { + this.logger?.debug(`syncChannelState failed: ${JSON.stringify(res)}`); + } } catch (e) { this.logger?.debug(`syncChannelState failed: ${e}`); } diff --git a/test/authLink.test.ts b/test/authLink.test.ts index ee59e0fb..d7409d42 100644 --- a/test/authLink.test.ts +++ b/test/authLink.test.ts @@ -9,11 +9,12 @@ const mockAxios = axios as jest.Mocked; import dotenv from 'dotenv'; import { ApolloClient, ApolloLink, from, HttpLink, InMemoryCache } from '@apollo/client/core'; -import fetch from 'cross-fetch'; +import fetch, { Headers } from 'cross-fetch'; import gql from 'graphql-tag'; import Pino from 'pino'; import { ProjectType } from '../packages/apollo-links/src/types'; import { Logger } from '../packages/apollo-links/src/utils/logger'; +import { Base64 } from 'js-base64'; dotenv.config(); @@ -316,7 +317,7 @@ describe('mock: auth link with auth center', () => { expect(result.data._metadata).toBeTruthy(); }, 5000); - it.skip('mock: can query data with payg', async () => { + it('mock: can query data with payg', async () => { const deploymentId = 'QmV6sbiPyTDUjcQNJs2eGcAQp2SMXL2BU6qdv5aKrRr7Hg'; const { deploymentHttpLink } = await getLinks(); const signBeforeQueryPayg = jest.fn(); @@ -362,14 +363,18 @@ describe('mock: auth link with auth center', () => { return Promise.resolve({ data: { - channelId: '0x91ABB40D77FE1F340A98A57A0C5BC24B3A9B91007E345EA4795901D9698ADF4', - consumer: '0x0000000000000000', - consumerSign: indexerSign, - indexer: '0x000000000000000c', - indexerSign, - isFinal: false, - remote: '10000000000000000', - spent: '10000000000000000', + authorization: Base64.encode( + JSON.stringify({ + channelId: '0x91ABB40D77FE1F340A98A57A0C5BC24B3A9B91007E345EA4795901D9698ADF4', + consumer: '0x0000000000000000', + consumerSign: indexerSign, + indexer: '0x000000000000000c', + indexerSign, + isFinal: false, + remote: '10000000000000000', + spent: '10000000000000000', + }) + ), }, }); } @@ -384,6 +389,12 @@ describe('mock: auth link with auth center', () => { expect((data as { indexerSign: string }).indexerSign).not.toEqual(indexerSign); stateAfterQueryPayg(); + + return Promise.resolve({ + data: { + consumerSign: indexerSign, + }, + }); } return Promise.resolve(); @@ -396,21 +407,18 @@ describe('mock: auth link with auth center', () => { ...httpOptions, fetch: (uri: RequestInfo | URL, options: any): Promise => { if (uri.toString().includes('mock-request/payg')) { - const authorization = JSON.parse(options.headers.authorization); + const authorization = JSON.parse(Base64.decode(options.headers.authorization)); expect(authorization).toHaveProperty('channelId'); expect(authorization).toHaveProperty('consumer'); expect(authorization).toHaveProperty('consumerSign'); + // @ts-ignore return Promise.resolve({ - json: () => - Promise.resolve({ - data: { - _metadata: { - indexerHealthy: true, - indexerNodeVersion: '00.00', - }, - }, - state: { + headers: new Headers({ + ...options.headers, + 'Access-Control-Expose-Headers': '*', + 'X-Channel-State': Base64.encode( + JSON.stringify({ channelId: '0x91ABB40D77FE1F340A98A57A0C5BC24B3A9B91007E345EA4795901D9698ADF4', consumer: '0x0000000', consumerSign: @@ -421,6 +429,16 @@ describe('mock: auth link with auth center', () => { isFinal: false, remote: '10000000000000000', spent: '10000000000000000', + }) + ), + }), + json: () => + Promise.resolve({ + data: { + _metadata: { + indexerHealthy: true, + indexerNodeVersion: '00.00', + }, }, }), text: () => @@ -432,19 +450,6 @@ describe('mock: auth link with auth center', () => { indexerNodeVersion: '00.00', }, }, - state: { - channelId: - '0x91ABB40D77FE1F340A98A57A0C5BC24B3A9B91007E345EA4795901D9698ADF4', - consumer: '0x0000000', - consumerSign: - 'e239518860984116c1bee0264c3ad1df02c574db56be715e9a74b665a160fc56782db19f7a40c354b661ad8279e1a1ca99dfed25264e9d8bfc89427a70d5c0451c', - indexer: '0x11111111', - indexerSign: - '4db7ad2c0c4426cec02c05586b2363c23358394ea540d516dc6f7efdffd3a6967205c115fea4e3054f74aaf0f27c4b90416bee71f9775915d315644720a61d2a1b', - isFinal: false, - remote: '10000000000000000', - spent: '10000000000000000', - }, }) ), }); @@ -461,7 +466,7 @@ describe('mock: auth link with auth center', () => { expect(result.data._metadata).toBeTruthy(); expect(signBeforeQueryPayg).toBeCalledTimes(1); expect(stateAfterQueryPayg).toBeCalledTimes(1); - }, 5000); + }, 25000); it('mock: can query data with service agreement', async () => { const deploymentId = 'QmV6sbiPyTDUjcQNJs2eGcAQp2SMXL2BU6qdv5aKrRr7Hg'; @@ -1179,12 +1184,12 @@ describe('mock: auth link with auth center', () => { }); /// real data test -const authUrl = process.env.AUTH_URL ?? 'https://kepler-auth.subquery.network'; +const authUrl = process.env.AUTH_URL ?? 'https://kepler-auth.thechaindata.com'; const httpOptions = { fetch, fetchOptions: { timeout: 5000 } }; const createDictionaryClient = async (chainId: string, fallbackServiceUrl: string) => { const options = { - authUrl, + authUrl: process.env.AUTH_URL ?? 'https://kepler-auth.subquery.network', chainId, httpOptions, logger: mockLogger, @@ -1211,10 +1216,10 @@ const createDeploymentClient = async (deploymentId: string, fallbackServiceUrl?: }; describe('Auth http link with real data', () => { - const defaultFallbackUrl = 'https://api.subquery.network/sq/subquery/karura-dictionary'; + const defaultFallbackUrl = 'https://api.subquery.network/sq/subquery/aleph-zero-dictionary'; const chainId = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3'; // TODO: need to update this one to network deploymentId - const deploymentId = 'QmV6sbiPyTDUjcQNJs2eGcAQp2SMXL2BU6qdv5aKrRr7Hg'; + const deploymentId = 'QmStgQRJVMGxj1LdzNirEcppPf7t8Zm4pgDkCqChqvrDKG'; const unavailableChainId = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c4'; beforeEach(async () => { From 6c8d228ed358fddc92ee8fffafc4c620e32e29dc Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 13:45:38 +0800 Subject: [PATCH 6/7] chore: reset --- packages/apollo-links/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-links/package.json b/packages/apollo-links/package.json index be50ea87..d917699a 100644 --- a/packages/apollo-links/package.json +++ b/packages/apollo-links/package.json @@ -2,7 +2,7 @@ "name": "@subql/apollo-links", "version": "1.1.0", "description": "SubQuery Network - graphql links", - "main": "src/index.ts", + "main": "dist/index.js", "author": "SubQuery Pte Limited", "license": "Apache-2.0", "scripts": { From 62386b8f1eb2824413ca1fdbf097d74728c0a160 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Fri, 13 Oct 2023 13:54:21 +0800 Subject: [PATCH 7/7] chore: change test auth url --- test/authLink.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/authLink.test.ts b/test/authLink.test.ts index d7409d42..3db790c9 100644 --- a/test/authLink.test.ts +++ b/test/authLink.test.ts @@ -1184,7 +1184,6 @@ describe('mock: auth link with auth center', () => { }); /// real data test -const authUrl = process.env.AUTH_URL ?? 'https://kepler-auth.thechaindata.com'; const httpOptions = { fetch, fetchOptions: { timeout: 5000 } }; const createDictionaryClient = async (chainId: string, fallbackServiceUrl: string) => { @@ -1203,7 +1202,7 @@ const createDictionaryClient = async (chainId: string, fallbackServiceUrl: strin const createDeploymentClient = async (deploymentId: string, fallbackServiceUrl?: string) => { const options = { - authUrl, + authUrl: 'https://kepler-auth.thechaindata.com', deploymentId, httpOptions, logger: mockLogger,