From 6c8a4adec7f3579bbc76fc31fa7db1c459f550e9 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 10 Nov 2023 14:36:03 +1100 Subject: [PATCH 01/48] fix: changed `identities/CommandAuthenticate`, `identities/CommandClaim`, and `identities/CommandInvite` now use appropriate formats rather than list --- src/identities/CommandAuthenticate.ts | 16 ++++++++++------ src/identities/CommandClaim.ts | 10 ++++++---- src/identities/CommandInvite.ts | 23 +++++++++++++---------- tests/identities/claim.test.ts | 5 ++++- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/identities/CommandAuthenticate.ts b/src/identities/CommandAuthenticate.ts index 77d9bedf..737b13bf 100644 --- a/src/identities/CommandAuthenticate.ts +++ b/src/identities/CommandAuthenticate.ts @@ -91,12 +91,16 @@ class CommandAuthenticate extends CommandPolykey { this.logger.info( `Authenticated digital identity provider ${providerId}`, ); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [message.response.identityId], - }), - ); + let output: string | Uint8Array; + if (options.format === 'json') { + output = binUtils.outputFormatter({ + type: 'json', + data: { identityId: message.response.identityId }, + }); + } else { + output = `${message.response.identityId}\n`; + } + process.stdout.write(output); } else { never(); } diff --git a/src/identities/CommandClaim.ts b/src/identities/CommandClaim.ts index 0c70f0ca..5f2679fc 100644 --- a/src/identities/CommandClaim.ts +++ b/src/identities/CommandClaim.ts @@ -60,14 +60,16 @@ class CommandClaim extends CommandPolykey { }), auth, ); - const output = [`Claim Id: ${claimMessage.claimId}`]; + const data: { claimId: string; url?: string } = { + claimId: claimMessage.claimId, + }; if (claimMessage.url) { - output.push(`Url: ${claimMessage.url}`); + data.url = claimMessage.url; } process.stdout.write( binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, + type: options.format === 'json' ? 'json' : 'dict', + data, }), ); } finally { diff --git a/src/identities/CommandInvite.ts b/src/identities/CommandInvite.ts index 0f1d9272..ee00f56a 100644 --- a/src/identities/CommandInvite.ts +++ b/src/identities/CommandInvite.ts @@ -59,16 +59,19 @@ class CommandClaim extends CommandPolykey { }), auth, ); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [ - `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( - nodeId, - )}`, - ], - }), - ); + const message = `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( + nodeId, + )}`; + let output: string | Uint8Array; + if (options.format === 'json') { + output = binUtils.outputFormatter({ + type: 'json', + data: { message }, + }); + } else { + output = `${message}\n`; + } + process.stdout.write(output); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/tests/identities/claim.test.ts b/tests/identities/claim.test.ts index 11c6f9b3..c35c658b 100644 --- a/tests/identities/claim.test.ts +++ b/tests/identities/claim.test.ts @@ -92,7 +92,10 @@ describe('claim', () => { }, ); expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual(['Claim Id: 0', 'Url: test.com']); + expect(JSON.parse(stdout)).toEqual({ + claimId: '0', + url: 'test.com', + }); // Check for claim on the provider const claim = await testProvider.getClaim( testToken.identityId, From 92e105aed269ef5ae66c9acda9c433ef59986512 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 13:16:12 +1100 Subject: [PATCH 02/48] fix: `agent/CommandStatus` will now be more accurate to RPC call return under `json` format --- src/agent/CommandStatus.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/agent/CommandStatus.ts b/src/agent/CommandStatus.ts index c226c862..8f8fd985 100644 --- a/src/agent/CommandStatus.ts +++ b/src/agent/CommandStatus.ts @@ -87,7 +87,9 @@ class CommandStatus extends CommandPolykey { sourceVersion: response.sourceVersion, stateVersion: response.stateVersion, networkVersion: response.networkVersion, - ...response.versionMetadata, + ...(options.format === 'json' + ? { versionMetadata: response.versionMetadata } + : response.versionMetadata), }, }), ); From efc6b88930ed627befd1b4947bd3e20a9d5a676f Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 13:21:48 +1100 Subject: [PATCH 03/48] fix: `bootstrap/CommandBootstrap` now has a more verbose recoveryCode output --- src/bootstrap/CommandBootstrap.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/CommandBootstrap.ts b/src/bootstrap/CommandBootstrap.ts index 18dbd714..1662df4f 100644 --- a/src/bootstrap/CommandBootstrap.ts +++ b/src/bootstrap/CommandBootstrap.ts @@ -1,5 +1,6 @@ import process from 'process'; import CommandPolykey from '../CommandPolykey'; +import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; import * as binProcessors from '../utils/processors'; @@ -36,7 +37,14 @@ class CommandBootstrap extends CommandPolykey { logger: this.logger, }); this.logger.info(`Bootstrapped ${options.nodePath}`); - if (recoveryCodeOut != null) process.stdout.write(recoveryCodeOut + '\n'); + process.stdout.write( + binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'dict', + data: { + recoveryCode: recoveryCodeOut, + }, + }), + ); }); } } From 6747b8c2a55f6ff4a3dcb7951717cb1c71c4e0fb Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 13:26:41 +1100 Subject: [PATCH 04/48] fix: `identities/CommandAuthenticate` now has more concise formatting using `dict` --- src/identities/CommandAuthenticate.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/identities/CommandAuthenticate.ts b/src/identities/CommandAuthenticate.ts index 737b13bf..0003daae 100644 --- a/src/identities/CommandAuthenticate.ts +++ b/src/identities/CommandAuthenticate.ts @@ -83,7 +83,9 @@ class CommandAuthenticate extends CommandPolykey { type: options.format === 'json' ? 'json' : 'dict', data: { url: message.request.url, - ...message.request.dataMap, + ...(options.format === 'json' + ? { dataMap: message.request.dataMap } + : message.request.dataMap), }, }), ); @@ -91,16 +93,12 @@ class CommandAuthenticate extends CommandPolykey { this.logger.info( `Authenticated digital identity provider ${providerId}`, ); - let output: string | Uint8Array; - if (options.format === 'json') { - output = binUtils.outputFormatter({ - type: 'json', + process.stdout.write( + binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'dict', data: { identityId: message.response.identityId }, - }); - } else { - output = `${message.response.identityId}\n`; - } - process.stdout.write(output); + }), + ); } else { never(); } From 4177998a5c7ec26ba243ce72fc5c33f38eb17ed3 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 13:31:47 +1100 Subject: [PATCH 05/48] fix `identities/CommandInvite` now logs the feedback to `stderr` instead --- src/identities/CommandInvite.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/identities/CommandInvite.ts b/src/identities/CommandInvite.ts index ee00f56a..6367b8fa 100644 --- a/src/identities/CommandInvite.ts +++ b/src/identities/CommandInvite.ts @@ -59,19 +59,11 @@ class CommandClaim extends CommandPolykey { }), auth, ); - const message = `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( - nodeId, - )}`; - let output: string | Uint8Array; - if (options.format === 'json') { - output = binUtils.outputFormatter({ - type: 'json', - data: { message }, - }); - } else { - output = `${message}\n`; - } - process.stdout.write(output); + this.logger.info( + `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( + nodeId, + )}`, + ); } finally { if (pkClient! != null) await pkClient.stop(); } From dbe1bf03c562ee93ea3abb80aa204c5da0d6cf5d Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 16:02:16 +1100 Subject: [PATCH 06/48] fix: `keys/CommandCert` now uses dict format --- src/keys/CommandCert.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/keys/CommandCert.ts b/src/keys/CommandCert.ts index 8c94ebee..52c33ac4 100644 --- a/src/keys/CommandCert.ts +++ b/src/keys/CommandCert.ts @@ -49,17 +49,12 @@ class CommandCert extends CommandPolykey { }), auth, ); - const result = { - cert: response.cert, - }; - let output: any = result; - if (options.format === 'human') { - output = ['Root certificate:', result.cert]; - } process.stdout.write( binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, + type: options.format === 'json' ? 'json' : 'dict', + data: { + cert: response.cert, + }, }), ); } finally { From d1b92b321cdf179f54db82b7890beff8bd04d82e Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 16:07:04 +1100 Subject: [PATCH 07/48] fix: `keys/CommandCertchain` made more concise --- src/keys/CommandCertchain.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/keys/CommandCertchain.ts b/src/keys/CommandCertchain.ts index ff212622..63accba4 100644 --- a/src/keys/CommandCertchain.ts +++ b/src/keys/CommandCertchain.ts @@ -52,19 +52,23 @@ class CommandsCertchain extends CommandPolykey { } return data; }, auth); - const result = { - certchain: data, - }; - let output: any = result; - if (options.format === 'human') { - output = ['Root Certificate Chain:', ...result.certchain]; + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + certchain: data, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'list', + data, + }), + ); } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }), - ); } finally { if (pkClient! != null) await pkClient.stop(); } From f47d75af2b6fe9d9f01e647a895ede4a65fb9310 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 29 Feb 2024 16:28:23 +1100 Subject: [PATCH 08/48] fix: `keys/CommandDecrypt` and `keys/CommandEncrypt` now use raw output for non-json --- src/keys/CommandDecrypt.ts | 28 ++++++++++++++++------------ src/keys/CommandEncrypt.ts | 28 ++++++++++++++++------------ tests/keys/encryptDecrypt.test.ts | 10 +++++----- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/keys/CommandDecrypt.ts b/src/keys/CommandDecrypt.ts index f6c8af78..06e2716c 100644 --- a/src/keys/CommandDecrypt.ts +++ b/src/keys/CommandDecrypt.ts @@ -71,19 +71,23 @@ class CommandDecrypt extends CommandPolykey { }), auth, ); - const result = { - decryptedData: response.data, - }; - let output: any = result; - if (options.format === 'human') { - output = { 'Decrypted data:': result.decryptedData }; + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + data: response.data, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'raw', + data: response.data, + }), + ); } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: output, - }), - ); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/keys/CommandEncrypt.ts b/src/keys/CommandEncrypt.ts index bc124aee..7bb2d6bb 100644 --- a/src/keys/CommandEncrypt.ts +++ b/src/keys/CommandEncrypt.ts @@ -98,19 +98,23 @@ class CommandEncypt extends CommandPolykey { }), auth, ); - const result = { - encryptedData: response.data, - }; - let output: any = result; - if (options.format === 'human') { - output = { 'Encrypted data:': result.encryptedData }; + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + data: response.data, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'raw', + data: response.data, + }), + ); } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: output, - }), - ); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/tests/keys/encryptDecrypt.test.ts b/tests/keys/encryptDecrypt.test.ts index 7522901e..e1197d47 100644 --- a/tests/keys/encryptDecrypt.test.ts +++ b/tests/keys/encryptDecrypt.test.ts @@ -44,7 +44,7 @@ describe('encrypt-decrypt', () => { ); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - decryptedData: 'abc', + data: 'abc', }); }); test('encrypts data using NodeId', async () => { @@ -74,9 +74,9 @@ describe('encrypt-decrypt', () => { ); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - encryptedData: expect.any(String), + data: expect.any(String), }); - const encrypted = JSON.parse(stdout).encryptedData; + const encrypted = JSON.parse(stdout).data; const decrypted = keysUtils.decryptWithPrivateKey( targetKeyPair, Buffer.from(encrypted, 'binary'), @@ -105,9 +105,9 @@ describe('encrypt-decrypt', () => { ); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - encryptedData: expect.any(String), + data: expect.any(String), }); - const encrypted = JSON.parse(stdout).encryptedData; + const encrypted = JSON.parse(stdout).data; const decrypted = keysUtils.decryptWithPrivateKey( targetKeyPair, Buffer.from(encrypted, 'binary'), From e1f938e1c2e4fa0a7e7550b29508a68a306a7c34 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 10:56:02 +1100 Subject: [PATCH 09/48] fix: `keys/CommandSign` now uses camelcase for non-json formats --- src/keys/CommandSign.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/keys/CommandSign.ts b/src/keys/CommandSign.ts index 554f6191..fbdd7c60 100644 --- a/src/keys/CommandSign.ts +++ b/src/keys/CommandSign.ts @@ -71,17 +71,12 @@ class CommandSign extends CommandPolykey { }), auth, ); - const result = { - signature: response.signature, - }; - let output: any = result; - if (options.format === 'human') { - output = { 'Signature:': result.signature }; - } process.stdout.write( binUtils.outputFormatter({ type: options.format === 'json' ? 'json' : 'dict', - data: output, + data: { + signature: response.signature, + }, }), ); } finally { From bd22f1fa5d7eb0d2f068ae9dc4c211bfdfe33e07 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 10:57:38 +1100 Subject: [PATCH 10/48] fix: `keys/CommandVerify` now uses camelCase for non-json formats --- src/keys/CommandVerify.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/keys/CommandVerify.ts b/src/keys/CommandVerify.ts index 31389af0..7bd6ca53 100644 --- a/src/keys/CommandVerify.ts +++ b/src/keys/CommandVerify.ts @@ -107,17 +107,12 @@ class CommandVerify extends CommandPolykey { }), auth, ); - const result = { - signatureVerified: response.success, - }; - let output: any = result; - if (options.format === 'human') { - output = { 'Signature verified:': result.signatureVerified }; - } process.stdout.write( binUtils.outputFormatter({ type: options.format === 'json' ? 'json' : 'dict', - data: output, + data: { + signatureVerified: response.success, + }, }), ); } finally { From c15b7e904ef464b14282a0f81f636e461205235f Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 11:25:38 +1100 Subject: [PATCH 11/48] fix: `nodes/CommandClaim` now logs feedback to stderr, and the response to stdout. --- src/nodes/CommandClaim.ts | 39 +++++++++++++++++++-------------------- tests/nodes/claim.test.ts | 24 ++++++++++++------------ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/nodes/CommandClaim.ts b/src/nodes/CommandClaim.ts index 1d16b4c4..d5e8c7bf 100644 --- a/src/nodes/CommandClaim.ts +++ b/src/nodes/CommandClaim.ts @@ -63,28 +63,27 @@ class CommandClaim extends CommandPolykey { }), auth, ); - const claimed = response.success; - if (claimed) { - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [ - `Successfully generated a cryptolink claim on Keynode with ID ${nodesUtils.encodeNodeId( - nodeId, - )}`, - ], - }); - process.stdout.write(outputFormatted); + if (response.success) { + process.stderr.write( + `Successfully generated a cryptolink claim on Keynode with ID ${nodesUtils.encodeNodeId( + nodeId, + )}\n`, + ); } else { - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [ - `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( - nodeId, - )}`, - ], - }); - process.stdout.write(outputFormatted); + process.stderr.write( + `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( + nodeId, + )}\n`, + ); } + process.stdout.write( + binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'dict', + data: { + success: response.success, + }, + }), + ); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/tests/nodes/claim.test.ts b/tests/nodes/claim.test.ts index 531f2f7e..b33a0c12 100644 --- a/tests/nodes/claim.test.ts +++ b/tests/nodes/claim.test.ts @@ -81,8 +81,8 @@ describe('claim', () => { }); }); test('sends a gestalt invite', async () => { - const { exitCode, stdout } = await testUtils.pkStdio( - ['nodes', 'claim', remoteIdEncoded], + const { exitCode, stdout, stderr } = await testUtils.pkStdio( + ['nodes', 'claim', remoteIdEncoded, '--format', 'json'], { env: { PK_NODE_PATH: nodePath, @@ -92,15 +92,15 @@ describe('claim', () => { }, ); expect(exitCode).toBe(0); - expect(stdout).toContain('Successfully generated a cryptolink claim'); - expect(stdout).toContain(remoteIdEncoded); + expect(JSON.parse(stdout)).toMatchObject({ success: true }); + expect(stderr).toContain(remoteIdEncoded); }); test('sends a gestalt invite (force invite)', async () => { await remoteNode.notificationsManager.sendNotification(localId, { type: 'GestaltInvite', }); - const { exitCode, stdout } = await testUtils.pkStdio( - ['nodes', 'claim', remoteIdEncoded, '--force-invite'], + const { exitCode, stdout, stderr } = await testUtils.pkStdio( + ['nodes', 'claim', remoteIdEncoded, '--force-invite', '--format', 'json'], { env: { PK_NODE_PATH: nodePath, @@ -110,15 +110,15 @@ describe('claim', () => { }, ); expect(exitCode).toBe(0); - expect(stdout).toContain('Successfully generated a cryptolink'); - expect(stdout).toContain(nodesUtils.encodeNodeId(remoteId)); + expect(JSON.parse(stdout)).toMatchObject({ success: true }); + expect(stderr).toContain(nodesUtils.encodeNodeId(remoteId)); }); test('claims a node', async () => { await remoteNode.notificationsManager.sendNotification(localId, { type: 'GestaltInvite', }); - const { exitCode, stdout } = await testUtils.pkStdio( - ['nodes', 'claim', remoteIdEncoded], + const { exitCode, stdout, stderr } = await testUtils.pkStdio( + ['nodes', 'claim', remoteIdEncoded, '--format', 'json'], { env: { PK_NODE_PATH: nodePath, @@ -128,7 +128,7 @@ describe('claim', () => { }, ); expect(exitCode).toBe(0); - expect(stdout).toContain('cryptolink claim'); - expect(stdout).toContain(remoteIdEncoded); + expect(JSON.parse(stdout)).toMatchObject({ success: true }); + expect(stderr).toContain(remoteIdEncoded); }); }); From 5ebc923e40211653aae249aba0be626d413fbc58 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 11:28:35 +1100 Subject: [PATCH 12/48] fix: replaced usage of `this.info.log` with `process.stderr.write` for command feedback --- src/agent/CommandStop.ts | 8 ++++---- src/identities/CommandAuthenticate.ts | 12 +++++++----- src/identities/CommandInvite.ts | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/agent/CommandStop.ts b/src/agent/CommandStop.ts index 217ae237..de9b1da1 100644 --- a/src/agent/CommandStop.ts +++ b/src/agent/CommandStop.ts @@ -27,13 +27,13 @@ class CommandStop extends CommandPolykey { ); const statusInfo = clientStatus.statusInfo; if (statusInfo?.status === 'DEAD') { - this.logger.info('Agent is already dead'); + process.stderr.write('Agent is already dead\n'); return; } else if (statusInfo?.status === 'STOPPING') { - this.logger.info('Agent is already stopping'); + process.stderr.write('Agent is already stopping\n'); return; } else if (statusInfo?.status === 'STARTING') { - throw new errors.ErrorPolykeyCLIAgentStatus('agent is starting'); + throw new errors.ErrorPolykeyCLIAgentStatus('Agent is starting'); } const auth = await binProcessors.processAuthentication( options.passwordFile, @@ -62,7 +62,7 @@ class CommandStop extends CommandPolykey { }), auth, ); - this.logger.info('Stopping Agent'); + process.stderr.write('Stopping Agent\n'); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/identities/CommandAuthenticate.ts b/src/identities/CommandAuthenticate.ts index 0003daae..f56fde24 100644 --- a/src/identities/CommandAuthenticate.ts +++ b/src/identities/CommandAuthenticate.ts @@ -68,9 +68,11 @@ class CommandAuthenticate extends CommandPolykey { ); for await (const message of genReadable) { if (message.request != null) { - this.logger.info(`Navigate to the URL in order to authenticate`); - this.logger.info( - 'Use any additional additional properties to complete authentication', + process.stderr.write( + `Navigate to the URL in order to authenticate\n`, + ); + process.stderr.write( + 'Use any additional additional properties to complete authentication\n', ); try { await identitiesUtils.browser(message.request.url); @@ -90,8 +92,8 @@ class CommandAuthenticate extends CommandPolykey { }), ); } else if (message.response != null) { - this.logger.info( - `Authenticated digital identity provider ${providerId}`, + process.stderr.write( + `Authenticated digital identity provider ${providerId}\n`, ); process.stdout.write( binUtils.outputFormatter({ diff --git a/src/identities/CommandInvite.ts b/src/identities/CommandInvite.ts index 6367b8fa..0d40a6ed 100644 --- a/src/identities/CommandInvite.ts +++ b/src/identities/CommandInvite.ts @@ -59,10 +59,10 @@ class CommandClaim extends CommandPolykey { }), auth, ); - this.logger.info( + process.stderr.write( `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( nodeId, - )}`, + )}\n`, ); } finally { if (pkClient! != null) await pkClient.stop(); From b33a3fb92a2d2fd00014f0001f6c6cd0110ed927 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 12:10:50 +1100 Subject: [PATCH 13/48] fix: `tests/agent` tests --- tests/agent/stop.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/agent/stop.test.ts b/tests/agent/stop.test.ts index 5aa0c68f..a3e95e5d 100644 --- a/tests/agent/stop.test.ts +++ b/tests/agent/stop.test.ts @@ -220,7 +220,7 @@ describe('stop', () => { }, ); testUtils.expectProcessError(exitCode, stderr, [ - new binErrors.ErrorPolykeyCLIAgentStatus('agent is starting'), + new binErrors.ErrorPolykeyCLIAgentStatus('Agent is starting'), ]); await status.waitFor('LIVE'); await testUtils.pkStdio(['agent', 'stop'], { From aa67d09cf3aa62626ab1f0e8752aa4358e5da1b7 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 13:06:55 +1100 Subject: [PATCH 14/48] fix: `nodes/CommandConnections` has the json format check switched around --- src/nodes/CommandConnections.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/nodes/CommandConnections.ts b/src/nodes/CommandConnections.ts index 62ddece0..de8fc73d 100644 --- a/src/nodes/CommandConnections.ts +++ b/src/nodes/CommandConnections.ts @@ -57,7 +57,14 @@ class CommandAdd extends CommandPolykey { } return connectionEntries; }, auth); - if (options.format === 'human') { + if (options.format === 'json') { + // Wait for outputFormatter to complete and then write to stdout + const outputFormatted = binUtils.outputFormatter({ + type: 'json', + data: connections, + }); + process.stdout.write(outputFormatted); + } else { // Wait for outputFormatter to complete and then write to stdout const outputFormatted = binUtils.outputFormatter({ type: 'table', @@ -75,13 +82,6 @@ class CommandAdd extends CommandPolykey { }, }); process.stdout.write(outputFormatted); - } else { - // Wait for outputFormatter to complete and then write to stdout - const outputFormatted = binUtils.outputFormatter({ - type: 'json', - data: connections, - }); - process.stdout.write(outputFormatted); } } finally { if (pkClient! != null) await pkClient.stop(); From c2d6e8698c84a30f82bb3fbec4bb346190dc7db1 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 13:12:48 +1100 Subject: [PATCH 15/48] fix: `nodes/CommandFind` now outputs a dict rather than a message --- src/nodes/CommandFind.ts | 61 ++++++++++++++++++---------------------- tests/nodes/find.test.ts | 23 +++++---------- 2 files changed, 34 insertions(+), 50 deletions(-) diff --git a/src/nodes/CommandFind.ts b/src/nodes/CommandFind.ts index 83ea1d09..b00f3f41 100644 --- a/src/nodes/CommandFind.ts +++ b/src/nodes/CommandFind.ts @@ -61,12 +61,6 @@ class CommandFind extends CommandPolykey { message: '', id: '', }; - let foundAddress: - | { - host: Host | Hostname; - port: Port; - } - | undefined; try { const response = await binUtils.retryAuthentication( (auth) => @@ -79,42 +73,41 @@ class CommandFind extends CommandPolykey { result.success = true; result.id = nodesUtils.encodeNodeId(nodeId); const [host, port] = response.nodeAddress; - foundAddress = { - host, - port, - }; - result.address = foundAddress; - result.message = `Found node at ${networkUtils.buildAddress( + const formattedNodeAddress = networkUtils.buildAddress( host as Host, port as Port, - )}`; + ); + process.stderr.write(`Found node at ${formattedNodeAddress}\n`); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + nodeAddress: response.nodeAddress, + nodeContactAddressData: response.nodeContactAddressData, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: { + nodeAddress: formattedNodeAddress, + ...response.nodeContactAddressData, + }, + }), + ); + } } catch (err) { if ( !(err.cause instanceof nodesErrors.ErrorNodeGraphNodeIdNotFound) ) { throw err; } - // Else failed to find the node. - result.success = false; - result.id = nodesUtils.encodeNodeId(nodeId); - result.message = `Failed to find node ${result.id}`; - } - let outputFormatted: string | Uint8Array; - if (options.format === 'json') { - outputFormatted = binUtils.outputFormatter({ - type: 'json', - data: result, - }); - } else { - outputFormatted = binUtils.outputFormatter({ - type: 'list', - data: [`Found node at ${foundAddress}`], - }); - } - process.stdout.write(outputFormatted); - // Like ping, it should error when failing to find node for automation reasons. - if (!result.success) { - throw new errors.ErrorPolykeyCLINodeFindFailed(result.message); + throw new errors.ErrorPolykeyCLINodeFindFailed( + `Failed to find node ${nodesUtils.encodeNodeId(nodeId)}`, + ); } } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/tests/nodes/find.test.ts b/tests/nodes/find.test.ts index 7b4540be..ec5fce5f 100644 --- a/tests/nodes/find.test.ts +++ b/tests/nodes/find.test.ts @@ -98,7 +98,7 @@ describe('find', () => { }); }); test('finds an online node', async () => { - const { exitCode, stdout } = await testUtils.pkStdio( + const { exitCode, stdout, stderr } = await testUtils.pkStdio( [ 'nodes', 'find', @@ -117,14 +117,11 @@ describe('find', () => { expect(exitCode).toBe(0); const output = JSON.parse(stdout); expect(output).toMatchObject({ - success: true, - id: nodesUtils.encodeNodeId(remoteOnlineNodeId), + nodeAddress: expect.any(Object), + nodeContactAddressData: expect.any(Object), }); - expect(output.address).toMatchObject({ - host: remoteOnlineHost, - port: remoteOnlinePort, - }); - expect(output.message).toMatch( + expect(output.nodeAddress).toEqual([remoteOnlineHost, remoteOnlinePort]); + expect(stderr).toMatch( new RegExp(`Found node at .*?${remoteOnlineHost}:${remoteOnlinePort}.*?`), ); }); @@ -135,7 +132,7 @@ describe('find', () => { const unknownNodeId = nodesUtils.decodeNodeId( 'vrcacp9vsb4ht25hds6s4lpp2abfaso0mptcfnh499n35vfcn2gkg', ); - const { exitCode, stdout } = await testUtils.pkStdio( + const { exitCode, stderr } = await testUtils.pkExec( [ 'nodes', 'find', @@ -152,13 +149,7 @@ describe('find', () => { }, ); expect(exitCode).toBe(sysexits.GENERAL); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: `Failed to find node ${nodesUtils.encodeNodeId( - unknownNodeId!, - )}`, - id: nodesUtils.encodeNodeId(unknownNodeId!), - }); + expect(JSON.parse(stderr).type).toBe('ErrorPolykeyCLINodeFindFailed'); }, globalThis.failedConnectionTimeout, ); From bb00f46ffe1ce1e12374e77b0be24b5442313f70 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 13:22:13 +1100 Subject: [PATCH 16/48] fix: `nodes/CommandGetAll` now uses table format --- src/nodes/CommandGetAll.ts | 40 ++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/nodes/CommandGetAll.ts b/src/nodes/CommandGetAll.ts index 09177b60..7d96c235 100644 --- a/src/nodes/CommandGetAll.ts +++ b/src/nodes/CommandGetAll.ts @@ -57,25 +57,41 @@ class CommandGetAll extends CommandPolykey { }, auth, ); - let output: Array = []; - if (options.format === 'human') { + if (options.format === 'json') { + process.stdout.write(binUtils.outputFormatter({ + type: 'json', + data: resultOutput, + })); + } + else { + let output: Array<{ + nodeId: string; + address: string; + bucketIndex: number; + }> = []; for (const nodesGetMessage of resultOutput) { const nodeIdEncoded = nodesGetMessage.nodeIdEncoded; const bucketIndex = nodesGetMessage.bucketIndex; for (const address of Object.keys(nodesGetMessage.nodeContact)) { - output.push( - `NodeId ${nodeIdEncoded}, Address ${address}, bucketIndex ${bucketIndex}`, - ); + output.push({ + nodeId: nodeIdEncoded, + address, + bucketIndex, + }); } } - } else { - output = resultOutput; + process.stdout.write(binUtils.outputFormatter({ + type: 'table', + options: { + columns: [ + "nodeId", + "address", + "bucketIndex", + ] + }, + data: output, + })); } - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }); - process.stdout.write(outputFormatted); } finally { if (pkClient! != null) await pkClient.stop(); } From 23e98ae8c6f925c6b0e0a5cd6dd277d471b48082 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 1 Mar 2024 13:31:04 +1100 Subject: [PATCH 17/48] fix: `nodes/CommandPing` output fix: `nodes/CommandPing` stderr response message --- src/nodes/CommandGetAll.ts | 35 +++++++++++++++++------------------ src/nodes/CommandPing.ts | 16 ++++++---------- tests/nodes/ping.test.ts | 6 ++---- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/nodes/CommandGetAll.ts b/src/nodes/CommandGetAll.ts index 7d96c235..245ea048 100644 --- a/src/nodes/CommandGetAll.ts +++ b/src/nodes/CommandGetAll.ts @@ -58,13 +58,14 @@ class CommandGetAll extends CommandPolykey { auth, ); if (options.format === 'json') { - process.stdout.write(binUtils.outputFormatter({ - type: 'json', - data: resultOutput, - })); - } - else { - let output: Array<{ + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: resultOutput, + }), + ); + } else { + const output: Array<{ nodeId: string; address: string; bucketIndex: number; @@ -80,17 +81,15 @@ class CommandGetAll extends CommandPolykey { }); } } - process.stdout.write(binUtils.outputFormatter({ - type: 'table', - options: { - columns: [ - "nodeId", - "address", - "bucketIndex", - ] - }, - data: output, - })); + process.stdout.write( + binUtils.outputFormatter({ + type: 'table', + options: { + columns: ['nodeId', 'address', 'bucketIndex'], + }, + data: output, + }), + ); } } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/src/nodes/CommandPing.ts b/src/nodes/CommandPing.ts index 258e64e5..88fee3ce 100644 --- a/src/nodes/CommandPing.ts +++ b/src/nodes/CommandPing.ts @@ -57,23 +57,19 @@ class CommandPing extends CommandPolykey { }), auth, ); - const status = { success: false, message: '' }; - status.success = statusMessage ? statusMessage.success : false; - if (!status.success && !error) { + if (!statusMessage.success) { error = new errors.ErrorPolykeyCLINodePingFailed( 'No response received', ); } - if (status.success) status.message = 'Node is Active.'; - else status.message = error.message; - const output: any = - options.format === 'json' ? status : [status.message]; + if (statusMessage.success) process.stderr.write('Node is Active\n'); const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, + type: options.format === 'json' ? 'json' : 'dict', + data: { + success: statusMessage.success, + }, }); process.stdout.write(outputFormatted); - if (error != null) throw error; } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/tests/nodes/ping.test.ts b/tests/nodes/ping.test.ts index c9e8cb56..8f252933 100644 --- a/tests/nodes/ping.test.ts +++ b/tests/nodes/ping.test.ts @@ -106,7 +106,6 @@ describe('ping', () => { expect(stderr).toContain('No response received'); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', }); }, globalThis.failedConnectionTimeout, @@ -136,13 +135,12 @@ describe('ping', () => { expect(exitCode).not.toBe(0); // Should fail if node doesn't exist. expect(JSON.parse(stdout)).toEqual({ success: false, - message: `No response received`, }); }, globalThis.failedConnectionTimeout, ); test('succeed when pinging a live node', async () => { - const { exitCode, stdout } = await testUtils.pkStdio( + const { exitCode, stdout, stderr } = await testUtils.pkStdio( [ 'nodes', 'ping', @@ -161,7 +159,7 @@ describe('ping', () => { expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ success: true, - message: 'Node is Active.', }); + expect(stderr).toContain('Node is Active'); }); }); From e08d1311d80a8e2dac79d933e7f47ea7a37ff3de Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Mon, 4 Mar 2024 15:49:08 +1100 Subject: [PATCH 18/48] feat: `identities/CommandList` now uses dict and list formats --- src/identities/CommandList.ts | 46 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/identities/CommandList.ts b/src/identities/CommandList.ts index 7d7c7e43..41d2bac4 100644 --- a/src/identities/CommandList.ts +++ b/src/identities/CommandList.ts @@ -42,7 +42,6 @@ class CommandList extends CommandPolykey { }, logger: this.logger.getChild(PolykeyClient.name), }); - let output: any; const gestalts = await binUtils.retryAuthentication(async (auth) => { const gestalts: Array = []; const stream = await pkClient.rpcClient.methods.gestaltsGestaltList({ @@ -82,32 +81,41 @@ class CommandList extends CommandPolykey { } return gestalts; }, auth); - output = gestalts; - if (options.format !== 'json') { + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: gestalts, + }), + ); + } else { // Convert to a human-readable list. - output = []; let count = 1; for (const gestalt of gestalts) { - output.push(`gestalt ${count}`); - output.push(`permissions: ${gestalt.permissions ?? 'None'}`); + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: { + gestalt: count, + permissions: gestalt.permissions, + }, + }), + ); // Listing nodes - for (const node of gestalt.nodes) { - output.push(`${node.nodeId}`); - } + const nodeIds = gestalt.nodes.map((node) => node.nodeId); // Listing identities - for (const identity of gestalt.identities) { - output.push(`${identity.providerId}:${identity.identityId}`); - } - output.push(''); + const identities = gestalt.identities.map( + (identity) => `${identity.providerId}:${identity.identityId}`, + ); + process.stdout.write( + binUtils.outputFormatter({ + type: 'list', + data: nodeIds.concat(identities), + }) + '\n', + ); count++; } } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }), - ); } finally { if (pkClient! != null) await pkClient.stop(); } From f2366b950c61ed9fb5aeea0b07c9190845bdc86d Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 7 Mar 2024 16:37:55 +1100 Subject: [PATCH 19/48] feat: `secrets/CommandGet` now returns JSON with json format --- src/secrets/CommandGet.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/secrets/CommandGet.ts b/src/secrets/CommandGet.ts index 42040e53..334c1975 100644 --- a/src/secrets/CommandGet.ts +++ b/src/secrets/CommandGet.ts @@ -59,11 +59,23 @@ class CommandGet extends CommandPolykey { meta, ); const secretContent = Buffer.from(response.secretContent, 'binary'); - const outputFormatted = binUtils.outputFormatter({ - type: 'raw', - data: secretContent, - }); - process.stdout.write(outputFormatted); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + secretContent: secretContent.toString(), + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'raw', + data: secretContent, + }), + ); + } } finally { if (pkClient! != null) await pkClient.stop(); } From e87f40c7a42ba2093a9fab2d267f0a93f476619b Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 7 Mar 2024 16:40:46 +1100 Subject: [PATCH 20/48] fix: `secrets/CommandList` now returns array of objects on json format --- src/secrets/CommandList.ts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/secrets/CommandList.ts b/src/secrets/CommandList.ts index 0dc2d3a7..641d071c 100644 --- a/src/secrets/CommandList.ts +++ b/src/secrets/CommandList.ts @@ -46,23 +46,36 @@ class CommandList extends CommandPolykey { logger: this.logger.getChild(PolykeyClient.name), }); const data = await binUtils.retryAuthentication(async (auth) => { - const data: Array = []; + const data: Array<{ secretName: string }> = []; const stream = await pkClient.rpcClient.methods.vaultsSecretsList({ metadata: auth, nameOrId: vaultName, }); for await (const secret of stream) { - data.push(secret.secretName); + data.push({ + secretName: secret.secretName, + }); } return data; }, auth); - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }); - - process.stdout.write(outputFormatted); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: data, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'list', + data: data.map( + (secretsListMessage) => secretsListMessage.secretName, + ), + }), + ); + } } finally { if (pkClient! != null) await pkClient.stop(); } From afb4e50a529cdac2977655e4e8b0feab2c2fdde0 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 7 Mar 2024 16:49:46 +1100 Subject: [PATCH 21/48] fix: `secrets/CommandStat` now uses dict format --- src/secrets/CommandStat.ts | 27 ++++++++++++++++----------- tests/secrets/stat.test.ts | 22 +++++++++++++++++----- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/secrets/CommandStat.ts b/src/secrets/CommandStat.ts index 5cc6934d..78bd561e 100644 --- a/src/secrets/CommandStat.ts +++ b/src/secrets/CommandStat.ts @@ -60,18 +60,23 @@ class CommandStat extends CommandPolykey { meta, ); - const data: Array = [`Stats for "${secretPath[1]}"`]; - for (const [key, value] of Object.entries(response.stat)) { - data.push(`${key}: ${value}`); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + stat: response.stat, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: response.stat, + }), + ); } - - // Assuming the surrounding function is async - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data, - }); - - process.stdout.write(outputFormatted); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/tests/secrets/stat.test.ts b/tests/secrets/stat.test.ts index 95cbe51c..a6955336 100644 --- a/tests/secrets/stat.test.ts +++ b/tests/secrets/stat.test.ts @@ -59,16 +59,28 @@ describe('commandStat', () => { await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); }); - command = ['secrets', 'stat', '-np', dataDir, `${vaultName}:MySecret`]; + command = [ + 'secrets', + 'stat', + '-np', + dataDir, + `${vaultName}:MySecret`, + '--format', + 'json', + ]; const result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir, }); expect(result.exitCode).toBe(0); - expect(result.stdout).toContain('nlink: 1'); - expect(result.stdout).toContain('blocks: 1'); - expect(result.stdout).toContain('blksize: 4096'); - expect(result.stdout).toContain('size: 18'); + expect(JSON.parse(result.stdout)).toMatchObject({ + stat: { + nlink: 1, + blocks: 1, + blksize: 4096, + size: 18, + }, + }); }); }); From c98d50d604a7c2200752880585dad7e865c2d216 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 14 Mar 2024 13:13:23 +1100 Subject: [PATCH 22/48] fix: `vaults/CommandCreate` feedback is now in stderr --- src/vaults/CommandCreate.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vaults/CommandCreate.ts b/src/vaults/CommandCreate.ts index 456cb886..6c7c756c 100644 --- a/src/vaults/CommandCreate.ts +++ b/src/vaults/CommandCreate.ts @@ -54,11 +54,9 @@ class CommandCreate extends CommandPolykey { }), meta, ); - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [`Vault ${response.vaultIdEncoded} created successfully`], - }); - process.stdout.write(outputFormatted); + process.stderr.write( + `Vault ${response.vaultIdEncoded} created successfully`, + ); } finally { if (pkClient! != null) await pkClient.stop(); } From 11493aa0fd1b0b480658c904fb9162877e36d7ad Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 14 Mar 2024 13:14:03 +1100 Subject: [PATCH 23/48] feat: `vaults/CommandLog` now uses dict format --- src/vaults/CommandLog.ts | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/vaults/CommandLog.ts b/src/vaults/CommandLog.ts index 51852ce4..3137b172 100644 --- a/src/vaults/CommandLog.ts +++ b/src/vaults/CommandLog.ts @@ -48,7 +48,12 @@ class CommandLog extends CommandPolykey { logger: this.logger.getChild(PolykeyClient.name), }); const data = await binUtils.retryAuthentication(async (auth) => { - const data: Array = []; + const data: Array<{ + commitId: string; + committer: string; + timestamp: string; + message: string; + }> = []; const logStream = await pkClient.rpcClient.methods.vaultsLog({ metadata: auth, nameOrId: vault, @@ -56,18 +61,26 @@ class CommandLog extends CommandPolykey { commitId: options.commitId, }); for await (const logEntryMessage of logStream) { - data.push(`commit ${logEntryMessage.commitId}`); - data.push(`committer ${logEntryMessage.committer}`); - data.push(`Date: ${logEntryMessage.timestamp}`); - data.push(`${logEntryMessage.message}`); + data.push({ + commitId: logEntryMessage.commitId, + committer: logEntryMessage.committer, + timestamp: logEntryMessage.timestamp, + message: logEntryMessage.message, + }); } return data; }, meta); - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }); - process.stdout.write(outputFormatted); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ type: 'json', data }), + ); + } else { + for (const entry of data) { + process.stdout.write( + binUtils.outputFormatter({ type: 'dict', data: entry }), + ); + } + } } finally { if (pkClient! != null) await pkClient.stop(); } From 6f5034bef09e551914dd1a5f1864a7bff1cc4ced Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 14 Mar 2024 13:26:31 +1100 Subject: [PATCH 24/48] fix: `vaults/CommandPermissions` now uses dict format --- src/vaults/CommandCreate.ts | 2 +- src/vaults/CommandPermissions.ts | 43 ++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/vaults/CommandCreate.ts b/src/vaults/CommandCreate.ts index 6c7c756c..4624955b 100644 --- a/src/vaults/CommandCreate.ts +++ b/src/vaults/CommandCreate.ts @@ -55,7 +55,7 @@ class CommandCreate extends CommandPolykey { meta, ); process.stderr.write( - `Vault ${response.vaultIdEncoded} created successfully`, + `Vault ${response.vaultIdEncoded} created successfully\n`, ); } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/src/vaults/CommandPermissions.ts b/src/vaults/CommandPermissions.ts index 69251bd2..4d202c14 100644 --- a/src/vaults/CommandPermissions.ts +++ b/src/vaults/CommandPermissions.ts @@ -45,7 +45,11 @@ class CommandPermissions extends CommandPolykey { }, logger: this.logger.getChild(PolykeyClient.name), }); - const data: Array = []; + const data: Array<{ + vaultIdEncoded: string; + nodeIdEncoded: string; + vaultPermissionList: Array; + }> = []; await binUtils.retryAuthentication(async (auth) => { const permissionStream = await pkClient.rpcClient.methods.vaultsPermissionGet({ @@ -53,19 +57,38 @@ class CommandPermissions extends CommandPolykey { nameOrId: vaultName, }); for await (const permission of permissionStream) { - const nodeId = permission.nodeIdEncoded; - const actions = permission.vaultPermissionList.join(', '); - data.push(`${nodeId}: ${actions}`); + data.push({ + vaultIdEncoded: permission.vaultIdEncoded, + nodeIdEncoded: permission.nodeIdEncoded, + vaultPermissionList: permission.vaultPermissionList, + }); } return true; }, meta); - if (data.length === 0) data.push('No permissions were found'); - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }); - process.stdout.write(outputFormatted); + if (data.length === 0) { + process.stderr.write('No permissions were found\n'); + } + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: data, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: data.map((permission: any) => { + delete permission.vaultIdEncoded; + permission.actions = permission.vaultPermissionList.join(','); + delete permission.vaultPermissionList; + return permission; + }), + }), + ); + } } finally { if (pkClient! != null) await pkClient.stop(); } From 4ddf8b8b597cd8e969ccc66fb0e36dc0817233ee Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 14 Mar 2024 13:41:15 +1100 Subject: [PATCH 25/48] fix: `notifications/CommandRead` now responds with array in case of JSON --- src/notifications/CommandRead.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/notifications/CommandRead.ts b/src/notifications/CommandRead.ts index 47b6c159..228b6fd2 100644 --- a/src/notifications/CommandRead.ts +++ b/src/notifications/CommandRead.ts @@ -82,12 +82,25 @@ class CommandRead extends CommandPolykey { }, meta, ); - for (const notification of notifications) { - const outputFormatted = binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: notification, - }); - process.stdout.write(outputFormatted); + if (notifications.length === 0) { + process.stderr.write('No notifications received\n'); + } + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: notifications, + }), + ); + } else { + for (const notification of notifications) { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: notification, + }), + ); + } } } finally { if (pkClient! != null) await pkClient.stop(); From 9aacee3854f5870b09479f0d838aab5ad59d6ef3 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 14 Mar 2024 15:18:28 +1100 Subject: [PATCH 26/48] fix: `identities/CommandPermissions` now uses `actionsList` and `vaults/CommandPermissions.ts` now uses `vaultPermissionList` --- src/identities/CommandPermissions.ts | 8 ++++---- src/vaults/CommandPermissions.ts | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/identities/CommandPermissions.ts b/src/identities/CommandPermissions.ts index 10622ebf..0468d103 100644 --- a/src/identities/CommandPermissions.ts +++ b/src/identities/CommandPermissions.ts @@ -52,7 +52,7 @@ class CommandPermissions extends CommandPolykey { logger: this.logger.getChild(PolykeyClient.name), }); const [type, id] = gestaltId; - let actions: Array = []; + let actionsList: Array = []; switch (type) { case 'node': { @@ -65,7 +65,7 @@ class CommandPermissions extends CommandPolykey { }), auth, ); - actions = res.actionsList; + actionsList = res.actionsList; } break; case 'identity': @@ -80,7 +80,7 @@ class CommandPermissions extends CommandPolykey { }), auth, ); - actions = res.actionsList; + actionsList = res.actionsList; } break; default: @@ -90,7 +90,7 @@ class CommandPermissions extends CommandPolykey { binUtils.outputFormatter({ type: options.format === 'json' ? 'json' : 'dict', data: { - permissions: actions, + actionsList, }, }), ); diff --git a/src/vaults/CommandPermissions.ts b/src/vaults/CommandPermissions.ts index 4d202c14..89df22cd 100644 --- a/src/vaults/CommandPermissions.ts +++ b/src/vaults/CommandPermissions.ts @@ -80,10 +80,8 @@ class CommandPermissions extends CommandPolykey { process.stdout.write( binUtils.outputFormatter({ type: 'dict', - data: data.map((permission: any) => { - delete permission.vaultIdEncoded; - permission.actions = permission.vaultPermissionList.join(','); - delete permission.vaultPermissionList; + data: data.map((permission) => { + permission.vaultPermissionList = permission.vaultPermissionList.join(',') as any; return permission; }), }), From 11afb516e131656fffdb76a5bc07c01560e41e1a Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 11:21:11 +1100 Subject: [PATCH 27/48] fix: `identities/CommandSearch` now has whitespace to separate each dictionary entry --- src/identities/CommandSearch.ts | 53 +++++++++++++++++++++++--------- src/vaults/CommandPermissions.ts | 3 +- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/identities/CommandSearch.ts b/src/identities/CommandSearch.ts index 56400305..0509568a 100644 --- a/src/identities/CommandSearch.ts +++ b/src/identities/CommandSearch.ts @@ -2,6 +2,7 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; import type { IdentityInfoMessage } from 'polykey/dist/client/types'; import type { ReadableStream } from 'stream/web'; import type { ClientRPCResponseResult } from 'polykey/dist/client/types'; +import { TransformStream } from 'stream/web'; import CommandPolykey from '../CommandPolykey'; import * as binOptions from '../utils/options'; import * as binUtils from '../utils'; @@ -101,20 +102,44 @@ class CommandSearch extends CommandPolykey { limit: options.limit, }); } - for await (const identityInfoMessage of readableStream) { - const output = { - providerId: identityInfoMessage.providerId, - identityId: identityInfoMessage.identityId, - name: identityInfoMessage.name, - email: identityInfoMessage.email, - url: identityInfoMessage.url, - }; - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: output, - }), - ); + readableStream = readableStream.pipeThrough( + new TransformStream({ + transform: (chunk, controller) => { + controller.enqueue({ + providerId: chunk.providerId, + identityId: chunk.identityId, + name: chunk.name, + email: chunk.email, + url: chunk.url, + }); + }, + }), + ); + if (options.format === 'json') { + for await (const output of readableStream) { + process.stdout.write( + binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'dict', + data: output, + }), + ); + } + } else { + let firstElement = true; + for await (const output of readableStream) { + if (!firstElement) { + process.stdout.write('\n'); + } + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: output, + }), + ); + if (firstElement) { + firstElement = false; + } + } } }, auth); } finally { diff --git a/src/vaults/CommandPermissions.ts b/src/vaults/CommandPermissions.ts index 89df22cd..ed839dcf 100644 --- a/src/vaults/CommandPermissions.ts +++ b/src/vaults/CommandPermissions.ts @@ -81,7 +81,8 @@ class CommandPermissions extends CommandPolykey { binUtils.outputFormatter({ type: 'dict', data: data.map((permission) => { - permission.vaultPermissionList = permission.vaultPermissionList.join(',') as any; + permission.vaultPermissionList = + permission.vaultPermissionList.join(',') as any; return permission; }), }), From f5453221f93f257f67ee5278b7d80548a6c4a621 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 11:40:14 +1100 Subject: [PATCH 28/48] fix: `nodes/CommandGetAll` now does not leak authentication metadata --- src/nodes/CommandGetAll.ts | 55 ++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/nodes/CommandGetAll.ts b/src/nodes/CommandGetAll.ts index 245ea048..5f4b03c9 100644 --- a/src/nodes/CommandGetAll.ts +++ b/src/nodes/CommandGetAll.ts @@ -44,50 +44,41 @@ class CommandGetAll extends CommandPolykey { }, logger: this.logger.getChild(PolykeyClient.name), }); - const resultOutput = await binUtils.retryAuthentication( - async (auth) => { - const result = await pkClient.rpcClient.methods.nodesGetAll({ - metadata: auth, - }); - const output: Array = []; - for await (const nodesGetMessage of result) { - output.push(nodesGetMessage); - } - return output; - }, - auth, - ); + const result = await binUtils.retryAuthentication(async (auth) => { + const result = await pkClient.rpcClient.methods.nodesGetAll({ + metadata: auth, + }); + const output: Array = []; + for await (const nodesGetMessage of result) { + output.push(nodesGetMessage); + } + return output; + }, auth); if (options.format === 'json') { process.stdout.write( binUtils.outputFormatter({ type: 'json', - data: resultOutput, + data: result.map((nodesGetMessage) => ({ + nodeIdEncoded: nodesGetMessage.nodeIdEncoded, + nodeContact: nodesGetMessage.nodeContact, + bucketIndex: nodesGetMessage.bucketIndex, + })), }), ); } else { - const output: Array<{ - nodeId: string; - address: string; - bucketIndex: number; - }> = []; - for (const nodesGetMessage of resultOutput) { - const nodeIdEncoded = nodesGetMessage.nodeIdEncoded; - const bucketIndex = nodesGetMessage.bucketIndex; - for (const address of Object.keys(nodesGetMessage.nodeContact)) { - output.push({ - nodeId: nodeIdEncoded, - address, - bucketIndex, - }); - } - } process.stdout.write( binUtils.outputFormatter({ type: 'table', options: { - columns: ['nodeId', 'address', 'bucketIndex'], + columns: ['nodeIdEncoded', 'address', 'bucketIndex'], }, - data: output, + data: result.flatMap((nodesGetMessage) => + Object.keys(nodesGetMessage.nodeContact).map((address) => ({ + nodeIdEncoded: nodesGetMessage.nodeIdEncoded, + address, + bucketIndex: nodesGetMessage.bucketIndex, + })), + ), }), ); } From a9eb4bdf472bab4627c2a058f388b70877ff2ba4 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 11:41:58 +1100 Subject: [PATCH 29/48] fix: `nodes/CommandGetAll` now has `nodeAddress` instead of `address` --- src/nodes/CommandGetAll.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nodes/CommandGetAll.ts b/src/nodes/CommandGetAll.ts index 5f4b03c9..1a7f43af 100644 --- a/src/nodes/CommandGetAll.ts +++ b/src/nodes/CommandGetAll.ts @@ -70,12 +70,12 @@ class CommandGetAll extends CommandPolykey { binUtils.outputFormatter({ type: 'table', options: { - columns: ['nodeIdEncoded', 'address', 'bucketIndex'], + columns: ['nodeIdEncoded', 'nodeAddress', 'bucketIndex'], }, data: result.flatMap((nodesGetMessage) => - Object.keys(nodesGetMessage.nodeContact).map((address) => ({ + Object.keys(nodesGetMessage.nodeContact).map((nodeAddress) => ({ nodeIdEncoded: nodesGetMessage.nodeIdEncoded, - address, + nodeAddress, bucketIndex: nodesGetMessage.bucketIndex, })), ), From e5754d7c40724a66d38f7abb9ffb11acfcc825c3 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 11:43:44 +1100 Subject: [PATCH 30/48] fix: `nodes/CommandFind` now has scopes displayed as a CSV --- src/nodes/CommandFind.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nodes/CommandFind.ts b/src/nodes/CommandFind.ts index b00f3f41..1af6a050 100644 --- a/src/nodes/CommandFind.ts +++ b/src/nodes/CommandFind.ts @@ -95,6 +95,7 @@ class CommandFind extends CommandPolykey { data: { nodeAddress: formattedNodeAddress, ...response.nodeContactAddressData, + scopes: response.nodeContactAddressData.scopes.join(','), }, }), ); From 1cb2d0bcfe3b8220a573eb0823e627c0bded2a68 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 11:47:29 +1100 Subject: [PATCH 31/48] fix: `keys/CommandVerify` output follows RPC response better --- src/keys/CommandVerify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keys/CommandVerify.ts b/src/keys/CommandVerify.ts index 7bd6ca53..4703696d 100644 --- a/src/keys/CommandVerify.ts +++ b/src/keys/CommandVerify.ts @@ -111,7 +111,7 @@ class CommandVerify extends CommandPolykey { binUtils.outputFormatter({ type: options.format === 'json' ? 'json' : 'dict', data: { - signatureVerified: response.success, + success: response.success, }, }), ); From a7faffe339064582d6bbb417a68cebfd7cb49e61 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 11:57:34 +1100 Subject: [PATCH 32/48] fix: `keys/CommandPair` is now more similar to RPC sresponse --- src/keys/CommandPair.ts | 4 ++-- tests/keys/keypair.test.ts | 4 ++-- tests/keys/renew.test.ts | 8 ++++---- tests/keys/reset.test.ts | 8 ++++---- tests/keys/signVerify.test.ts | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/keys/CommandPair.ts b/src/keys/CommandPair.ts index a9cd9218..2c5dfb10 100644 --- a/src/keys/CommandPair.ts +++ b/src/keys/CommandPair.ts @@ -59,8 +59,8 @@ class CommandKeypair extends CommandPolykey { auth, ); const pair = { - publicKey: keyPairJWK.publicKeyJwk, - privateKey: keyPairJWK.privateKeyJwe, + publicKeyJwk: keyPairJWK.publicKeyJwk, + privateKeyJwe: keyPairJWK.privateKeyJwe, }; process.stdout.write( binUtils.outputFormatter({ diff --git a/tests/keys/keypair.test.ts b/tests/keys/keypair.test.ts index 840877fd..18a8ef06 100644 --- a/tests/keys/keypair.test.ts +++ b/tests/keys/keypair.test.ts @@ -29,7 +29,7 @@ describe('keypair', () => { ); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - publicKey: { + publicKeyJwk: { alg: expect.any(String), crv: expect.any(String), ext: expect.any(Boolean), @@ -37,7 +37,7 @@ describe('keypair', () => { kty: expect.any(String), x: expect.any(String), }, - privateKey: { + privateKeyJwe: { ciphertext: expect.any(String), iv: expect.any(String), protected: expect.any(String), diff --git a/tests/keys/renew.test.ts b/tests/keys/renew.test.ts index a5500ee3..938da9a6 100644 --- a/tests/keys/renew.test.ts +++ b/tests/keys/renew.test.ts @@ -55,8 +55,8 @@ describe('renew', () => { }, ); expect(exitCode).toBe(0); - const prevPublicKey = JSON.parse(stdout).publicKey; - const prevPrivateKey = JSON.parse(stdout).privateKey; + const prevPublicKey = JSON.parse(stdout).publicKeyJwk; + const prevPrivateKey = JSON.parse(stdout).privateKeyJwe; ({ exitCode, stdout } = await testUtils.pkStdio( ['agent', 'status', '--format', 'json'], { @@ -103,8 +103,8 @@ describe('renew', () => { }, )); expect(exitCode).toBe(0); - const newPublicKey = JSON.parse(stdout).publicKey; - const newPrivateKey = JSON.parse(stdout).privateKey; + const newPublicKey = JSON.parse(stdout).publicKeyJwk; + const newPrivateKey = JSON.parse(stdout).privateKeyJwe; ({ exitCode, stdout } = await testUtils.pkStdio( ['agent', 'status', '--format', 'json'], { diff --git a/tests/keys/reset.test.ts b/tests/keys/reset.test.ts index 76aa9d7b..a37a099e 100644 --- a/tests/keys/reset.test.ts +++ b/tests/keys/reset.test.ts @@ -55,8 +55,8 @@ describe('reset', () => { }, ); expect(exitCode).toBe(0); - const prevPublicKey = JSON.parse(stdout).publicKey; - const prevPrivateKey = JSON.parse(stdout).privateKey; + const prevPublicKey = JSON.parse(stdout).publicKeyJwk; + const prevPrivateKey = JSON.parse(stdout).privateKeyJwe; ({ exitCode, stdout } = await testUtils.pkStdio( ['agent', 'status', '--format', 'json'], { @@ -104,8 +104,8 @@ describe('reset', () => { }, )); expect(exitCode).toBe(0); - const newPublicKey = JSON.parse(stdout).publicKey; - const newPrivateKey = JSON.parse(stdout).privateKey; + const newPublicKey = JSON.parse(stdout).publicKeyJwk; + const newPrivateKey = JSON.parse(stdout).privateKeyJwe; ({ exitCode, stdout } = await testUtils.pkStdio( ['agent', 'status', '--format', 'json'], { diff --git a/tests/keys/signVerify.test.ts b/tests/keys/signVerify.test.ts index 011b40a6..2de5ab6f 100644 --- a/tests/keys/signVerify.test.ts +++ b/tests/keys/signVerify.test.ts @@ -88,7 +88,7 @@ describe('sign-verify', () => { ); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - signatureVerified: true, + success: true, }); }); test('verifies a signature with JWK', async () => { @@ -122,7 +122,7 @@ describe('sign-verify', () => { ); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - signatureVerified: true, + success: true, }); }); test('verifies a signature fails with invalid JWK', async () => { From e56e0dec00f699da867e2987e1ec4e04d1ef8ba3 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:08:03 +1100 Subject: [PATCH 33/48] fix: `tests/notifications` tests --- tests/notifications/sendReadClear.test.ts | 25 +++++------------------ 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/notifications/sendReadClear.test.ts b/tests/notifications/sendReadClear.test.ts index 5bf85e8b..928c1c0e 100644 --- a/tests/notifications/sendReadClear.test.ts +++ b/tests/notifications/sendReadClear.test.ts @@ -173,10 +173,7 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = stdout - .split('\n') - .slice(undefined, -1) - .map((v) => JSON.parse(v)); + readNotifications = JSON.parse(stdout); expect(readNotifications).toHaveLength(3); expect(readNotifications[0]).toMatchObject({ data: { @@ -217,10 +214,7 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = stdout - .split('\n') - .slice(undefined, -1) - .map((v) => JSON.parse(v)); + readNotifications = JSON.parse(stdout); expect(readNotifications).toHaveLength(0); // Read notifications on reverse order ({ exitCode, stdout } = await testUtils.pkExec( @@ -234,10 +228,7 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = stdout - .split('\n') - .slice(undefined, -1) - .map((v) => JSON.parse(v)); + readNotifications = JSON.parse(stdout); expect(readNotifications).toHaveLength(3); expect(readNotifications[0]).toMatchObject({ data: { @@ -278,10 +269,7 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = stdout - .split('\n') - .slice(undefined, -1) - .map((v) => JSON.parse(v)); + readNotifications = JSON.parse(stdout); expect(readNotifications).toHaveLength(1); expect(readNotifications[0]).toMatchObject({ data: { @@ -312,10 +300,7 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = stdout - .split('\n') - .slice(undefined, -1) - .map((v) => JSON.parse(v)); + readNotifications = JSON.parse(stdout); expect(readNotifications).toHaveLength(0); }, globalThis.defaultTimeout * 3, From 47091e25142b3692288842b28611f93881253784 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:12:45 +1100 Subject: [PATCH 34/48] fix: `tests/identities` --- tests/identities/allowDisallowPermissions.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/identities/allowDisallowPermissions.test.ts b/tests/identities/allowDisallowPermissions.test.ts index f395f134..78e8806c 100644 --- a/tests/identities/allowDisallowPermissions.test.ts +++ b/tests/identities/allowDisallowPermissions.test.ts @@ -164,7 +164,7 @@ describe('allow/disallow/permissions', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - permissions: ['notify', 'scan'], + actionsList: ['notify', 'scan'], }); // Disallow both permissions ({ exitCode } = await testUtils.pkStdio( @@ -208,7 +208,7 @@ describe('allow/disallow/permissions', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - permissions: [], + actionsList: [], }); }); test('allows/disallows/gets gestalt permissions by identity', async () => { @@ -299,7 +299,7 @@ describe('allow/disallow/permissions', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - permissions: ['notify', 'scan'], + actionsList: ['notify', 'scan'], }); // Disallow both permissions ({ exitCode } = await testUtils.pkStdio( @@ -337,7 +337,7 @@ describe('allow/disallow/permissions', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ - permissions: [], + actionsList: [], }); }); test('should fail on invalid inputs', async () => { From 39a25f8508de5387240446eb247c5a60ef2db20e Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:36:10 +1100 Subject: [PATCH 35/48] fix: `keys/CommandCertchain` now outputs array directly on JSON format --- src/keys/CommandCertchain.ts | 23 ++++++----------------- tests/keys/certchain.test.ts | 4 +--- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/keys/CommandCertchain.ts b/src/keys/CommandCertchain.ts index 63accba4..20147b7b 100644 --- a/src/keys/CommandCertchain.ts +++ b/src/keys/CommandCertchain.ts @@ -52,23 +52,12 @@ class CommandsCertchain extends CommandPolykey { } return data; }, auth); - if (options.format === 'json') { - process.stdout.write( - binUtils.outputFormatter({ - type: 'json', - data: { - certchain: data, - }, - }), - ); - } else { - process.stdout.write( - binUtils.outputFormatter({ - type: 'list', - data, - }), - ); - } + process.stdout.write( + binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: data, + }), + ); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/tests/keys/certchain.test.ts b/tests/keys/certchain.test.ts index dc53eec1..2dede7f2 100644 --- a/tests/keys/certchain.test.ts +++ b/tests/keys/certchain.test.ts @@ -27,8 +27,6 @@ describe('certchain', () => { }, ); expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - certchain: expect.any(Array), - }); + expect(JSON.parse(stdout)).toEqual(expect.any(Array)); }); }); From a4f468a8dfad65cf15247421861d70661b85db1f Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:39:03 +1100 Subject: [PATCH 36/48] fix: `keys/CommandCert` will now output raw format --- src/keys/CommandCert.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/keys/CommandCert.ts b/src/keys/CommandCert.ts index 52c33ac4..11659553 100644 --- a/src/keys/CommandCert.ts +++ b/src/keys/CommandCert.ts @@ -49,14 +49,23 @@ class CommandCert extends CommandPolykey { }), auth, ); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: { - cert: response.cert, - }, - }), - ); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + cert: response.cert, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'raw', + data: response.cert, + }), + ); + } } finally { if (pkClient! != null) await pkClient.stop(); } From d02db44f705325cc756b41e1f42b8a71097942aa Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:47:58 +1100 Subject: [PATCH 37/48] fix: `keys/CommandCertchain` and `notifications/CommandRead` now better follow RPC response --- src/keys/CommandCertchain.ts | 27 +++++++++++------ src/notifications/CommandRead.ts | 18 +++++++----- tests/notifications/sendReadClear.test.ts | 36 +++++++++++------------ 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/keys/CommandCertchain.ts b/src/keys/CommandCertchain.ts index 20147b7b..23ac4775 100644 --- a/src/keys/CommandCertchain.ts +++ b/src/keys/CommandCertchain.ts @@ -43,21 +43,30 @@ class CommandsCertchain extends CommandPolykey { logger: this.logger.getChild(PolykeyClient.name), }); const data = await binUtils.retryAuthentication(async (auth) => { - const data: Array = []; + const data: Array<{ cert: string }> = []; const stream = await pkClient.rpcClient.methods.keysCertsChainGet({ metadata: auth, }); - for await (const cert of stream) { - data.push(cert.cert); + for await (const certchainGetMessage of stream) { + data.push({ cert: certchainGetMessage.cert }); } return data; }, auth); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }), - ); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: data, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'list', + data: data.map((certchainGetMessage) => certchainGetMessage.cert), + }), + ); + } } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/notifications/CommandRead.ts b/src/notifications/CommandRead.ts index 228b6fd2..330f966e 100644 --- a/src/notifications/CommandRead.ts +++ b/src/notifications/CommandRead.ts @@ -61,7 +61,7 @@ class CommandRead extends CommandPolykey { }, logger: this.logger.getChild(PolykeyClient.name), }); - const notifications = await binUtils.retryAuthentication( + const notificationReadMessages = await binUtils.retryAuthentication( async (auth) => { const response = await pkClient.rpcClient.methods.notificationsRead( { @@ -71,33 +71,35 @@ class CommandRead extends CommandPolykey { order: options.order, }, ); - const notifications: Array = []; + const notificationReadMessages: Array<{ + notification: Notification; + }> = []; for await (const notificationMessage of response) { const notification = notificationsUtils.parseNotification( notificationMessage.notification, ); - notifications.push(notification); + notificationReadMessages.push({ notification }); } - return notifications; + return notificationReadMessages; }, meta, ); - if (notifications.length === 0) { + if (notificationReadMessages.length === 0) { process.stderr.write('No notifications received\n'); } if (options.format === 'json') { process.stdout.write( binUtils.outputFormatter({ type: 'json', - data: notifications, + data: notificationReadMessages, }), ); } else { - for (const notification of notifications) { + for (const notificationReadMessage of notificationReadMessages) { process.stdout.write( binUtils.outputFormatter({ type: 'dict', - data: notification, + data: notificationReadMessage.notification, }), ); } diff --git a/tests/notifications/sendReadClear.test.ts b/tests/notifications/sendReadClear.test.ts index 928c1c0e..17ecc18f 100644 --- a/tests/notifications/sendReadClear.test.ts +++ b/tests/notifications/sendReadClear.test.ts @@ -63,7 +63,7 @@ describe('send/read/claim', () => { 'sends, receives, and clears notifications', async () => { let exitCode: number, stdout: string; - let readNotifications: Array; + let readNotificationMessages: Array<{ notification: Notification }>; // Add receiver to sender's node graph, so it can be contacted ({ exitCode } = await testUtils.pkExec( [ @@ -173,9 +173,9 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = JSON.parse(stdout); - expect(readNotifications).toHaveLength(3); - expect(readNotifications[0]).toMatchObject({ + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(3); + expect(readNotificationMessages[0].notification).toMatchObject({ data: { type: 'General', message: 'test message 3', @@ -184,7 +184,7 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - expect(readNotifications[1]).toMatchObject({ + expect(readNotificationMessages[1].notification).toMatchObject({ data: { type: 'General', message: 'test message 2', @@ -193,7 +193,7 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - expect(readNotifications[2]).toMatchObject({ + expect(readNotificationMessages[2].notification).toMatchObject({ data: { type: 'General', message: 'test message 1', @@ -214,8 +214,8 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = JSON.parse(stdout); - expect(readNotifications).toHaveLength(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(0); // Read notifications on reverse order ({ exitCode, stdout } = await testUtils.pkExec( ['notifications', 'read', '--order=oldest', '--format', 'json'], @@ -228,9 +228,9 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = JSON.parse(stdout); - expect(readNotifications).toHaveLength(3); - expect(readNotifications[0]).toMatchObject({ + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(3); + expect(readNotificationMessages[0].notification).toMatchObject({ data: { type: 'General', message: 'test message 1', @@ -239,7 +239,7 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - expect(readNotifications[1]).toMatchObject({ + expect(readNotificationMessages[1].notification).toMatchObject({ data: { type: 'General', message: 'test message 2', @@ -248,7 +248,7 @@ describe('send/read/claim', () => { sub: nodesUtils.encodeNodeId(receiverId), isRead: true, }); - expect(readNotifications[2]).toMatchObject({ + expect(readNotificationMessages[2].notification).toMatchObject({ data: { type: 'General', message: 'test message 3', @@ -269,9 +269,9 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = JSON.parse(stdout); - expect(readNotifications).toHaveLength(1); - expect(readNotifications[0]).toMatchObject({ + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(1); + expect(readNotificationMessages[0].notification).toMatchObject({ data: { type: 'General', message: 'test message 3', @@ -300,8 +300,8 @@ describe('send/read/claim', () => { }, )); expect(exitCode).toBe(0); - readNotifications = JSON.parse(stdout); - expect(readNotifications).toHaveLength(0); + readNotificationMessages = JSON.parse(stdout); + expect(readNotificationMessages).toHaveLength(0); }, globalThis.defaultTimeout * 3, ); From b521b502e824e0dfe455d384354d3fdd0a0f3085 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:49:45 +1100 Subject: [PATCH 38/48] fix: `identities/CommandList` now only has newline separators for each gestalt --- src/identities/CommandList.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/identities/CommandList.ts b/src/identities/CommandList.ts index 41d2bac4..35ec5569 100644 --- a/src/identities/CommandList.ts +++ b/src/identities/CommandList.ts @@ -107,11 +107,12 @@ class CommandList extends CommandPolykey { const identities = gestalt.identities.map( (identity) => `${identity.providerId}:${identity.identityId}`, ); + if (count !== 1) process.stdout.write('\n'); process.stdout.write( binUtils.outputFormatter({ type: 'list', data: nodeIds.concat(identities), - }) + '\n', + }), ); count++; } From 5be1faec1bcdff96af845cc6668fbadf437186b6 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:53:40 +1100 Subject: [PATCH 39/48] fix: `identities/CommandGet` is now closer to RPC output --- src/identities/CommandGet.ts | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/identities/CommandGet.ts b/src/identities/CommandGet.ts index e8b1e267..28514107 100644 --- a/src/identities/CommandGet.ts +++ b/src/identities/CommandGet.ts @@ -88,10 +88,17 @@ class CommandGet extends CommandPolykey { utils.never(); } const gestalt = res!.gestalt; - let output: any = gestalt; - if (options.format !== 'json') { - // Creating a list. - output = []; + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + gestalt, + }, + }), + ); + } else { + const output: Array = []; // Listing nodes. for (const nodeKey of Object.keys(gestalt.nodes)) { const node = gestalt.nodes[nodeKey]; @@ -102,13 +109,13 @@ class CommandGet extends CommandPolykey { const identity = gestalt.identities[identityKey]; output.push(`${identity.providerId}:${identity.identityId}`); } + process.stdout.write( + binUtils.outputFormatter({ + type: 'list', + data: output, + }), + ); } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }), - ); } finally { if (pkClient! != null) await pkClient.stop(); } From 1d10aa2b1032a242a000bf38f062947c5b883a32 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:56:39 +1100 Subject: [PATCH 40/48] fix: `identities/CommandPermissions` now shows CSV permissions --- src/identities/CommandPermissions.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/identities/CommandPermissions.ts b/src/identities/CommandPermissions.ts index 0468d103..803ae521 100644 --- a/src/identities/CommandPermissions.ts +++ b/src/identities/CommandPermissions.ts @@ -86,14 +86,25 @@ class CommandPermissions extends CommandPolykey { default: utils.never(); } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: { - actionsList, - }, - }), - ); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + actionsList, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: { + actionsList: actionsList.join(','), + }, + }), + ); + } } finally { if (pkClient! != null) await pkClient.stop(); } From 56654ca57b5ddad98c4586a7c78fc368c71446ad Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 15 Mar 2024 12:58:53 +1100 Subject: [PATCH 41/48] fix: `identities/CommandAuthenticate` is logic now more concise --- src/identities/CommandAuthenticate.ts | 32 ++++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/identities/CommandAuthenticate.ts b/src/identities/CommandAuthenticate.ts index f56fde24..4064d995 100644 --- a/src/identities/CommandAuthenticate.ts +++ b/src/identities/CommandAuthenticate.ts @@ -80,17 +80,27 @@ class CommandAuthenticate extends CommandPolykey { if (e.code !== 'ENOENT') throw e; // Otherwise we ignore `ENOENT` as a failure to spawn a browser } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: { - url: message.request.url, - ...(options.format === 'json' - ? { dataMap: message.request.dataMap } - : message.request.dataMap), - }, - }), - ); + if (options.format === 'json') { + process.stdout.write( + binUtils.outputFormatter({ + type: 'json', + data: { + url: message.request.url, + dataMap: message.request.dataMap, + }, + }), + ); + } else { + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: { + url: message.request.url, + ...message.request.dataMap, + }, + }), + ); + } } else if (message.response != null) { process.stderr.write( `Authenticated digital identity provider ${providerId}\n`, From 3fda9b451aa951ce1d89d57837e63be674d7faa2 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Mon, 18 Mar 2024 11:34:34 +1100 Subject: [PATCH 42/48] fix: `identities/CommandList` now more closely follows RPC response fix: whitespace on `identities/CommandList` fix: `test/identities/trustUntrustList.test.ts` --- src/identities/CommandList.ts | 87 +++++++++++----------- tests/identities/trustUntrustList.test.ts | 88 ++++++++++++++--------- 2 files changed, 99 insertions(+), 76 deletions(-) diff --git a/src/identities/CommandList.ts b/src/identities/CommandList.ts index 35ec5569..7b4ead87 100644 --- a/src/identities/CommandList.ts +++ b/src/identities/CommandList.ts @@ -1,4 +1,5 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import type { GestaltMessage } from 'polykey/dist/client/types'; import CommandPolykey from '../CommandPolykey'; import * as binOptions from '../utils/options'; import * as binUtils from '../utils'; @@ -42,72 +43,70 @@ class CommandList extends CommandPolykey { }, logger: this.logger.getChild(PolykeyClient.name), }); - const gestalts = await binUtils.retryAuthentication(async (auth) => { - const gestalts: Array = []; - const stream = await pkClient.rpcClient.methods.gestaltsGestaltList({ - metadata: auth, - }); - for await (const gestaltMessage of stream) { - const gestalt = gestaltMessage.gestalt; - const newGestalt: any = { - permissions: [], - nodes: [], - identities: [], - }; - for (const node of Object.keys(gestalt.nodes)) { - const nodeInfo = gestalt.nodes[node]; - newGestalt.nodes.push({ nodeId: nodeInfo.nodeId }); - } - for (const identity of Object.keys(gestalt.identities)) { - const identityInfo = gestalt.identities[identity]; - newGestalt.identities.push({ - providerId: identityInfo.providerId, - identityId: identityInfo.identityId, + const gestaltMessages = await binUtils.retryAuthentication( + async (auth) => { + const gestaltMessages: Array< + GestaltMessage & { gestalt: { actionsList: Array } } + > = []; + const stream = await pkClient.rpcClient.methods.gestaltsGestaltList( + { + metadata: auth, + }, + ); + for await (const gestaltMessage of stream) { + // Getting the permissions for the gestalt. + const actionsMessage = await binUtils.retryAuthentication( + (auth) => + pkClient.rpcClient.methods.gestaltsActionsGetByNode({ + metadata: auth, + nodeIdEncoded: Object.values( + gestaltMessage.gestalt.nodes, + )[0].nodeId, + }), + auth, + ); + const actionsList = actionsMessage.actionsList; + gestaltMessages.push({ + gestalt: { + ...gestaltMessage.gestalt, + actionsList, + }, }); } - // Getting the permissions for the gestalt. - const actionsMessage = await binUtils.retryAuthentication( - (auth) => - pkClient.rpcClient.methods.gestaltsActionsGetByNode({ - metadata: auth, - nodeIdEncoded: newGestalt.nodes[0].nodeId, - }), - auth, - ); - const actionList = actionsMessage.actionsList; - if (actionList.length === 0) newGestalt.permissions = null; - else newGestalt.permissions = actionList; - gestalts.push(newGestalt); - } - return gestalts; - }, auth); + return gestaltMessages; + }, + auth, + ); if (options.format === 'json') { process.stdout.write( binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: gestalts, + type: 'json', + data: gestaltMessages, }), ); } else { // Convert to a human-readable list. let count = 1; - for (const gestalt of gestalts) { + for (const gestaltMessage of gestaltMessages) { + const gestalt = gestaltMessage.gestalt; + if (count !== 1) process.stdout.write('\n'); process.stdout.write( binUtils.outputFormatter({ type: 'dict', data: { gestalt: count, - permissions: gestalt.permissions, + actionsList: gestalt.actionsList.join(','), }, }), ); // Listing nodes - const nodeIds = gestalt.nodes.map((node) => node.nodeId); + const nodeIds = Object.values(gestalt.nodes).map( + (node) => node.nodeId as string, + ); // Listing identities - const identities = gestalt.identities.map( + const identities = Object.values(gestalt.identities).map( (identity) => `${identity.providerId}:${identity.identityId}`, ); - if (count !== 1) process.stdout.write('\n'); process.stdout.write( binUtils.outputFormatter({ type: 'list', diff --git a/tests/identities/trustUntrustList.test.ts b/tests/identities/trustUntrustList.test.ts index 40952748..8dabd913 100644 --- a/tests/identities/trustUntrustList.test.ts +++ b/tests/identities/trustUntrustList.test.ts @@ -172,15 +172,21 @@ describe('trust/untrust/list', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toHaveLength(2); - expect(JSON.parse(stdout)[0]).toEqual({ - permissions: ['notify'], - nodes: [{ nodeId: nodesUtils.encodeNodeId(nodeId) }], - identities: [ - { - providerId: provider.id, - identityId: identity, + expect(JSON.parse(stdout)[0]).toMatchObject({ + gestalt: { + actionsList: ['notify'], + nodes: { + ['node-' + nodesUtils.encodeNodeId(nodeId)]: { + nodeId: nodesUtils.encodeNodeId(nodeId), + }, }, - ], + identities: { + ['identity-' + JSON.stringify([provider.id, identity])]: { + providerId: provider.id, + identityId: identity, + }, + }, + }, }); // Untrust the gestalt by node // This should remove the permission, but not the gestalt (from the gestalt @@ -209,15 +215,21 @@ describe('trust/untrust/list', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toHaveLength(2); - expect(JSON.parse(stdout)[0]).toEqual({ - permissions: null, - nodes: [{ nodeId: nodesUtils.encodeNodeId(nodeId) }], - identities: [ - { - providerId: provider.id, - identityId: identity, + expect(JSON.parse(stdout)[0]).toMatchObject({ + gestalt: { + actionsList: [], + nodes: { + ['node-' + nodesUtils.encodeNodeId(nodeId)]: { + nodeId: nodesUtils.encodeNodeId(nodeId), + }, }, - ], + identities: { + ['identity-' + JSON.stringify([provider.id, identity])]: { + providerId: provider.id, + identityId: identity, + }, + }, + }, }); // Revert side-effects await pkAgent.gestaltGraph.unsetNode(nodeId); @@ -316,15 +328,21 @@ describe('trust/untrust/list', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toHaveLength(2); - expect(JSON.parse(stdout)[0]).toEqual({ - permissions: ['notify'], - nodes: [{ nodeId: nodesUtils.encodeNodeId(nodeId) }], - identities: [ - { - providerId: provider.id, - identityId: identity, + expect(JSON.parse(stdout)[0]).toMatchObject({ + gestalt: { + actionsList: ['notify'], + nodes: { + ['node-' + nodesUtils.encodeNodeId(nodeId)]: { + nodeId: nodesUtils.encodeNodeId(nodeId), + }, }, - ], + identities: { + ['identity-' + JSON.stringify([provider.id, identity])]: { + providerId: provider.id, + identityId: identity, + }, + }, + }, }); // Untrust the gestalt by node // This should remove the permission, but not the gestalt (from the gestalt @@ -353,15 +371,21 @@ describe('trust/untrust/list', () => { )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toHaveLength(2); - expect(JSON.parse(stdout)[0]).toEqual({ - permissions: null, - nodes: [{ nodeId: nodesUtils.encodeNodeId(nodeId) }], - identities: [ - { - providerId: provider.id, - identityId: identity, + expect(JSON.parse(stdout)[0]).toMatchObject({ + gestalt: { + actionsList: [], + nodes: { + ['node-' + nodesUtils.encodeNodeId(nodeId)]: { + nodeId: nodesUtils.encodeNodeId(nodeId), + }, }, - ], + identities: { + ['identity-' + JSON.stringify([provider.id, identity])]: { + providerId: provider.id, + identityId: identity, + }, + }, + }, }); // Revert side-effects await pkAgent.gestaltGraph.unsetNode(nodeId); From 1462efc27990f646d7c72e3961da9486688efb79 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 21 Mar 2024 13:15:42 +1100 Subject: [PATCH 43/48] feat: nested objects on `outputFormatterDict` feat: `dict` formatter now also supports nested arrays fix: `outputFormatterDict` will not have incorrect output when an array element is a number --- src/agent/CommandStatus.ts | 5 ++-- src/types.ts | 5 ++++ src/utils/utils.ts | 58 ++++++++++++++++++++++++++++---------- tests/utils.test.ts | 45 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/agent/CommandStatus.ts b/src/agent/CommandStatus.ts index 8f8fd985..3805e4c7 100644 --- a/src/agent/CommandStatus.ts +++ b/src/agent/CommandStatus.ts @@ -81,15 +81,14 @@ class CommandStatus extends CommandPolykey { agentHost: response.agentHost, agentPort: response.agentPort, upTime: getUnixtime() - response.startTime, + startTime: response.startTime, connectionsActive: response.connectionsActive, nodesTotal: response.nodesTotal, version: response.version, sourceVersion: response.sourceVersion, stateVersion: response.stateVersion, networkVersion: response.networkVersion, - ...(options.format === 'json' - ? { versionMetadata: response.versionMetadata } - : response.versionMetadata), + versionMetadata: response.versionMetadata, }, }), ); diff --git a/src/types.ts b/src/types.ts index a6ded81e..cc9fe0f7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,6 +13,10 @@ interface TableOptions { includeRowCount?: boolean; } +interface DictOptions { + padding?: number; +} + type AgentStatusLiveData = Omit & { nodeId: NodeIdEncoded; }; @@ -51,6 +55,7 @@ type AgentChildProcessOutput = export type { TableRow, TableOptions, + DictOptions, AgentStatusLiveData, AgentChildProcessInput, AgentChildProcessOutput, diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 8ff76bd5..bfefb68a 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,5 @@ import type { POJO } from 'polykey/dist/types'; -import type { TableRow, TableOptions } from '../types'; +import type { TableRow, TableOptions, DictOptions } from '../types'; import process from 'process'; import { LogLevel } from '@matrixai/logger'; import ErrorPolykey from 'polykey/dist/ErrorPolykey'; @@ -40,6 +40,7 @@ type OutputObject = | { type: 'dict'; data: POJO; + options?: DictOptions; } | { type: 'json'; @@ -219,7 +220,7 @@ function outputFormatter(msg: OutputObject): string | Uint8Array { case 'table': return outputFormatterTable(msg.data, msg.options); case 'dict': - return outputFormatterDict(msg.data); + return outputFormatterDict(msg.data, msg.options); case 'json': return outputFormatterJson(msg.data); case 'error': @@ -351,35 +352,62 @@ function outputFormatterTable( return output; } -function outputFormatterDict(data: POJO): string { +function outputFormatterDict( + data: POJO, + { + padding = 0, + }: { + padding?: number; + } = {}, +): string { let output = ''; let maxKeyLength = 0; + const leftPadding = ' '.repeat(padding); // Array<[originalKey, encodedKey]> const keypairs: Array<[string, string]> = []; - for (const key in data) { - const encodedKey = encodeEscapedWrapped(key); - keypairs.push([key, encodedKey]); - if (encodedKey.length > maxKeyLength) { - maxKeyLength = encodedKey.length; + const dataIsArray = Array.isArray(data); + if (!dataIsArray) { + for (const key in data) { + const encodedKey = encodeEscapedWrapped(key); + keypairs.push([key, encodedKey]); + if (encodedKey.length > maxKeyLength) { + maxKeyLength = encodedKey.length; + } + } + } else { + for (const key of data) { + const encodedKey = encodeEscapedWrapped(key); + keypairs.push([key, encodedKey]); + if (encodedKey.length > maxKeyLength) { + maxKeyLength = encodedKey.length; + } } } for (const [originalKey, encodedKey] of keypairs) { + const rightPadding = ' '.repeat(maxKeyLength - encodedKey.length); + output += `${leftPadding}${encodedKey}${rightPadding}\t`; + + if (dataIsArray) { + output += '\n'; + continue; + } + let value = data[originalKey]; if (value == null) { value = ''; - } - - if (typeof value === 'string') { + } else if (typeof value == 'object') { + output += `\n${outputFormatterDict(value, { + padding: padding + 2, + })}`; + continue; + } else if (typeof value === 'string') { value = encodeEscapedWrapped(value); } else { value = JSON.stringify(value, encodeEscapedReplacer); } - value = value.replace(/(?:\r\n|\n)$/, ''); value = value.replace(/(\r\n|\n)/g, '$1\t'); - - const padding = ' '.repeat(maxKeyLength - encodedKey.length); - output += `${encodedKey}${padding}\t${value}\n`; + output += `${value}\n`; } return output; } diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 8ddd69af..a6c1e857 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -119,6 +119,51 @@ describe('bin/utils', () => { }), ).toBe('{"key1":"value1","key2":"value2"}\n'); }); + test('dict nesting in human format', () => { + // Dict + expect( + binUtils.outputFormatter({ + type: 'dict', + data: { key1: {}, key2: {} }, + }), + ).toBe('key1\t\nkey2\t\n'); + expect( + binUtils.outputFormatter({ + type: 'dict', + data: { key1: ['value1', 'value2', 'value3'] }, + }), + ).toBe('key1\t\n value1\t\n value2\t\n value3\t\n'); + expect( + binUtils.outputFormatter({ + type: 'dict', + data: { + key1: { + key2: null, + key3: undefined, + key4: 'value', + }, + key5: 'value', + key6: { + key7: { + key8: { + key9: 'value', + }, + }, + }, + }, + }), + ).toBe( + 'key1\t\n' + + ' key2\t\n' + + ' key3\t\n' + + ' key4\tvalue\n' + + 'key5\tvalue\n' + + 'key6\t\n' + + ' key7\t\n' + + ' key8\t\n' + + ' key9\tvalue\n', + ); + }); test('outputFormatter should encode non-printable characters within a dict', () => { fc.assert( fc.property( From aa9306b5b609462dcfc315d2e3921a00f3517668 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 21 Mar 2024 13:59:42 +1100 Subject: [PATCH 44/48] fix: `identities/CommandList` now uses nested `dict` format --- src/identities/CommandList.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/identities/CommandList.ts b/src/identities/CommandList.ts index 7b4ead87..151cca74 100644 --- a/src/identities/CommandList.ts +++ b/src/identities/CommandList.ts @@ -90,27 +90,21 @@ class CommandList extends CommandPolykey { for (const gestaltMessage of gestaltMessages) { const gestalt = gestaltMessage.gestalt; if (count !== 1) process.stdout.write('\n'); - process.stdout.write( - binUtils.outputFormatter({ - type: 'dict', - data: { - gestalt: count, - actionsList: gestalt.actionsList.join(','), - }, - }), - ); - // Listing nodes const nodeIds = Object.values(gestalt.nodes).map( (node) => node.nodeId as string, ); - // Listing identities const identities = Object.values(gestalt.identities).map( (identity) => `${identity.providerId}:${identity.identityId}`, ); process.stdout.write( binUtils.outputFormatter({ - type: 'list', - data: nodeIds.concat(identities), + type: 'dict', + data: { + gestalt: count, + actionsList: gestalt.actionsList.join(','), + identities, + nodeIds, + }, }), ); count++; From 2aae8bfc424db06ab0d6f291be023038c8fcddab Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 21 Mar 2024 14:23:18 +1100 Subject: [PATCH 45/48] fix: `identities/CommandList` and `identities/CommandSearch` have had their double newlines removed --- src/identities/CommandList.ts | 1 - src/identities/CommandSearch.ts | 7 ------- 2 files changed, 8 deletions(-) diff --git a/src/identities/CommandList.ts b/src/identities/CommandList.ts index 151cca74..cec11578 100644 --- a/src/identities/CommandList.ts +++ b/src/identities/CommandList.ts @@ -89,7 +89,6 @@ class CommandList extends CommandPolykey { let count = 1; for (const gestaltMessage of gestaltMessages) { const gestalt = gestaltMessage.gestalt; - if (count !== 1) process.stdout.write('\n'); const nodeIds = Object.values(gestalt.nodes).map( (node) => node.nodeId as string, ); diff --git a/src/identities/CommandSearch.ts b/src/identities/CommandSearch.ts index 0509568a..b4489bd9 100644 --- a/src/identities/CommandSearch.ts +++ b/src/identities/CommandSearch.ts @@ -125,20 +125,13 @@ class CommandSearch extends CommandPolykey { ); } } else { - let firstElement = true; for await (const output of readableStream) { - if (!firstElement) { - process.stdout.write('\n'); - } process.stdout.write( binUtils.outputFormatter({ type: 'dict', data: output, }), ); - if (firstElement) { - firstElement = false; - } } } }, auth); From 0c74fe629b69500ebe2757daadbec4ec62052f95 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 21 Mar 2024 15:43:10 +1100 Subject: [PATCH 46/48] fix: `vaults/CommandPermissions` output now correctly iterates over each permission --- src/vaults/CommandPermissions.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vaults/CommandPermissions.ts b/src/vaults/CommandPermissions.ts index ed839dcf..add23da9 100644 --- a/src/vaults/CommandPermissions.ts +++ b/src/vaults/CommandPermissions.ts @@ -77,16 +77,16 @@ class CommandPermissions extends CommandPolykey { }), ); } else { - process.stdout.write( - binUtils.outputFormatter({ - type: 'dict', - data: data.map((permission) => { - permission.vaultPermissionList = - permission.vaultPermissionList.join(',') as any; - return permission; + for (const permission of data) { + permission.vaultPermissionList = + permission.vaultPermissionList.join(',') as any; + process.stdout.write( + binUtils.outputFormatter({ + type: 'dict', + data: permission, }), - }), - ); + ); + } } } finally { if (pkClient! != null) await pkClient.stop(); From b5c3e581168a049aa53c922d0e07fbd0bcd3c644 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 22 Mar 2024 10:28:41 +1100 Subject: [PATCH 47/48] feat: `notifications/CommandRead` uses nesting to be better differentiate each notification --- src/notifications/CommandRead.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/notifications/CommandRead.ts b/src/notifications/CommandRead.ts index 330f966e..4b2811e9 100644 --- a/src/notifications/CommandRead.ts +++ b/src/notifications/CommandRead.ts @@ -99,7 +99,9 @@ class CommandRead extends CommandPolykey { process.stdout.write( binUtils.outputFormatter({ type: 'dict', - data: notificationReadMessage.notification, + data: { + notificiation: notificationReadMessage.notification, + }, }), ); } From 18d223c74fb066b5ab4040691e96063a8e18c51b Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Fri, 22 Mar 2024 10:39:11 +1100 Subject: [PATCH 48/48] feat: `identities/CommandList` now uses nesting to represent each element --- src/identities/CommandList.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/identities/CommandList.ts b/src/identities/CommandList.ts index cec11578..c8905d49 100644 --- a/src/identities/CommandList.ts +++ b/src/identities/CommandList.ts @@ -86,7 +86,6 @@ class CommandList extends CommandPolykey { ); } else { // Convert to a human-readable list. - let count = 1; for (const gestaltMessage of gestaltMessages) { const gestalt = gestaltMessage.gestalt; const nodeIds = Object.values(gestalt.nodes).map( @@ -99,14 +98,14 @@ class CommandList extends CommandPolykey { binUtils.outputFormatter({ type: 'dict', data: { - gestalt: count, - actionsList: gestalt.actionsList.join(','), - identities, - nodeIds, + gestalt: { + actionsList: gestalt.actionsList.join(','), + identities, + nodeIds, + }, }, }), ); - count++; } } } finally {