From 5db5d32c0bc71bc0d0c0f3656c8f3d20c861a261 Mon Sep 17 00:00:00 2001 From: Matthew Dean Date: Fri, 17 Mar 2023 14:57:17 +0000 Subject: [PATCH] Improve error on ProcessInvalid (#85) * Improve error on ProcessInvalid * Make tests great again --- app/api-v3/routes/run-process.js | 8 ++++++++ app/util/appUtil.js | 12 +++++++++++- app/util/errors.js | 10 ++++++++++ package-lock.json | 4 ++-- package.json | 2 +- test/helper/substrateHelper.js | 19 ++++++++++--------- test/integration/regressions.test.js | 5 +++++ test/integration/routes.test.js | 17 ++++++++++++----- test/test.env | 2 +- 9 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 app/util/errors.js diff --git a/app/api-v3/routes/run-process.js b/app/api-v3/routes/run-process.js index d8ce2e6..2d5e112 100644 --- a/app/api-v3/routes/run-process.js +++ b/app/api-v3/routes/run-process.js @@ -2,6 +2,7 @@ import logger from '../../logger.js' import { validateInputIds, processRoles, processMetadata, validateProcess } from '../../util/appUtil.js' import { getDefaultSecurity } from '../../util/auth.js' import env from '../../env.js' +import { ExtrinsicError } from '../../util/errors.js' const { PROCESS_IDENTIFIER_LENGTH } = env @@ -84,6 +85,13 @@ export default function (apiService) { try { result = await apiService.runProcess(process, request.inputs, outputs) } catch (err) { + if (err instanceof ExtrinsicError) { + res.status(err.code).json({ + message: err.message, + }) + return + } + logger.error(`Unexpected error running process: ${err}`) res.status(500).json({ message: `Unexpected error processing items`, diff --git a/app/util/appUtil.js b/app/util/appUtil.js index 499a6ae..35c7ae0 100644 --- a/app/util/appUtil.js +++ b/app/util/appUtil.js @@ -12,6 +12,7 @@ const bs58 = basex(BASE58) import env from '../env.js' import logger from '../logger.js' import { substrateApi as api, keyring } from './substrateApi.js' +import { ExtrinsicError } from './errors.js' const { USER_URI, @@ -226,6 +227,15 @@ export async function getMembers() { return membersRaw.map((m) => m.toString()) } +function ProcessExtrinsicError(error) { + if (!error.isModule) { + return new ExtrinsicError('Unknown', 500) + } + + const decoded = api.registry.findMetaError(error.asModule) + return new ExtrinsicError(decoded.name, decoded.name === 'ProcessInvalid' ? 400 : 500) +} + export async function runProcess(process, inputs, outputs) { if (inputs && outputs) { await api.isReady @@ -247,7 +257,7 @@ export async function runProcess(process, inputs, outputs) { .map(({ event: { data } }) => data[0]) if (errors.length > 0) { - reject('ExtrinsicFailed error in simpleNFT') + reject(ProcessExtrinsicError(errors[0])) } const tokens = result.events diff --git a/app/util/errors.js b/app/util/errors.js new file mode 100644 index 0000000..7b1ee54 --- /dev/null +++ b/app/util/errors.js @@ -0,0 +1,10 @@ +export class ExtrinsicError extends Error { + error + code + + constructor(errorType, code) { + super(`Error processing extrinsic: ${errorType}`) + this.error = errorType + this.code = code + } +} diff --git a/package-lock.json b/package-lock.json index bbe95b2..dd80578 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dscp-api", - "version": "5.0.0", + "version": "5.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dscp-api", - "version": "5.0.0", + "version": "5.0.1", "license": "Apache-2.0", "dependencies": { "@polkadot/api": "^10.0.0", diff --git a/package.json b/package.json index 41278af..0fd18a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dscp-api", - "version": "5.0.0", + "version": "5.0.1", "description": "DSCP API", "type": "module", "repository": { diff --git a/test/helper/substrateHelper.js b/test/helper/substrateHelper.js index 404c417..f6eb24b 100644 --- a/test/helper/substrateHelper.js +++ b/test/helper/substrateHelper.js @@ -1,8 +1,15 @@ import { before, after } from 'mocha' import { substrateApi as api, keyring } from '../../app/util/substrateApi.js' -export const withNewTestProcess = (process) => { - const processStr = 'test-process' +export const withNewTestProcess = ( + process, + restrictions = [ + { + Restriction: 'None', + }, + ] +) => { + const processStr = process.name || 'test-process' const buffer = Buffer.from(processStr, 'utf8') const processId = `0x${buffer.toString('hex')}` let processVersion @@ -14,13 +21,7 @@ export const withNewTestProcess = (process) => { const newProcess = await new Promise((resolve) => { let unsub = null api.tx.sudo - .sudo( - api.tx.processValidation.createProcess(processId, [ - { - Restriction: 'None', - }, - ]) - ) + .sudo(api.tx.processValidation.createProcess(processId, restrictions)) .signAndSend(sudo, (result) => { if (result.status.isInBlock) { const { event } = result.events.find(({ event: { method } }) => method === 'ProcessCreated') diff --git a/test/integration/regressions.test.js b/test/integration/regressions.test.js index 0556e3f..e21a3cf 100644 --- a/test/integration/regressions.test.js +++ b/test/integration/regressions.test.js @@ -12,6 +12,7 @@ import { getItemRoute, getLastTokenIdRoute } from '../helper/routeHelper.js' const USER_ALICE_TOKEN = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' import { indexToRole } from '../../app/util/appUtil.js' import env from '../../app/env.js' +import { withNewTestProcess } from '../helper/substrateHelper.js' const { API_MAJOR_VERSION, AUTH_ISSUER, AUTH_AUDIENCE, AUTH_TYPE } = env const describeAuthOnly = AUTH_TYPE === 'JWT' ? describe : describe.skip @@ -26,6 +27,7 @@ describeAuthOnly('Bug regression tests', function () { let jwksMock let authToken let statusHandler + let process = {} before(async () => { nock.disableNetConnect() @@ -50,6 +52,8 @@ describeAuthOnly('Bug regression tests', function () { }) }) + withNewTestProcess(process) + after(async function () { await jwksMock.stop() }) @@ -70,6 +74,7 @@ describeAuthOnly('Bug regression tests', function () { .field( 'request', JSON.stringify({ + process, inputs: [], outputs, }) diff --git a/test/integration/routes.test.js b/test/integration/routes.test.js index 37ebb36..7420498 100644 --- a/test/integration/routes.test.js +++ b/test/integration/routes.test.js @@ -278,6 +278,7 @@ describe('routes', function () { let authToken let statusHandler const process = {} + const failProcess = { name: 'fail' } before(async function () { const server = await createHttpServer() @@ -301,6 +302,7 @@ describe('routes', function () { }) withNewTestProcess(process) + withNewTestProcess(failProcess, [{ Restriction: 'Fail' }]) describe('happy path', function () { test('add and get item - single metadata FILE', async function () { @@ -338,7 +340,6 @@ describe('routes', function () { const getItemResult = await getItemRoute(app, authToken, { id: firstTokenId + 1 }) expect(getItemResult.status).to.equal(200) expect(getItemResult.body.id).to.equal(firstTokenId + 1) - expect(getItemResult.body.original_id).to.equal(firstTokenId) }) test('add and get item - single metadata LITERAL', async function () { @@ -552,7 +553,7 @@ describe('routes', function () { metadata: new Map([[key, { File: base64Metadata }]]), } - await runProcess(null, [], [output]) + await runProcess(process, [], [output]) await getItemRoute(app, authToken, { id: lastToken.body.id + 1 }) @@ -871,7 +872,7 @@ describe('routes', function () { metadata: { testNone: { type: 'NONE' } }, }, ] - await postRunProcess(app, authToken, [], outputs) + await postRunProcess(app, authToken, process, [], outputs) const actualResult = await postRunProcess(app, authToken, process, [lastTokenId + 1], outputs) @@ -889,7 +890,7 @@ describe('routes', function () { metadata: { testNone: { type: 'NONE' } }, }, ] - await postRunProcess(app, authToken, [], outputs) + await postRunProcess(app, authToken, process, [], outputs) const firstBurn = await postRunProcess(app, authToken, process, [lastTokenId + 1], outputs) expect(firstBurn.status).to.equal(200) @@ -1060,6 +1061,12 @@ describe('routes', function () { expect(runProcessResult.status).to.equal(400) expect(runProcessResult.body.message).to.equal(`Invalid process version: ${version}`) }) + + test('invalid inputs for process', async function () { + const runProcessResult = await postRunProcess(app, authToken, failProcess, [], []) + expect(runProcessResult.status).to.equal(400) + expect(runProcessResult.body.message).to.equal(`Error processing extrinsic: ProcessInvalid`) + }) }) }) @@ -1080,7 +1087,7 @@ describe('routes', function () { withNewTestProcess(process) - describe.only('happy path', function () { + describe('happy path', function () { test('add and get item metadata - FILE + LITERAL + TOKEN_ID + NONE', async function () { const outputs = [ { diff --git a/test/test.env b/test/test.env index 2330b63..30c6235 100644 --- a/test/test.env +++ b/test/test.env @@ -1,4 +1,4 @@ -LOG_LEVEL=trace +LOG_LEVEL=fatal PORT=3001 API_HOST=localhost