Skip to content

Commit

Permalink
chore: added option to continue on error
Browse files Browse the repository at this point in the history
  • Loading branch information
aryanjassal committed Sep 24, 2024
1 parent acf03b3 commit 90aeb6a
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 8 deletions.
35 changes: 27 additions & 8 deletions src/client/handlers/VaultsSecretsGet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { DB } from '@matrixai/db';
import type {
ClientRPCRequestParams,
ClientRPCResponseResult,
ContentMessage,
ContentWithErrorMessage,
SecretIdentifierMessage,
} from '../types';
import type VaultManager from '../../vaults/VaultManager';
Expand All @@ -17,36 +17,55 @@ class VaultsSecretsGet extends DuplexHandler<
db: DB;
},
ClientRPCRequestParams<SecretIdentifierMessage>,
ClientRPCResponseResult<ContentMessage>
ClientRPCResponseResult<ContentWithErrorMessage>
> {
public handle = async function* (
input: AsyncIterable<ClientRPCRequestParams<SecretIdentifierMessage>>,
_cancel,
_meta,
ctx,
): AsyncGenerator<ClientRPCResponseResult<ContentMessage>> {
): AsyncGenerator<ClientRPCResponseResult<ContentWithErrorMessage>> {
if (ctx.signal.aborted) throw ctx.signal.reason;
const { vaultManager, db } = this.container;
yield* db.withTransactionG(async function* (tran): AsyncGenerator<
ClientRPCResponseResult<ContentMessage>
ClientRPCResponseResult<ContentWithErrorMessage>
> {
if (ctx.signal.aborted) throw ctx.signal.reason;
// As we need to preserve the order of parameters, we need to loop over
// them individually, as grouping them would make them go out of order.
let metadata: any = undefined;
for await (const secretIdentiferMessage of input) {
if (ctx.signal.aborted) throw ctx.signal.reason;
if (metadata == null) metadata = secretIdentiferMessage.metadata ?? {};
const { nameOrId, secretName } = secretIdentiferMessage;
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
const content: Buffer = await vaultManager.withVaults(
yield await vaultManager.withVaults(
[vaultId],
async (vault) => {
return await vaultOps.getSecret(vault, secretName);
try {
const content = await vaultOps.getSecret(vault, secretName);
return { secretContent: content.toString('binary') };
} catch (e) {
if (metadata?.options?.continueOnError === true) {
if (e instanceof vaultsErrors.ErrorSecretsSecretUndefined) {
return {
secretContent: '',
error: `${e.name}: ${secretName}: No such secret or directory\n`,
};
} else if (e instanceof vaultsErrors.ErrorSecretsIsDirectory) {
return {
secretContent: '',
error: `${e.name}: ${secretName}: Is a directory\n`,
};
}
}
throw e;
}
},
tran,
);

yield { secretContent: content.toString('binary') };
}
});
};
Expand Down
5 changes: 5 additions & 0 deletions src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ type ContentMessage = {
secretContent: string;
};

type ContentWithErrorMessage = ContentMessage & {
error?: string;
};

type SecretContentMessage = SecretIdentifierMessage & ContentMessage;

type SecretMkdirMessage = VaultIdentifierMessage & {
Expand Down Expand Up @@ -417,6 +421,7 @@ export type {
SecretPathMessage,
SecretIdentifierMessage,
ContentMessage,
ContentWithErrorMessage,
SecretContentMessage,
SecretMkdirMessage,
SecretDirMessage,
Expand Down
34 changes: 34 additions & 0 deletions tests/client/handlers/vaults.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,40 @@ describe('vaultsSecretsNew and vaultsSecretsDelete, vaultsSecretsGet', () => {
`${secretName1}${secretName2}${secretName3}`,
);
});
test('should not fail to get secrets on error when continueOnError is set', async () => {
// Create secrets
const secretName1 = 'test-secret1';
const secretName2 = 'test-secret2';
const vaultId = await vaultManager.createVault('test-vault');
const vaultIdEncoded = vaultsUtils.encodeVaultId(vaultId);
await createVaultSecret(vaultIdEncoded, secretName1);
await createVaultSecret(vaultIdEncoded, secretName2);
// Get secrets
const getStream = await rpcClient.methods.vaultsSecretsGet();
await (async () => {
const writer = getStream.writable.getWriter();
await writer.write({
nameOrId: vaultIdEncoded,
secretName: secretName1,
metadata: { options: { continueOnError: true } },
});
await writer.write({ nameOrId: vaultIdEncoded, secretName: 'invalid' });
await writer.write({ nameOrId: vaultIdEncoded, secretName: secretName2 });
await writer.close();
})();
let secretContent: string = '';
let errorContent: string = '';
await expect(
(async () => {
for await (const data of getStream.readable) {
if (data.error) errorContent += data.error;
else secretContent += data.secretContent;
}
})(),
).toResolve();
expect(secretContent).toStrictEqual(`${secretName1}${secretName2}`);
expect(errorContent.length).not.toBe(0);
});
test('deletes multiple secrets from the same vault', async () => {
// Create secrets
const secretName1 = 'test-secret1';
Expand Down

0 comments on commit 90aeb6a

Please sign in to comment.