Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Experimental JSON storage support #444

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ export const addEnvironment = (name, collectionUid) => (dispatch, getState) => {
}

ipcRenderer
.invoke('renderer:create-environment', collection.pathname, name)
.invoke('renderer:create-environment', collection, name)
.then(
dispatch(
updateLastAction({
Expand Down Expand Up @@ -688,7 +688,7 @@ export const copyEnvironment = (name, baseEnvUid, collectionUid) => (dispatch, g
}

ipcRenderer
.invoke('renderer:copy-environment', collection.pathname, name, baseEnv.variables)
.invoke('renderer:copy-environment', collection, name, baseEnv.variables)
.then(
dispatch(
updateLastAction({
Expand Down Expand Up @@ -724,7 +724,7 @@ export const renameEnvironment = (newName, environmentUid, collectionUid) => (di

environmentSchema
.validate(environment)
.then(() => ipcRenderer.invoke('renderer:rename-environment', collection.pathname, oldName, newName))
.then(() => ipcRenderer.invoke('renderer:rename-environment', collection, oldName, newName))
.then(resolve)
.catch(reject);
});
Expand All @@ -745,10 +745,7 @@ export const deleteEnvironment = (environmentUid, collectionUid) => (dispatch, g
return reject(new Error('Environment not found'));
}

ipcRenderer
.invoke('renderer:delete-environment', collection.pathname, environment.name)
.then(resolve)
.catch(reject);
ipcRenderer.invoke('renderer:delete-environment', collection, environment.name).then(resolve).catch(reject);
});
};

Expand All @@ -770,7 +767,7 @@ export const saveEnvironment = (variables, environmentUid, collectionUid) => (di

environmentSchema
.validate(environment)
.then(() => ipcRenderer.invoke('renderer:save-environment', collection.pathname, environment))
.then(() => ipcRenderer.invoke('renderer:save-environment', collection, environment))
.then(resolve)
.catch(reject);
});
Expand Down
1 change: 1 addition & 0 deletions packages/bruno-cli/src/utils/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const hasJsonExtension = (filename) => {

const hasBruExtension = (filename) => {
if (!filename || typeof filename !== 'string') return false;

return ['bru'].some((ext) => filename.toLowerCase().endsWith(`.${ext}`));
};

Expand Down
3 changes: 2 additions & 1 deletion packages/bruno-electron/src/app/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const { generateUidBasedOnHash } = require('../utils/common');
const configSchema = Yup.object({
name: Yup.string().max(256, 'name must be 256 characters or less').required('name is required'),
type: Yup.string().oneOf(['collection']).required('type is required'),
version: Yup.string().oneOf(['1']).required('type is required')
version: Yup.string().oneOf(['1']).required('type is required'),
lang: Yup.string().oneOf(['bru', 'json']).default('bru')
});

const readConfigFile = async (pathname) => {
Expand Down
83 changes: 60 additions & 23 deletions packages/bruno-electron/src/app/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { uuid } = require('../utils/common');
const { getRequestUid } = require('../cache/requestUids');
const { decryptString } = require('../utils/encryption');
const { setDotEnvVars } = require('../store/process-env');
const { setBrunoConfig } = require('../store/bruno-config');
const { setBrunoConfig, getBrunoConfig, getLangFromBrunoConfig } = require('../store/bruno-config');
const EnvironmentSecretsStore = require('../store/env-secrets');

const environmentSecretsStore = new EnvironmentSecretsStore();
Expand All @@ -36,12 +36,17 @@ const isBrunoConfigFile = (pathname, collectionPath) => {
return dirname === collectionPath && basename === 'bruno.json';
};

const isBruEnvironmentConfig = (pathname, collectionPath) => {
const dirname = path.dirname(pathname);
/**
* @param {string} pathname
* @param {string} collectionPath
* @param {string} collectionUid
* @returns {boolean}
*/
const isBruEnvironmentConfig = (pathname, collectionPath, collectionUid) => {
const { dir, ext } = path.parse(pathname);
const envDirectory = path.join(collectionPath, 'environments');
const basename = path.basename(pathname);

return dirname === envDirectory && hasBruExtension(basename);
return dir === envDirectory && getLangFromBrunoConfig(collectionUid) === ext;
};

const hydrateRequestWithUuid = (request, pathname) => {
Expand Down Expand Up @@ -72,22 +77,37 @@ const envHasSecrets = (environment = {}) => {
return secrets && secrets.length > 0;
};

/**
* @param {*} win
* @param {string} pathname
* @param {string} collectionUid
* @param {string} collectionPath
*/
const addEnvironmentFile = async (win, pathname, collectionUid, collectionPath) => {
try {
const basename = path.basename(pathname);
const { root, dir, name, base } = path.parse(pathname);
const lang = getLangFromBrunoConfig(collectionUid);
const targetFilePath = path.format({
root,
dir,
name,
// Overriding extension here to enforce lang preference
ext: `.${lang}`
});

const file = {
meta: {
collectionUid,
pathname,
name: basename
pathname: targetFilePath,
name: base
}
};

let bruContent = fs.readFileSync(pathname, 'utf8');

file.data = bruToEnvJson(bruContent);
file.data.name = basename.substring(0, basename.length - 4);
file.data.uid = getRequestUid(pathname);
const envFileContent = fs.readFileSync(targetFilePath, 'utf8');
// TODO: If we add other formats we should extract this functionality for reuse
file.data = lang === 'json' ? JSON.parse(envFileContent) : bruToEnvJson(envFileContent);
file.name = name;
file.uid = getRequestUid(pathname);

_.each(_.get(file, 'data.variables', []), (variable) => (variable.uid = uuid()));

Expand All @@ -108,21 +128,38 @@ const addEnvironmentFile = async (win, pathname, collectionUid, collectionPath)
}
};

/**
* @param {*} win
* @param {string} pathname
* @param {string} collectionUid
* @param {string} collectionPath
*/
const changeEnvironmentFile = async (win, pathname, collectionUid, collectionPath) => {
try {
const basename = path.basename(pathname);
const { root, dir, name, base } = path.parse(pathname);
const lang = getLangFromBrunoConfig(collectionUid);
const targetFilePath = path.format({
root,
dir,
name,
// Overriding extension here to enforce lang preference
ext: `.${lang}`
});

const file = {
meta: {
collectionUid,
pathname,
name: basename
pathname: targetFilePath,
name: base
}
};

const bruContent = fs.readFileSync(pathname, 'utf8');
file.data = bruToEnvJson(bruContent);
file.data.name = basename.substring(0, basename.length - 4);
file.data.uid = getRequestUid(pathname);
const envFileContent = fs.readFileSync(targetFilePath, 'utf8');
// TODO: If we add other formats we should extract this functionality for reuse
file.data = lang === 'json' ? JSON.parse(envFileContent) : bruToEnvJson(envFileContent);
file.name = name;
file.uid = getRequestUid(pathname);

_.each(_.get(file, 'data.variables', []), (variable) => (variable.uid = uuid()));

// hydrate environment variables with secrets
Expand Down Expand Up @@ -224,7 +261,7 @@ const add = async (win, pathname, collectionUid, collectionPath) => {
return;
}

if (isBruEnvironmentConfig(pathname, collectionPath)) {
if (isBruEnvironmentConfig(pathname, collectionPath, collectionUid)) {
return addEnvironmentFile(win, pathname, collectionUid, collectionPath);
}

Expand Down Expand Up @@ -303,7 +340,7 @@ const change = async (win, pathname, collectionUid, collectionPath) => {
}
}

if (isBruEnvironmentConfig(pathname, collectionPath)) {
if (isBruEnvironmentConfig(pathname, collectionPath, collectionUid)) {
return changeEnvironmentFile(win, pathname, collectionUid, collectionPath);
}

Expand All @@ -328,7 +365,7 @@ const change = async (win, pathname, collectionUid, collectionPath) => {
};

const unlink = (win, pathname, collectionUid, collectionPath) => {
if (isBruEnvironmentConfig(pathname, collectionPath)) {
if (isBruEnvironmentConfig(pathname, collectionPath, collectionUid)) {
return unlinkEnvironmentFile(win, pathname, collectionUid);
}

Expand Down
79 changes: 55 additions & 24 deletions packages/bruno-electron/src/ipc/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const { generateUidBasedOnHash } = require('../utils/common');
const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids');
const { setPreferences } = require('../store/preferences');
const EnvironmentSecretsStore = require('../store/env-secrets');
const { getLangFromBrunoConfig } = require('../store/bruno-config');

const environmentSecretsStore = new EnvironmentSecretsStore();

Expand Down Expand Up @@ -130,107 +131,137 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
});

// create environment
ipcMain.handle('renderer:create-environment', async (event, collectionPathname, name) => {
ipcMain.handle('renderer:create-environment', async (event, collection, name) => {
try {
const envDirPath = path.join(collectionPathname, 'environments');
const envDirPath = path.join(collection.pathname, 'environments');
if (!fs.existsSync(envDirPath)) {
await createDirectory(envDirPath);
}

const envFilePath = path.join(envDirPath, `${name}.bru`);
const lang = getLangFromBrunoConfig(collection.uid);
const envFilePath = path.format({
dir: envDirPath,
name,
ext: `.${lang}`
});
if (fs.existsSync(envFilePath)) {
throw new Error(`environment: ${envFilePath} already exists`);
}

const content = envJsonToBru({
const environment = {
variables: []
});
};
const content = lang === 'json' ? JSON.stringify(environment, null, 2) : envJsonToBru(environment);
await writeFile(envFilePath, content);
} catch (error) {
return Promise.reject(error);
}
});

// copy environment
ipcMain.handle('renderer:copy-environment', async (event, collectionPathname, name, baseVariables) => {
ipcMain.handle('renderer:copy-environment', async (event, collection, name, baseVariables) => {
try {
const envDirPath = path.join(collectionPathname, 'environments');
const envDirPath = path.join(collection.pathname, 'environments');
if (!fs.existsSync(envDirPath)) {
await createDirectory(envDirPath);
}

const envFilePath = path.join(envDirPath, `${name}.bru`);
const lang = getLangFromBrunoConfig(collection.uid);
const envFilePath = path.format({
dir: envDirPath,
name,
ext: `.${getLangFromBrunoConfig}`
});
if (fs.existsSync(envFilePath)) {
throw new Error(`environment: ${envFilePath} already exists`);
}

const content = envJsonToBru({
const environment = {
variables: baseVariables
});
};
const content = lang === 'json' ? JSON.stringify(environment, null, 2) : envJsonToBru(environment);
await writeFile(envFilePath, content);
} catch (error) {
return Promise.reject(error);
}
});

// save environment
ipcMain.handle('renderer:save-environment', async (event, collectionPathname, environment) => {
ipcMain.handle('renderer:save-environment', async (event, collection, environment) => {
try {
const envDirPath = path.join(collectionPathname, 'environments');
const envDirPath = path.join(collection.pathname, 'environments');
if (!fs.existsSync(envDirPath)) {
await createDirectory(envDirPath);
}

const envFilePath = path.join(envDirPath, `${environment.name}.bru`);
const lang = getLangFromBrunoConfig(collection.uid);
const envFilePath = path.format({
dir: envDirPath,
name: environment.name,
ext: `.${lang}`
});
if (!fs.existsSync(envFilePath)) {
throw new Error(`environment: ${envFilePath} does not exist`);
}

if (envHasSecrets(environment)) {
environmentSecretsStore.storeEnvSecrets(collectionPathname, environment);
environmentSecretsStore.storeEnvSecrets(collection.pathname, environment);
}

const content = envJsonToBru(environment);
const content = lang === 'json' ? JSON.stringify(environment, null, 2) : envJsonToBru(environment);
await writeFile(envFilePath, content);
} catch (error) {
return Promise.reject(error);
}
});

// rename environment
ipcMain.handle('renderer:rename-environment', async (event, collectionPathname, environmentName, newName) => {
ipcMain.handle('renderer:rename-environment', async (event, collection, environmentName, newName) => {
try {
const envDirPath = path.join(collectionPathname, 'environments');
const envFilePath = path.join(envDirPath, `${environmentName}.bru`);
const envDirPath = path.join(collection.pathname, 'environments');
const lang = getLangFromBrunoConfig(collection.uid);
const envFilePath = path.format({
dir: envDirPath,
name: environmentName,
ext: `.${lang}`
});
if (!fs.existsSync(envFilePath)) {
throw new Error(`environment: ${envFilePath} does not exist`);
}

const newEnvFilePath = path.join(envDirPath, `${newName}.bru`);
const newEnvFilePath = path.format({
dir: envDirPath,
name: newName,
ext: `.${lang}`
});
if (fs.existsSync(newEnvFilePath)) {
throw new Error(`environment: ${newEnvFilePath} already exists`);
}

fs.renameSync(envFilePath, newEnvFilePath);

environmentSecretsStore.renameEnvironment(collectionPathname, environmentName, newName);
environmentSecretsStore.renameEnvironment(collection.pathname, environmentName, newName);
} catch (error) {
return Promise.reject(error);
}
});

// delete environment
ipcMain.handle('renderer:delete-environment', async (event, collectionPathname, environmentName) => {
ipcMain.handle('renderer:delete-environment', async (event, collection, environmentName) => {
try {
const envDirPath = path.join(collectionPathname, 'environments');
const envFilePath = path.join(envDirPath, `${environmentName}.bru`);
const envDirPath = path.join(collection.pathname, 'environments');
const envFilePath = path.format({
dir: envDirPath,
name: environmentName,
ext: `.${getLangFromBrunoConfig(collection.uid)}`
});
if (!fs.existsSync(envFilePath)) {
throw new Error(`environment: ${envFilePath} does not exist`);
}

fs.unlinkSync(envFilePath);

environmentSecretsStore.deleteEnvironment(collectionPathname, environmentName);
environmentSecretsStore.deleteEnvironment(collection.pathname, environmentName);
} catch (error) {
return Promise.reject(error);
}
Expand Down
Loading