From 330cae565a96046a08bc3fcf5bbcd3b27f4810d2 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Thu, 25 Apr 2024 16:07:19 +0300 Subject: [PATCH 1/5] feat: delegation id random errors. Signed-off-by: Eugene Panteleymonchuk --- api/src/utils/nova/searchExecutor.ts | 88 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/api/src/utils/nova/searchExecutor.ts b/api/src/utils/nova/searchExecutor.ts index 1f4748847..dc0103157 100644 --- a/api/src/utils/nova/searchExecutor.ts +++ b/api/src/utils/nova/searchExecutor.ts @@ -27,10 +27,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.block(searchQuery.blockId), (response) => { - promisesResult = { - block: response.block, - error: response.error || response.message, - }; + if (response.block) { + promisesResult = { + block: response.block, + }; + } }, "Block fetch failed", ), @@ -42,10 +43,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.outputDetails(searchQuery.outputId), (response) => { - promisesResult = { - output: response.output, - error: response.error || response.message, - }; + if (response.output) { + promisesResult = { + output: response.output, + }; + } }, "Output fetch failed", ), @@ -57,10 +59,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.accountDetails(searchQuery.accountId), (response) => { - promisesResult = { - accountId: response.accountOutputDetails ? searchQuery.accountId : undefined, - error: response.error || response.message, - }; + if (response.accountOutputDetails) { + promisesResult = { + accountId: searchQuery.accountId, + }; + } }, "Account id fetch failed", ), @@ -72,10 +75,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.nftDetails(searchQuery.nftId), (response) => { - promisesResult = { - nftId: response.nftOutputDetails ? searchQuery.nftId : undefined, - error: response.error || response.message, - }; + if (response.nftOutputDetails) { + promisesResult = { + nftId: searchQuery.nftId, + }; + } }, "Nft id fetch failed", ), @@ -87,10 +91,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.anchorDetails(searchQuery.anchorId), (response) => { - promisesResult = { - anchorId: response.anchorOutputDetails ? searchQuery.anchorId : undefined, - error: response.error || response.message, - }; + if (response.anchorOutputDetails) { + promisesResult = { + anchorId: searchQuery.anchorId, + }; + } }, "Anchor id fetch failed", ), @@ -102,10 +107,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.delegationDetails(searchQuery.delegationId), (response) => { - promisesResult = { - output: response.output, - error: response.error || response.message, - }; + if (response.output) { + promisesResult = { + output: response.output, + }; + } }, "Delegation id fetch failed", ), @@ -117,10 +123,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.transactionIncludedBlock(searchQuery.transactionId), (response) => { - promisesResult = { - transactionBlock: response.block, - error: response.error || response.message, - }; + if (response.block) { + promisesResult = { + transactionBlock: response.block, + }; + } }, "Transaction included block fetch failed", ), @@ -132,10 +139,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.foundryDetails(searchQuery.foundryId), (response) => { - promisesResult = { - foundryId: response.foundryDetails ? searchQuery.foundryId : undefined, - error: response.error || response.message, - }; + if (response.foundryDetails) { + promisesResult = { + foundryId: searchQuery.foundryId, + }; + } }, "Foundry details fetch failed", ), @@ -147,9 +155,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.getSlotCommitment(searchQuery.slotIndex), (_) => { - promisesResult = { - slotIndex: String(searchQuery.slotIndex), - }; + if (searchQuery.slotIndex) { + promisesResult = { + slotIndex: String(searchQuery.slotIndex), + }; + } }, "Slot commitment fetch failed", ), @@ -161,9 +171,11 @@ export class SearchExecutor { this.executeQuery( this.apiService.getCommitment(searchQuery.slotCommitmentId), (result) => { - promisesResult = { - slotIndex: String(result.slot.slot), - }; + if (result?.slot?.slot) { + promisesResult = { + slotIndex: String(result.slot.slot), + }; + } }, "Slot commitment fetch failed", ), From 38b03c10e822e915041bdb535eb7fe3b0c69ad66 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Thu, 25 Apr 2024 17:39:11 +0300 Subject: [PATCH 2/5] Change Promise.all to Promise.allSettled Signed-off-by: Eugene Panteleymonchuk --- api/src/utils/nova/searchExecutor.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/src/utils/nova/searchExecutor.ts b/api/src/utils/nova/searchExecutor.ts index dc0103157..38c85b463 100644 --- a/api/src/utils/nova/searchExecutor.ts +++ b/api/src/utils/nova/searchExecutor.ts @@ -111,6 +111,7 @@ export class SearchExecutor { promisesResult = { output: response.output, }; + return response.output; } }, "Delegation id fetch failed", @@ -198,7 +199,7 @@ export class SearchExecutor { ); } - await Promise.any(promises).catch((_) => {}); + await Promise.allSettled(promises); if (promisesResult !== null) { return promisesResult; @@ -217,10 +218,10 @@ export class SearchExecutor { try { const result = await query; if (result) { - successHandler(result); - } else { - throw new Error(failureMessage); + return successHandler(result); } + + throw new Error(failureMessage); } catch { throw new Error(`${failureMessage}`); } From 0a22aed89cdec2f4d270090d35c7a90cab452936 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 26 Apr 2024 15:14:21 +0300 Subject: [PATCH 3/5] feat: Change Promise.allSettled to Promise.any back. Signed-off-by: Eugene Panteleymonchuk --- api/src/utils/nova/searchExecutor.ts | 323 ++++++++++++++++----------- 1 file changed, 187 insertions(+), 136 deletions(-) diff --git a/api/src/utils/nova/searchExecutor.ts b/api/src/utils/nova/searchExecutor.ts index 38c85b463..b906f4d57 100644 --- a/api/src/utils/nova/searchExecutor.ts +++ b/api/src/utils/nova/searchExecutor.ts @@ -24,182 +24,246 @@ export class SearchExecutor { if (searchQuery.blockId) { promises.push( - this.executeQuery( - this.apiService.block(searchQuery.blockId), - (response) => { - if (response.block) { - promisesResult = { - block: response.block, - }; - } - }, - "Block fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .block(searchQuery.blockId) + .then((response) => { + if (response.block) { + promisesResult = { + block: response.block, + }; + resolve(); + } else { + reject(new Error("Block fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Block fetch failed")); + }); + }), ); } if (searchQuery.outputId) { promises.push( - this.executeQuery( - this.apiService.outputDetails(searchQuery.outputId), - (response) => { - if (response.output) { - promisesResult = { - output: response.output, - }; - } - }, - "Output fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .outputDetails(searchQuery.outputId) + .then((response) => { + if (response.output) { + promisesResult = { + output: response.output, + }; + resolve(); + } else { + reject(new Error("Output fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Output fetch failed")); + }); + }), ); } if (searchQuery.accountId) { promises.push( - this.executeQuery( - this.apiService.accountDetails(searchQuery.accountId), - (response) => { - if (response.accountOutputDetails) { - promisesResult = { - accountId: searchQuery.accountId, - }; - } - }, - "Account id fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .accountDetails(searchQuery.accountId) + .then((response) => { + if (response.accountOutputDetails) { + promisesResult = { + accountId: searchQuery.accountId, + }; + resolve(); + } else { + reject(new Error("Account id fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Account id fetch failed")); + }); + }), ); } if (searchQuery.nftId) { promises.push( - this.executeQuery( - this.apiService.nftDetails(searchQuery.nftId), - (response) => { - if (response.nftOutputDetails) { - promisesResult = { - nftId: searchQuery.nftId, - }; - } - }, - "Nft id fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .nftDetails(searchQuery.nftId) + .then((response) => { + if (response.nftOutputDetails) { + promisesResult = { + nftId: searchQuery.nftId, + }; + resolve(); + } else { + reject(new Error("Nft id fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Nft id fetch failed")); + }); + }), ); } if (searchQuery.anchorId) { promises.push( - this.executeQuery( - this.apiService.anchorDetails(searchQuery.anchorId), - (response) => { - if (response.anchorOutputDetails) { - promisesResult = { - anchorId: searchQuery.anchorId, - }; - } - }, - "Anchor id fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .anchorDetails(searchQuery.anchorId) + .then((response) => { + if (response.anchorOutputDetails) { + promisesResult = { + anchorId: searchQuery.anchorId, + }; + } else { + reject(new Error("Anchor id fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Anchor id fetch failed")); + }); + }), ); } if (searchQuery.delegationId) { promises.push( - this.executeQuery( - this.apiService.delegationDetails(searchQuery.delegationId), - (response) => { - if (response.output) { - promisesResult = { - output: response.output, - }; - return response.output; - } - }, - "Delegation id fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .delegationDetails(searchQuery.delegationId) + .then((response) => { + if (response.output) { + promisesResult = { + output: response.output, + }; + resolve(); + } else { + reject(new Error("Anchor id fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Anchor id fetch failed")); + }); + }), ); } if (searchQuery.transactionId) { promises.push( - this.executeQuery( - this.apiService.transactionIncludedBlock(searchQuery.transactionId), - (response) => { - if (response.block) { - promisesResult = { - transactionBlock: response.block, - }; - } - }, - "Transaction included block fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .transactionIncludedBlock(searchQuery.transactionId) + .then((response) => { + if (response.block) { + promisesResult = { + transactionBlock: response.block, + }; + resolve(); + } else { + reject(new Error("Transaction included block fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Transaction included block fetch failed")); + }); + }), ); } if (searchQuery.foundryId) { promises.push( - this.executeQuery( - this.apiService.foundryDetails(searchQuery.foundryId), - (response) => { - if (response.foundryDetails) { - promisesResult = { - foundryId: searchQuery.foundryId, - }; - } - }, - "Foundry details fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .foundryDetails(searchQuery.foundryId) + .then((response) => { + if (response.foundryDetails) { + promisesResult = { + foundryId: searchQuery.foundryId, + }; + resolve(); + } else { + reject(new Error("Foundry details fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Foundry details fetch failed")); + }); + }), ); } if (searchQuery.slotIndex) { promises.push( - this.executeQuery( - this.apiService.getSlotCommitment(searchQuery.slotIndex), - (_) => { - if (searchQuery.slotIndex) { - promisesResult = { - slotIndex: String(searchQuery.slotIndex), - }; - } - }, - "Slot commitment fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .getSlotCommitment(searchQuery.slotIndex) + .then((response) => { + if (searchQuery.slotIndex) { + promisesResult = { + slotIndex: String(searchQuery.slotIndex), + }; + resolve(); + } else { + reject(new Error("Slot commitment fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Slot commitment fetch failed")); + }); + }), ); } if (searchQuery.slotCommitmentId) { promises.push( - this.executeQuery( - this.apiService.getCommitment(searchQuery.slotCommitmentId), - (result) => { - if (result?.slot?.slot) { - promisesResult = { - slotIndex: String(result.slot.slot), - }; - } - }, - "Slot commitment fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .getCommitment(searchQuery.slotCommitmentId) + .then((response) => { + if (response?.slot?.slot) { + promisesResult = { + slotIndex: String(response.slot.slot), + }; + resolve(); + } else { + reject(new Error("Slot commitment fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Slot commitment fetch failed")); + }); + }), ); } if (searchQuery.tag) { promises.push( - this.executeQuery( - this.apiService.taggedOutputs(searchQuery.tag), - (response) => { - if (!response.basicOutputs.error || !response.nftOutputs.error) { - promisesResult = { - taggedOutputs: response, - }; - } - }, - "Tagged details fetch failed", - ), + new Promise((resolve, reject) => { + this.apiService + .taggedOutputs(searchQuery.tag) + .then((response) => { + if (!response.basicOutputs.error || !response.nftOutputs.error) { + promisesResult = { + taggedOutputs: response, + }; + resolve(); + } else { + reject(new Error("Tagged details fetch failed")); + } + }) + .catch((_) => { + reject(new Error("Tagged details fetch failed")); + }); + }), ); } - await Promise.allSettled(promises); + await Promise.any(promises).catch((_) => {}); if (promisesResult !== null) { return promisesResult; @@ -213,17 +277,4 @@ export class SearchExecutor { return { message: "Nothing found" }; } - - private async executeQuery(query: Promise, successHandler: (result: T) => void, failureMessage: string): Promise { - try { - const result = await query; - if (result) { - return successHandler(result); - } - - throw new Error(failureMessage); - } catch { - throw new Error(`${failureMessage}`); - } - } } From 1207d2482f1875b48c3c1c297d565e01fbb863cb Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 26 Apr 2024 16:16:44 +0300 Subject: [PATCH 4/5] feat: PR comments. Signed-off-by: Eugene Panteleymonchuk --- api/src/utils/nova/searchExecutor.ts | 12 +++++++----- api/src/utils/validationHelper.ts | 4 ++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api/src/utils/nova/searchExecutor.ts b/api/src/utils/nova/searchExecutor.ts index b906f4d57..1ebf6b952 100644 --- a/api/src/utils/nova/searchExecutor.ts +++ b/api/src/utils/nova/searchExecutor.ts @@ -3,6 +3,7 @@ import { ServiceFactory } from "../../factories/serviceFactory"; import { ISearchResponse } from "../../models/api/nova/ISearchResponse"; import { INetwork } from "../../models/db/INetwork"; import { NovaApiService } from "../../services/nova/novaApiService"; +import { ValidationHelper } from "../validationHelper"; export class SearchExecutor { /** @@ -120,6 +121,7 @@ export class SearchExecutor { promisesResult = { anchorId: searchQuery.anchorId, }; + resolve(); } else { reject(new Error("Anchor id fetch failed")); } @@ -143,11 +145,11 @@ export class SearchExecutor { }; resolve(); } else { - reject(new Error("Anchor id fetch failed")); + reject(new Error("Delegation id fetch failed")); } }) .catch((_) => { - reject(new Error("Anchor id fetch failed")); + reject(new Error("Delegation id fetch failed")); }); }), ); @@ -203,9 +205,9 @@ export class SearchExecutor { this.apiService .getSlotCommitment(searchQuery.slotIndex) .then((response) => { - if (searchQuery.slotIndex) { + if (ValidationHelper.isNumber(response?.slot?.slot)) { promisesResult = { - slotIndex: String(searchQuery.slotIndex), + slotIndex: String(response.slot?.slot), }; resolve(); } else { @@ -225,7 +227,7 @@ export class SearchExecutor { this.apiService .getCommitment(searchQuery.slotCommitmentId) .then((response) => { - if (response?.slot?.slot) { + if (ValidationHelper.isNumber(response?.slot?.slot)) { promisesResult = { slotIndex: String(response.slot.slot), }; diff --git a/api/src/utils/validationHelper.ts b/api/src/utils/validationHelper.ts index 53bd126d4..5f15e50d3 100644 --- a/api/src/utils/validationHelper.ts +++ b/api/src/utils/validationHelper.ts @@ -13,6 +13,10 @@ export class ValidationHelper { } } + public static isNumber(value?: number | null): boolean { + return typeof value === "number" && !Number.isNaN(value); + } + /** * Does the string have some content and is a decimal number. * @param str The string to validate. From d36a2966636a30385e12538669cba94e06411903 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 26 Apr 2024 16:17:55 +0300 Subject: [PATCH 5/5] feat: PR comments pt2. Signed-off-by: Eugene Panteleymonchuk --- api/src/utils/nova/searchExecutor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/utils/nova/searchExecutor.ts b/api/src/utils/nova/searchExecutor.ts index 1ebf6b952..7d12fedac 100644 --- a/api/src/utils/nova/searchExecutor.ts +++ b/api/src/utils/nova/searchExecutor.ts @@ -249,7 +249,7 @@ export class SearchExecutor { this.apiService .taggedOutputs(searchQuery.tag) .then((response) => { - if (!response.basicOutputs.error || !response.nftOutputs.error) { + if (response?.basicOutputs?.outputs || response?.nftOutputs?.outputs) { promisesResult = { taggedOutputs: response, };