Skip to content

Commit

Permalink
feat(middleware): generate autorest wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
davidyuk committed Jun 5, 2023
1 parent 14099b1 commit bd08a08
Show file tree
Hide file tree
Showing 7 changed files with 681 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ site
/src/apis/
/src/tx/builder/schema.generated.ts
/tooling/autorest/compiler-swagger.yaml
/tooling/autorest/middleware-openapi.yaml
/test/environment/ledger/browser
/types-legacy
/bin
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@
"build:assets": "node tooling/fetch-aesophia-cli.mjs",
"build:types": "tsc && node tooling/downlevel/run.mjs",
"build:es": "babel src --config-file ./babel.esm.config.js --out-dir es --extensions .js,.ts --out-file-extension .mjs --source-maps true",
"build:api": "node tooling/autorest/compiler-prepare.mjs && autorest tooling/autorest/compiler.yaml && autorest tooling/autorest/node.yaml",
"build:api:node": "autorest tooling/autorest/node.yaml",
"build:api:compiler": "node tooling/autorest/compiler-prepare.mjs && autorest tooling/autorest/compiler.yaml",
"build:api:middleware": "node tooling/autorest/middleware-prepare.mjs && autorest tooling/autorest/middleware.yaml",
"build:api": "npm run build:api:node && npm run build:api:compiler && npm run build:api:middleware",
"build:generate": "ts-node --transpileOnly tooling/generate-schema.ts",
"build": "npm run build:api && npm run build:generate && webpack && npm run build:types && npm run build:es && npm run build:assets",
"docs:examples": "node tooling/docs/examples-to-md.js examples/node/*.mjs",
Expand Down
141 changes: 141 additions & 0 deletions src/Middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// eslint-disable-next-line max-classes-per-file
import BigNumber from 'bignumber.js';
import { OperationArguments, OperationSpec } from '@azure/core-client';
import {
genRequestQueuesPolicy, genCombineGetRequestsPolicy, genErrorFormatterPolicy,
genVersionCheckPolicy, genRetryOnFailurePolicy,
} from './utils/autorest';
import { Middleware as MiddlewareApi, MiddlewareOptionalParams, ErrorResponse } from './apis/middleware';
import { mapObject } from './utils/other';
import { Encoded } from './utils/encoder';
import { ConsensusProtocolVersion } from './tx/builder/constants';

const bigIntPropertyNames = [] as const;

const numberPropertyNames = [] as const;

class MiddlewareTransformed extends MiddlewareApi {
override async sendOperationRequest(
operationArguments: OperationArguments,
operationSpec: OperationSpec,
): Promise<any> {
const args = mapObject(
operationArguments,
([key, value]) => [key, this.#encodeArg(value)],
) as OperationArguments;
return this.#decodeRes(await super.sendOperationRequest(args, operationSpec));
}

#mapData(data: any, transform: {
bigInt: (v: any) => any;
number: (v: any) => any;
}): unknown {
if (Array.isArray(data)) return data.map((d) => this.#mapData(d, transform));
if (data != null && typeof data === 'object') {
return mapObject(data, ([key, value]) => {
if (value == null) return [key, value];
if (bigIntPropertyNames.some((k) => k === key)) return [key, transform.bigInt(value)];
if (numberPropertyNames.some((k) => k === key)) return [key, transform.number(value)];
return [key, this.#mapData(value, transform)];
});
}
return data;
}

#encodeArg(data: any): any {
return this.#mapData(data, {
bigInt: (value) => {
if (value instanceof BigNumber) return value.toFixed();
return value.toString();
},
number: (value) => value.toString(),
});
}

#decodeRes(data: any): any {
return this.#mapData(data, {
bigInt: (value) => BigInt(value),
number: (value) => +value,
});
}
}

type BigIntPropertyNames = typeof bigIntPropertyNames[number];
type NumberPropertyNames = typeof numberPropertyNames[number];
type PreserveOptional<NewType, OrigType> =
OrigType extends undefined ? NewType | undefined : NewType;
export type TransformMiddlewareType<Type> =
Type extends (...args: infer Args) => infer Ret
? (...args: TransformMiddlewareType<Args>) => TransformMiddlewareType<Ret>
: Type extends [infer Item, ...infer Rest]
? [TransformMiddlewareType<Item>, ...TransformMiddlewareType<Rest>]
: Type extends Array<infer Item>
? Array<TransformMiddlewareType<Item>>
: Type extends Promise<infer T>
? Promise<TransformMiddlewareType<T>>
: Type extends { [P in any]: any }
? {
[Property in keyof Type]:
Property extends BigIntPropertyNames
? PreserveOptional<bigint, Type[Property]>
: Property extends NumberPropertyNames
? PreserveOptional<number, Type[Property]>
: Property extends 'txHash'
? PreserveOptional<Encoded.TxHash, Type[Property]>
: Property extends 'bytecode'
? PreserveOptional<Encoded.ContractBytearray, Type[Property]>
: TransformMiddlewareType<Type[Property]>
}
: Type;
type MiddlewareTransformedApi = new (...args: ConstructorParameters<typeof MiddlewareApi>) => {
[Name in keyof InstanceType<typeof MiddlewareApi>]:
Name extends 'pipeline' | 'sendRequest' | 'sendOperationRequest'
? MiddlewareApi[Name] : TransformMiddlewareType<MiddlewareApi[Name]>
};

