forked from extern/bruno
feat: local collections environment sync
This commit is contained in:
parent
ff87586a1d
commit
e98f219448
@ -8,7 +8,10 @@ import {
|
||||
localCollectionUnlinkDirectoryEvent
|
||||
} from 'providers/ReduxStore/slices/collections';
|
||||
import toast from 'react-hot-toast';
|
||||
import { openLocalCollectionEvent } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import {
|
||||
openLocalCollectionEvent,
|
||||
localCollectionLoadEnvironmentsEvent
|
||||
} from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { isElectron } from 'utils/common/platform';
|
||||
|
||||
const useLocalCollectionTreeSync = () => {
|
||||
@ -54,6 +57,12 @@ const useLocalCollectionTreeSync = () => {
|
||||
directory: val
|
||||
}));
|
||||
}
|
||||
if(type === 'addEnvironmentFile') {
|
||||
dispatch(localCollectionLoadEnvironmentsEvent(val));
|
||||
}
|
||||
if(type === 'changeEnvironmentFile') {
|
||||
dispatch(localCollectionLoadEnvironmentsEvent(val));
|
||||
}
|
||||
};
|
||||
|
||||
const _collectionAlreadyOpened = (pathname) => {
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
refreshUidsInItem,
|
||||
interpolateEnvironmentVars
|
||||
} from 'utils/collections';
|
||||
import { collectionSchema, itemSchema } from '@usebruno/schema';
|
||||
import { collectionSchema, itemSchema, environmentsSchema } from '@usebruno/schema';
|
||||
import { waitForNextTick } from 'utils/common';
|
||||
import { getCollectionsFromIdb, saveCollectionToIdb, deleteCollectionInIdb } from 'utils/idb';
|
||||
import { sendNetworkRequest, cancelNetworkRequest } from 'utils/network';
|
||||
@ -41,6 +41,7 @@ import {
|
||||
createCollection as _createCollection,
|
||||
renameCollection as _renameCollection,
|
||||
deleteCollection as _deleteCollection,
|
||||
localCollectionLoadEnvironmentsEvent as _localCollectionLoadEnvironmentsEvent
|
||||
} from './index';
|
||||
|
||||
import { closeTabs, addTab } from 'providers/ReduxStore/slices/tabs';
|
||||
@ -57,24 +58,6 @@ export const loadCollectionsFromIdb = () => (dispatch) => {
|
||||
.catch(() => toast.error("Error occured while loading collections from IndexedDB"));
|
||||
};
|
||||
|
||||
export const openLocalCollectionEvent = (uid, pathname, name) => (dispatch, getState) => {
|
||||
const localCollection = {
|
||||
version: "1",
|
||||
uid: uid,
|
||||
name: name,
|
||||
pathname: pathname,
|
||||
items: []
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
collectionSchema
|
||||
.validate(localCollection)
|
||||
.then(() => dispatch(_createCollection(localCollection)))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const createCollection = (collectionName) => (dispatch, getState) => {
|
||||
const newCollection = {
|
||||
version: "1",
|
||||
@ -659,6 +642,15 @@ export const addEnvironment = (name, collectionUid) => (dispatch, getState) =>
|
||||
collectionToSave.environments = collectionToSave.environments || [];
|
||||
collectionToSave.environments.push(environment);
|
||||
|
||||
if(isLocalCollection(collection)) {
|
||||
environmentsSchema
|
||||
.validate(collectionToSave.environments)
|
||||
.then(() => ipcRenderer.invoke('renderer:save-environment', collection.pathname, collectionToSave.environments))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
collectionSchema
|
||||
.validate(collectionToSave)
|
||||
.then(() => saveCollectionToIdb(window.__idb, collectionToSave))
|
||||
@ -683,9 +675,18 @@ export const renameEnvironment = (newName, environmentUid, collectionUid) => (d
|
||||
}
|
||||
|
||||
environment.name = newName;
|
||||
|
||||
const collectionToSave = transformCollectionToSaveToIdb(collectionCopy);
|
||||
|
||||
if(isLocalCollection(collection)) {
|
||||
const environments = collectionToSave.environments;
|
||||
environmentsSchema
|
||||
.validate(environments)
|
||||
.then(() => ipcRenderer.invoke('renderer:save-environment', collection.pathname, environments))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
collectionSchema
|
||||
.validate(collectionToSave)
|
||||
.then(() => saveCollectionToIdb(window.__idb, collectionToSave))
|
||||
@ -704,15 +705,25 @@ export const deleteEnvironment = (environmentUid, collectionUid) => (dispatch,
|
||||
}
|
||||
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
|
||||
const environment = findEnvironmentInCollection(collectionCopy, environmentUid);
|
||||
if(!environment) {
|
||||
return reject(new Error('Environment not found'));
|
||||
}
|
||||
|
||||
collectionCopy.environments = filter(collectionCopy.environments, (e) => e.uid !== environmentUid);
|
||||
|
||||
const collectionToSave = transformCollectionToSaveToIdb(collectionCopy);
|
||||
|
||||
if(isLocalCollection(collection)) {
|
||||
const environments = collectionToSave.environments;
|
||||
environmentsSchema
|
||||
.validate(environments)
|
||||
.then(() => ipcRenderer.invoke('renderer:save-environment', collection.pathname, environments))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
collectionSchema
|
||||
.validate(collectionToSave)
|
||||
.then(() => saveCollectionToIdb(window.__idb, collectionToSave))
|
||||
@ -739,6 +750,15 @@ export const saveEnvironment = (variables, environmentUid, collectionUid) => (di
|
||||
environment.variables = variables;
|
||||
|
||||
const collectionToSave = transformCollectionToSaveToIdb(collectionCopy);
|
||||
if(isLocalCollection(collection)) {
|
||||
const environments = collectionToSave.environments;
|
||||
environmentsSchema
|
||||
.validate(environments)
|
||||
.then(() => ipcRenderer.invoke('renderer:save-environment', collection.pathname, environments))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
collectionSchema
|
||||
.validate(collectionToSave)
|
||||
@ -814,6 +834,24 @@ export const browserLocalDirectory = () => (dispatch, getState) => {
|
||||
});
|
||||
}
|
||||
|
||||
export const openLocalCollectionEvent = (uid, pathname, name) => (dispatch, getState) => {
|
||||
const localCollection = {
|
||||
version: "1",
|
||||
uid: uid,
|
||||
name: name,
|
||||
pathname: pathname,
|
||||
items: []
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
collectionSchema
|
||||
.validate(localCollection)
|
||||
.then(() => dispatch(_createCollection(localCollection)))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const createLocalCollection = (collectionName, collectionLocation) => () => {
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
@ -834,4 +872,25 @@ export const openLocalCollection = () => () => {
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const localCollectionLoadEnvironmentsEvent = (payload) => (dispatch, getState) => {
|
||||
const { data: environments, meta } = payload;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const state = getState();
|
||||
const collection = findCollectionByUid(state.collections.collections, meta.collectionUid);
|
||||
if(!collection) {
|
||||
return reject(new Error('Collection not found'));
|
||||
}
|
||||
|
||||
environmentsSchema
|
||||
.validate(environments)
|
||||
.then(() => dispatch(_localCollectionLoadEnvironmentsEvent({
|
||||
environments,
|
||||
collectionUid: meta.collectionUid
|
||||
})))
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
@ -742,7 +742,15 @@ export const collectionsSlice = createSlice({
|
||||
deleteItemInCollection(item.uid, collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
localCollectionLoadEnvironmentsEvent: (state, action) => {
|
||||
const { environments, collectionUid } = action.payload;
|
||||
const collection = findCollectionByUid(state.collections, collectionUid);
|
||||
|
||||
if(collection) {
|
||||
collection.environments = environments;
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@ -788,7 +796,8 @@ export const {
|
||||
localCollectionAddDirectoryEvent,
|
||||
localCollectionChangeFileEvent,
|
||||
localCollectionUnlinkFileEvent,
|
||||
localCollectionUnlinkDirectoryEvent
|
||||
localCollectionUnlinkDirectoryEvent,
|
||||
localCollectionLoadEnvironmentsEvent
|
||||
} = collectionsSlice.actions;
|
||||
|
||||
export default collectionsSlice.reducer;
|
||||
|
@ -3,11 +3,75 @@ const path = require('path');
|
||||
const chokidar = require('chokidar');
|
||||
const { hasJsonExtension } = require('../utils/filesystem');
|
||||
|
||||
const add = async (win, pathname, collectionUid) => {
|
||||
const isEnvironmentConfig = (pathname, collectionPath) => {
|
||||
const dirname = path.dirname(pathname);
|
||||
const basename = path.basename(pathname);
|
||||
|
||||
return dirname === collectionPath && basename === 'environments.json';
|
||||
}
|
||||
|
||||
const addEnvironmentFile = async (win, pathname, collectionUid) => {
|
||||
try {
|
||||
const file = {
|
||||
meta: {
|
||||
collectionUid,
|
||||
pathname,
|
||||
name: path.basename(pathname),
|
||||
}
|
||||
};
|
||||
|
||||
const jsonData = fs.readFileSync(pathname, 'utf8');
|
||||
file.data = JSON.parse(jsonData);
|
||||
win.webContents.send('main:collection-tree-updated', 'addEnvironmentFile', file);
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
};
|
||||
|
||||
const changeEnvironmentFile = async (win, pathname, collectionUid) => {
|
||||
try {
|
||||
const file = {
|
||||
meta: {
|
||||
collectionUid,
|
||||
pathname,
|
||||
name: path.basename(pathname),
|
||||
}
|
||||
};
|
||||
|
||||
const jsonData = fs.readFileSync(pathname, 'utf8');
|
||||
file.data = JSON.parse(jsonData);
|
||||
win.webContents.send('main:collection-tree-updated', 'changeEnvironmentFile', file);
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
};
|
||||
|
||||
const unlinkEnvironmentFile = async (win, pathname, collectionUid) => {
|
||||
try {
|
||||
const file = {
|
||||
meta: {
|
||||
collectionUid,
|
||||
pathname,
|
||||
name: path.basename(pathname),
|
||||
},
|
||||
data: []
|
||||
};
|
||||
|
||||
win.webContents.send('main:collection-tree-updated', 'changeEnvironmentFile', file);
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
};
|
||||
|
||||
const add = async (win, pathname, collectionUid, collectionPath) => {
|
||||
const isJson = hasJsonExtension(pathname);
|
||||
console.log(`watcher add: ${pathname}`);
|
||||
|
||||
if(isJson) {
|
||||
if(isEnvironmentConfig(pathname, collectionPath)) {
|
||||
return addEnvironmentFile(win, pathname, collectionUid);
|
||||
}
|
||||
|
||||
const file = {
|
||||
meta: {
|
||||
collectionUid,
|
||||
@ -38,17 +102,21 @@ const addDirectory = (win, pathname, collectionUid) => {
|
||||
win.webContents.send('main:collection-tree-updated', 'addDir', directory);
|
||||
};
|
||||
|
||||
const change = async (win, pathname, collectionUid) => {
|
||||
const change = async (win, pathname, collectionUid, collectionPath) => {
|
||||
console.log(`watcher change: ${pathname}`);
|
||||
const file = {
|
||||
meta: {
|
||||
collectionUid,
|
||||
pathname,
|
||||
name: path.basename(pathname),
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
if(isEnvironmentConfig(pathname, collectionPath)) {
|
||||
return changeEnvironmentFile(win, pathname, collectionUid);
|
||||
}
|
||||
|
||||
const file = {
|
||||
meta: {
|
||||
collectionUid,
|
||||
pathname,
|
||||
name: path.basename(pathname),
|
||||
}
|
||||
};
|
||||
|
||||
const jsonData = fs.readFileSync(pathname, 'utf8');
|
||||
file.data = await JSON.parse(jsonData);
|
||||
win.webContents.send('main:collection-tree-updated', 'change', file);
|
||||
@ -57,7 +125,11 @@ const change = async (win, pathname, collectionUid) => {
|
||||
}
|
||||
};
|
||||
|
||||
const unlink = (win, pathname, collectionUid) => {
|
||||
const unlink = (win, pathname, collectionUid, collectionPath) => {
|
||||
if(isEnvironmentConfig(pathname, collectionPath)) {
|
||||
return unlinkEnvironmentFile(win, pathname, collectionUid);
|
||||
}
|
||||
|
||||
console.log(`watcher unlink: ${pathname}`);
|
||||
const file = {
|
||||
meta: {
|
||||
@ -107,11 +179,11 @@ class Watcher {
|
||||
});
|
||||
|
||||
watcher
|
||||
.on('add', pathname => add(win, pathname, collectionUid))
|
||||
.on('addDir', pathname => addDirectory(win, pathname, collectionUid))
|
||||
.on('change', pathname => change(win, pathname, collectionUid))
|
||||
.on('unlink', pathname => unlink(win, pathname, collectionUid))
|
||||
.on('unlinkDir', pathname => unlinkDir(win, pathname, collectionUid))
|
||||
.on('add', pathname => add(win, pathname, collectionUid, watchPath))
|
||||
.on('addDir', pathname => addDirectory(win, pathname, collectionUid, watchPath))
|
||||
.on('change', pathname => change(win, pathname, collectionUid, watchPath))
|
||||
.on('unlink', pathname => unlink(win, pathname, collectionUid, watchPath))
|
||||
.on('unlinkDir', pathname => unlinkDir(win, pathname, collectionUid, watchPath))
|
||||
|
||||
self.watchers[watchPath] = watcher;
|
||||
}, 100);
|
||||
|
@ -85,6 +85,18 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
||||
}
|
||||
});
|
||||
|
||||
// save environment
|
||||
ipcMain.handle('renderer:save-environment', async (event, collectionPathname, environments) => {
|
||||
try {
|
||||
const envFilePath = path.join(collectionPathname, 'environments.json');
|
||||
|
||||
const content = await stringifyJson(environments);
|
||||
await writeFile(envFilePath, content);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
// rename item
|
||||
ipcMain.handle('renderer:rename-item', async (event, oldPath, newPath, newName) => {
|
||||
try {
|
||||
|
@ -16,6 +16,8 @@ const environmentSchema = Yup.object({
|
||||
variables: Yup.array().of(environmentVariablesSchema).required('variables are required')
|
||||
}).noUnknown(true).strict();
|
||||
|
||||
const environmentsSchema = Yup.array().of(environmentSchema);
|
||||
|
||||
const keyValueSchema = Yup.object({
|
||||
uid: uidSchema,
|
||||
name: Yup.string().nullable().max(256, 'name must be 256 characters or less'),
|
||||
@ -75,12 +77,14 @@ const collectionSchema = Yup.object({
|
||||
.length(21, 'activeEnvironmentUid must be 21 characters in length')
|
||||
.matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric')
|
||||
.nullable(),
|
||||
environments: Yup.array().of(environmentSchema),
|
||||
environments: environmentsSchema,
|
||||
pathname: Yup.string().max(1024, 'pathname cannot be more than 1024 characters').nullable()
|
||||
}).noUnknown(true).strict();
|
||||
|
||||
|
||||
module.exports = {
|
||||
requestSchema,
|
||||
itemSchema,
|
||||
environmentsSchema,
|
||||
collectionSchema
|
||||
};
|
@ -1,8 +1,9 @@
|
||||
const { workspaceSchema } = require("./workspaces");
|
||||
const { collectionSchema, itemSchema } = require("./collections");
|
||||
const { collectionSchema, itemSchema, environmentsSchema } = require("./collections");
|
||||
|
||||
module.exports = {
|
||||
itemSchema,
|
||||
environmentsSchema,
|
||||
collectionSchema,
|
||||
workspaceSchema
|
||||
};
|
Loading…
Reference in New Issue
Block a user