diff --git a/packages/indexer-cli/CHANGELOG.md b/packages/indexer-cli/CHANGELOG.md index 50d68a2da..cad6c3a80 100644 --- a/packages/indexer-cli/CHANGELOG.md +++ b/packages/indexer-cli/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- New `--filter` option to select only a subset of Actions query columns, when printing results to stdout. ## [0.20.3] - 2022-08-31 ### Added diff --git a/packages/indexer-cli/package.json b/packages/indexer-cli/package.json index 0441863e9..035d5c6e9 100644 --- a/packages/indexer-cli/package.json +++ b/packages/indexer-cli/package.json @@ -20,7 +20,7 @@ "prepare": "yarn format && yarn lint && yarn compile", "disputes": "yarn prepare && ./dist/cli.js indexer disputes get", "clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo", - "test": "jest --colors --verbose --runInBand", + "test": "jest --colors --verbose --runInBand --detectOpenHandles", "test:ci": "LOG_LEVEL=error jest --verbose --runInBand --ci", "test:debug": "LOG_LEVEL=debug jest --runInBand --detectOpenHandles --verbose --forceExit", "test:watch": "jest --watch --detectOpenHandles --verbose" diff --git a/packages/indexer-cli/src/__tests__/indexer/actions.test.ts b/packages/indexer-cli/src/__tests__/indexer/actions.test.ts new file mode 100644 index 000000000..64a3b76e4 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/indexer/actions.test.ts @@ -0,0 +1,103 @@ +import { cliTest, setup, teardown } from '../util' +import path from 'path' +import { Action, ActionType, ActionStatus } from '@graphprotocol/indexer-common' + +const baseDir = path.join(__dirname, '..') +describe('Indexer actions tests', () => { + describe('With indexer management server', () => { + beforeAll(setup) + afterAll(teardown) + describe('Actions help', () => { + cliTest('Indexer actions', ['indexer', 'actions'], 'references/indexer-actions', { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }) + cliTest( + 'Indexer actions help', + ['indexer', 'actions', '--help'], + 'references/indexer-actions', + { + expectedExitCode: 1, + cwd: baseDir, + timeout: 10000, + }, + ) + }) + describe('Actions queue', () => { + beforeAll(createTestAction) + afterAll(truncateActions) + cliTest( + 'Indexer actions get', + ['indexer', 'actions', 'get', 'all'], + 'references/indexer-actions-get', + { + expectedExitCode: 0, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer actions get - fields', + ['indexer', 'actions', 'get', 'all', '--fields', 'id,type,deploymentID,status'], + 'references/indexer-actions-get-fields', + { + expectedExitCode: 0, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer actions get - first', + ['indexer', 'actions', 'get', '--first', '1'], + 'references/indexer-actions-get-first', + { + expectedExitCode: 0, + cwd: baseDir, + timeout: 10000, + }, + ) + cliTest( + 'Indexer actions get - first + fields', + [ + 'indexer', + 'actions', + 'get', + '--first', + '1', + '--fields', + 'id,type,deploymentID,status', + ], + 'references/indexer-actions-get-first-fields', + { + expectedExitCode: 0, + cwd: baseDir, + timeout: 10000, + }, + ) + }) + }) +}) + +async function createTestAction() { + await Action.create({ + type: ActionType.ALLOCATE, + status: ActionStatus.SUCCESS, + deploymentID: 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr', + source: 'test', + reason: 'test', + }) + await Action.create({ + type: ActionType.UNALLOCATE, + status: ActionStatus.FAILED, + deploymentID: 'QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66', + source: 'test', + reason: 'test', + }) +} + +async function truncateActions() { + await Action.destroy({ + truncate: true, + }) +} diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions-get-fields.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-fields.stdout new file mode 100644 index 000000000..6629b7e40 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-fields.stdout @@ -0,0 +1,7 @@ +┌────┬────────────┬────────────────────────────────────────────────┬─────────┐ +│ id │ type │ deploymentID │ status │ +├────┼────────────┼────────────────────────────────────────────────┼─────────┤ +│ 2 │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ failed │ +├────┼────────────┼────────────────────────────────────────────────┼─────────┤ +│ 1 │ allocate │ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ success │ +└────┴────────────┴────────────────────────────────────────────────┴─────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first-fields.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first-fields.stdout new file mode 100644 index 000000000..d11ed69d4 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first-fields.stdout @@ -0,0 +1,5 @@ +┌────┬────────────┬────────────────────────────────────────────────┬────────┐ +│ id │ type │ deploymentID │ status │ +├────┼────────────┼────────────────────────────────────────────────┼────────┤ +│ 2 │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ failed │ +└────┴────────────┴────────────────────────────────────────────────┴────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout new file mode 100644 index 000000000..773bca940 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions-get-first.stdout @@ -0,0 +1,5 @@ +┌────┬────────────┬────────────────────────────────────────────────┬──────────────┬────────┬──────┬───────┬──────────┬────────┬────────┬───────────────┬─────────────┬────────┐ +│ id │ type │ deploymentID │ allocationID │ amount │ poi │ force │ priority │ status │ source │ failureReason │ transaction │ reason │ +├────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼────────┼────────┼───────────────┼─────────────┼────────┤ +│ 2 │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ null │ null │ null │ null │ 0 │ failed │ test │ null │ null │ test │ +└────┴────────────┴────────────────────────────────────────────────┴──────────────┴────────┴──────┴───────┴──────────┴────────┴────────┴───────────────┴─────────────┴────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout new file mode 100644 index 000000000..145603b61 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions-get.stdout @@ -0,0 +1,7 @@ +┌────┬────────────┬────────────────────────────────────────────────┬──────────────┬────────┬──────┬───────┬──────────┬─────────┬────────┬───────────────┬─────────────┬────────┐ +│ id │ type │ deploymentID │ allocationID │ amount │ poi │ force │ priority │ status │ source │ failureReason │ transaction │ reason │ +├────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼─────────┼────────┼───────────────┼─────────────┼────────┤ +│ 2 │ unallocate │ QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 │ null │ null │ null │ null │ 0 │ failed │ test │ null │ null │ test │ +├────┼────────────┼────────────────────────────────────────────────┼──────────────┼────────┼──────┼───────┼──────────┼─────────┼────────┼───────────────┼─────────────┼────────┤ +│ 1 │ allocate │ QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr │ null │ null │ null │ null │ 0 │ success │ test │ null │ null │ test │ +└────┴────────────┴────────────────────────────────────────────────┴──────────────┴────────┴──────┴───────┴──────────┴─────────┴────────┴───────────────┴─────────────┴────────┘ diff --git a/packages/indexer-cli/src/__tests__/references/indexer-actions.stdout b/packages/indexer-cli/src/__tests__/references/indexer-actions.stdout new file mode 100644 index 000000000..350144569 --- /dev/null +++ b/packages/indexer-cli/src/__tests__/references/indexer-actions.stdout @@ -0,0 +1,9 @@ +Manage indexer actions + + indexer actions queue Queue an action item + indexer actions get List one or more actions + indexer actions execute Execute approved items in the action queue + indexer actions delete Delete an item in the queue + indexer actions cancel Cancel an item in the queue + indexer actions approve Approve an action item + indexer actions Manage indexer actions diff --git a/packages/indexer-cli/src/commands/indexer/actions/get.ts b/packages/indexer-cli/src/commands/indexer/actions/get.ts index f96d8b96b..41db0f108 100644 --- a/packages/indexer-cli/src/commands/indexer/actions/get.ts +++ b/packages/indexer-cli/src/commands/indexer/actions/get.ts @@ -27,9 +27,42 @@ ${chalk.dim('Options:')} --orderBy id|deploymentID|amount|priority|...|updatedAt Order actions by a specific field (default: id) --orderDirection asc|desc Order direction (default: desc) --first [N] Fetch only the N first records (default: all records) + --fields [field1,field2,...] Comma-separated names of the fields to display (no spaces allowed between fields) -o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML ` +const actionFields: (keyof Action)[] = [ + 'id', + 'type', + 'deploymentID', + 'allocationID', + 'amount', + 'poi', + 'force', + 'priority', + 'status', + 'source', + 'failureReason', + 'transaction', + 'reason', +] + +/// Validates input for the `--fieds` option. +function validateFields(fields: string | undefined): (keyof Action)[] { + if (fields === undefined) { + return [] + } + const keys = [] + for (const key of fields.split(',')) { + if (actionFields.includes(key as keyof Action)) { + keys.push(key) + } else { + throw Error(`invalid field selector: ${key}`) + } + } + return keys as (keyof Action)[] +} + module.exports = { name: 'get', alias: [], @@ -51,6 +84,7 @@ module.exports = { o, output, first, + fields, } = parameters.options const [action] = fixParameters(parameters, { h, help }) || [] @@ -62,6 +96,7 @@ module.exports = { inputSpinner.stopAndPersist({ symbol: '💁', text: HELP }) return } + let selectedFields: (keyof Action)[] try { if (!['json', 'yaml', 'table'].includes(outputFormat)) { throw Error( @@ -107,6 +142,13 @@ module.exports = { throw Error(`Invalid value for '--first' option, must have a numeric value.`) } + if (!['undefined', 'string'].includes(typeof fields)) { + throw Error( + `Invalid value for '--fields' option, must be a comma-separated list of field names`, + ) + } + selectedFields = fields === undefined ? actionFields : validateFields(fields) + inputSpinner.succeed('Processed input parameters') } catch (error) { inputSpinner.fail(error.toString()) @@ -157,23 +199,9 @@ module.exports = { process.exitCode = 1 return } - - const displayProperties: (keyof Action)[] = [ - 'id', - 'type', - 'deploymentID', - 'allocationID', - 'amount', - 'poi', - 'force', - 'priority', - 'status', - 'source', - 'failureReason', - 'transaction', - 'reason', - ] - + const displayProperties = actionFields.filter(field => + selectedFields.includes(field), + ) printObjectOrArray(print, outputFormat, actions, displayProperties) } catch (error) { actionSpinner.fail(error.toString())