export interface MiddlewareInfo {
url: string;
nodeNetworkId: string;
version: string;
consensusProtocolVersion: ConsensusProtocolVersion;
}

export default class Middleware
extends (MiddlewareTransformed as unknown as MiddlewareTransformedApi) {
/**
* @param url - Url for middleware API
* @param options - Options
* @param options.ignoreVersion - Don't check node version
* @param options.retryCount - Amount of extra requests to do in case of failure
* @param options.retryOverallDelay - Time in ms to wait between all retries
*/
constructor(
url: string,
{
ignoreVersion = false, retryCount = 3, retryOverallDelay = 800, ...options
}: MiddlewareOptionalParams & {
ignoreVersion?: boolean;
retryCount?: number;
retryOverallDelay?: number;
} = {},
) {
// eslint-disable-next-line constructor-super
super(url, {
allowInsecureConnection: true,
additionalPolicies: [
genRequestQueuesPolicy(),
genCombineGetRequestsPolicy(),
genRetryOnFailurePolicy(retryCount, retryOverallDelay),
genErrorFormatterPolicy((body: ErrorResponse) => ` ${body.error}`),
],
...options,
});
if (!ignoreVersion) {
const statusPromise = this.getStatus();
const versionPromise = statusPromise.then(({ mdwVersion }) => mdwVersion, (error) => error);
this.pipeline.addPolicy(
genVersionCheckPolicy('middleware', '/v2/status', versionPromise, '1.47.0', '2.0.0'),
);
}
}
}
1 change: 1 addition & 0 deletions src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export {
MiddlewareSubscriberError as _MiddlewareSubscriberError,
MiddlewareSubscriberDisconnected as _MiddlewareSubscriberDisconnected,
} from './MiddlewareSubscriber';
export { default as _Middleware } from './Middleware';

export { default as connectionProxy } from './aepp-wallet-communication/connection-proxy';
export * from './aepp-wallet-communication/schema';
Expand Down
142 changes: 142 additions & 0 deletions test/integration/Middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { describe, it } from 'mocha';
import { expect } from 'chai';
import '../index';
import { _Middleware } from '../../src';

