Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@subql/apollo-links

Package Overview
Dependencies
Maintainers
2
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@subql/apollo-links - npm Package Compare versions

Comparing version 1.2.2 to 1.2.3

5

CHANGELOG.md

@@ -10,2 +10,4 @@ # Changelog

## [1.2.3] - 2023-11-27
## [1.2.2] - 2023-11-13

@@ -88,3 +90,4 @@

[unreleased]: https://github.com/subquery/network-clients/compare/v1.2.2...HEAD
[unreleased]: https://github.com/subquery/network-clients/compare/v1.2.3...HEAD
[1.2.3]: https://github.com/subquery/network-clients/compare/v1.2.2...v1.2.3
[1.2.2]: https://github.com/subquery/network-clients/compare/v1.1.0...v1.2.2

@@ -91,0 +94,0 @@ [1.1.0]: https://github.com/subquery/network-clients/compare/v1.0.8...v1.1.0

1

dist/auth/authHelper.d.ts
import { AuthMessage } from './eip712';
export declare function isTokenExpired(token: string): boolean;
export declare function signMessage(msg: AuthMessage, sk: string, chainId: number): string;
export declare function requestAuthToken(authUrl: string, msg: AuthMessage, sk: string, chainId: number): Promise<string>;

@@ -8,22 +8,8 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.requestAuthToken = exports.signMessage = exports.isTokenExpired = void 0;
exports.requestAuthToken = exports.signMessage = void 0;
const buffer_1 = __importDefault(require("buffer"));
const eth_sig_util_1 = require("@metamask/eth-sig-util");
const jwt_decode_1 = __importDefault(require("jwt-decode"));
const buffer_1 = __importDefault(require("buffer"));
const network_support_1 = require("@subql/network-support");
const eip712_1 = require("./eip712");
const query_1 = require("../utils/query");
const Buffer = buffer_1.default.Buffer;
function isTokenExpired(token) {
if (!token)
return true;
try {
const { exp } = (0, jwt_decode_1.default)(token);
const currentDate = new Date().getTime();
return exp < currentDate;
}
catch (_a) {
return true;
}
}
exports.isTokenExpired = isTokenExpired;
function signMessage(msg, sk, chainId) {

@@ -44,3 +30,3 @@ if (!sk)

const body = (0, eip712_1.createAuthRequestBody)(msg, signature, chainId);
const res = await (0, query_1.POST)(authUrl, body);
const res = await (0, network_support_1.POST)(authUrl, body);
return res.token;

@@ -47,0 +33,0 @@ }

