Skip to content

Commit

Permalink
chore: simplified RPC handler
Browse files Browse the repository at this point in the history
  • Loading branch information
aryanjassal committed Oct 8, 2024
1 parent f672cd4 commit e949857
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 123 deletions.
43 changes: 16 additions & 27 deletions src/client/handlers/VaultsSecretsMkdir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,46 +24,35 @@ class VaultsSecretsMkdir extends DuplexHandler<
): AsyncGenerator<ClientRPCResponseResult<SuccessWithErrorMessage>> {
const { vaultManager, db }: { vaultManager: VaultManager; db: DB } =
this.container;
// Create a record of secrets to be removed, grouped by vault names
const vaultGroups: Record<string, Array<string>> = {};
const dirPaths: Array<[string, string]> = [];
let metadata: any = undefined;
for await (const secretDirMessage of input) {
if (metadata == null) metadata = secretDirMessage.metadata ?? {};
dirPaths.push([secretDirMessage.nameOrId, secretDirMessage.dirName]);
}
dirPaths.forEach(([vaultName, dirPath]) => {
if (vaultGroups[vaultName] == null) {
vaultGroups[vaultName] = [];
}
vaultGroups[vaultName].push(dirPath);
});
// Use the grouping to create directories for each vault in one commit
yield* db.withTransactionG(
async function* (tran): AsyncGenerator<SuccessWithErrorMessage> {
for (const [vaultName, dirPaths] of Object.entries(vaultGroups)) {
const vaultIdFromName = await vaultManager.getVaultId(
vaultName,
tran,
);
for await (const secretDirMessage of input) {
// Unpack input
if (metadata == null) metadata = secretDirMessage.metadata ?? {};
const nameOrId = secretDirMessage.nameOrId;
const dirName = secretDirMessage.dirName;
// Get vaultId
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
const vaultId =
vaultIdFromName ?? vaultsUtils.decodeVaultId(vaultName);
vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
if (vaultId == null) {
throw new vaultsErrors.ErrorVaultsVaultUndefined();
}
yield* vaultManager.withVaultsG(
// Write directories. This doesn't need to be grouped by vault names,
// as no commit is created for empty directories anyways.
let response: SuccessWithErrorMessage = { success: false };
await vaultManager.withVaults(
[vaultId],
async function* (vault): AsyncGenerator<SuccessWithErrorMessage> {
const response = vaultOps.mkdir(vault, dirPaths, {
async (vault) => {
response = await vaultOps.mkdir(vault, dirName, {
recursive: metadata?.options?.recursive,
});
for await (const data of response) {
if (data.success) yield { success: true };
else yield { success: false, error: data.error };
}
},
tran,
);
if (response.success) yield { success: true };
else yield { success: false, error: response.error };
}
},
);
Expand Down
59 changes: 28 additions & 31 deletions src/vaults/VaultOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type Logger from '@matrixai/logger';
import type { Vault } from './Vault';
import type { Stat } from 'encryptedfs';
import type { SuccessWithErrorMessage } from '../client/types';
import path from 'path';
import * as vaultsErrors from './errors';
import * as vaultsUtils from './utils';
Expand All @@ -12,11 +13,6 @@ type FileOptions = {
recursive?: boolean;
};

type SuccessMessage = {
success: boolean;
error?: string;
};

async function addSecret(
vault: Vault,
secretName: string,
Expand Down Expand Up @@ -173,37 +169,38 @@ async function deleteSecret(
* Adds an empty directory to the root of the vault.
* i.e. mkdir("folder", { recursive = false }) creates the "<vaultDir>/folder" directory
*/
async function* mkdir(
async function mkdir(
vault: Vault,
dirPaths: Array<string>,
dirPath: string,
fileOptions?: FileOptions,
logger?: Logger,
): AsyncGenerator<SuccessMessage> {
): Promise<SuccessWithErrorMessage> {
const recursive = fileOptions?.recursive ?? false;
yield* vault.writeG(async function* (efs): AsyncGenerator<SuccessMessage> {
for (const dirPath of dirPaths) {
try {
await efs.mkdir(dirPath, fileOptions);
logger?.info(`Created secret directory at '${dirPath}'`);
yield { success: true };
} catch (e) {
logger?.info(`Failed to create directory '${dirPath}'. Why: ${e.code}`);
if (e.code === 'ENOENT' && !recursive) {
yield {
success: false,
error: `${e.code}: cannot create directory ${dirPath}: No such secret or directory`,
};
} else if (e.code === 'EEXIST') {
yield {
success: false,
error: `${e.code}: cannot create directory ${dirPath}: Secret or directory exists`,
};
} else {
throw e;
}
}
// Technically, writing an empty directory won't make a commit, and doesn't
// need a write resource as git doesn't track empty directories. It is
// still being used to allow concurrency.
try {
await vault.writeF(async (efs) => {
await efs.mkdir(dirPath, fileOptions);
logger?.info(`Created secret directory at '${dirPath}'`);
});
return { success: true };
} catch (e) {
logger?.error(`Failed to create directory '${dirPath}'. Reason: ${e.code}`);
if (e.code === 'ENOENT' && !recursive) {
return {
success: false,
error: `${e.code}: cannot create directory ${dirPath}: No such secret or directory`,
};
} else if (e.code === 'EEXIST') {
return {
success: false,
error: `${e.code}: cannot create directory ${dirPath}: Secret or directory exists`,
};
} else {
throw e;
}
});
}
}

/**
Expand Down
84 changes: 19 additions & 65 deletions tests/vaults/VaultOps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,84 +323,38 @@ describe('VaultOps', () => {
});
describe('mkdir', () => {
test('can create directory', async () => {
const response = vaultOps.mkdir(vault, [dirName]);
for await (const data of response) {
expect(data.success).toBeTruthy();
expect(data.error).toBeUndefined();
}
const response = await vaultOps.mkdir(vault, dirName);
expect(response.success).toBeTruthy();
expect(response.error).toBeUndefined();
await expectDirExists(dirName);
});
test('can create recursive directory', async () => {
const dirPath = path.join(dirName, dirName);
const response = vaultOps.mkdir(vault, [dirPath], {
const response = await vaultOps.mkdir(vault, dirPath, {
recursive: true,
});
for await (const data of response) {
expect(data.success).toBeTruthy();
expect(data.error).toBeUndefined();
}
expect(response.success).toBeTruthy();
expect(response.error).toBeUndefined();
await expectDirExists(dirPath);
});
test('creating recursive directory fails without recursive set', async () => {
test('creating directories fails without recursive', async () => {
const dirPath = path.join(dirName, dirName);
const response = vaultOps.mkdir(vault, [dirPath]);
for await (const data of response) {
expect(data.success).toBeFalsy();
expect(data.error).toBeDefined();
}
const response = await vaultOps.mkdir(vault, dirPath);
expect(response.success).toBeFalsy();
expect(response.error).toBeDefined();
await expectDirExistsNot(dirPath);
});
test('creating existing directory should fail', async () => {
await mkdir(dirName);
const response = vaultOps.mkdir(vault, [dirName]);
for await (const data of response) {
expect(data.success).toBeFalsy();
expect(data.error).toBeDefined();
}
const response = await vaultOps.mkdir(vault, dirName);
expect(response.success).toBeFalsy();
expect(response.error).toBeDefined();
});
test('creating existing secret should fail', async () => {
await writeSecret(secretName, secretContent);
const response = vaultOps.mkdir(vault, [secretName]);
for await (const data of response) {
expect(data.success).toBeFalsy();
expect(data.error).toBeDefined();
}
});
test('can create multiple directories at the same time', async () => {
const dirName1 = `${dirName}1`;
const dirName2 = `${dirName}2`;
const dirName3 = `${dirName}3`;
const response = vaultOps.mkdir(vault, [dirName1, dirName2, dirName3]);
for await (const data of response) {
expect(data.success).toBeTruthy();
expect(data.error).toBeUndefined();
}
await expectDirExists(dirName1);
await expectDirExists(dirName2);
await expectDirExists(dirName3);
});
test('can create multiple directories recursively', async () => {
const dirName1 = `${dirName}1`;
const dirName2 = `${dirName}/${dirName}2`;
const response = vaultOps.mkdir(vault, [dirName1, dirName2], {
recursive: true,
});
for await (const data of response) {
expect(data.success).toBeTruthy();
expect(data.error).toBeUndefined();
}
await expectDirExists(dirName1);
await expectDirExists(dirName2);
});
test('fails to make recursive directories without recursive', async () => {
const dirNameRecursive = `${dirName}/${dirName}2`;
const response = vaultOps.mkdir(vault, [dirNameRecursive]);
for await (const data of response) {
expect(data.success).toBeFalsy();
expect(data.error).toBeDefined();
}
await expectDirExistsNot(dirName);
await expectDirExistsNot(dirNameRecursive);
const response = await vaultOps.mkdir(vault, secretName);
expect(response.success).toBeFalsy();
expect(response.error).toBeDefined();
});
});
describe('addSecretDirectory', () => {
Expand Down Expand Up @@ -493,7 +447,7 @@ describe('VaultOps', () => {
'secret5',
);

vaultOps.mkdir(vault, [secretDirName], { recursive: true });
await vaultOps.mkdir(vault, secretDirName, { recursive: true });
await vaultOps.addSecret(
vault,
path.join(secretDirName, 'secret1'),
Expand Down Expand Up @@ -572,7 +526,7 @@ describe('VaultOps', () => {
});
test('adding hidden files and directories', async () => {
await vaultOps.addSecret(vault, '.hiddenSecret', 'hidden_contents');
vaultOps.mkdir(vault, ['.hiddenDir'], { recursive: true });
await vaultOps.mkdir(vault, '.hiddenDir', { recursive: true });
await vaultOps.addSecret(
vault,
'.hiddenDir/.hiddenInSecret',
Expand All @@ -587,7 +541,7 @@ describe('VaultOps', () => {
'updating and deleting hidden files and directories',
async () => {
await vaultOps.addSecret(vault, '.hiddenSecret', 'hidden_contents');
vaultOps.mkdir(vault, ['.hiddenDir'], { recursive: true });
await vaultOps.mkdir(vault, '.hiddenDir', { recursive: true });
await vaultOps.addSecret(
vault,
'.hiddenDir/.hiddenInSecret',
Expand Down

0 comments on commit e949857

Please sign in to comment.