From 776bb7d706a06539a8794efae79a5e95e999a3ec Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Mon, 6 May 2024 15:36:44 +0300 Subject: [PATCH 01/42] feat: create pay before persist request flow --- .../src/api/request-network.ts | 23 ++++++++++ packages/request-client.js/src/api/request.ts | 15 +++++++ .../src/http-request-network.ts | 12 ++++- .../src/no-confirm-http-data-access.ts | 45 +++++++++++++++++++ .../src/transaction-manager.ts | 2 + 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 packages/request-client.js/src/no-confirm-http-data-access.ts diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index cf4721fb8..9a5a62c14 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -23,6 +23,7 @@ import * as Types from '../types'; import ContentDataExtension from './content-data-extension'; import Request from './request'; import localUtils from './utils'; +import { NoConfirmHttpDataAccess } from '../no-confirm-http-data-access'; /** * Entry point of the request-client.js library. Create requests, get requests, manipulate requests. @@ -34,6 +35,7 @@ export default class RequestNetwork { private requestLogic: RequestLogicTypes.IRequestLogic; private transaction: TransactionTypes.ITransactionManager; private advancedLogic: AdvancedLogicTypes.IAdvancedLogic; + private dataAccess: DataAccessTypes.IDataAccess; private contentData: ContentDataExtension; private currencyManager: ICurrencyManager; @@ -58,6 +60,7 @@ export default class RequestNetwork { paymentOptions?: Partial; }) { this.currencyManager = currencyManager || CurrencyManager.getDefault(); + this.dataAccess = dataAccess; this.advancedLogic = new AdvancedLogic(this.currencyManager); this.transaction = new TransactionManager(dataAccess, decryptionProvider); this.requestLogic = new RequestLogic(this.transaction, signatureProvider, this.advancedLogic); @@ -99,6 +102,8 @@ export default class RequestNetwork { requestLogicCreateResult, skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: requestLogicCreateResult.meta?.transactionManagerMeta.transactionData, }, ); @@ -110,6 +115,22 @@ export default class RequestNetwork { return request; } + public async persistRequest( + transactionData: DataAccessTypes.ITransaction, + channelId: string, + topics?: string[], + ): Promise { + if (this.dataAccess instanceof NoConfirmHttpDataAccess) { + throw new Error( + 'Cannot persist request when skipCreateConfirmation is used. Create a new instance of RequestNetwork without skipCreateConfirmation to persist the request.', + ); + } + const result: DataAccessTypes.IReturnPersistTransaction = + await this.dataAccess.persistTransaction(transactionData, channelId, topics); + + return result; + } + /** * Creates an encrypted request. * @@ -143,6 +164,8 @@ export default class RequestNetwork { requestLogicCreateResult, skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: requestLogicCreateResult.meta?.transactionManagerMeta.transactionData, }, ); diff --git a/packages/request-client.js/src/api/request.ts b/packages/request-client.js/src/api/request.ts index c40656814..0054969a3 100644 --- a/packages/request-client.js/src/api/request.ts +++ b/packages/request-client.js/src/api/request.ts @@ -5,6 +5,7 @@ import { } from '@requestnetwork/payment-detection'; import { CurrencyTypes, + DataAccessTypes, EncryptionTypes, IdentityTypes, PaymentTypes, @@ -79,6 +80,16 @@ export default class Request { */ private currencyManager: ICurrencyManager; + /** + * Transaction data of a in-memory request, necesary for persisting the request later on. + */ + private transactionData: DataAccessTypes.ITransaction | undefined; + + /** + * Topics of a in-memory request, necesary for persisting the request later on. + */ + private topics: string[] | undefined; + /** * Creates an instance of Request * @@ -99,6 +110,8 @@ export default class Request { requestLogicCreateResult?: RequestLogicTypes.IReturnCreateRequest; skipPaymentDetection?: boolean; disableEvents?: boolean; + transactionData?: DataAccessTypes.ITransaction; + topics?: string[]; }, ) { this.requestLogic = requestLogic; @@ -109,6 +122,8 @@ export default class Request { this.skipPaymentDetection = options?.skipPaymentDetection || false; this.disableEvents = options?.disableEvents || false; this.currencyManager = currencyManager; + this.topics = options?.topics; + this.transactionData = options?.transactionData; if (options?.requestLogicCreateResult && !this.disableEvents) { const originalEmitter = options.requestLogicCreateResult; diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index 6762f1343..4a60cac18 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -10,6 +10,7 @@ import RequestNetwork from './api/request-network'; import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; import { MockDataAccess } from '@requestnetwork/data-access'; import { MockStorage } from './mock-storage'; +import { NoConfirmHttpDataAccess } from './no-confirm-http-data-access'; /** * Exposes RequestNetwork module configured to use http-data-access. @@ -23,7 +24,8 @@ export default class HttpRequestNetwork extends RequestNetwork { * @param options.useMockStorage When true, will use a mock storage in memory. Meant to simplify local development and should never be used in production. * @param options.signatureProvider Module to handle the signature. If not given it will be impossible to create new transaction (it requires to sign). * @param options.currencies custom currency list - * @param options.currencyManager custom currency manager (will override `currencies`) + * @param options.currencyManager custom currency manager (will override `currencies`)http + * @param options.skipCreateConfirmation allows to create a transaction without persisting it. */ constructor( { @@ -35,6 +37,7 @@ export default class HttpRequestNetwork extends RequestNetwork { currencies, currencyManager, paymentOptions, + skipCreateConfirmation, }: { decryptionProvider?: DecryptionProviderTypes.IDecryptionProvider; httpConfig?: Partial; @@ -44,13 +47,20 @@ export default class HttpRequestNetwork extends RequestNetwork { currencies?: CurrencyInput[]; currencyManager?: ICurrencyManager; paymentOptions?: Partial; + skipCreateConfirmation?: boolean; } = { httpConfig: {}, useMockStorage: false, + skipCreateConfirmation: false, }, ) { const dataAccess: DataAccessTypes.IDataAccess = useMockStorage ? new MockDataAccess(new MockStorage()) + : skipCreateConfirmation + ? new NoConfirmHttpDataAccess({ + httpConfig, + nodeConnectionConfig, + }) : new HttpDataAccess({ httpConfig, nodeConnectionConfig }); if (!currencyManager) { diff --git a/packages/request-client.js/src/no-confirm-http-data-access.ts b/packages/request-client.js/src/no-confirm-http-data-access.ts new file mode 100644 index 000000000..3c0e2e77d --- /dev/null +++ b/packages/request-client.js/src/no-confirm-http-data-access.ts @@ -0,0 +1,45 @@ +import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; +import { ClientTypes, DataAccessTypes, StorageTypes } from '@requestnetwork/types'; +import { EventEmitter } from 'events'; + +export class NoConfirmHttpDataAccess extends HttpDataAccess { + constructor( + { + httpConfig, + nodeConnectionConfig, + }: { + httpConfig?: Partial; + nodeConnectionConfig?: Partial; + } = { + httpConfig: {}, + nodeConnectionConfig: {}, + }, + ) { + super({ httpConfig, nodeConnectionConfig }); + } + + async persistTransaction( + transactionData: DataAccessTypes.ITransaction, + channelId: string, + topics?: string[], + ): Promise { + const data: DataAccessTypes.IReturnPersistTransactionRaw = { + meta: { + topics: topics || [], + transactionStorageLocation: '', + storageMeta: { + state: StorageTypes.ContentState.PENDING, + timestamp: Date.now() / 1000, + }, + }, + result: {}, + }; + + const result: DataAccessTypes.IReturnPersistTransaction = Object.assign( + new EventEmitter() as DataAccessTypes.PersistTransactionEmitter, + data, + ); + + return result; + } +} diff --git a/packages/transaction-manager/src/transaction-manager.ts b/packages/transaction-manager/src/transaction-manager.ts index d693c80aa..81cded955 100644 --- a/packages/transaction-manager/src/transaction-manager.ts +++ b/packages/transaction-manager/src/transaction-manager.ts @@ -109,6 +109,8 @@ export default class TransactionManager implements TransactionTypes.ITransaction meta: { dataAccessMeta: persistResult.meta, encryptionMethod: channelEncryptionMethod, + transactionData: transaction, + topics: topics.concat([hash]), }, result: {}, }); From fc5e671827554de333f1ed376003d8ba578a48b1 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 5 Jun 2024 15:04:23 +0300 Subject: [PATCH 02/42] feat: add preparePaymentRequest --- .../src/api/request-network.ts | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index f17d7c46b..550bc86be 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -4,10 +4,12 @@ import { RequestLogic } from '@requestnetwork/request-logic'; import { TransactionManager } from '@requestnetwork/transaction-manager'; import { AdvancedLogicTypes, + ClientTypes, CurrencyTypes, DataAccessTypes, DecryptionProviderTypes, EncryptionTypes, + ExtensionTypes, IdentityTypes, PaymentTypes, RequestLogicTypes, @@ -69,6 +71,81 @@ export default class RequestNetwork { ); } + /** + * Prepares a payment request structure from transaction data. + * + * This method is used to create a request structure similar to a persisted request, + * allowing users to pay before the request is persisted. This is useful in scenarios + * where a request is created, paid, and then persisted, as opposed to the normal flow + * of creating, persisting, and then paying the request. + * + * @param transactionData The transaction data containing the request information + * @param requestId The ID of the request + * @returns The prepared payment request structure or undefined if transaction data is missing + */ + private preparePaymentRequest( + transactionData: DataAccessTypes.ITransaction, + requestId: string, + ): ClientTypes.IRequestData | undefined { + if (!transactionData.data) return undefined; + + const requestData = JSON.parse(transactionData.data).data; + const originalExtensionsData = requestData.parameters.extensionsData; + const newExtensions: RequestLogicTypes.IExtensionStates = {}; + + for (const extension of originalExtensionsData) { + if (extension.id !== ExtensionTypes.OTHER_ID.CONTENT_DATA) { + newExtensions[extension.id] = { + events: [ + { + name: extension.action, + parameters: { + paymentAddress: extension.parameters.paymentAddress, + salt: extension.parameters.salt, + }, + timestamp: requestData.parameters.timestamp, + }, + ], + id: extension.id, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + salt: extension.parameters.salt, + receivedPaymentAmount: '0', + receivedRefundAmount: '0', + sentPaymentAmount: '0', + sentRefundAmount: '0', + paymentAddress: extension.parameters.paymentAddress, + }, + version: extension.version, + }; + } + } + + return { + requestId: requestId, + currency: requestData.parameters.currency.type, + meta: null, + balance: null, + expectedAmount: requestData.parameters.expectedAmount, + contentData: requestData.parameters.extensionsData.find( + (ext: ExtensionTypes.IAction) => ext.id === ExtensionTypes.OTHER_ID.CONTENT_DATA, + )?.parameters.content, + currencyInfo: { + type: requestData.parameters.currency.type, + network: requestData.parameters.currency.network, + value: requestData.parameters.currency.value || '', + }, + pending: null, + extensions: newExtensions, + extensionsData: requestData.parameters.extensionsData, + timestamp: requestData.parameters.timestamp, + version: requestData.parameters.version, + creator: requestData.parameters.creator, + state: requestData.parameters.state, + events: requestData.parameters.events, + }; + } + /** * Creates a request. * From 4c41cdf855b3cfd25a2c82ace33e355f583036b9 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 5 Jun 2024 15:36:33 +0300 Subject: [PATCH 03/42] feat: add requestPayment to `createRequest` return --- .../src/api/request-network.ts | 26 +++++++++---------- packages/request-client.js/src/api/request.ts | 12 +++++++-- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 550bc86be..e070ad547 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -165,21 +165,19 @@ export default class RequestNetwork { topics, ); + const transactionData = requestLogicCreateResult.meta?.transactionManagerMeta.transactionData; + const requestId = requestLogicCreateResult.result.requestId; // create the request object - const request = new Request( - requestLogicCreateResult.result.requestId, - this.requestLogic, - this.currencyManager, - { - contentDataExtension: this.contentData, - paymentNetwork, - requestLogicCreateResult, - skipPaymentDetection: parameters.disablePaymentDetection, - disableEvents: parameters.disableEvents, - topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, - transactionData: requestLogicCreateResult.meta?.transactionManagerMeta.transactionData, - }, - ); + const request = new Request(requestId, this.requestLogic, this.currencyManager, { + contentDataExtension: this.contentData, + paymentNetwork, + requestLogicCreateResult, + skipPaymentDetection: parameters.disablePaymentDetection, + disableEvents: parameters.disableEvents, + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: transactionData, + paymentRequest: this.preparePaymentRequest(transactionData, requestId), + }); if (!options?.skipRefresh) { // refresh the local request data diff --git a/packages/request-client.js/src/api/request.ts b/packages/request-client.js/src/api/request.ts index ccc823d00..e45da2d5e 100644 --- a/packages/request-client.js/src/api/request.ts +++ b/packages/request-client.js/src/api/request.ts @@ -4,6 +4,7 @@ import { EscrowERC20InfoRetriever, } from '@requestnetwork/payment-detection'; import { + ClientTypes, CurrencyTypes, DataAccessTypes, EncryptionTypes, @@ -82,12 +83,17 @@ export default class Request { /** * Transaction data of a in-memory request, necesary for persisting the request later on. */ - private transactionData: DataAccessTypes.ITransaction | undefined; + public readonly transactionData: DataAccessTypes.ITransaction | undefined; /** * Topics of a in-memory request, necesary for persisting the request later on. */ - private topics: string[] | undefined; + public readonly topics: string[] | undefined; + + /** + * Structured data for an in-memory request, primarily used for processing payments before the request is persisted. + */ + public readonly paymentRequest: ClientTypes.IRequestData | undefined; /** * Creates an instance of Request @@ -111,6 +117,7 @@ export default class Request { disableEvents?: boolean; transactionData?: DataAccessTypes.ITransaction; topics?: string[]; + paymentRequest?: ClientTypes.IRequestData | undefined; }, ) { this.requestLogic = requestLogic; @@ -123,6 +130,7 @@ export default class Request { this.currencyManager = currencyManager; this.topics = options?.topics; this.transactionData = options?.transactionData; + this.paymentRequest = options?.paymentRequest; if (options?.requestLogicCreateResult && !this.disableEvents) { const originalEmitter = options.requestLogicCreateResult; From e9d51c7d85a6be3c05b768a5ea3d7614c3e579a5 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Thu, 6 Jun 2024 12:16:03 +0300 Subject: [PATCH 04/42] docs: document `persistRequest` method --- packages/request-client.js/src/api/request-network.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index e070ad547..6aad8ab0d 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -187,6 +187,15 @@ export default class RequestNetwork { return request; } + /** + * Persists an in-memory request to the data-access layer. + * + * @param transactionData The transaction data containing the request information + * @param channelId The ID of the channel + * @param topics Optional topics for indexing the request + * @returns The result of the persist transaction operation + * @throws Error if the data access instance does not support persistence + */ public async persistRequest( transactionData: DataAccessTypes.ITransaction, channelId: string, From c2ec1c2bf7807fe0d2b12a3643a36330a2182785 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Thu, 6 Jun 2024 13:41:47 +0300 Subject: [PATCH 05/42] test: add creation and persistance tests for in-memory requests --- .../src/api/request-network.ts | 33 +++--- packages/request-client.js/test/index.test.ts | 102 ++++++++++++++++++ 2 files changed, 118 insertions(+), 17 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 6aad8ab0d..b4e634bb8 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -191,14 +191,14 @@ export default class RequestNetwork { * Persists an in-memory request to the data-access layer. * * @param transactionData The transaction data containing the request information - * @param channelId The ID of the channel + * @param requestId The ID of the request * @param topics Optional topics for indexing the request * @returns The result of the persist transaction operation * @throws Error if the data access instance does not support persistence */ public async persistRequest( transactionData: DataAccessTypes.ITransaction, - channelId: string, + requestId: string, topics?: string[], ): Promise { if (this.dataAccess instanceof NoConfirmHttpDataAccess) { @@ -207,7 +207,7 @@ export default class RequestNetwork { ); } const result: DataAccessTypes.IReturnPersistTransaction = - await this.dataAccess.persistTransaction(transactionData, channelId, topics); + await this.dataAccess.persistTransaction(transactionData, requestId, topics); return result; } @@ -234,21 +234,20 @@ export default class RequestNetwork { topics, ); + const transactionData = requestLogicCreateResult.meta?.transactionManagerMeta.transactionData; + const requestId = requestLogicCreateResult.result.requestId; + // create the request object - const request = new Request( - requestLogicCreateResult.result.requestId, - this.requestLogic, - this.currencyManager, - { - contentDataExtension: this.contentData, - paymentNetwork, - requestLogicCreateResult, - skipPaymentDetection: parameters.disablePaymentDetection, - disableEvents: parameters.disableEvents, - topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, - transactionData: requestLogicCreateResult.meta?.transactionManagerMeta.transactionData, - }, - ); + const request = new Request(requestId, this.requestLogic, this.currencyManager, { + contentDataExtension: this.contentData, + paymentNetwork, + requestLogicCreateResult, + skipPaymentDetection: parameters.disablePaymentDetection, + disableEvents: parameters.disableEvents, + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: transactionData, + paymentRequest: this.preparePaymentRequest(transactionData, requestId), + }); if (!options?.skipRefresh) { // refresh the local request data diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index ba41b1488..e32e343fd 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1149,6 +1149,108 @@ describe('request-client.js', () => { }); }); + describe('handle in-memory request', () => { + let requestNetwork: RequestNetwork; + let spyPersistTransaction: jest.Mock; + let mockServer: SetupServer; + + beforeAll(() => { + spyPersistTransaction = jest.fn(); + + mockServer = setupServer( + http.post('*/persistTransaction', ({ request }) => { + if (!request.headers.get(config.requestClientVersionHeader)) { + throw new Error('Missing version header'); + } + return HttpResponse.json(spyPersistTransaction()); + }), + http.get('*/getTransactionsByChannelId', () => + HttpResponse.json({ + result: { transactions: [TestData.timestampedTransactionWithoutPaymentInfo] }, + }), + ), + http.post('*/ipfsAdd', () => HttpResponse.json({})), + http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), + ); + mockServer.listen({ onUnhandledRequest: 'bypass' }); + }); + + beforeEach(() => { + spyPersistTransaction.mockReturnValue({}); + }); + + afterAll(() => { + mockServer.close(); + }); + + const requestCreationParams = { + paymentNetwork: TestData.declarativePaymentNetworkNoPaymentInfo, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }; + + it('creates a request without persisting it.', async () => { + requestNetwork = new RequestNetwork({ + skipCreateConfirmation: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request).toBeDefined(); + expect(request.requestId).toBeDefined(); + expect(request.transactionData).toBeDefined(); + expect(request.topics).toBeDefined(); + expect(request.paymentRequest).toBeDefined(); + expect(spyPersistTransaction).not.toHaveBeenCalled(); + }); + + it('throws an error when trying to persist a request with skipCreateConfirmation as true', async () => { + requestNetwork = new RequestNetwork({ + skipCreateConfirmation: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request.transactionData).toBeDefined(); + expect(request.topics).toBeDefined(); + expect(request.requestId).toBeDefined(); + + await expect( + requestNetwork.persistRequest(request.transactionData!, request.requestId, request.topics), + ).rejects.toThrow( + 'Cannot persist request when skipCreateConfirmation is used. Create a new instance of RequestNetwork without skipCreateConfirmation to persist the request.', + ); + }); + + it('persists the in-memory request', async () => { + requestNetwork = new RequestNetwork({ + skipCreateConfirmation: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request.transactionData).toBeDefined(); + expect(request.topics).toBeDefined(); + expect(request.requestId).toBeDefined(); + + const newRequestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + }); + + const persistResult = await newRequestNetwork.persistRequest( + request.transactionData!, + request.requestId, + request.topics, + ); + + expect(persistResult).toBeDefined(); + expect(spyPersistTransaction).toHaveBeenCalledTimes(1); + }); + }); + describe('ETH requests', () => { beforeEach(() => { jest.clearAllMocks(); From 399217cde592ea4bd40c9dc8b2446fbc19397943 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Thu, 6 Jun 2024 13:59:45 +0300 Subject: [PATCH 06/42] test: update transactiom-manager tests to check only relavant parts of object --- .../transaction-manager/test/index.test.ts | 269 ++++++++++-------- 1 file changed, 151 insertions(+), 118 deletions(-) diff --git a/packages/transaction-manager/test/index.test.ts b/packages/transaction-manager/test/index.test.ts index 2e863f571..2c2e1be48 100644 --- a/packages/transaction-manager/test/index.test.ts +++ b/packages/transaction-manager/test/index.test.ts @@ -97,10 +97,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual({}); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: undefined, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: undefined, + }), + ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledWith( await TransactionsFactory.createClearTransaction(data), channelId, @@ -120,10 +122,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual({}); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: 'ecies-aes256-gcm', - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: 'ecies-aes256-gcm', + }), + ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledTimes(1); }); @@ -153,10 +157,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual({}); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: undefined, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: undefined, + }), + ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledWith( await TransactionsFactory.createClearTransaction(data), channelId, @@ -177,10 +183,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual({}); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: undefined, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: undefined, + }), + ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledWith( await TransactionsFactory.createClearTransaction(data2), channelId, @@ -230,10 +238,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual({}); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: 'ecies-aes256-gcm', - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: 'ecies-aes256-gcm', + }), + ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledTimes(1); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledWith( @@ -319,10 +329,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual({}); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: 'ecies-aes256-gcm', - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: 'ecies-aes256-gcm', + }), + ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledTimes(1); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledWith( @@ -348,10 +360,12 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual(fakeMetaDataAccessGetReturn.result); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturn.meta, - ignoredTransactions: [null, null], - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturn.meta, + ignoredTransactions: [null, null], + }), + ); expect(fakeDataAccess.getTransactionsByChannelId).toHaveBeenCalledWith(channelId, undefined); }); @@ -384,17 +398,20 @@ describe('index', () => { const ret = await transactionManager.getTransactionsByChannelId(channelId); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnFirstHashWrong.meta, - ignoredTransactions: [ - { - reason: 'as first transaction, the hash of the transaction do not match the channelId', - transaction: txWrongHash, - }, - null, - null, - ], - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnFirstHashWrong.meta, + ignoredTransactions: [ + { + reason: + 'as first transaction, the hash of the transaction do not match the channelId', + transaction: txWrongHash, + }, + null, + null, + ], + }), + ); // 'ret.result is wrong' expect(ret.result).toEqual({ @@ -432,17 +449,19 @@ describe('index', () => { const ret = await transactionManager.getTransactionsByChannelId(channelId); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnFirstHashWrong.meta, - ignoredTransactions: [ - { - reason: 'Impossible to JSON parse the transaction', - transaction: txWrongHash, - }, - null, - null, - ], - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnFirstHashWrong.meta, + ignoredTransactions: [ + { + reason: 'Impossible to JSON parse the transaction', + transaction: txWrongHash, + }, + null, + null, + ], + }), + ); // 'ret.result is wrong' expect(ret.result).toEqual({ @@ -1099,12 +1118,14 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual(fakeMetaDataAccessGetChannelsReturn.result); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetChannelsReturn.meta, - ignoredTransactions: { - '01a98f126de3fab2b5130af5161998bf6e59b2c380deafeff938ff3f798281bf23': [null, null], - }, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetChannelsReturn.meta, + ignoredTransactions: { + '01a98f126de3fab2b5130af5161998bf6e59b2c380deafeff938ff3f798281bf23': [null, null], + }, + }), + ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); }); @@ -1160,12 +1181,14 @@ describe('index', () => { }, }); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, - ignoredTransactions: { - [channelId]: [null], - }, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, + ignoredTransactions: { + [channelId]: [null], + }, + }), + ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); }); @@ -1218,21 +1241,23 @@ describe('index', () => { }, }); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, - ignoredTransactions: { - [channelId]: [ - { - reason: 'No decryption provider given', - transaction: { - state: TransactionTypes.TransactionState.PENDING, - timestamp: 1, - transaction: encryptedTx, + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, + ignoredTransactions: { + [channelId]: [ + { + reason: 'No decryption provider given', + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, }, - }, - ], - }, - }); + ], + }, + }), + ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); }); @@ -1297,22 +1322,24 @@ describe('index', () => { }, }); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, - ignoredTransactions: { - [channelId]: [ - { - reason: 'No decryption provider given', - transaction: { - state: TransactionTypes.TransactionState.PENDING, - timestamp: 1, - transaction: encryptedTx, + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, + ignoredTransactions: { + [channelId]: [ + { + reason: 'No decryption provider given', + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, }, - }, - null, - ], - }, - }); + null, + ], + }, + }), + ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); }); @@ -1350,20 +1377,22 @@ describe('index', () => { transactions: { [channelId]: [null, tx, tx2] }, }); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnFirstHashWrong.meta, - ignoredTransactions: { - [channelId]: [ - { - reason: - 'as first transaction, the hash of the transaction do not match the channelId', - transaction: txWrongHash, - }, - null, - null, - ], - }, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnFirstHashWrong.meta, + ignoredTransactions: { + [channelId]: [ + { + reason: + 'as first transaction, the hash of the transaction do not match the channelId', + transaction: txWrongHash, + }, + null, + null, + ], + }, + }), + ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); }); @@ -1428,13 +1457,15 @@ describe('index', () => { }, }); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, - ignoredTransactions: { - [channelId]: [null], - [channelId2]: [null], - }, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetReturnWithEncryptedTransaction.meta, + ignoredTransactions: { + [channelId]: [null], + [channelId2]: [null], + }, + }), + ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); }); }); @@ -1448,12 +1479,14 @@ describe('index', () => { // 'ret.result is wrong' expect(ret.result).toEqual(fakeMetaDataAccessGetChannelsReturn.result); // 'ret.meta is wrong' - expect(ret.meta).toEqual({ - dataAccessMeta: fakeMetaDataAccessGetChannelsReturn.meta, - ignoredTransactions: { - '01a98f126de3fab2b5130af5161998bf6e59b2c380deafeff938ff3f798281bf23': [null, null], - }, - }); + expect(ret.meta).toEqual( + expect.objectContaining({ + dataAccessMeta: fakeMetaDataAccessGetChannelsReturn.meta, + ignoredTransactions: { + '01a98f126de3fab2b5130af5161998bf6e59b2c380deafeff938ff3f798281bf23': [null, null], + }, + }), + ); // eslint-disable-next-line @typescript-eslint/unbound-method expect(fakeDataAccess.getChannelsByMultipleTopics).toHaveBeenCalledWith( [extraTopics[0]], From 12631ac252974f5763efa325a293ed4719148bef Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Thu, 6 Jun 2024 14:06:58 +0300 Subject: [PATCH 07/42] test: updating request-client tests to check object containing similar props. --- packages/request-client.js/test/index.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index e32e343fd..15eeee874 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -812,7 +812,7 @@ describe('request-client.js', () => { const requestFromId = await requestNetwork.fromRequestId(request.requestId); - expect(requestFromId).toMatchObject(request); + expect(requestFromId).toEqual(expect.objectContaining(request)); const requestData = requestFromId.getData(); expect(requestData.meta).not.toBeNull(); @@ -988,7 +988,7 @@ describe('request-client.js', () => { const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); - expect(fetchedRequest).toMatchObject(request); + expect(fetchedRequest).toEqual(expect.objectContaining(request)); const requestData = fetchedRequest.getData(); expect(requestData.meta).not.toBeNull(); @@ -1018,7 +1018,7 @@ describe('request-client.js', () => { ); const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); - expect(fetchedRequest).toMatchObject(request); + expect(fetchedRequest).toEqual(expect.objectContaining(request)); const requestData = fetchedRequest.getData(); expect(requestData.meta).not.toBeNull(); From 6dffdfd411ed43c05d3a60122887ff79a7722f47 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Mon, 15 Jul 2024 11:27:16 +0300 Subject: [PATCH 08/42] refactor: merge in memory variables into one object `inMemoryInfo` --- .../src/api/request-network.ts | 60 ++++++++++++------- packages/request-client.js/src/api/request.ts | 30 ++++------ .../src/http-request-network.ts | 18 +++--- ...cess.ts => no-persist-http-data-access.ts} | 2 +- packages/request-client.js/test/index.test.ts | 57 +++++++++++------- packages/types/src/request-logic-types.ts | 9 +++ 6 files changed, 101 insertions(+), 75 deletions(-) rename packages/request-client.js/src/{no-confirm-http-data-access.ts => no-persist-http-data-access.ts} (95%) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index b4e634bb8..388a38c86 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -22,7 +22,7 @@ import * as Types from '../types'; import ContentDataExtension from './content-data-extension'; import Request from './request'; import localUtils from './utils'; -import { NoConfirmHttpDataAccess } from '../no-confirm-http-data-access'; +import { NoPersistHttpDataAccess } from '../no-persist-http-data-access'; /** * Entry point of the request-client.js library. Create requests, get requests, manipulate requests. @@ -86,10 +86,8 @@ export default class RequestNetwork { private preparePaymentRequest( transactionData: DataAccessTypes.ITransaction, requestId: string, - ): ClientTypes.IRequestData | undefined { - if (!transactionData.data) return undefined; - - const requestData = JSON.parse(transactionData.data).data; + ): ClientTypes.IRequestData { + const requestData = JSON.parse(transactionData.data as string).data; const originalExtensionsData = requestData.parameters.extensionsData; const newExtensions: RequestLogicTypes.IExtensionStates = {}; @@ -174,9 +172,15 @@ export default class RequestNetwork { requestLogicCreateResult, skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, - topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, - transactionData: transactionData, - paymentRequest: this.preparePaymentRequest(transactionData, requestId), + // inMemoryInfo is only used when skipPersistence is enabled + inMemoryInfo: + this.dataAccess instanceof NoPersistHttpDataAccess + ? { + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: transactionData, + paymentRequest: this.preparePaymentRequest(transactionData, requestId), + } + : null, }); if (!options?.skipRefresh) { @@ -190,24 +194,31 @@ export default class RequestNetwork { /** * Persists an in-memory request to the data-access layer. * - * @param transactionData The transaction data containing the request information - * @param requestId The ID of the request - * @param topics Optional topics for indexing the request - * @returns The result of the persist transaction operation - * @throws Error if the data access instance does not support persistence + * This method is used to persist requests that were initially created with skipPersistence enabled. + * + * @param request The Request object to persist. This must be a request that was created with skipPersistence enabled. + * @returns A promise that resolves to the result of the persist transaction operation. + * @throws {Error} If the request's `inMemoryInfo` is not provided, indicating it wasn't created with skipPersistence. + * @throws {Error} If the current data access instance does not support persistence (e.g., NoPersistHttpDataAccess). */ public async persistRequest( - transactionData: DataAccessTypes.ITransaction, - requestId: string, - topics?: string[], + request: Request, ): Promise { - if (this.dataAccess instanceof NoConfirmHttpDataAccess) { + if (!request.inMemoryInfo) { + throw new Error('Cannot persist request without inMemoryInfo'); + } + + if (this.dataAccess instanceof NoPersistHttpDataAccess) { throw new Error( - 'Cannot persist request when skipCreateConfirmation is used. Create a new instance of RequestNetwork without skipCreateConfirmation to persist the request.', + 'Cannot persist request when skipPersistence is enabled. Create a new instance of RequestNetwork without skipPersistence to persist the request.', ); } const result: DataAccessTypes.IReturnPersistTransaction = - await this.dataAccess.persistTransaction(transactionData, requestId, topics); + await this.dataAccess.persistTransaction( + request.inMemoryInfo.transactionData, + request.requestId, + request.inMemoryInfo.topics, + ); return result; } @@ -244,9 +255,14 @@ export default class RequestNetwork { requestLogicCreateResult, skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, - topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, - transactionData: transactionData, - paymentRequest: this.preparePaymentRequest(transactionData, requestId), + inMemoryInfo: + this.dataAccess instanceof NoPersistHttpDataAccess + ? { + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: transactionData, + paymentRequest: this.preparePaymentRequest(transactionData, requestId), + } + : null, }); if (!options?.skipRefresh) { diff --git a/packages/request-client.js/src/api/request.ts b/packages/request-client.js/src/api/request.ts index e45da2d5e..bd3c9f7a4 100644 --- a/packages/request-client.js/src/api/request.ts +++ b/packages/request-client.js/src/api/request.ts @@ -4,9 +4,7 @@ import { EscrowERC20InfoRetriever, } from '@requestnetwork/payment-detection'; import { - ClientTypes, CurrencyTypes, - DataAccessTypes, EncryptionTypes, IdentityTypes, PaymentTypes, @@ -81,19 +79,15 @@ export default class Request { private currencyManager: CurrencyTypes.ICurrencyManager; /** - * Transaction data of a in-memory request, necesary for persisting the request later on. - */ - public readonly transactionData: DataAccessTypes.ITransaction | undefined; - - /** - * Topics of a in-memory request, necesary for persisting the request later on. - */ - public readonly topics: string[] | undefined; - - /** - * Structured data for an in-memory request, primarily used for processing payments before the request is persisted. + * Information for an in-memory request, including transaction data, topics, and payment request data. + * This is used for requests that haven't been persisted yet, allowing for operations like payments + * before the request is stored in the data access layer. + * + * @property transactionData - Transaction data necessary for persisting the request later on. + * @property topics - Topics of the request, used for indexing and retrieval when persisting. + * @property paymentRequest - Structured data primarily used for processing payments before the request is persisted. */ - public readonly paymentRequest: ClientTypes.IRequestData | undefined; + public readonly inMemoryInfo: RequestLogicTypes.IInMemoryInfo | null = null; /** * Creates an instance of Request @@ -115,9 +109,7 @@ export default class Request { requestLogicCreateResult?: RequestLogicTypes.IReturnCreateRequest; skipPaymentDetection?: boolean; disableEvents?: boolean; - transactionData?: DataAccessTypes.ITransaction; - topics?: string[]; - paymentRequest?: ClientTypes.IRequestData | undefined; + inMemoryInfo?: RequestLogicTypes.IInMemoryInfo | null; }, ) { this.requestLogic = requestLogic; @@ -128,9 +120,7 @@ export default class Request { this.skipPaymentDetection = options?.skipPaymentDetection || false; this.disableEvents = options?.disableEvents || false; this.currencyManager = currencyManager; - this.topics = options?.topics; - this.transactionData = options?.transactionData; - this.paymentRequest = options?.paymentRequest; + this.inMemoryInfo = options?.inMemoryInfo || null; if (options?.requestLogicCreateResult && !this.disableEvents) { const originalEmitter = options.requestLogicCreateResult; diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index fad694646..8d8944391 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -11,7 +11,7 @@ import RequestNetwork from './api/request-network'; import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; import { MockDataAccess } from '@requestnetwork/data-access'; import { MockStorage } from './mock-storage'; -import { NoConfirmHttpDataAccess } from './no-confirm-http-data-access'; +import { NoPersistHttpDataAccess } from './no-persist-http-data-access'; /** * Exposes RequestNetwork module configured to use http-data-access. @@ -24,9 +24,9 @@ export default class HttpRequestNetwork extends RequestNetwork { * @param options.nodeConnectionConfig Configuration options to connect to the node. * @param options.useMockStorage When true, will use a mock storage in memory. Meant to simplify local development and should never be used in production. * @param options.signatureProvider Module to handle the signature. If not given it will be impossible to create new transaction (it requires to sign). - * @param options.currencies custom currency list - * @param options.currencyManager custom currency manager (will override `currencies`)http - * @param options.skipCreateConfirmation allows to create a transaction without persisting it. + * @param options.currencies custom currency list. + * @param options.currencyManager custom currency manager (will override `currencies`). + * @param options.skipPersistence allows to create a transaction without persisting it. */ constructor( { @@ -37,7 +37,7 @@ export default class HttpRequestNetwork extends RequestNetwork { useMockStorage, currencyManager, paymentOptions, - skipCreateConfirmation, + skipPersistence, }: { decryptionProvider?: DecryptionProviderTypes.IDecryptionProvider; httpConfig?: Partial; @@ -46,17 +46,17 @@ export default class HttpRequestNetwork extends RequestNetwork { useMockStorage?: boolean; currencyManager?: CurrencyTypes.ICurrencyManager; paymentOptions?: Partial; - skipCreateConfirmation?: boolean; + skipPersistence?: boolean; } = { httpConfig: {}, useMockStorage: false, - skipCreateConfirmation: false, + skipPersistence: false, }, ) { const dataAccess: DataAccessTypes.IDataAccess = useMockStorage ? new MockDataAccess(new MockStorage()) - : skipCreateConfirmation - ? new NoConfirmHttpDataAccess({ + : skipPersistence + ? new NoPersistHttpDataAccess({ httpConfig, nodeConnectionConfig, }) diff --git a/packages/request-client.js/src/no-confirm-http-data-access.ts b/packages/request-client.js/src/no-persist-http-data-access.ts similarity index 95% rename from packages/request-client.js/src/no-confirm-http-data-access.ts rename to packages/request-client.js/src/no-persist-http-data-access.ts index 3c0e2e77d..7261f9344 100644 --- a/packages/request-client.js/src/no-confirm-http-data-access.ts +++ b/packages/request-client.js/src/no-persist-http-data-access.ts @@ -2,7 +2,7 @@ import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; import { ClientTypes, DataAccessTypes, StorageTypes } from '@requestnetwork/types'; import { EventEmitter } from 'events'; -export class NoConfirmHttpDataAccess extends HttpDataAccess { +export class NoPersistHttpDataAccess extends HttpDataAccess { constructor( { httpConfig, diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index 15eeee874..17b3ef3b8 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1173,9 +1173,7 @@ describe('request-client.js', () => { http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), ); mockServer.listen({ onUnhandledRequest: 'bypass' }); - }); - beforeEach(() => { spyPersistTransaction.mockReturnValue({}); }); @@ -1191,7 +1189,7 @@ describe('request-client.js', () => { it('creates a request without persisting it.', async () => { requestNetwork = new RequestNetwork({ - skipCreateConfirmation: true, + skipPersistence: true, signatureProvider: TestData.fakeSignatureProvider, }); @@ -1199,52 +1197,65 @@ describe('request-client.js', () => { expect(request).toBeDefined(); expect(request.requestId).toBeDefined(); - expect(request.transactionData).toBeDefined(); - expect(request.topics).toBeDefined(); - expect(request.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo).toBeDefined(); + expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.topics).toBeDefined(); + expect(request.inMemoryInfo?.transactionData).toBeDefined(); expect(spyPersistTransaction).not.toHaveBeenCalled(); }); - it('throws an error when trying to persist a request with skipCreateConfirmation as true', async () => { + it('throws an error when trying to persist a request with skipPersistence as true', async () => { requestNetwork = new RequestNetwork({ - skipCreateConfirmation: true, + skipPersistence: true, signatureProvider: TestData.fakeSignatureProvider, }); const request = await requestNetwork.createRequest(requestCreationParams); - expect(request.transactionData).toBeDefined(); - expect(request.topics).toBeDefined(); + expect(request.inMemoryInfo).toBeDefined(); + expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.topics).toBeDefined(); + expect(request.inMemoryInfo?.transactionData).toBeDefined(); expect(request.requestId).toBeDefined(); - await expect( - requestNetwork.persistRequest(request.transactionData!, request.requestId, request.topics), - ).rejects.toThrow( - 'Cannot persist request when skipCreateConfirmation is used. Create a new instance of RequestNetwork without skipCreateConfirmation to persist the request.', + await expect(requestNetwork.persistRequest(request)).rejects.toThrow( + 'Cannot persist request when skipPersistence is enabled. Create a new instance of RequestNetwork without skipPersistence to persist the request.', ); }); - it('persists the in-memory request', async () => { + it('throw an error when trying to persist a request without inMemoryInfo', async () => { requestNetwork = new RequestNetwork({ - skipCreateConfirmation: true, signatureProvider: TestData.fakeSignatureProvider, }); const request = await requestNetwork.createRequest(requestCreationParams); - expect(request.transactionData).toBeDefined(); - expect(request.topics).toBeDefined(); + expect(request.inMemoryInfo).not.toBeDefined(); + + await expect(requestNetwork.persistRequest(request)).rejects.toThrow( + 'Cannot persist request without inMemoryInfo', + ); + }); + + it('persists a previously created in-memory request', async () => { + requestNetwork = new RequestNetwork({ + skipPersistence: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request.inMemoryInfo).toBeDefined(); + expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.topics).toBeDefined(); + expect(request.inMemoryInfo?.transactionData).toBeDefined(); expect(request.requestId).toBeDefined(); const newRequestNetwork = new RequestNetwork({ signatureProvider: TestData.fakeSignatureProvider, }); - const persistResult = await newRequestNetwork.persistRequest( - request.transactionData!, - request.requestId, - request.topics, - ); + const persistResult = await newRequestNetwork.persistRequest(request); expect(persistResult).toBeDefined(); expect(spyPersistTransaction).toHaveBeenCalledTimes(1); diff --git a/packages/types/src/request-logic-types.ts b/packages/types/src/request-logic-types.ts index 3aea78052..371483075 100644 --- a/packages/types/src/request-logic-types.ts +++ b/packages/types/src/request-logic-types.ts @@ -5,6 +5,8 @@ import * as Extension from './extension-types'; import * as Identity from './identity-types'; import * as Signature from './signature-types'; import * as Transaction from './transaction-types'; +import * as DataAccess from './data-access-types'; +import * as Client from './client-types'; import { CurrencyTypes } from './index'; /** Request Logic layer */ @@ -212,6 +214,13 @@ export interface IAcceptParameters { extensionsData?: any[]; } +// ** Parameters added to the request when it is created with skipping persistence */ +export interface IInMemoryInfo { + transactionData: DataAccess.ITransaction; + topics: string[]; + paymentRequest: Client.IRequestData; +} + /** Parameters to cancel a request */ export interface ICancelParameters { requestId: RequestId; From d3d9368820f474650a7da883b63eb4f35bfd223f Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 12:32:39 +0300 Subject: [PATCH 09/42] test: move `spyPersistTransaction` back to beforeEach --- packages/request-client.js/test/index.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index 17b3ef3b8..453c28e2c 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1173,7 +1173,9 @@ describe('request-client.js', () => { http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), ); mockServer.listen({ onUnhandledRequest: 'bypass' }); + }); + beforeEach(() => { spyPersistTransaction.mockReturnValue({}); }); @@ -1230,7 +1232,7 @@ describe('request-client.js', () => { const request = await requestNetwork.createRequest(requestCreationParams); - expect(request.inMemoryInfo).not.toBeDefined(); + expect(request.inMemoryInfo).toBeNull(); await expect(requestNetwork.persistRequest(request)).rejects.toThrow( 'Cannot persist request without inMemoryInfo', From af2afa86ad69ffbb4950940e04eb60fe9be094d0 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 15:36:22 +0300 Subject: [PATCH 10/42] test: add logs to failing test --- .../test/http-request-network.test.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 5f4c7bb51..3a5c7c397 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -91,12 +91,21 @@ describe('HttpRequestNetwork', () => { }); it('increase the expected amount', async () => { - failAtCall(6); - const request = await createRequest(); - await request.waitForConfirmation(); - await request.increaseExpectedAmountRequest(3, TestData.payer.identity); - await checkForError(request); - }); + try { + failAtCall(6); + console.log('Creating request...'); + const request = await createRequest(); + console.log('Waiting for confirmation...'); + await request.waitForConfirmation(); + console.log('Increasing expected amount...'); + await request.increaseExpectedAmountRequest(3, TestData.payer.identity); + console.log('Checking for error...'); + await checkForError(request); + } catch (error) { + console.error('Test failed with error:', error); + throw error; + } + }, 30000); it('reduce the expected amount', async () => { failAtCall(6); From 186b163a10f2bb1e2035284218925895f2b70622 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 16:52:02 +0300 Subject: [PATCH 11/42] test: add more logs to test --- packages/request-client.js/test/http-request-network.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 3a5c7c397..655163689 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -65,6 +65,9 @@ describe('HttpRequestNetwork', () => { r(e); }); }); + + console.log('Error :', error.message); + expect(error.message).toBe('Internal Server Error'); }; From 8e91696762b224195e3d2e166504b903cc2cb108 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:01:58 +0300 Subject: [PATCH 12/42] test: revert skip persistence --- packages/request-client.js/src/http-request-network.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index 8d8944391..ac7df8e44 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -11,7 +11,7 @@ import RequestNetwork from './api/request-network'; import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; import { MockDataAccess } from '@requestnetwork/data-access'; import { MockStorage } from './mock-storage'; -import { NoPersistHttpDataAccess } from './no-persist-http-data-access'; +// import { NoPersistHttpDataAccess } from './no-persist-http-data-access'; /** * Exposes RequestNetwork module configured to use http-data-access. @@ -36,8 +36,7 @@ export default class HttpRequestNetwork extends RequestNetwork { signatureProvider, useMockStorage, currencyManager, - paymentOptions, - skipPersistence, + paymentOptions, // skipPersistence, }: { decryptionProvider?: DecryptionProviderTypes.IDecryptionProvider; httpConfig?: Partial; @@ -55,11 +54,6 @@ export default class HttpRequestNetwork extends RequestNetwork { ) { const dataAccess: DataAccessTypes.IDataAccess = useMockStorage ? new MockDataAccess(new MockStorage()) - : skipPersistence - ? new NoPersistHttpDataAccess({ - httpConfig, - nodeConnectionConfig, - }) : new HttpDataAccess({ httpConfig, nodeConnectionConfig }); if (!currencyManager) { From 613e6c3a1dc8023ce56a2f12a1053ed14b2b6c77 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:10:38 +0300 Subject: [PATCH 13/42] test: add persistence back --- packages/request-client.js/src/http-request-network.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index ac7df8e44..8d8944391 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -11,7 +11,7 @@ import RequestNetwork from './api/request-network'; import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; import { MockDataAccess } from '@requestnetwork/data-access'; import { MockStorage } from './mock-storage'; -// import { NoPersistHttpDataAccess } from './no-persist-http-data-access'; +import { NoPersistHttpDataAccess } from './no-persist-http-data-access'; /** * Exposes RequestNetwork module configured to use http-data-access. @@ -36,7 +36,8 @@ export default class HttpRequestNetwork extends RequestNetwork { signatureProvider, useMockStorage, currencyManager, - paymentOptions, // skipPersistence, + paymentOptions, + skipPersistence, }: { decryptionProvider?: DecryptionProviderTypes.IDecryptionProvider; httpConfig?: Partial; @@ -54,6 +55,11 @@ export default class HttpRequestNetwork extends RequestNetwork { ) { const dataAccess: DataAccessTypes.IDataAccess = useMockStorage ? new MockDataAccess(new MockStorage()) + : skipPersistence + ? new NoPersistHttpDataAccess({ + httpConfig, + nodeConnectionConfig, + }) : new HttpDataAccess({ httpConfig, nodeConnectionConfig }); if (!currencyManager) { From 10ed848035cec8fbcf97ed07c945c20b754851ee Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:11:29 +0300 Subject: [PATCH 14/42] test: add more logs to failing test --- .../test/http-request-network.test.ts | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 655163689..83b215a39 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -94,21 +94,17 @@ describe('HttpRequestNetwork', () => { }); it('increase the expected amount', async () => { - try { - failAtCall(6); - console.log('Creating request...'); - const request = await createRequest(); - console.log('Waiting for confirmation...'); - await request.waitForConfirmation(); - console.log('Increasing expected amount...'); - await request.increaseExpectedAmountRequest(3, TestData.payer.identity); - console.log('Checking for error...'); - await checkForError(request); - } catch (error) { - console.error('Test failed with error:', error); - throw error; - } - }, 30000); + failAtCall(6); + console.log('Creating request...'); + const request = await createRequest(); + console.log('Waiting for confirmation...'); + await request.waitForConfirmation(); + console.log('Increasing expected amount...'); + await request.increaseExpectedAmountRequest(3, TestData.payer.identity); + console.log('Checking for error...'); + await checkForError(request); + console.log('Test should have passed by now'); + }); it('reduce the expected amount', async () => { failAtCall(6); From 638d730f364bd6bcd144fca0f6a2f543b6d479fb Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:21:54 +0300 Subject: [PATCH 15/42] test: increase failAtCall --- packages/request-client.js/test/http-request-network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 83b215a39..d4dbbca5a 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -94,7 +94,7 @@ describe('HttpRequestNetwork', () => { }); it('increase the expected amount', async () => { - failAtCall(6); + failAtCall(9); console.log('Creating request...'); const request = await createRequest(); console.log('Waiting for confirmation...'); From d4b611febdfa8c3b6dd67b519ccb479a0140ff7b Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:30:33 +0300 Subject: [PATCH 16/42] test: increase timeout --- .circleci/config.yml | 114 +++++++++--------- .../test/http-request-network.test.ts | 2 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fd0d8d2cd..418f6bb21 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -525,63 +525,63 @@ workflows: - test-request-client: requires: - build - - test-transaction-manager: - requires: - - build - - test-data-access: - requires: - - build - - test-data-format: - requires: - - build - - test-ethereum-storage: - requires: - - build - - test-smart-contracts: - requires: - - build - - test-request-logic: - requires: - - build - - test-multi-format: - requires: - - build - - test-advanced-logic: - requires: - - build - - test-request-node: - requires: - - build - - test-utils: - requires: - - build - - test-currency: - requires: - - build - - test-epk-signature: - requires: - - build - - test-epk-decryption: - requires: - - build - - test-web3-signature: - requires: - - build - - test-usage-examples: - requires: - - build - - test-toolbox: - requires: - - build - - test-integration-test: - requires: - - build - - test-payment-detection: - requires: - - build - - test-payment-processor: - requires: - - build + # - test-transaction-manager: + # requires: + # - build + # - test-data-access: + # requires: + # - build + # - test-data-format: + # requires: + # - build + # - test-ethereum-storage: + # requires: + # - build + # - test-smart-contracts: + # requires: + # - build + # - test-request-logic: + # requires: + # - build + # - test-multi-format: + # requires: + # - build + # - test-advanced-logic: + # requires: + # - build + # - test-request-node: + # requires: + # - build + # - test-utils: + # requires: + # - build + # - test-currency: + # requires: + # - build + # - test-epk-signature: + # requires: + # - build + # - test-epk-decryption: + # requires: + # - build + # - test-web3-signature: + # requires: + # - build + # - test-usage-examples: + # requires: + # - build + # - test-toolbox: + # requires: + # - build + # - test-integration-test: + # requires: + # - build + # - test-payment-detection: + # requires: + # - build + # - test-payment-processor: + # requires: + # - build # Release a next version package everytime we merge to master - next-release: diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index d4dbbca5a..c8c08b831 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -104,7 +104,7 @@ describe('HttpRequestNetwork', () => { console.log('Checking for error...'); await checkForError(request); console.log('Test should have passed by now'); - }); + }, 100000); it('reduce the expected amount', async () => { failAtCall(6); From 0900c53c4649925fe28ba4e47f9c3326763aa4e7 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:32:38 +0300 Subject: [PATCH 17/42] test: uncomment workflows --- .circleci/config.yml | 114 +++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 418f6bb21..fd0d8d2cd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -525,63 +525,63 @@ workflows: - test-request-client: requires: - build - # - test-transaction-manager: - # requires: - # - build - # - test-data-access: - # requires: - # - build - # - test-data-format: - # requires: - # - build - # - test-ethereum-storage: - # requires: - # - build - # - test-smart-contracts: - # requires: - # - build - # - test-request-logic: - # requires: - # - build - # - test-multi-format: - # requires: - # - build - # - test-advanced-logic: - # requires: - # - build - # - test-request-node: - # requires: - # - build - # - test-utils: - # requires: - # - build - # - test-currency: - # requires: - # - build - # - test-epk-signature: - # requires: - # - build - # - test-epk-decryption: - # requires: - # - build - # - test-web3-signature: - # requires: - # - build - # - test-usage-examples: - # requires: - # - build - # - test-toolbox: - # requires: - # - build - # - test-integration-test: - # requires: - # - build - # - test-payment-detection: - # requires: - # - build - # - test-payment-processor: - # requires: - # - build + - test-transaction-manager: + requires: + - build + - test-data-access: + requires: + - build + - test-data-format: + requires: + - build + - test-ethereum-storage: + requires: + - build + - test-smart-contracts: + requires: + - build + - test-request-logic: + requires: + - build + - test-multi-format: + requires: + - build + - test-advanced-logic: + requires: + - build + - test-request-node: + requires: + - build + - test-utils: + requires: + - build + - test-currency: + requires: + - build + - test-epk-signature: + requires: + - build + - test-epk-decryption: + requires: + - build + - test-web3-signature: + requires: + - build + - test-usage-examples: + requires: + - build + - test-toolbox: + requires: + - build + - test-integration-test: + requires: + - build + - test-payment-detection: + requires: + - build + - test-payment-processor: + requires: + - build # Release a next version package everytime we merge to master - next-release: From 91f0cc682f077f39d9238318db867e2544331fbb Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Tue, 16 Jul 2024 17:42:40 +0300 Subject: [PATCH 18/42] test: lower fail at call --- packages/request-client.js/test/http-request-network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index c8c08b831..6d39f9a35 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -94,7 +94,7 @@ describe('HttpRequestNetwork', () => { }); it('increase the expected amount', async () => { - failAtCall(9); + failAtCall(2); console.log('Creating request...'); const request = await createRequest(); console.log('Waiting for confirmation...'); From 3dc465c5c826f9aa4632a2424ee68d71fb3c19cb Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 13:10:15 -0400 Subject: [PATCH 19/42] Revert to fail at call 6 --- packages/request-client.js/test/http-request-network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 6d39f9a35..0968e73e2 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -94,7 +94,7 @@ describe('HttpRequestNetwork', () => { }); it('increase the expected amount', async () => { - failAtCall(2); + failAtCall(6); console.log('Creating request...'); const request = await createRequest(); console.log('Waiting for confirmation...'); From 6df24aa2415e81714361858fa1f631891cd23028 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 14:25:38 -0400 Subject: [PATCH 20/42] Revert debug log messages and extended timeout --- .../test/http-request-network.test.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 0968e73e2..5f4c7bb51 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -65,9 +65,6 @@ describe('HttpRequestNetwork', () => { r(e); }); }); - - console.log('Error :', error.message); - expect(error.message).toBe('Internal Server Error'); }; @@ -95,16 +92,11 @@ describe('HttpRequestNetwork', () => { it('increase the expected amount', async () => { failAtCall(6); - console.log('Creating request...'); const request = await createRequest(); - console.log('Waiting for confirmation...'); await request.waitForConfirmation(); - console.log('Increasing expected amount...'); await request.increaseExpectedAmountRequest(3, TestData.payer.identity); - console.log('Checking for error...'); await checkForError(request); - console.log('Test should have passed by now'); - }, 100000); + }); it('reduce the expected amount', async () => { failAtCall(6); From 7ec1197856435e75bba2902790ea238cde21299f Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 14:38:41 -0400 Subject: [PATCH 21/42] Throw error on unhandled request --- packages/request-client.js/test/data-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/data-test.ts b/packages/request-client.js/test/data-test.ts index 53d0f94fb..f66d0573c 100644 --- a/packages/request-client.js/test/data-test.ts +++ b/packages/request-client.js/test/data-test.ts @@ -280,6 +280,6 @@ export const mockRequestNode = (): SetupServer => { ), http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), ); - mockServer.listen({ onUnhandledRequest: 'bypass' }); + mockServer.listen({ onUnhandledRequest: 'error' }); return mockServer; }; From 6b3513bf772e2935e49063f62fa3bcb8ec26140d Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 14:47:26 -0400 Subject: [PATCH 22/42] Add debug logging related to msw server --- .../test/http-request-network.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 5f4c7bb51..55f387b49 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -9,6 +9,10 @@ let mockServer: SetupServer; beforeAll(() => { mockServer = TestData.mockRequestNode(); + + // FIXME: Remove this line when the issue is fixed + // log all request handlers + console.log(`beforeAll ${mockServer.listHandlers()}`); }); afterAll(() => { @@ -18,6 +22,10 @@ afterAll(() => { }); afterEach(() => { + // FIXME: Remove this line when the issue is fixed + // log all request handlers + console.log(`afterEach ${mockServer.listHandlers()}`); + mockServer.restoreHandlers(); }); @@ -92,6 +100,11 @@ describe('HttpRequestNetwork', () => { it('increase the expected amount', async () => { failAtCall(6); + + // FIXME: Remove this line when the issue is fixed + // log all request handlers + console.log(`after failAtCall ${mockServer.listHandlers()}`); + const request = await createRequest(); await request.waitForConfirmation(); await request.increaseExpectedAmountRequest(3, TestData.payer.identity); From 8027e49ca7d0286aa08aa0d102d5fd6f4b8f2659 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 14:57:09 -0400 Subject: [PATCH 23/42] Print as strings --- .../request-client.js/test/http-request-network.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 55f387b49..92b17d079 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -12,7 +12,7 @@ beforeAll(() => { // FIXME: Remove this line when the issue is fixed // log all request handlers - console.log(`beforeAll ${mockServer.listHandlers()}`); + console.log(`beforeAll ${mockServer.listHandlers().toString()}`); }); afterAll(() => { @@ -24,7 +24,7 @@ afterAll(() => { afterEach(() => { // FIXME: Remove this line when the issue is fixed // log all request handlers - console.log(`afterEach ${mockServer.listHandlers()}`); + console.log(`afterEach ${mockServer.listHandlers().toString()}`); mockServer.restoreHandlers(); }); @@ -103,7 +103,7 @@ describe('HttpRequestNetwork', () => { // FIXME: Remove this line when the issue is fixed // log all request handlers - console.log(`after failAtCall ${mockServer.listHandlers()}`); + console.log(`after failAtCall ${mockServer.listHandlers().toString()}`); const request = await createRequest(); await request.waitForConfirmation(); From 0f0fe60eb6d3775ddec3f98d9fd021b8cad8efac Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 15:17:28 -0400 Subject: [PATCH 24/42] Fix debug logs --- .../request-client.js/test/http-request-network.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index 92b17d079..d2a43885b 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -12,7 +12,7 @@ beforeAll(() => { // FIXME: Remove this line when the issue is fixed // log all request handlers - console.log(`beforeAll ${mockServer.listHandlers().toString()}`); + console.log(`beforeAll ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); }); afterAll(() => { @@ -24,7 +24,7 @@ afterAll(() => { afterEach(() => { // FIXME: Remove this line when the issue is fixed // log all request handlers - console.log(`afterEach ${mockServer.listHandlers().toString()}`); + console.log(`afterEach ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); mockServer.restoreHandlers(); }); @@ -103,7 +103,7 @@ describe('HttpRequestNetwork', () => { // FIXME: Remove this line when the issue is fixed // log all request handlers - console.log(`after failAtCall ${mockServer.listHandlers().toString()}`); + console.log(`after failAtCall ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); const request = await createRequest(); await request.waitForConfirmation(); From 6172c07475449997ff40c6adf74dc4026ff507bc Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 15:29:18 -0400 Subject: [PATCH 25/42] Try resetting handlers after each test --- packages/request-client.js/test/http-request-network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index d2a43885b..cecc85632 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -25,7 +25,7 @@ afterEach(() => { // FIXME: Remove this line when the issue is fixed // log all request handlers console.log(`afterEach ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); - + mockServer.resetHandlers(); mockServer.restoreHandlers(); }); From ffc7559751df772b035217832fa14660d7588d44 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 16 Jul 2024 15:44:12 -0400 Subject: [PATCH 26/42] Remove debug logs. Call `resetHandlers()` afterEach test --- .../test/http-request-network.test.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/request-client.js/test/http-request-network.test.ts b/packages/request-client.js/test/http-request-network.test.ts index cecc85632..40d0a0be5 100644 --- a/packages/request-client.js/test/http-request-network.test.ts +++ b/packages/request-client.js/test/http-request-network.test.ts @@ -9,10 +9,6 @@ let mockServer: SetupServer; beforeAll(() => { mockServer = TestData.mockRequestNode(); - - // FIXME: Remove this line when the issue is fixed - // log all request handlers - console.log(`beforeAll ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); }); afterAll(() => { @@ -22,11 +18,7 @@ afterAll(() => { }); afterEach(() => { - // FIXME: Remove this line when the issue is fixed - // log all request handlers - console.log(`afterEach ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); mockServer.resetHandlers(); - mockServer.restoreHandlers(); }); describe('HttpRequestNetwork', () => { @@ -100,11 +92,6 @@ describe('HttpRequestNetwork', () => { it('increase the expected amount', async () => { failAtCall(6); - - // FIXME: Remove this line when the issue is fixed - // log all request handlers - console.log(`after failAtCall ${JSON.stringify(mockServer.listHandlers(), null, 2)}`); - const request = await createRequest(); await request.waitForConfirmation(); await request.increaseExpectedAmountRequest(3, TestData.payer.identity); From e45294e606ec92e394b219c609ee0cfc76856383 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 14:53:36 +0300 Subject: [PATCH 27/42] feat: skip refreshing if `skipePersistence` is active --- .../src/api/request-network.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 388a38c86..4a1853de0 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -165,6 +165,7 @@ export default class RequestNetwork { const transactionData = requestLogicCreateResult.meta?.transactionManagerMeta.transactionData; const requestId = requestLogicCreateResult.result.requestId; + const isSkipingPersistence = this.dataAccess instanceof NoPersistHttpDataAccess; // create the request object const request = new Request(requestId, this.requestLogic, this.currencyManager, { contentDataExtension: this.contentData, @@ -173,17 +174,16 @@ export default class RequestNetwork { skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, // inMemoryInfo is only used when skipPersistence is enabled - inMemoryInfo: - this.dataAccess instanceof NoPersistHttpDataAccess - ? { - topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, - transactionData: transactionData, - paymentRequest: this.preparePaymentRequest(transactionData, requestId), - } - : null, + inMemoryInfo: isSkipingPersistence + ? { + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: transactionData, + paymentRequest: this.preparePaymentRequest(transactionData, requestId), + } + : null, }); - if (!options?.skipRefresh) { + if (!options?.skipRefresh && !isSkipingPersistence) { // refresh the local request data await request.refresh(); } @@ -247,6 +247,7 @@ export default class RequestNetwork { const transactionData = requestLogicCreateResult.meta?.transactionManagerMeta.transactionData; const requestId = requestLogicCreateResult.result.requestId; + const isSkipingPersistence = this.dataAccess instanceof NoPersistHttpDataAccess; // create the request object const request = new Request(requestId, this.requestLogic, this.currencyManager, { @@ -255,17 +256,16 @@ export default class RequestNetwork { requestLogicCreateResult, skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, - inMemoryInfo: - this.dataAccess instanceof NoPersistHttpDataAccess - ? { - topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, - transactionData: transactionData, - paymentRequest: this.preparePaymentRequest(transactionData, requestId), - } - : null, + inMemoryInfo: isSkipingPersistence + ? { + topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, + transactionData: transactionData, + paymentRequest: this.preparePaymentRequest(transactionData, requestId), + } + : null, }); - if (!options?.skipRefresh) { + if (!options?.skipRefresh && !isSkipingPersistence) { // refresh the local request data await request.refresh(); } From 915b0a13ee811690d0053543a3a060dbb0a60165 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 14:56:43 +0300 Subject: [PATCH 28/42] feat: emit confirmation on no-persist-http-data-access --- packages/request-client.js/src/no-persist-http-data-access.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/request-client.js/src/no-persist-http-data-access.ts b/packages/request-client.js/src/no-persist-http-data-access.ts index 7261f9344..d2b3a4769 100644 --- a/packages/request-client.js/src/no-persist-http-data-access.ts +++ b/packages/request-client.js/src/no-persist-http-data-access.ts @@ -40,6 +40,9 @@ export class NoPersistHttpDataAccess extends HttpDataAccess { data, ); + // Emit confirmation instantly since data is not going to be persisted + result.emit('confirmed', result); + return result; } } From cf5884d8d364e259a5e3d0babc8cf357d11ab82d Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 14:58:49 +0300 Subject: [PATCH 29/42] test: fix `in-memory request` tests --- packages/request-client.js/test/index.test.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index 453c28e2c..bac7b2f5a 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1173,14 +1173,23 @@ describe('request-client.js', () => { http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), ); mockServer.listen({ onUnhandledRequest: 'bypass' }); + + mockServer.events.on('request:unhandled', (error) => { + console.error( + 'Found an unhandled %s request to %s', + error.request.method, + error.request.url, + ); + }); }); beforeEach(() => { spyPersistTransaction.mockReturnValue({}); }); - afterAll(() => { - mockServer.close(); + afterAll(async () => { + await mockServer.close(); + mockServer.resetHandlers(); }); const requestCreationParams = { @@ -1231,7 +1240,7 @@ describe('request-client.js', () => { }); const request = await requestNetwork.createRequest(requestCreationParams); - + await request.waitForConfirmation(); expect(request.inMemoryInfo).toBeNull(); await expect(requestNetwork.persistRequest(request)).rejects.toThrow( @@ -1255,12 +1264,13 @@ describe('request-client.js', () => { const newRequestNetwork = new RequestNetwork({ signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, }); const persistResult = await newRequestNetwork.persistRequest(request); expect(persistResult).toBeDefined(); - expect(spyPersistTransaction).toHaveBeenCalledTimes(1); + expect(spyPersistTransaction).toHaveBeenCalled(); }); }); From 8f5053071c4e112eaede454fcf7780f21ab987f5 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 15:03:03 +0300 Subject: [PATCH 30/42] test: revert test changes --- packages/request-client.js/test/data-test.ts | 2 +- packages/request-client.js/test/index.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/request-client.js/test/data-test.ts b/packages/request-client.js/test/data-test.ts index f66d0573c..53d0f94fb 100644 --- a/packages/request-client.js/test/data-test.ts +++ b/packages/request-client.js/test/data-test.ts @@ -280,6 +280,6 @@ export const mockRequestNode = (): SetupServer => { ), http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), ); - mockServer.listen({ onUnhandledRequest: 'error' }); + mockServer.listen({ onUnhandledRequest: 'bypass' }); return mockServer; }; diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index bac7b2f5a..e2dcb68d7 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -812,7 +812,7 @@ describe('request-client.js', () => { const requestFromId = await requestNetwork.fromRequestId(request.requestId); - expect(requestFromId).toEqual(expect.objectContaining(request)); + expect(requestFromId).toMatchObject(request); const requestData = requestFromId.getData(); expect(requestData.meta).not.toBeNull(); @@ -988,7 +988,7 @@ describe('request-client.js', () => { const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); - expect(fetchedRequest).toEqual(expect.objectContaining(request)); + expect(fetchedRequest).toMatchObject(request); const requestData = fetchedRequest.getData(); expect(requestData.meta).not.toBeNull(); @@ -1018,7 +1018,7 @@ describe('request-client.js', () => { ); const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); - expect(fetchedRequest).toEqual(expect.objectContaining(request)); + expect(fetchedRequest).toMatchObject(request); const requestData = fetchedRequest.getData(); expect(requestData.meta).not.toBeNull(); From e2dac6726c2c60e985a094d6e66355b6c9474ae9 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 15:10:15 +0300 Subject: [PATCH 31/42] fix: fix test --- packages/request-client.js/test/index.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index e2dcb68d7..60fdcf0a2 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1187,6 +1187,10 @@ describe('request-client.js', () => { spyPersistTransaction.mockReturnValue({}); }); + afterEach(() => { + mockServer.resetHandlers(); + }); + afterAll(async () => { await mockServer.close(); mockServer.resetHandlers(); From 55ec0b4077f6b4d8fbacbe4fbb98f088854c534d Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 15:18:18 +0300 Subject: [PATCH 32/42] test: move in-memory reuqests to their own file --- .../test/in-memory-request.test.ts | 131 ++++++++++++++++++ packages/request-client.js/test/index.test.ts | 129 ----------------- 2 files changed, 131 insertions(+), 129 deletions(-) create mode 100644 packages/request-client.js/test/in-memory-request.test.ts diff --git a/packages/request-client.js/test/in-memory-request.test.ts b/packages/request-client.js/test/in-memory-request.test.ts new file mode 100644 index 000000000..2212c4a3b --- /dev/null +++ b/packages/request-client.js/test/in-memory-request.test.ts @@ -0,0 +1,131 @@ +import { RequestNetwork } from '../src/index'; +import * as TestData from './data-test'; + +import { http, HttpResponse } from 'msw'; +import { setupServer, SetupServer } from 'msw/node'; +import config from '../src/http-config-defaults'; + +describe('handle in-memory request', () => { + let requestNetwork: RequestNetwork; + let spyPersistTransaction: jest.Mock; + let mockServer: SetupServer; + + beforeAll(() => { + spyPersistTransaction = jest.fn(); + + mockServer = setupServer( + http.post('*/persistTransaction', ({ request }) => { + if (!request.headers.get(config.requestClientVersionHeader)) { + throw new Error('Missing version header'); + } + return HttpResponse.json(spyPersistTransaction()); + }), + http.get('*/getTransactionsByChannelId', () => + HttpResponse.json({ + result: { transactions: [TestData.timestampedTransactionWithoutPaymentInfo] }, + }), + ), + http.post('*/ipfsAdd', () => HttpResponse.json({})), + http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), + ); + mockServer.listen({ onUnhandledRequest: 'bypass' }); + + mockServer.events.on('request:unhandled', (error) => { + console.error('Found an unhandled %s request to %s', error.request.method, error.request.url); + }); + }); + + beforeEach(() => { + spyPersistTransaction.mockReturnValue({}); + }); + + afterEach(() => { + mockServer.resetHandlers(); + }); + + afterAll(async () => { + await mockServer.close(); + mockServer.resetHandlers(); + }); + + const requestCreationParams = { + paymentNetwork: TestData.declarativePaymentNetworkNoPaymentInfo, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }; + + it('creates a request without persisting it.', async () => { + requestNetwork = new RequestNetwork({ + skipPersistence: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request).toBeDefined(); + expect(request.requestId).toBeDefined(); + expect(request.inMemoryInfo).toBeDefined(); + expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.topics).toBeDefined(); + expect(request.inMemoryInfo?.transactionData).toBeDefined(); + expect(spyPersistTransaction).not.toHaveBeenCalled(); + }); + + it('throws an error when trying to persist a request with skipPersistence as true', async () => { + requestNetwork = new RequestNetwork({ + skipPersistence: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request.inMemoryInfo).toBeDefined(); + expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.topics).toBeDefined(); + expect(request.inMemoryInfo?.transactionData).toBeDefined(); + expect(request.requestId).toBeDefined(); + + await expect(requestNetwork.persistRequest(request)).rejects.toThrow( + 'Cannot persist request when skipPersistence is enabled. Create a new instance of RequestNetwork without skipPersistence to persist the request.', + ); + }); + + it('throw an error when trying to persist a request without inMemoryInfo', async () => { + requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + await request.waitForConfirmation(); + expect(request.inMemoryInfo).toBeNull(); + + await expect(requestNetwork.persistRequest(request)).rejects.toThrow( + 'Cannot persist request without inMemoryInfo', + ); + }); + + it('persists a previously created in-memory request', async () => { + requestNetwork = new RequestNetwork({ + skipPersistence: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const request = await requestNetwork.createRequest(requestCreationParams); + + expect(request.inMemoryInfo).toBeDefined(); + expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.topics).toBeDefined(); + expect(request.inMemoryInfo?.transactionData).toBeDefined(); + expect(request.requestId).toBeDefined(); + + const newRequestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + }); + + const persistResult = await newRequestNetwork.persistRequest(request); + + expect(persistResult).toBeDefined(); + expect(spyPersistTransaction).toHaveBeenCalled(); + }); +}); diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index 60fdcf0a2..ba41b1488 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1149,135 +1149,6 @@ describe('request-client.js', () => { }); }); - describe('handle in-memory request', () => { - let requestNetwork: RequestNetwork; - let spyPersistTransaction: jest.Mock; - let mockServer: SetupServer; - - beforeAll(() => { - spyPersistTransaction = jest.fn(); - - mockServer = setupServer( - http.post('*/persistTransaction', ({ request }) => { - if (!request.headers.get(config.requestClientVersionHeader)) { - throw new Error('Missing version header'); - } - return HttpResponse.json(spyPersistTransaction()); - }), - http.get('*/getTransactionsByChannelId', () => - HttpResponse.json({ - result: { transactions: [TestData.timestampedTransactionWithoutPaymentInfo] }, - }), - ), - http.post('*/ipfsAdd', () => HttpResponse.json({})), - http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), - ); - mockServer.listen({ onUnhandledRequest: 'bypass' }); - - mockServer.events.on('request:unhandled', (error) => { - console.error( - 'Found an unhandled %s request to %s', - error.request.method, - error.request.url, - ); - }); - }); - - beforeEach(() => { - spyPersistTransaction.mockReturnValue({}); - }); - - afterEach(() => { - mockServer.resetHandlers(); - }); - - afterAll(async () => { - await mockServer.close(); - mockServer.resetHandlers(); - }); - - const requestCreationParams = { - paymentNetwork: TestData.declarativePaymentNetworkNoPaymentInfo, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }; - - it('creates a request without persisting it.', async () => { - requestNetwork = new RequestNetwork({ - skipPersistence: true, - signatureProvider: TestData.fakeSignatureProvider, - }); - - const request = await requestNetwork.createRequest(requestCreationParams); - - expect(request).toBeDefined(); - expect(request.requestId).toBeDefined(); - expect(request.inMemoryInfo).toBeDefined(); - expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); - expect(request.inMemoryInfo?.topics).toBeDefined(); - expect(request.inMemoryInfo?.transactionData).toBeDefined(); - expect(spyPersistTransaction).not.toHaveBeenCalled(); - }); - - it('throws an error when trying to persist a request with skipPersistence as true', async () => { - requestNetwork = new RequestNetwork({ - skipPersistence: true, - signatureProvider: TestData.fakeSignatureProvider, - }); - - const request = await requestNetwork.createRequest(requestCreationParams); - - expect(request.inMemoryInfo).toBeDefined(); - expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); - expect(request.inMemoryInfo?.topics).toBeDefined(); - expect(request.inMemoryInfo?.transactionData).toBeDefined(); - expect(request.requestId).toBeDefined(); - - await expect(requestNetwork.persistRequest(request)).rejects.toThrow( - 'Cannot persist request when skipPersistence is enabled. Create a new instance of RequestNetwork without skipPersistence to persist the request.', - ); - }); - - it('throw an error when trying to persist a request without inMemoryInfo', async () => { - requestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - }); - - const request = await requestNetwork.createRequest(requestCreationParams); - await request.waitForConfirmation(); - expect(request.inMemoryInfo).toBeNull(); - - await expect(requestNetwork.persistRequest(request)).rejects.toThrow( - 'Cannot persist request without inMemoryInfo', - ); - }); - - it('persists a previously created in-memory request', async () => { - requestNetwork = new RequestNetwork({ - skipPersistence: true, - signatureProvider: TestData.fakeSignatureProvider, - }); - - const request = await requestNetwork.createRequest(requestCreationParams); - - expect(request.inMemoryInfo).toBeDefined(); - expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); - expect(request.inMemoryInfo?.topics).toBeDefined(); - expect(request.inMemoryInfo?.transactionData).toBeDefined(); - expect(request.requestId).toBeDefined(); - - const newRequestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, - }); - - const persistResult = await newRequestNetwork.persistRequest(request); - - expect(persistResult).toBeDefined(); - expect(spyPersistTransaction).toHaveBeenCalled(); - }); - }); - describe('ETH requests', () => { beforeEach(() => { jest.clearAllMocks(); From 8027739f16419fa06dfa2dfec1f779041c2aa99f Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 15:56:36 +0300 Subject: [PATCH 33/42] test: move spyPersistTransatction to before all --- .../request-client.js/test/in-memory-request.test.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/request-client.js/test/in-memory-request.test.ts b/packages/request-client.js/test/in-memory-request.test.ts index 2212c4a3b..8937d21e0 100644 --- a/packages/request-client.js/test/in-memory-request.test.ts +++ b/packages/request-client.js/test/in-memory-request.test.ts @@ -29,20 +29,9 @@ describe('handle in-memory request', () => { http.get('*/getConfirmedTransaction', () => HttpResponse.json({ result: {} })), ); mockServer.listen({ onUnhandledRequest: 'bypass' }); - - mockServer.events.on('request:unhandled', (error) => { - console.error('Found an unhandled %s request to %s', error.request.method, error.request.url); - }); - }); - - beforeEach(() => { spyPersistTransaction.mockReturnValue({}); }); - afterEach(() => { - mockServer.resetHandlers(); - }); - afterAll(async () => { await mockServer.close(); mockServer.resetHandlers(); From c3b08ad192b44ec7185cf358f55812e9adc48cd2 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 16:41:24 +0300 Subject: [PATCH 34/42] refactor: update error message to be more clear --- packages/request-client.js/src/api/request-network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 4a1853de0..40d94b038 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -210,7 +210,7 @@ export default class RequestNetwork { if (this.dataAccess instanceof NoPersistHttpDataAccess) { throw new Error( - 'Cannot persist request when skipPersistence is enabled. Create a new instance of RequestNetwork without skipPersistence to persist the request.', + 'To persist the request, create a new instance of RequestNetwork without skipPersistence being set to true.', ); } const result: DataAccessTypes.IReturnPersistTransaction = From 11b3121e6c8276db7e5919e8d4d697618aa71df4 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 17 Jul 2024 09:55:42 -0400 Subject: [PATCH 35/42] clarify error message --- packages/request-client.js/src/api/request-network.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 40d94b038..65ef6b489 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -205,12 +205,12 @@ export default class RequestNetwork { request: Request, ): Promise { if (!request.inMemoryInfo) { - throw new Error('Cannot persist request without inMemoryInfo'); + throw new Error('Cannot persist request without inMemoryInfo.'); } if (this.dataAccess instanceof NoPersistHttpDataAccess) { throw new Error( - 'To persist the request, create a new instance of RequestNetwork without skipPersistence being set to true.', + 'Cannot persist request when skipPersistence is enabled. To persist the request, create a new instance of RequestNetwork without skipPersistence being set to true.', ); } const result: DataAccessTypes.IReturnPersistTransaction = From ab44ab41bab2e839f8c0b9b16dc5d734e8ba1caf Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 17 Jul 2024 09:58:40 -0400 Subject: [PATCH 36/42] fix: skipPersistence doc --- packages/request-client.js/src/http-request-network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index 8d8944391..e920e214b 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -26,7 +26,7 @@ export default class HttpRequestNetwork extends RequestNetwork { * @param options.signatureProvider Module to handle the signature. If not given it will be impossible to create new transaction (it requires to sign). * @param options.currencies custom currency list. * @param options.currencyManager custom currency manager (will override `currencies`). - * @param options.skipPersistence allows to create a transaction without persisting it. + * @param options.skipPersistence allows creating a transaction without immediate persistence. */ constructor( { From 7b71180d97bb5b7f58cdbbd75bb64720c7fd6319 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 16:59:39 +0300 Subject: [PATCH 37/42] test: update test with updated error message --- packages/request-client.js/test/in-memory-request.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/in-memory-request.test.ts b/packages/request-client.js/test/in-memory-request.test.ts index 8937d21e0..411d0be28 100644 --- a/packages/request-client.js/test/in-memory-request.test.ts +++ b/packages/request-client.js/test/in-memory-request.test.ts @@ -75,7 +75,7 @@ describe('handle in-memory request', () => { expect(request.requestId).toBeDefined(); await expect(requestNetwork.persistRequest(request)).rejects.toThrow( - 'Cannot persist request when skipPersistence is enabled. Create a new instance of RequestNetwork without skipPersistence to persist the request.', + 'Cannot persist request when skipPersistence is enabled. To persist the request, create a new instance of RequestNetwork without skipPersistence being set to true.', ); }); From a5d9668b5dcb766e134b8b8ce036887ba9208e40 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 17:19:46 +0300 Subject: [PATCH 38/42] fix: fix typo --- .../request-client.js/src/api/request-network.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 65ef6b489..66438fabe 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -165,7 +165,7 @@ export default class RequestNetwork { const transactionData = requestLogicCreateResult.meta?.transactionManagerMeta.transactionData; const requestId = requestLogicCreateResult.result.requestId; - const isSkipingPersistence = this.dataAccess instanceof NoPersistHttpDataAccess; + const isSkippingPersistence = this.dataAccess instanceof NoPersistHttpDataAccess; // create the request object const request = new Request(requestId, this.requestLogic, this.currencyManager, { contentDataExtension: this.contentData, @@ -174,7 +174,7 @@ export default class RequestNetwork { skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, // inMemoryInfo is only used when skipPersistence is enabled - inMemoryInfo: isSkipingPersistence + inMemoryInfo: isSkippingPersistence ? { topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, transactionData: transactionData, @@ -183,7 +183,7 @@ export default class RequestNetwork { : null, }); - if (!options?.skipRefresh && !isSkipingPersistence) { + if (!options?.skipRefresh && !isSkippingPersistence) { // refresh the local request data await request.refresh(); } @@ -247,7 +247,7 @@ export default class RequestNetwork { const transactionData = requestLogicCreateResult.meta?.transactionManagerMeta.transactionData; const requestId = requestLogicCreateResult.result.requestId; - const isSkipingPersistence = this.dataAccess instanceof NoPersistHttpDataAccess; + const isSkippingPersistence = this.dataAccess instanceof NoPersistHttpDataAccess; // create the request object const request = new Request(requestId, this.requestLogic, this.currencyManager, { @@ -256,7 +256,7 @@ export default class RequestNetwork { requestLogicCreateResult, skipPaymentDetection: parameters.disablePaymentDetection, disableEvents: parameters.disableEvents, - inMemoryInfo: isSkipingPersistence + inMemoryInfo: isSkippingPersistence ? { topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, transactionData: transactionData, @@ -265,7 +265,7 @@ export default class RequestNetwork { : null, }); - if (!options?.skipRefresh && !isSkipingPersistence) { + if (!options?.skipRefresh && !isSkippingPersistence) { // refresh the local request data await request.refresh(); } From 422e9e055db607a0616bb52f5f674dc03d013b11 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 17:25:19 +0300 Subject: [PATCH 39/42] refactor: move `preparePaymentRequest` to the bottom of file --- .../src/api/request-network.ts | 146 +++++++++--------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 66438fabe..25b7a5e8d 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -71,79 +71,6 @@ export default class RequestNetwork { ); } - /** - * Prepares a payment request structure from transaction data. - * - * This method is used to create a request structure similar to a persisted request, - * allowing users to pay before the request is persisted. This is useful in scenarios - * where a request is created, paid, and then persisted, as opposed to the normal flow - * of creating, persisting, and then paying the request. - * - * @param transactionData The transaction data containing the request information - * @param requestId The ID of the request - * @returns The prepared payment request structure or undefined if transaction data is missing - */ - private preparePaymentRequest( - transactionData: DataAccessTypes.ITransaction, - requestId: string, - ): ClientTypes.IRequestData { - const requestData = JSON.parse(transactionData.data as string).data; - const originalExtensionsData = requestData.parameters.extensionsData; - const newExtensions: RequestLogicTypes.IExtensionStates = {}; - - for (const extension of originalExtensionsData) { - if (extension.id !== ExtensionTypes.OTHER_ID.CONTENT_DATA) { - newExtensions[extension.id] = { - events: [ - { - name: extension.action, - parameters: { - paymentAddress: extension.parameters.paymentAddress, - salt: extension.parameters.salt, - }, - timestamp: requestData.parameters.timestamp, - }, - ], - id: extension.id, - type: ExtensionTypes.TYPE.PAYMENT_NETWORK, - values: { - salt: extension.parameters.salt, - receivedPaymentAmount: '0', - receivedRefundAmount: '0', - sentPaymentAmount: '0', - sentRefundAmount: '0', - paymentAddress: extension.parameters.paymentAddress, - }, - version: extension.version, - }; - } - } - - return { - requestId: requestId, - currency: requestData.parameters.currency.type, - meta: null, - balance: null, - expectedAmount: requestData.parameters.expectedAmount, - contentData: requestData.parameters.extensionsData.find( - (ext: ExtensionTypes.IAction) => ext.id === ExtensionTypes.OTHER_ID.CONTENT_DATA, - )?.parameters.content, - currencyInfo: { - type: requestData.parameters.currency.type, - network: requestData.parameters.currency.network, - value: requestData.parameters.currency.value || '', - }, - pending: null, - extensions: newExtensions, - extensionsData: requestData.parameters.extensionsData, - timestamp: requestData.parameters.timestamp, - version: requestData.parameters.version, - creator: requestData.parameters.creator, - state: requestData.parameters.state, - events: requestData.parameters.events, - }; - } - /** * Creates a request. * @@ -559,4 +486,77 @@ export default class RequestNetwork { return { requestParameters: copiedRequestParameters, topics, paymentNetwork }; } + + /** + * Prepares a payment request structure from transaction data. + * + * This method is used to create a request structure similar to a persisted request, + * allowing users to pay before the request is persisted. This is useful in scenarios + * where a request is created, paid, and then persisted, as opposed to the normal flow + * of creating, persisting, and then paying the request. + * + * @param transactionData The transaction data containing the request information + * @param requestId The ID of the request + * @returns The prepared payment request structure or undefined if transaction data is missing + */ + private preparePaymentRequest( + transactionData: DataAccessTypes.ITransaction, + requestId: string, + ): ClientTypes.IRequestData { + const requestData = JSON.parse(transactionData.data as string).data; + const originalExtensionsData = requestData.parameters.extensionsData; + const newExtensions: RequestLogicTypes.IExtensionStates = {}; + + for (const extension of originalExtensionsData) { + if (extension.id !== ExtensionTypes.OTHER_ID.CONTENT_DATA) { + newExtensions[extension.id] = { + events: [ + { + name: extension.action, + parameters: { + paymentAddress: extension.parameters.paymentAddress, + salt: extension.parameters.salt, + }, + timestamp: requestData.parameters.timestamp, + }, + ], + id: extension.id, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + salt: extension.parameters.salt, + receivedPaymentAmount: '0', + receivedRefundAmount: '0', + sentPaymentAmount: '0', + sentRefundAmount: '0', + paymentAddress: extension.parameters.paymentAddress, + }, + version: extension.version, + }; + } + } + + return { + requestId: requestId, + currency: requestData.parameters.currency.type, + meta: null, + balance: null, + expectedAmount: requestData.parameters.expectedAmount, + contentData: requestData.parameters.extensionsData.find( + (ext: ExtensionTypes.IAction) => ext.id === ExtensionTypes.OTHER_ID.CONTENT_DATA, + )?.parameters.content, + currencyInfo: { + type: requestData.parameters.currency.type, + network: requestData.parameters.currency.network, + value: requestData.parameters.currency.value || '', + }, + pending: null, + extensions: newExtensions, + extensionsData: requestData.parameters.extensionsData, + timestamp: requestData.parameters.timestamp, + version: requestData.parameters.version, + creator: requestData.parameters.creator, + state: requestData.parameters.state, + events: requestData.parameters.events, + }; + } } From 3ccf360c8ed5a5c7bccfb3da6d73c357f7e3b078 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 17:37:28 +0300 Subject: [PATCH 40/42] refactor: rename `preparePaymentRequest` and `paymentRequest` --- packages/request-client.js/src/api/request-network.ts | 6 +++--- packages/request-client.js/src/api/request.ts | 2 +- packages/request-client.js/test/in-memory-request.test.ts | 6 +++--- packages/types/src/request-logic-types.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 25b7a5e8d..cdadae3e7 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -105,7 +105,7 @@ export default class RequestNetwork { ? { topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, transactionData: transactionData, - paymentRequest: this.preparePaymentRequest(transactionData, requestId), + requestData: this.prepareRequestDataForPayment(transactionData, requestId), } : null, }); @@ -187,7 +187,7 @@ export default class RequestNetwork { ? { topics: requestLogicCreateResult.meta.transactionManagerMeta?.topics, transactionData: transactionData, - paymentRequest: this.preparePaymentRequest(transactionData, requestId), + requestData: this.prepareRequestDataForPayment(transactionData, requestId), } : null, }); @@ -499,7 +499,7 @@ export default class RequestNetwork { * @param requestId The ID of the request * @returns The prepared payment request structure or undefined if transaction data is missing */ - private preparePaymentRequest( + private prepareRequestDataForPayment( transactionData: DataAccessTypes.ITransaction, requestId: string, ): ClientTypes.IRequestData { diff --git a/packages/request-client.js/src/api/request.ts b/packages/request-client.js/src/api/request.ts index bd3c9f7a4..bfaf15ead 100644 --- a/packages/request-client.js/src/api/request.ts +++ b/packages/request-client.js/src/api/request.ts @@ -85,7 +85,7 @@ export default class Request { * * @property transactionData - Transaction data necessary for persisting the request later on. * @property topics - Topics of the request, used for indexing and retrieval when persisting. - * @property paymentRequest - Structured data primarily used for processing payments before the request is persisted. + * @property requestData - Structured data primarily used for processing payments before the request is persisted. */ public readonly inMemoryInfo: RequestLogicTypes.IInMemoryInfo | null = null; diff --git a/packages/request-client.js/test/in-memory-request.test.ts b/packages/request-client.js/test/in-memory-request.test.ts index 411d0be28..4c367df3f 100644 --- a/packages/request-client.js/test/in-memory-request.test.ts +++ b/packages/request-client.js/test/in-memory-request.test.ts @@ -54,7 +54,7 @@ describe('handle in-memory request', () => { expect(request).toBeDefined(); expect(request.requestId).toBeDefined(); expect(request.inMemoryInfo).toBeDefined(); - expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.requestData).toBeDefined(); expect(request.inMemoryInfo?.topics).toBeDefined(); expect(request.inMemoryInfo?.transactionData).toBeDefined(); expect(spyPersistTransaction).not.toHaveBeenCalled(); @@ -69,7 +69,7 @@ describe('handle in-memory request', () => { const request = await requestNetwork.createRequest(requestCreationParams); expect(request.inMemoryInfo).toBeDefined(); - expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.requestData).toBeDefined(); expect(request.inMemoryInfo?.topics).toBeDefined(); expect(request.inMemoryInfo?.transactionData).toBeDefined(); expect(request.requestId).toBeDefined(); @@ -102,7 +102,7 @@ describe('handle in-memory request', () => { const request = await requestNetwork.createRequest(requestCreationParams); expect(request.inMemoryInfo).toBeDefined(); - expect(request.inMemoryInfo?.paymentRequest).toBeDefined(); + expect(request.inMemoryInfo?.requestData).toBeDefined(); expect(request.inMemoryInfo?.topics).toBeDefined(); expect(request.inMemoryInfo?.transactionData).toBeDefined(); expect(request.requestId).toBeDefined(); diff --git a/packages/types/src/request-logic-types.ts b/packages/types/src/request-logic-types.ts index 371483075..2caabf596 100644 --- a/packages/types/src/request-logic-types.ts +++ b/packages/types/src/request-logic-types.ts @@ -218,7 +218,7 @@ export interface IAcceptParameters { export interface IInMemoryInfo { transactionData: DataAccess.ITransaction; topics: string[]; - paymentRequest: Client.IRequestData; + requestData: Client.IRequestData; } /** Parameters to cancel a request */ From 157530fa9198688605d5806e6b488d707d1f6921 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 18:03:33 +0300 Subject: [PATCH 41/42] chore: remove await from mockServer.close --- packages/request-client.js/test/in-memory-request.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/test/in-memory-request.test.ts b/packages/request-client.js/test/in-memory-request.test.ts index 4c367df3f..e2cf5164f 100644 --- a/packages/request-client.js/test/in-memory-request.test.ts +++ b/packages/request-client.js/test/in-memory-request.test.ts @@ -33,7 +33,7 @@ describe('handle in-memory request', () => { }); afterAll(async () => { - await mockServer.close(); + mockServer.close(); mockServer.resetHandlers(); }); From 487dffe86561e601223ae0d3ec95b7f44c1cc263 Mon Sep 17 00:00:00 2001 From: Aimen Sahnoun Date: Wed, 17 Jul 2024 18:21:14 +0300 Subject: [PATCH 42/42] chore: add warning to useMockStorage about overriding skipPersistence --- packages/request-client.js/src/http-request-network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index e920e214b..4092876fd 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -22,7 +22,7 @@ export default class HttpRequestNetwork extends RequestNetwork { * * @param options.httpConfig Http config that will be used by the underlying data-access. @see ClientTypes.IHttpDataAccessConfig for available options. * @param options.nodeConnectionConfig Configuration options to connect to the node. - * @param options.useMockStorage When true, will use a mock storage in memory. Meant to simplify local development and should never be used in production. + * @param options.useMockStorage When true, will use a mock storage in memory. Meant to simplify local development and should never be used in production. Overrides `skipPersistence` when both are true. * @param options.signatureProvider Module to handle the signature. If not given it will be impossible to create new transaction (it requires to sign). * @param options.currencies custom currency list. * @param options.currencyManager custom currency manager (will override `currencies`).