From d5a609bf5f0f4732291e46640f7474d0a3d9f547 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 13 Mar 2023 11:37:34 +0000 Subject: [PATCH 1/2] Added more detailed error messages for jdonld safeValidation errors --- managers/asset-operations-manager.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/managers/asset-operations-manager.js b/managers/asset-operations-manager.js index 4bb4678c..060be519 100644 --- a/managers/asset-operations-manager.js +++ b/managers/asset-operations-manager.js @@ -1,4 +1,5 @@ const { assertionMetadata, formatAssertion, calculateRoot } = require('assertion-tools'); +const JsonLdError = require('jsonld/lib/JsonLdError.js'); const { isEmptyObject, deriveUAL, @@ -314,9 +315,16 @@ class AssetOperationsManager { formattedPublicAssertion = publicAssertion.join('\n'); } } catch (error) { + let errorMessage; + if (error instanceof JsonLdError) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + getPublicOperationResult.data = { errorType: 'DKG_CLIENT_ERROR', - errorMessage: error.message, + errorMessage, }; } @@ -402,9 +410,15 @@ class AssetOperationsManager { formattedPrivateAssertion = privateAssertion.join('\n'); } } catch (error) { + let errorMessage; + if (error instanceof JsonLdError) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } queryPrivateOperationResult.data = { errorType: 'DKG_CLIENT_ERROR', - errorMessage: error.message, + errorMessage, }; } From 2411f9381c06f23ac2b764ebbb6fe527034ab8ca Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 13 Mar 2023 14:23:10 +0000 Subject: [PATCH 2/2] Added additional errors handling for asset operations --- managers/asset-operations-manager.js | 235 ++++++++++++++++++++------- services/utilities.js | 8 + 2 files changed, 188 insertions(+), 55 deletions(-) diff --git a/managers/asset-operations-manager.js b/managers/asset-operations-manager.js index 060be519..1f937e9e 100644 --- a/managers/asset-operations-manager.js +++ b/managers/asset-operations-manager.js @@ -78,10 +78,32 @@ class AssetOperationsManager { authToken, ); + let operationResult = {}; + let privateAssertion; let privateAssertionId; if (jsonContent.private && !isEmptyObject(jsonContent.private)) { - privateAssertion = await formatAssertion(jsonContent.private); + try { + privateAssertion = await formatAssertion(jsonContent.private); + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + operationResult.status = undefined; + operationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(operationResult, undefined), + }; + } + privateAssertionId = calculateRoot(privateAssertion); } const publicGraph = { @@ -96,7 +118,29 @@ class AssetOperationsManager { : null, ], }; - const publicAssertion = await formatAssertion(publicGraph); + + let publicAssertion; + try { + publicAssertion = await formatAssertion(publicGraph); + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + operationResult.status = undefined; + operationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(operationResult, undefined), + }; + } + const publicAssertionId = calculateRoot(publicAssertion); const contentAssetStorageAddress = await this.blockchainService.getContractAddress( @@ -160,7 +204,7 @@ class AssetOperationsManager { authToken, assertions, ); - let operationResult = await this.nodeApiService.getOperationResult( + operationResult = await this.nodeApiService.getOperationResult( endpoint, port, authToken, @@ -299,34 +343,44 @@ class AssetOperationsManager { const publicAssertion = getPublicOperationResult.data.assertion; if (validate === true && calculateRoot(publicAssertion) !== publicAssertionId) { + getPublicOperationResult.status = undefined; getPublicOperationResult.data = { errorType: 'DKG_CLIENT_ERROR', errorMessage: "Calculated root hashes don't match!", }; + + return { + operation: getOperationStatusObject(getPublicOperationResult, undefined), + }; } let result = { operation: {} }; if (contentType !== CONTENT_TYPES.PRIVATE) { let formattedPublicAssertion = publicAssertion; - try { - if (outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) { - formattedPublicAssertion = await toJSONLD(publicAssertion.join('\n')); - } else { - formattedPublicAssertion = publicAssertion.join('\n'); - } - } catch (error) { - let errorMessage; - if (error instanceof JsonLdError) { - errorMessage = error.details.message; - } else { - errorMessage = error.message; - } - - getPublicOperationResult.data = { - errorType: 'DKG_CLIENT_ERROR', - errorMessage, - }; - } + if (outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) { + try { + formattedPublicAssertion = await toJSONLD(publicAssertion.join('\n')); + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + getPublicOperationResult.status = undefined; + getPublicOperationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(getPublicOperationResult, undefined), + }; + } + } else { + formattedPublicAssertion = publicAssertion.join('\n'); + } if (contentType === CONTENT_TYPES.PUBLIC) { result = { @@ -341,7 +395,7 @@ class AssetOperationsManager { }; } - result.operation.publicGet = getOperationStatusObject( + result.operation = getOperationStatusObject( getPublicOperationResult, getPublicOperationId, ); @@ -385,11 +439,31 @@ class AssetOperationsManager { ); const privateAssertionNQuads = queryPrivateOperationResult.data; - - const privateAssertion = await toNQuads( - privateAssertionNQuads, - 'application/n-quads', - ); + + let privateAssertion; + try { + privateAssertion = await toNQuads( + privateAssertionNQuads, + 'application/n-quads', + ); + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + queryPrivateOperationResult.status = undefined; + queryPrivateOperationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(queryPrivateOperationResult, undefined), + }; + } let formattedPrivateAssertion; if ( @@ -397,29 +471,40 @@ class AssetOperationsManager { validate === true && calculateRoot(privateAssertion) !== privateAssertionId ) { + queryPrivateOperationResult.status = undefined; queryPrivateOperationResult.data = { errorType: 'DKG_CLIENT_ERROR', errorMessage: "Calculated root hashes don't match!", }; + + return { + operation: getOperationStatusObject(queryPrivateOperationResult, undefined), + }; } - try { - if (outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) { + if (outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) { + try { formattedPrivateAssertion = await toJSONLD(privateAssertion.join('\n')); - } else { - formattedPrivateAssertion = privateAssertion.join('\n'); - } - } catch (error) { - let errorMessage; - if (error instanceof JsonLdError) { - errorMessage = error.details.message; - } else { - errorMessage = error.message; + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + queryPrivateOperationResult.status = undefined; + queryPrivateOperationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(queryPrivateOperationResult, undefined), + }; } - queryPrivateOperationResult.data = { - errorType: 'DKG_CLIENT_ERROR', - errorMessage, - }; + } else { + formattedPrivateAssertion = privateAssertion.join('\n'); } if (contentType === CONTENT_TYPES.PRIVATE) { @@ -434,7 +519,7 @@ class AssetOperationsManager { assertionId: privateAssertionId, }; } - result.operation.queryPrivate = getOperationStatusObject( + result.operation = getOperationStatusObject( queryPrivateOperationResult, queryPrivateOperationId, ); @@ -482,10 +567,32 @@ class AssetOperationsManager { ); const { tokenId } = resolveUAL(UAL); + + let operationResult = {}; + let privateAssertion; let privateAssertionId; if (jsonContent.private && !isEmptyObject(jsonContent.private)) { - privateAssertion = await formatAssertion(jsonContent.private); + try { + privateAssertion = await formatAssertion(jsonContent.private); + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + operationResult.status = undefined; + operationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(operationResult, undefined), + }; + } privateAssertionId = calculateRoot(privateAssertion); } @@ -518,6 +625,7 @@ class AssetOperationsManager { publicAssertion = getPublicOperationResult.data.assertion; if (calculateRoot(publicAssertion) !== publicAssertionId) { + getPublicOperationResult.status = undefined; getPublicOperationResult.data = { errorType: 'DKG_CLIENT_ERROR', errorMessage: "Calculated root hashes don't match!", @@ -525,9 +633,8 @@ class AssetOperationsManager { // TODO: Check returned response return { - UAL, - getPublicOperationResult - } + operation: getOperationStatusObject(getPublicOperationResult, undefined), + }; } // Transform public assertion to include updated private assertion Id @@ -551,7 +658,27 @@ class AssetOperationsManager { : null, ], }; - publicAssertion = await formatAssertion(publicGraph); + + try { + publicAssertion = await formatAssertion(publicGraph); + } catch (error) { + let errorMessage; + if (error instanceof JsonLdError && Object.hasOwn(error, 'details')) { + errorMessage = error.details.message; + } else { + errorMessage = error.message; + } + + operationResult.status = undefined; + operationResult.data = { + errorType: 'DKG_CLIENT_ERROR', + errorMessage, + }; + + return { + operation: getOperationStatusObject(operationResult, undefined), + }; + } } const publicAssertionId = calculateRoot(publicAssertion); @@ -616,7 +743,7 @@ class AssetOperationsManager { assertions, ); - let operationResult = await this.nodeApiService.getOperationResult( + operationResult = await this.nodeApiService.getOperationResult( endpoint, port, authToken, @@ -654,13 +781,11 @@ class AssetOperationsManager { frequency, operationId, ); + return { UAL, - operation: getOperationStatusObject( - operationResult, - operationId - ), - }; + operation: getOperationStatusObject(operationResult, operationId), + } } async waitFinalization(UAL, options = {}) { diff --git a/services/utilities.js b/services/utilities.js index 73e009ca..d08419fb 100644 --- a/services/utilities.js +++ b/services/utilities.js @@ -4,6 +4,7 @@ module.exports = { nodeSupported() { return typeof window === 'undefined'; }, + isEmptyObject(object) { // eslint-disable-next-line no-unreachable-loop for (const key in object) { @@ -11,14 +12,17 @@ module.exports = { } return true; }, + toNumber(hex) { return parseInt(hex.slice(2), 16); }, + deriveUAL(blockchain, contract, tokenId) { return `did:dkg:${ blockchain.startsWith('otp') ? 'otp' : blockchain.toLowerCase() }/${contract.toLowerCase()}/${tokenId}`; }, + resolveUAL(ual) { const segments = ual.split(':'); const argsString = segments.length === 3 ? segments[2] : segments[2] + segments[3]; @@ -34,13 +38,16 @@ module.exports = { tokenId: parseInt(args[2], 10), }; }, + async sleepForMilliseconds(milliseconds) { // eslint-disable-next-line no-promise-executor-return await new Promise((r) => setTimeout(r, milliseconds)); }, + capitalizeFirstLetter(str) { return str[0].toUpperCase() + str.slice(1); }, + getOperationStatusObject(operationResult, operationId) { const operationData = operationResult.data?.errorType ? { status: operationResult.status, ...operationResult.data } @@ -51,6 +58,7 @@ module.exports = { ...operationData, }; }, + async toNQuads(content, inputFormat) { const options = { algorithm: 'URDNA2015',