import { ApolloLink } from '@apollo/client/core';
import { IStore, RunnerSelector } from '@subql/network-support';
import { Options } from './core';
import { ProjectType } from './types';
import { Logger } from './utils/logger';
import { IStore } from './utils/store';
interface BaseAuthOptions {

@@ -11,11 +12,16 @@ authUrl: string;

scoreStore?: IStore;
selector?: RunnerSelector;
maxRetries?: number;
useImmediateFallbackOnError?: boolean;
timeout?: number;
}
interface DictAuthOptions extends BaseAuthOptions {
export interface DictAuthOptions extends BaseAuthOptions {
chainId: string;
}
interface DeploymentAuthOptions extends BaseAuthOptions {
export interface DeploymentAuthOptions extends BaseAuthOptions {
deploymentId: string;
}
export interface AuthOptions extends DeploymentAuthOptions {
projectType: ProjectType;
}
interface AuthHttpLink {

@@ -22,0 +28,0 @@ link: ApolloLink;

"use strict";
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

@@ -7,6 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });

const core_1 = require("@apollo/client/core");
const network_support_1 = require("@subql/network-support");
const core_2 = require("./core");
const types_1 = require("./types");
const logger_1 = require("./utils/logger");
const orderManager_1 = require("./core/orderManager");
function dictHttpLink(options) {

@@ -22,5 +22,6 @@ const { chainId } = options;

function authHttpLink(options) {
const { deploymentId, httpOptions, fallbackServiceUrl, authUrl, projectType, maxRetries, useImmediateFallbackOnError = false, logger: _logger, } = options;
const { deploymentId, httpOptions, fallbackServiceUrl, authUrl, projectType, scoreStore, maxRetries, useImmediateFallbackOnError = false, logger: _logger, timeout = 60000, selector, } = options;
(0, network_support_1.setFetchTimeout)(timeout);
const logger = _logger !== null && _logger !== void 0 ? _logger : (0, logger_1.silentLogger)();
const orderManager = new orderManager_1.OrderManager({
const orderManager = new network_support_1.OrderManager({
authUrl,

@@ -30,2 +31,6 @@ projectId: deploymentId,

logger,
scoreStore,
responseFormat: network_support_1.ResponseFormat.Inline,
selector,
timeout,
});

@@ -32,0 +37,0 @@ const retryLink = (0, core_2.createRetryLink)({ orderManager, logger, maxRetries });

import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apollo/client/core';
import { Message } from '../auth/eip712';
import { Message } from '../auth';
import { Logger } from '../utils/logger';

@@ -4,0 +4,0 @@ interface AuthOptions extends Message {

"use strict";
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

@@ -7,3 +7,4 @@ Object.defineProperty(exports, "__esModule", { value: true });

const core_1 = require("@apollo/client/core");
const authHelper_1 = require("../auth/authHelper");
const network_support_1 = require("@subql/network-support");
const auth_1 = require("../auth");
class AuthLink extends core_1.ApolloLink {

@@ -46,3 +47,3 @@ constructor(options, logger, token = '') {

async getUrlAndToken() {
if (!(0, authHelper_1.isTokenExpired)(this._token))
if (!(0, network_support_1.isTokenExpired)(this._token))
return { token: this._token, url: this.queryEndpoint };

@@ -54,3 +55,3 @@ const { sk, chainId, agreement, indexerUrl } = this._options;

const tokenUrl = new URL('/token', indexerUrl);
const authToken = await (0, authHelper_1.requestAuthToken)(tokenUrl.toString(), message, sk, chainId);
const authToken = await (0, auth_1.requestAuthToken)(tokenUrl.toString(), message, sk, chainId);
this._token = authToken;

@@ -57,0 +58,0 @@ return { token: authToken, url: this.queryEndpoint };

import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apollo/client/core';
import { OrderManager } from './orderManager';
import { OrderManager } from '@subql/network-support';
import { Logger } from '../utils/logger';
export declare type AuthOptions = {
export declare type ClusterAuthLinkOptions = {
authUrl: string;

@@ -14,8 +14,5 @@ projectId: string;

private orderManager;
constructor(options: AuthOptions);
constructor(options: ClusterAuthLinkOptions);
request(operation: Operation, forward?: NextLink): Observable<FetchResult> | null;
private tokenToAuthHeader;
private getRequestParams;
private getAgreementRequestParams;
private getPlanRequestParams;
}

@@ -7,5 +7,2 @@ "use strict";

const core_1 = require("@apollo/client/core");
const auth_1 = require("../auth");
const types_1 = require("../types");
const query_1 = require("../utils/query");
class ClusterAuthLink extends core_1.ApolloLink {

@@ -23,19 +20,13 @@ constructor(options) {

let sub;
this.getRequestParams()
this.orderManager
.getRequestParams()
.then((params) => {
var _a, _b;
if (params === null || params === void 0 ? void 0 : params.data) {
const { authorization, url, type, indexer } = params.data;
const headers = { authorization };
operation.setContext({ url, headers, type, indexer });
var _a;
if (params) {
const { headers, url, type, runner } = params;
operation.setContext({ url, headers, type, indexer: runner });
sub = forward(operation).subscribe(observer);
}
else if (params === null || params === void 0 ? void 0 : params.error) {
const { indexer, message } = params.error;
operation.setContext({ indexer });
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(`Failed to get token: ${message}`);
observer.error(new Error('failed to get indexer request params'));
}
else {
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug('no available orders');
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug('no available orders');
sub = forward(operation).subscribe(observer);

@@ -45,5 +36,12 @@ }

.catch((error) => {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(`Failed to get order request params: ${error.message}`);
observer.error(new Error('failed to get indexer url and token'));
var _a, _b;
if (error.indexer) {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(`Failed to get token: ${String(error.message)}`);
operation.setContext({ indexer: error.indexer });
observer.error(new Error('failed to get indexer request params'));
}
else {
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(`Failed to get order request params: ${String(error.message)}`);
observer.error(new Error('failed to get indexer url and token'));
}
});

@@ -56,68 +54,4 @@ return () => sub === null || sub === void 0 ? void 0 : sub.unsubscribe();

}
async getRequestParams() {
const orderType = await this.orderManager.getNextOrderType();
if (!orderType)
return undefined;
switch (orderType) {
case types_1.OrderType.agreement:
return this.getAgreementRequestParams();
case types_1.OrderType.flexPlan:
return this.getPlanRequestParams();
default:
return undefined;
}
}
async getAgreementRequestParams() {
var _a, _b, _c;
const nextAgreement = await this.orderManager.getNextAgreement();
if (!nextAgreement)
return undefined;
const type = types_1.OrderType.agreement;
const { token, id, url, indexer } = nextAgreement;
if (!(0, auth_1.isTokenExpired)(token))
return { data: Object.assign({ url, type, indexer }, this.tokenToAuthHeader(token)) };
try {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(`request new token for indexer ${indexer}`);
const { projectId, authUrl } = this.options;
const tokenUrl = new URL('/orders/token', authUrl);
const res = await (0, query_1.POST)(tokenUrl.toString(), {
projectId,
indexer,
agreementId: id,
});
this.orderManager.updateTokenById(id, res.token);
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(`request new token for indexer ${indexer} success`);
return { data: Object.assign({ url, type, indexer }, this.tokenToAuthHeader(res.token)) };
}
catch (error) {
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug(`request new token for indexer ${indexer} and url: ${nextAgreement.url} failed`);
return { error: { indexer: nextAgreement.indexer, message: error.message } };
}
}
async getPlanRequestParams() {
var _a, _b, _c;
const nextPlan = await this.orderManager.getNextPlan();
if (!nextPlan)
return undefined;
const type = types_1.OrderType.flexPlan;
const { id: channelId, url, indexer } = nextPlan;
try {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(`request new signature for indexer ${indexer}`);
const { projectId: deployment, authUrl } = this.options;
const tokenUrl = new URL('/channel/sign', authUrl);
const signedState = await (0, query_1.POST)(tokenUrl.toString(), {
deployment,
channelId,
});
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(`request new state signature for indexer ${indexer} success`);
const { authorization } = signedState;
return { data: { authorization, url, type, indexer } };
}
catch (error) {
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug(`request new state signature for indexer ${indexer} failed`);
return { error: { indexer: nextPlan.indexer, message: error.message } };
}
}
}
exports.ClusterAuthLink = ClusterAuthLink;
//# sourceMappingURL=clusterAuthLink.js.map
"use strict";
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicHttpLink = void 0;
const core_1 = require("@apollo/client/core");
const cross_fetch_1 = __importDefault(require("cross-fetch"));
const network_support_1 = require("@subql/network-support");
class DynamicHttpLink extends core_1.ApolloLink {

@@ -32,3 +29,5 @@ constructor(options) {

createHttpLink(uri) {
return new core_1.HttpLink(Object.assign(Object.assign({}, this.options.httpOptions), { fetch: this.options.httpOptions.fetch ? this.options.httpOptions.fetch : cross_fetch_1.default, uri }));
return new core_1.HttpLink(Object.assign(Object.assign({}, this.options.httpOptions), { fetchOptions: Object.assign(Object.assign({}, this.options.httpOptions.fetchOptions), { signal: (0, network_support_1.timeoutController)().signal }),
// note: fetch signal is not work even the signle is on customFetch, must pass timeout signle to fetchOptions
fetch: this.options.httpOptions.fetch ? this.options.httpOptions.fetch : network_support_1.customFetch, uri }));
}

@@ -35,0 +34,0 @@ }

import { ApolloLink } from '@apollo/client/core';
import { OrderManager } from '@subql/network-support';
import { Logger } from '../utils/logger';
import { OrderManager } from './orderManager';
export declare type ErrorLinkOption = {

@@ -5,0 +5,0 @@ orderManager: OrderManager;

@@ -7,6 +7,7 @@ "use strict";

const error_1 = require("@apollo/client/link/error");
const network_support_1 = require("@subql/network-support");
const creatErrorLink = ({ fallbackLink, httpLink, orderManager, useImmediateFallbackOnError, logger, }) => (0, error_1.onError)(({ graphQLErrors, networkError, operation }) => {
const { indexer } = operation.getContext();
if (networkError) {
orderManager.updateIndexerScore(indexer, 'network');
orderManager.updateScore(indexer, network_support_1.ScoreType.NETWORK);
logger === null || logger === void 0 ? void 0 : logger.debug(`[Network error]: ${networkError}`);

@@ -16,3 +17,3 @@ }

graphQLErrors.forEach(({ message, locations, path }) => {
orderManager.updateIndexerScore(indexer, 'graphql');
orderManager.updateScore(indexer, network_support_1.ScoreType.GRAPHQL);
logger === null || logger === void 0 ? void 0 : logger.debug(`[GraphQL error]: Message: ${message}, Location: ${JSON.stringify(locations)}, Path: ${path}`);

@@ -19,0 +20,0 @@ });

import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apollo/client/core';
import { ChannelState } from '@subql/network-support';
import { Logger } from '../utils/logger';
import { ChannelState } from '../types';
export declare type ResponseLinkOptions = {

@@ -5,0 +5,0 @@ authUrl: string;

@@ -7,4 +7,3 @@ "use strict";

const core_1 = require("@apollo/client/core");
const query_1 = require("../utils/query");
const types_1 = require("../types");
const network_support_1 = require("@subql/network-support");
const js_base64_1 = require("js-base64");

@@ -24,3 +23,3 @@ class ResponseLink extends core_1.ApolloLink {

const stateUrl = new URL('/channel/state', this.options.authUrl);
const res = await (0, query_1.POST)(stateUrl.toString(), state);
const res = await (0, network_support_1.POST)(stateUrl.toString(), state);
if (res.consumerSign) {

@@ -34,3 +33,3 @@ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(`syncChannelState succeed`);

catch (e) {
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug(`syncChannelState failed: ${e}`);
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug(`syncChannelState failed: ${String(e)}`);
}

@@ -45,3 +44,3 @@ }

next: (response) => {
if (type === types_1.OrderType.flexPlan) {
if (type === network_support_1.OrderType.flexPlan) {
const responseHeaders = operation.getContext().response.headers;

@@ -48,0 +47,0 @@ if (responseHeaders) {

import { RetryLink } from '@apollo/client/link/retry';
import { OrderManager } from '@subql/network-support';
import { Logger } from '../utils/logger';
import { OrderManager } from './orderManager';
export declare type RetryLinkOption = {

@@ -5,0 +5,0 @@ orderManager: OrderManager;

@@ -7,2 +7,3 @@ "use strict";

const retry_1 = require("@apollo/client/link/retry");
const network_support_1 = require("@subql/network-support");
const createRetryLink = ({ orderManager, maxRetries = 3, logger }) => new retry_1.RetryLink({

@@ -13,3 +14,3 @@ attempts: function (count, operation, error) {

const { indexer } = operation.getContext();
orderManager.updateIndexerScore(indexer, 'network');
orderManager.updateScore(indexer, network_support_1.ScoreType.NETWORK);
const isEmptyUrlError = (_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('empty url');

@@ -16,0 +17,0 @@ const isFallback = operation.getContext().fallback;

@@ -5,27 +5,1 @@ export declare enum ProjectType {

}
export declare enum OrderType {
agreement = "agreement",
flexPlan = "flexPlan"
}
export declare type Order = {
id: string;
indexer: string;
url: string;
};
export declare type Agreement = Order & {
token: string;
};
export declare type Plan = Order;
export declare type ChannelState = {
channelId: string;
indexer: string;
consumer: string;
spent: string;
remote: string;
isFinal: boolean;
indexerSign: string;
consumerSign: string;
};
export declare type ChannelAuth = {
authorization: string;
};

@@ -5,3 +5,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.OrderType = exports.ProjectType = void 0;
exports.ProjectType = void 0;
var ProjectType;

@@ -12,7 +12,2 @@ (function (ProjectType) {

})(ProjectType = exports.ProjectType || (exports.ProjectType = {}));
var OrderType;
(function (OrderType) {
OrderType["agreement"] = "agreement";
OrderType["flexPlan"] = "flexPlan";
})(OrderType = exports.OrderType || (exports.OrderType = {}));
//# sourceMappingURL=types.js.map
{
"name": "@subql/apollo-links",
"version": "1.2.2",
"version": "1.2.3",
"description": "SubQuery Network - graphql links",

@@ -13,4 +13,4 @@ "main": "dist/index.js",

"@metamask/eth-sig-util": "5.1.0",
"@subql/network-support": "0.1.0",
"apollo-link-error": "^1.1.13",
"axios": "^0.27.2",
"buffer": "^6.0.3",

@@ -17,0 +17,0 @@ "cross-fetch": "^4.0.0",

@@ -28,3 +28,5 @@ # @subql/apollo-links

## Usage - external authorization mode
### What scenario work best with external authorization mode
This is the recommended way to use `@subql/apollo-links`. With an auth-server to handle cryptography stuff that consumer needed to interact with indexer.

@@ -51,3 +53,2 @@ Client side doesn't need to expose anything to reveal the identity of consumer.

deploymentId: 'your_deployment_id_here',
httpOptions: { fetchOptions: { timeout: 5000 } },
// ... other optional configurations

@@ -97,2 +98,3 @@ // fallbackUrl:

## Usage - local authorization mode
Need to put consumer controller's private key with client so it can sign and authorise every requests sent to indexer.

@@ -102,21 +104,22 @@

const options = {
sk: '<private key>',
// don't put authUrl
}
sk: '<private key>',
// don't put authUrl
};
```
## Score Store
We have an internal store for indexer scores so bad performed, bad progressed or unreachable indexers will be punished and not getting new requests.
For browser side usage, after page refresh, the score will lose though. To solve that, you can instantiate a LocalStorageStore and pass in when constructing the link object.
```ts
const store = createLocalStorageStore({ttl: 86_400_000});
const store = createLocalStorageStore({ ttl: 86_400_000 });
const { link, cleanup } = deploymentHttpLink({
authUrl: 'https://kepler-auth.subquery.network',
deploymentId: 'your_deployment_id_here',
httpOptions: { fetchOptions: { timeout: 5000 } },
scoreStore: store,
authUrl: 'https://kepler-auth.subquery.network',
deploymentId: 'your_deployment_id_here',
httpOptions: { fetchOptions: { timeout: 5000 } },
scoreStore: store,
});
```

@@ -127,8 +130,8 @@

| params | usage |
|--------|------------------------------------------------------------------|
| ------ | ---------------------------------------------------------------- |
| logger | apollo link will write logs to it, by default no logs will print |
| | |
## Cleanup
## Cleanup
Because of the extra state management logic in it, call `cleanup()` to completely destroy the link and release resources.

@@ -138,2 +141,2 @@

cleanup();
```
```
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0
import buffer from 'buffer';
import { signTypedData, SignTypedDataVersion } from '@metamask/eth-sig-util';
import jwt_decode from 'jwt-decode';
import buffer from 'buffer';
import { POST } from '@subql/network-support';
import { AuthMessage, buildTypedMessage, createAuthRequestBody } from './eip712';
import { POST } from '../utils/query';
const Buffer = buffer.Buffer;
export function isTokenExpired(token: string): boolean {
if (!token) return true;
try {
const { exp } = jwt_decode(token) as { exp: number };
const currentDate = new Date().getTime();
return exp < currentDate;
} catch {
return true;
}
}
export function signMessage(msg: AuthMessage, sk: string, chainId: number): string {

@@ -26,0 +13,0 @@ if (!sk) return '';

@@ -1,2 +0,2 @@

// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

@@ -7,3 +7,12 @@

import {
IStore,
OrderManager,
ResponseFormat,
RunnerSelector,
setFetchTimeout,
} from '@subql/network-support';
import {
ClusterAuthLink,
createRetryLink,
creatErrorLink,
DynamicHttpLink,

@@ -13,9 +22,5 @@ FallbackLink,

ResponseLink,
creatErrorLink,
createRetryLink,
} from './core';
import { ProjectType } from './types';
import { Logger, silentLogger } from './utils/logger';
import { OrderManager } from './core/orderManager';
import { IStore } from './utils/store';

@@ -28,15 +33,17 @@ interface BaseAuthOptions {

scoreStore?: IStore; // pass store in, so it doesn't get lost between page refresh
selector?: RunnerSelector;
maxRetries?: number;
useImmediateFallbackOnError?: boolean;
timeout?: number;
}
interface DictAuthOptions extends BaseAuthOptions {
export interface DictAuthOptions extends BaseAuthOptions {
chainId: string; // chain id for the requested dictionary
}
interface DeploymentAuthOptions extends BaseAuthOptions {
export interface DeploymentAuthOptions extends BaseAuthOptions {
deploymentId: string; // deployment id
}
interface AuthOptions extends DeploymentAuthOptions {
export interface AuthOptions extends DeploymentAuthOptions {
projectType: ProjectType; // order type

@@ -66,6 +73,10 @@ }

projectType,
scoreStore,
maxRetries,
useImmediateFallbackOnError = false,
logger: _logger,
timeout = 60000,
selector,
} = options;
setFetchTimeout(timeout);

@@ -78,2 +89,6 @@ const logger = _logger ?? silentLogger();

logger,
scoreStore,
responseFormat: ResponseFormat.Inline,
selector,
timeout,
});

@@ -80,0 +95,0 @@

@@ -1,9 +0,9 @@

// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apollo/client/core';
import { isTokenExpired } from '@subql/network-support';
import { Subscription } from 'zen-observable-ts';
import { isTokenExpired, requestAuthToken } from '../auth/authHelper';
import { Message } from '../auth/eip712';
import { Message, requestAuthToken } from '../auth';
import { Logger } from '../utils/logger';

@@ -10,0 +10,0 @@

@@ -5,11 +5,8 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors

import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apollo/client/core';
import { OrderManager } from '@subql/network-support';
import { Subscription } from 'zen-observable-ts';
import { OrderManager } from './orderManager';
import { isTokenExpired } from '../auth';
import { ChannelAuth, OrderType } from '../types';
import { Logger } from '../utils/logger';
import { POST } from '../utils/query';
export type AuthOptions = {
export type ClusterAuthLinkOptions = {
authUrl: string; // the url for geting token

@@ -21,21 +18,8 @@ projectId: string; // chainId or deploymentId for the project

type ParamsResponse = {
data?: {
url: string;
authorization: string;
type: OrderType;
indexer: string;
};
error?: {
indexer: string;
message: string;
};
};
export class ClusterAuthLink extends ApolloLink {
private options: AuthOptions;
private options: ClusterAuthLinkOptions;
private logger: Logger;
private orderManager: OrderManager;
constructor(options: AuthOptions) {
constructor(options: ClusterAuthLinkOptions) {
super();

@@ -53,16 +37,9 @@ this.options = options;

this.getRequestParams()
this.orderManager
.getRequestParams()
.then((params) => {
if (params?.data) {
const { authorization, url, type, indexer } = params.data;
const headers = { authorization };
operation.setContext({ url, headers, type, indexer });
if (params) {
const { headers, url, type, runner } = params;
operation.setContext({ url, headers, type, indexer: runner });
sub = forward(operation).subscribe(observer);
} else if (params?.error) {
const { indexer, message } = params.error;
operation.setContext({ indexer });
this.logger?.debug(`Failed to get token: ${message}`);
observer.error(new Error('failed to get indexer request params'));
} else {

@@ -74,4 +51,10 @@ this.logger?.debug('no available orders');

.catch((error) => {
this.logger?.debug(`Failed to get order request params: ${error.message}`);
observer.error(new Error('failed to get indexer url and token'));
if (error.indexer) {
this.logger?.debug(`Failed to get token: ${String(error.message)}`);
operation.setContext({ indexer: error.indexer });
observer.error(new Error('failed to get indexer request params'));
} else {
this.logger?.debug(`Failed to get order request params: ${String(error.message)}`);
observer.error(new Error('failed to get indexer url and token'));
}
});

@@ -86,71 +69,2 @@

}
private async getRequestParams(): Promise<ParamsResponse | undefined> {
const orderType = await this.orderManager.getNextOrderType();
if (!orderType) return undefined;
switch (orderType) {
case OrderType.agreement:
return this.getAgreementRequestParams();
case OrderType.flexPlan:
return this.getPlanRequestParams();
default:
return undefined;
}
}
private async getAgreementRequestParams(): Promise<ParamsResponse | undefined> {
const nextAgreement = await this.orderManager.getNextAgreement();
if (!nextAgreement) return undefined;
const type = OrderType.agreement;
const { token, id, url, indexer } = nextAgreement;
if (!isTokenExpired(token))
return { data: { url, type, indexer, ...this.tokenToAuthHeader(token) } };
try {
this.logger?.debug(`request new token for indexer ${indexer}`);
const { projectId, authUrl } = this.options;
const tokenUrl = new URL('/orders/token', authUrl);
const res = await POST<{ token: string }>(tokenUrl.toString(), {
projectId,
indexer,
agreementId: id,
});
this.orderManager.updateTokenById(id, res.token);
this.logger?.debug(`request new token for indexer ${indexer} success`);
return { data: { url, type, indexer, ...this.tokenToAuthHeader(res.token) } };
} catch (error) {
this.logger?.debug(
`request new token for indexer ${indexer} and url: ${nextAgreement.url} failed`
);
return { error: { indexer: nextAgreement.indexer, message: (error as Error).message } };
}
}
private async getPlanRequestParams(): Promise<ParamsResponse | undefined> {
const nextPlan = await this.orderManager.getNextPlan();
if (!nextPlan) return undefined;
const type = OrderType.flexPlan;
const { id: channelId, url, indexer } = nextPlan;
try {
this.logger?.debug(`request new signature for indexer ${indexer}`);
const { projectId: deployment, authUrl } = this.options;
const tokenUrl = new URL('/channel/sign', authUrl);
const signedState = await POST<ChannelAuth>(tokenUrl.toString(), {
deployment,
channelId,
});
this.logger?.debug(`request new state signature for indexer ${indexer} success`);
const { authorization } = signedState;
return { data: { authorization, url, type, indexer } };
} catch (error) {
this.logger?.debug(`request new state signature for indexer ${indexer} failed`);
return { error: { indexer: nextPlan.indexer, message: (error as Error).message } };
}
}
}

@@ -13,4 +13,4 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors

} from '@apollo/client/core';
import { customFetch, timeoutController } from '@subql/network-support';
import { Logger } from '../utils/logger';
import fetch from 'cross-fetch';

@@ -57,3 +57,8 @@ export type Options = {

...this.options.httpOptions,
fetch: this.options.httpOptions.fetch ? this.options.httpOptions.fetch : fetch,
fetchOptions: {
...this.options.httpOptions.fetchOptions,
signal: timeoutController().signal,
},
// note: fetch signal is not work even the signle is on customFetch, must pass timeout signle to fetchOptions
fetch: this.options.httpOptions.fetch ? this.options.httpOptions.fetch : customFetch,
uri,

@@ -60,0 +65,0 @@ });

@@ -6,4 +6,4 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors

import { onError } from '@apollo/client/link/error';
import { OrderManager, ScoreType } from '@subql/network-support';
import { Logger } from '../utils/logger';
import { OrderManager } from './orderManager';

@@ -28,3 +28,3 @@ export type ErrorLinkOption = {

if (networkError) {
orderManager.updateIndexerScore(indexer, 'network');
orderManager.updateScore(indexer, ScoreType.NETWORK);
logger?.debug(`[Network error]: ${networkError}`);

@@ -35,4 +35,3 @@ }

graphQLErrors.forEach(({ message, locations, path }) => {
orderManager.updateIndexerScore(indexer, 'graphql');
orderManager.updateScore(indexer, ScoreType.GRAPHQL);
logger?.debug(

@@ -39,0 +38,0 @@ `[GraphQL error]: Message: ${message}, Location: ${JSON.stringify(

@@ -5,6 +5,5 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors

import { ApolloLink, FetchResult, NextLink, Observable, Operation } from '@apollo/client/core';
import { ChannelState, OrderType, POST } from '@subql/network-support';
import { Base64 } from 'js-base64';
import { Logger } from '../utils/logger';
import { POST } from '../utils/query';
import { ChannelState, OrderType } from '../types';
import { Base64 } from 'js-base64';

@@ -39,3 +38,3 @@ export type ResponseLinkOptions = {

} catch (e) {
this.logger?.debug(`syncChannelState failed: ${e}`);
this.logger?.debug(`syncChannelState failed: ${String(e)}`);
}

@@ -42,0 +41,0 @@ }

@@ -6,4 +6,4 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors

import { RetryLink } from '@apollo/client/link/retry';
import { OrderManager, ScoreType } from '@subql/network-support';
import { Logger } from '../utils/logger';
import { OrderManager } from './orderManager';

@@ -21,3 +21,3 @@ export type RetryLinkOption = {

const { indexer } = operation.getContext();
orderManager.updateIndexerScore(indexer, 'network');
orderManager.updateScore(indexer, ScoreType.NETWORK);

@@ -24,0 +24,0 @@ const isEmptyUrlError = error?.message?.includes('empty url');

@@ -8,33 +8,1 @@ // Copyright 2020-2022 SubQuery Pte Ltd authors & contributors

}
export enum OrderType {
agreement = 'agreement',
flexPlan = 'flexPlan',
}
export type Order = {
id: string;
indexer: string;
url: string;
};
export type Agreement = Order & {
token: string;
};
export type Plan = Order;
export type ChannelState = {
channelId: string;
indexer: string;
consumer: string;
spent: string;
remote: string;
isFinal: boolean;
indexerSign: string;
consumerSign: string;
};
export type ChannelAuth = {
authorization: string;
};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc