feat: fetch options plugin (#255)

This commit is contained in:
Asia
2022-12-29 11:07:51 +01:00
committed by GitHub
parent 92306e29d6
commit 277e692f95
6 changed files with 161 additions and 18 deletions

View File

@@ -588,6 +588,25 @@ An example - LMDB - implementation is available [here](https://github.com/warp-c
A dedicated CLI which eases the process of using main methods of the Warp SDK library has been created. Please refer to [`warp-contracts-cli` npm page](https://www.npmjs.com/package/warp-contracts-cli) for more details. A dedicated CLI which eases the process of using main methods of the Warp SDK library has been created. Please refer to [`warp-contracts-cli` npm page](https://www.npmjs.com/package/warp-contracts-cli) for more details.
### Customize `fetch` options
It is possible to customize `fetch` options using dedicated plugin. In order to change `fetch` options one needs to create an implementation of [WarpPlugin](https://github.com/warp-contracts/warp/blob/main/src/core/WarpPlugin.ts) interface. `process` method will receive following properties:
```ts
interface FetchRequest {
input: RequestInfo | URL;
init: Partial<RequestInit>;
}
```
...and it should return updated `fetch` options (by returning updated `init` object). An example of such implementation in [src/tools/fetch-options-plugin.ts](https://github.com/warp-contracts/warp/tree/main/tools/fetch-options-plugin.ts).
In order to use this plugin, it needs to be attached while creating `Warp` instance, e.g.:
```ts
const warp = WarpFactory.forMainnet().use(new FetchOptionsPlugin());
```
### Migrations ### Migrations
#### old factories to WarpFactory #### old factories to WarpFactory

View File

@@ -36,6 +36,7 @@ import { generateMockVrf } from '../utils/vrf';
import { Signature, SignatureType } from './Signature'; import { Signature, SignatureType } from './Signature';
import { ContractDefinition } from '../core/ContractDefinition'; import { ContractDefinition } from '../core/ContractDefinition';
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
/** /**
* An implementation of {@link Contract} that is backwards compatible with current style * An implementation of {@link Contract} that is backwards compatible with current style
@@ -59,6 +60,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
private _sorter: InteractionsSorter; private _sorter: InteractionsSorter;
private _rootSortKey: string; private _rootSortKey: string;
private signature: Signature; private signature: Signature;
private warpFetchWrapper: WarpFetchWrapper;
constructor( constructor(
private readonly _contractTxId: string, private readonly _contractTxId: string,
@@ -113,6 +115,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
} }
this.getCallStack = this.getCallStack.bind(this); this.getCallStack = this.getCallStack.bind(this);
this.warpFetchWrapper = new WarpFetchWrapper(this.warp);
} }
async readState( async readState(
@@ -301,7 +304,8 @@ export class HandlerBasedContract<State> implements Contract<State> {
options.vrf options.vrf
); );
const response = await fetch(`${this._evaluationOptions.sequencerUrl}gateway/sequencer/register`, { const response = await this.warpFetchWrapper
.fetch(`${this._evaluationOptions.sequencerUrl}gateway/sequencer/register`, {
method: 'POST', method: 'POST',
body: JSON.stringify(interactionTx), body: JSON.stringify(interactionTx),
headers: { headers: {
@@ -746,7 +750,8 @@ export class HandlerBasedContract<State> implements Contract<State> {
async syncState(externalUrl: string, params?: any): Promise<Contract> { async syncState(externalUrl: string, params?: any): Promise<Contract> {
const { stateEvaluator } = this.warp; const { stateEvaluator } = this.warp;
const response = await fetch( const response = await this.warpFetchWrapper
.fetch(
`${externalUrl}?${new URLSearchParams({ `${externalUrl}?${new URLSearchParams({
id: this._contractTxId, id: this._contractTxId,
...params ...params

View File

@@ -2,11 +2,20 @@
import Arweave from 'arweave'; import Arweave from 'arweave';
import Transaction from 'arweave/node/lib/transaction'; import Transaction from 'arweave/node/lib/transaction';
import { Signature, SignatureType } from '../../../contract/Signature'; import { Signature, SignatureType } from '../../../contract/Signature';
import { WarpFetchWrapper } from '../../../core/WarpFetchWrapper';
import { SmartWeaveTags } from '../../../core/SmartWeaveTags'; import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
import { Warp } from '../../../core/Warp'; import { Warp } from '../../../core/Warp';
import { WARP_GW_URL } from '../../../core/WarpFactory'; import { WARP_GW_URL } from '../../../core/WarpFactory';
import { LoggerFactory } from '../../../logging/LoggerFactory'; import { LoggerFactory } from '../../../logging/LoggerFactory';
import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData, ArWallet, BundlrNodeType, BUNDLR_NODES } from '../CreateContract'; import {
CreateContract,
ContractData,
ContractDeploy,
FromSrcTxContractData,
ArWallet,
BundlrNodeType,
BUNDLR_NODES
} from '../CreateContract';
import { SourceData, SourceImpl } from './SourceImpl'; import { SourceData, SourceImpl } from './SourceImpl';
import { Buffer } from 'redstone-isomorphic'; import { Buffer } from 'redstone-isomorphic';
@@ -15,10 +24,12 @@ export class DefaultCreateContract implements CreateContract {
private readonly source: SourceImpl; private readonly source: SourceImpl;
private signature: Signature; private signature: Signature;
private readonly warpFetchWrapper: WarpFetchWrapper;
constructor(private readonly arweave: Arweave, private warp: Warp) { constructor(private readonly arweave: Arweave, private warp: Warp) {
this.deployFromSourceTx = this.deployFromSourceTx.bind(this); this.deployFromSourceTx = this.deployFromSourceTx.bind(this);
this.source = new SourceImpl(this.warp); this.source = new SourceImpl(this.warp);
this.warpFetchWrapper = new WarpFetchWrapper(this.warp);
} }
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> { async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
@@ -190,7 +201,7 @@ export class DefaultCreateContract implements CreateContract {
}; };
} }
const response = await fetch(`${WARP_GW_URL}/gateway/contracts/deploy`, { const response = await this.warpFetchWrapper.fetch(`${WARP_GW_URL}/gateway/contracts/deploy`, {
method: 'POST', method: 'POST',
body: JSON.stringify(body), body: JSON.stringify(body),
headers: { headers: {

View File

@@ -0,0 +1,37 @@
import { LoggerFactory } from '../logging/LoggerFactory';
import { Warp } from './Warp';
export interface FetchRequest {
input: RequestInfo | URL;
init: Partial<RequestInit>;
}
export class WarpFetchWrapper {
private readonly name = 'WarpFetchWrapper';
private readonly logger = LoggerFactory.INST.create(this.name);
constructor(private warp: Warp) {
this.warp = warp;
}
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
let fetchOptions: RequestInit;
if (this.warp.hasPlugin('fetch-options')) {
const fetchOptionsPlugin = this.warp.loadPlugin<FetchRequest, Partial<RequestInit>>('fetch-options');
try {
const updatedFetchOptions = fetchOptionsPlugin.process({ input, init: init || {} });
fetchOptions = { ...init, ...updatedFetchOptions };
} catch (e) {
if (e.message) {
this.logger.error(e.message);
}
throw new Error(`Unable to process fetch options: ${e.message}`);
}
} else {
fetchOptions = init;
}
return fetch(input, fetchOptions);
}
}

View File

@@ -4,7 +4,8 @@ export const knownWarpPlugins = [
'smartweave-extension-ethers', 'smartweave-extension-ethers',
'subscription', 'subscription',
'ivm-handler-api', 'ivm-handler-api',
'evaluation-progress' 'evaluation-progress',
'fetch-options'
] as const; ] as const;
export type WarpPluginType = typeof knownWarpPlugins[number]; export type WarpPluginType = typeof knownWarpPlugins[number];

View File

@@ -0,0 +1,70 @@
import { WarpPlugin, WarpPluginType } from '../src/core/WarpPlugin';
import { FetchRequest } from '../src/core/WarpFetchWrapper';
import { JWKInterface } from 'arweave/node/lib/wallet';
import fs from 'fs';
import path from 'path';
import { LoggerFactory } from '../src/logging/LoggerFactory';
import { defaultCacheOptions, WarpFactory } from '../src/core/WarpFactory';
class FetchOptionsPlugin implements WarpPlugin<FetchRequest, RequestInit> {
process(request: FetchRequest): Partial<RequestInit> {
const url = request.input;
let fetchOptions: Partial<RequestInit> = {};
if (url == `https://d1o5nlqr4okus2.cloudfront.net/gateway/sequencer/register`) {
fetchOptions = {
keepalive: true
};
}
return fetchOptions;
}
type(): WarpPluginType {
return 'fetch-options';
}
}
async function main() {
const wallet: JWKInterface = readJSON('./.secrets/jwk.json');
LoggerFactory.INST.logLevel('debug');
const logger = LoggerFactory.INST.create('FetchOptionsPlugin');
try {
const warp = WarpFactory.forMainnet({ ...defaultCacheOptions, inMemory: true }).use(new FetchOptionsPlugin());
const jsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.js'), 'utf8');
const initialState = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.json'), 'utf8');
const { contractTxId } = await warp.createContract.deploy({
wallet,
initState: initialState,
src: jsContractSrc
});
const contract = warp.contract(contractTxId).connect(wallet);
await contract.writeInteraction({
function: 'transfer',
target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M',
qty: 55555
});
const { cachedValue } = await contract.readState();
logger.info(`Cached value: ${cachedValue}`);
} catch (e) {
logger.error(e);
}
}
export function readJSON(path: string): JWKInterface {
const content = fs.readFileSync(path, 'utf-8');
try {
return JSON.parse(content);
} catch (e) {
throw new Error(`File "${path}" does not contain a valid JSON`);
}
}
main().catch((e) => console.error(e));