describe('MiddlewareSubscriber', () => {
// TODO: remove after solving https://github.com/aeternity/ae_mdw/issues/1336
const middleware = new _Middleware('https://testnet.aeternity.io/mdw/');

it('gets status', async () => {
const res = await middleware.getStatus();
const expectedRes: typeof res = {
mdwAsyncTasks: {
longTasks: 0,
producerBuffer: 0,
totalPending: res.mdwAsyncTasks.totalPending,
},
mdwGensPerMinute: res.mdwGensPerMinute,
mdwHeight: res.mdwHeight,
mdwLastMigration: 20230519120000,
mdwRevision: '2d3ae3d',
mdwSynced: true,
mdwSyncing: true,
mdwTxIndex: res.mdwTxIndex,
mdwVersion: '1.49.0',
nodeHeight: res.nodeHeight,
nodeProgress: 100,
nodeRevision: 'a42c1b1e84dabdad350005213a2a9334113a6832',
nodeSyncing: false,
nodeVersion: '6.8.1',
};
expect(res).to.be.eql(expectedRes);
});

it('gets account activities', async () => {
const res = await middleware.getAccountActivities('ak_DtJrupsqqByQag76NrxAAimmkYJMk7on7z3xktdZpdkmxKQak');
const expectedRes: typeof res = {
data: [{
blockHash: 'mh_LAo6Cg6d8LGDpxJ3se2aGJZbCubDZyC6GonHK58MKiW4a4LWb',
height: 779178,
payload: {
block_hash: 'mh_LAo6Cg6d8LGDpxJ3se2aGJZbCubDZyC6GonHK58MKiW4a4LWb',
block_height: 779178,
hash: 'th_YHj5aB6JzHciY5f6jqgtso8u2iY9p6MT9RV96FwXQNS1MpSML',
micro_index: 18,
micro_time: 1684995426848,
tx: {
abi_version: 3,
auth_data: 'cb_KxFs8lcLOwKfAKC2+ARNbHQ/i2cN0jPpYmw2o6/mve+skTCovW+5OcIa4p8BAQBRsNynuucudvyocC+ZWBzpUbjiGWctldfdDOR9csCIDeXwMpQ3yyJlC91g8AuFAB2uhHMjHNz5fGqWhxbju/QDsvQe5g==',
fee: 78540000000000,
ga_id: 'ak_DtJrupsqqByQag76NrxAAimmkYJMk7on7z3xktdZpdkmxKQak',
gas: 50000,
gas_price: 1000000000,
gas_used: 8444,
return_type: 'ok',
tx: {
signatures: [],
tx: {
amount: 112,
fee: 16660000000000,
nonce: 0,
payload: 'ba_Xfbg4g==',
recipient_id: 'ak_VTNurrhRhJBVK19JfUnEv4qgdZJT7DUghxz3ACEFBNKgw8R3D',
sender_id: 'ak_DtJrupsqqByQag76NrxAAimmkYJMk7on7z3xktdZpdkmxKQak',
type: 'SpendTx',
version: 1,
},
},
type: 'GAMetaTx',
version: 2,
},
},
type: 'GAMetaTxEvent',
}, {
blockHash: 'mh_2R1PVwTNP3Jha7oRby9Me3SRBP4R9he6RMH6eCCJGyVBHAzy5f',
height: 779178,
payload: {
block_hash: 'mh_2R1PVwTNP3Jha7oRby9Me3SRBP4R9he6RMH6eCCJGyVBHAzy5f',
block_height: 779178,
hash: 'th_AScnu6AAGHvfewMELKvmc6KH9jaMfzopGpcFFo1HAoS7ne1fD',
micro_index: 11,
micro_time: 1684995366595,
signatures: [
'sg_SBD5RkjMkfDM8KGJTgTTgmfXaimac2cGy4x39efXZjrPdr5zVJAmqu8dLiMNoVX1FHTESERRvejAnDVDjxLq6x2gb1SzK',
],
tx: {
abi_version: 3,
args: [
{
type: 'address',
value: 'ak_2PahBfbkrmBFfbb4FGEVErP7mChFGPYFb9eGMARU514u5V3K52',
},
],
auth_fun: '0x6cf2570b0a1599b708291e50aa3daf13d0c7f2484bc337ddad2413a37fd4a009',
auth_fun_name: 'authorize',
call_data: 'cb_KxFE1kQfG58AoLb4BE1sdD+LZw3SM+libDajr+a976yRMKi9b7k5whrijU9L2Q==',
code: 'cb_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',
contract_id: 'ct_2TRQYcmvhRrbGrNKXy8uBZX2MQbgHrZmVCEHZBAJkhs24DdeVP',
fee: 107820000000000,
gas: 597,
gas_price: 1000000000,
gas_used: 478,
nonce: 1,
owner_id: 'ak_DtJrupsqqByQag76NrxAAimmkYJMk7on7z3xktdZpdkmxKQak',
return_type: 'ok',
type: 'GAAttachTx',
version: 1,
vm_version: 7,
},
},
type: 'GAAttachTxEvent',
}, {
blockHash: 'mh_25snWYwTkU1xjPCcH592XVNzL894qSpF4yqnt8tABKGEVm6nSz',
height: 779178,
payload: {
block_hash: 'mh_25snWYwTkU1xjPCcH592XVNzL894qSpF4yqnt8tABKGEVm6nSz',
block_height: 779178,
hash: 'th_242zV1qXwag6iBH3Pd8zn3DQC37h4uP38CoFjkaTgT19AVKMHo',
micro_index: 7,
micro_time: 1684995336526,
signatures: [
'sg_E1Ezn9EMsxTZz7a93Zc5kjXkS1t3WqFbr3X8DqqjdXXfXD9Xy6DZQpjB2rHfcoZ3ySZ1FHh9bzMjfQfdA9mRJJ4yeYdfN',
],
tx: {
amount: 5000000000000000000,
fee: 17040000000000,
nonce: 37016,
payload: 'ba_RmF1Y2V0IFR4tYtyuw==',
recipient_id: 'ak_DtJrupsqqByQag76NrxAAimmkYJMk7on7z3xktdZpdkmxKQak',
sender_id: 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688',
type: 'SpendTx',
version: 1,
},
},
type: 'SpendTxEvent',
}],
next: null,
prev: null,
};
expect(res).to.be.eql(expectedRes);
});
});
16 changes: 16 additions & 0 deletions tooling/autorest/middleware-prepare.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { spawnSync } from 'child_process';

const run = (getOutput, command, ...args) => {
const { error, stdout } = spawnSync(
command,
args,
{ shell: true, ...!getOutput && { stdio: 'inherit' } },
);
if (error) throw error;
return stdout?.toString().trim();
};

const id = run(true, 'docker', 'create', 'aeternity/ae_mdw:1.47.0');
const openapi = '/home/aeternity/node/lib/ae_mdw-1.47.0/priv/static/swagger/swagger_v2.yaml';
run(false, 'docker', 'cp', `${id}:${openapi}`, './tooling/autorest/middleware-openapi.yaml');
run(false, 'docker', 'rm', '-v', id);
Loading

0 comments on commit bd08a08

Please sign in to comment.