Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tool 233 extend user agent on js network providers #67

Merged
merged 7 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions src/NetworkProviderConfig.ts

This file was deleted.

16 changes: 9 additions & 7 deletions src/apiNetworkProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,23 @@ import { DefinitionOfFungibleTokenOnNetwork, DefinitionOfTokenCollectionOnNetwor
import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens";
import { TransactionOnNetwork, prepareTransactionForBroadcasting } from "./transactions";
import { TransactionStatus } from "./transactionStatus";
import { setUserAgent } from "./userAgent";
import { ExtendedAxiosRequestConfig } from "./NetworkProviderConfig";
import { extendUserAgent } from "./userAgent";
import { NetworkProviderConfig } from "./networkProviderConfig";
import { MetricsPrefix } from "./constants";

// TODO: Find & remove duplicate code between "ProxyNetworkProvider" and "ApiNetworkProvider".
export class ApiNetworkProvider implements INetworkProvider {
private url: string;
private config: ExtendedAxiosRequestConfig;
private config: NetworkProviderConfig;
private backingProxyNetworkProvider;
private userAgentPrefix = 'sdk-network-providers/api'
private userAgentPrefix = `${MetricsPrefix}/api`

constructor(url: string, config?: ExtendedAxiosRequestConfig) {
constructor(url: string, config?: NetworkProviderConfig) {
this.url = url;
this.config = { ...defaultAxiosConfig, ...config };
this.backingProxyNetworkProvider = new ProxyNetworkProvider(url, config);
setUserAgent(this.userAgentPrefix, this.config);
const proxyConfig = JSON.parse(JSON.stringify(this.config));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about structuredClone(), does it help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in order to use this I need to upgrade the types/node to a version higher then 17, maybe ww will do that with a major release

this.backingProxyNetworkProvider = new ProxyNetworkProvider(url, proxyConfig);
extendUserAgent(this.userAgentPrefix, this.config);
}

async getNetworkConfig(): Promise<NetworkConfig> {
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ import { Address } from "./primitives";

export const MaxUint64AsBigNumber = new BigNumber("18446744073709551615");
export const EsdtContractAddress = new Address("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u");
export const MetricsPrefix = "multiversx-sdk"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baseUserAgent (or something similar) instead of metricsPrefix?

export const UnknownClientName = "unknown"
5 changes: 5 additions & 0 deletions src/networkProviderConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AxiosRequestConfig } from 'axios';

export interface NetworkProviderConfig extends AxiosRequestConfig {
clientName?: string;
}
40 changes: 37 additions & 3 deletions src/providers.dev.net.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert, expect } from "chai";
import { ApiNetworkProvider } from "./apiNetworkProvider";
import { INetworkProvider, ITransactionNext } from "./interface";
import { Address } from "./primitives";
Expand All @@ -7,15 +7,16 @@ import { MockQuery } from "./testscommon/dummyQuery";
import { NonFungibleTokenOfAccountOnNetwork } from "./tokens";
import { TransactionEventData } from "./transactionEvents";
import { TransactionOnNetwork } from "./transactions";
import { AxiosHeaders } from "axios";

describe("test network providers on devnet: Proxy and API", function () {
let alice = new Address("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
let carol = new Address("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8");
let dan = new Address("erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7");
const MAX_NUMBER_OF_ITEMS_BY_DEFAULT = 20;

let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.multiversx.com", { timeout: 10000 });
let proxyProvider: INetworkProvider = new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { timeout: 10000 });
let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.multiversx.com", { timeout: 10000, clientName: 'test' });
let proxyProvider: INetworkProvider = new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { timeout: 10000, clientName: 'test' });

it("should have same response for getNetworkConfig()", async function () {
let apiResponse = await apiProvider.getNetworkConfig();
Expand All @@ -24,6 +25,39 @@ describe("test network providers on devnet: Proxy and API", function () {
assert.deepEqual(apiResponse, proxyResponse);
});

it("should add userAgent unknown for clientName when no clientName passed", async function () {
const expectedApiUserAgent = "multiversx-sdk/api/unknown"
const expectedProxyUserAgent = "multiversx-sdk/proxy/unknown"

let localApiProvider: any = new ApiNetworkProvider("https://devnet-api.multiversx.com", { timeout: 10000 });
let localProxyProvider: any = new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { timeout: 10000 });

assert.equal(localApiProvider.config.headers.getUserAgent(), expectedApiUserAgent);
assert.equal(localProxyProvider.config.headers.getUserAgent(), expectedProxyUserAgent);
});

it("should set userAgent with specified clientName ", async function () {
const expectedApiUserAgent = "multiversx-sdk/api/test"
const expectedProxyUserAgent = "multiversx-sdk/proxy/test"

let localApiProvider: any = new ApiNetworkProvider("https://devnet-api.multiversx.com", { timeout: 10000, clientName: 'test' });
let localProxyProvider: any = new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { timeout: 10000, clientName: 'test' });

assert.equal(localApiProvider.config.headers.getUserAgent(), expectedApiUserAgent);
assert.equal(localProxyProvider.config.headers.getUserAgent(), expectedProxyUserAgent);
});

it("should keep the set userAgent and add the sdk to it", async function () {
const expectedApiUserAgent = "Client-info multiversx-sdk/api/test"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

const expectedProxyUserAgent = "Client-info multiversx-sdk/proxy/test"

let localApiProvider: any = new ApiNetworkProvider("https://devnet-api.multiversx.com", { timeout: 10000, headers: new AxiosHeaders({ "User-Agent": "Client-info" }), clientName: 'test' });
let localProxyProvider: any = new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { timeout: 10000, headers: new AxiosHeaders({ "User-Agent": "Client-info" }), clientName: 'test' });

assert.equal(localApiProvider.config.headers.getUserAgent(), expectedApiUserAgent);
assert.equal(localProxyProvider.config.headers.getUserAgent(), expectedProxyUserAgent);
});

it("should have same response for getNetworkStatus()", async function () {
let apiResponse = await apiProvider.getNetworkStatus();
let proxyResponse = await proxyProvider.getNetworkStatus();
Expand Down
14 changes: 7 additions & 7 deletions src/proxyNetworkProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";
import { AccountOnNetwork, GuardianData } from "./accounts";
import { defaultAxiosConfig } from "./config";
import { EsdtContractAddress } from "./constants";
import { EsdtContractAddress, MetricsPrefix } from "./constants";
import { ContractQueryRequest } from "./contractQueryRequest";
import { ContractQueryResponse } from "./contractQueryResponse";
import { ErrContractQuery, ErrNetworkProvider } from "./errors";
Expand All @@ -14,19 +14,19 @@ import { DefinitionOfFungibleTokenOnNetwork, DefinitionOfTokenCollectionOnNetwor
import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens";
import { TransactionOnNetwork, prepareTransactionForBroadcasting } from "./transactions";
import { TransactionStatus } from "./transactionStatus";
import { setUserAgent } from "./userAgent";
import { ExtendedAxiosRequestConfig } from "./NetworkProviderConfig";
import { extendUserAgent } from "./userAgent";
import { NetworkProviderConfig } from "./networkProviderConfig";

// TODO: Find & remove duplicate code between "ProxyNetworkProvider" and "ApiNetworkProvider".
export class ProxyNetworkProvider implements INetworkProvider {
private url: string;
private config: ExtendedAxiosRequestConfig;
private userAgentPrefix = 'sdk-network-providers/proxy'
private config: NetworkProviderConfig;
private userAgentPrefix = `${MetricsPrefix}/proxy`

constructor(url: string, config?: ExtendedAxiosRequestConfig) {
constructor(url: string, config?: NetworkProviderConfig) {
this.url = url;
this.config = { ...defaultAxiosConfig, ...config };
setUserAgent(this.userAgentPrefix, this.config);
extendUserAgent(this.userAgentPrefix, this.config);
}

async getNetworkConfig(): Promise<NetworkConfig> {
Expand Down
17 changes: 7 additions & 10 deletions src/userAgent.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { AxiosHeaders } from "axios";
import { ExtendedAxiosRequestConfig } from "./NetworkProviderConfig";
import { NetworkProviderConfig } from "./networkProviderConfig";
import { UnknownClientName } from "./constants";

export function setUserAgent(userAgentPrefix: string, config: ExtendedAxiosRequestConfig | undefined) {
if (!config) {
config = { headers: new AxiosHeaders({}) }
}
export function extendUserAgent(userAgentPrefix: string, config: NetworkProviderConfig) {
if (!config.headers) {
config.headers = new AxiosHeaders({})
};

const headers = AxiosHeaders.from(config.headers as AxiosHeaders).normalize(true);
if (!config.clientName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check can be above const headers = ... (so that we have the checks nicely grouped above).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

console.log("Can you please provide the client name of the aplication that uses the sdk?")
console.log("Can you please provide the client name of the application that uses the SDK? It will be used for metrics.")
}
const resolvedClientName = config.clientName || 'unknown';
const headers = AxiosHeaders.from(config.headers as AxiosHeaders).normalize(true);
const resolvedClientName = config.clientName || UnknownClientName;

const currentUserAgent = headers.hasUserAgent() ? headers.getUserAgent() : '';
const newUserAgent = `${currentUserAgent} ${userAgentPrefix}${resolvedClientName}`.trim();
const newUserAgent = currentUserAgent ? `${currentUserAgent} ${userAgentPrefix}/${resolvedClientName}` : `${userAgentPrefix}/${resolvedClientName}`;

headers.setUserAgent(newUserAgent, true);
}
Loading