mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-25 09:23:17 +01:00
feat: bruno.json validation for local collections
This commit is contained in:
parent
8a96a0ce71
commit
ff87586a1d
@ -21,9 +21,9 @@ const useLocalCollectionTreeSync = () => {
|
|||||||
|
|
||||||
const { ipcRenderer } = window;
|
const { ipcRenderer } = window;
|
||||||
|
|
||||||
const _openCollection = (pathname, uid) => {
|
const _openCollection = (pathname, uid, name) => {
|
||||||
console.log(`collection uid: ${uid}, pathname: ${pathname}`);
|
console.log(`collection uid: ${uid}, pathname: ${pathname}, name: ${name}`);
|
||||||
dispatch(openLocalCollectionEvent(uid, pathname));
|
dispatch(openLocalCollectionEvent(uid, pathname, name));
|
||||||
};
|
};
|
||||||
|
|
||||||
const _collectionTreeUpdated = (type, val) => {
|
const _collectionTreeUpdated = (type, val) => {
|
||||||
@ -60,16 +60,22 @@ const useLocalCollectionTreeSync = () => {
|
|||||||
toast.success('Collection is already opened under local collections');
|
toast.success('Collection is already opened under local collections');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _displayError = (message) => {
|
||||||
|
toast.error(message || 'Something went wrong!');
|
||||||
|
};
|
||||||
|
|
||||||
ipcRenderer.invoke('renderer:ready');
|
ipcRenderer.invoke('renderer:ready');
|
||||||
|
|
||||||
const removeListener1 = ipcRenderer.on('main:collection-opened', _openCollection);
|
const removeListener1 = ipcRenderer.on('main:collection-opened', _openCollection);
|
||||||
const removeListener2 = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated);
|
const removeListener2 = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated);
|
||||||
const removeListener3 = ipcRenderer.on('main:collection-already-opened', _collectionAlreadyOpened);
|
const removeListener3 = ipcRenderer.on('main:collection-already-opened', _collectionAlreadyOpened);
|
||||||
|
const removeListener4 = ipcRenderer.on('main:display-error', _displayError);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
removeListener1();
|
removeListener1();
|
||||||
removeListener2();
|
removeListener2();
|
||||||
removeListener3();
|
removeListener3();
|
||||||
|
removeListener4();
|
||||||
};
|
};
|
||||||
}, [isElectron]);
|
}, [isElectron]);
|
||||||
};
|
};
|
||||||
|
@ -57,11 +57,11 @@ export const loadCollectionsFromIdb = () => (dispatch) => {
|
|||||||
.catch(() => toast.error("Error occured while loading collections from IndexedDB"));
|
.catch(() => toast.error("Error occured while loading collections from IndexedDB"));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openLocalCollectionEvent = (uid, pathname) => (dispatch, getState) => {
|
export const openLocalCollectionEvent = (uid, pathname, name) => (dispatch, getState) => {
|
||||||
const localCollection = {
|
const localCollection = {
|
||||||
version: "1",
|
version: "1",
|
||||||
uid: uid,
|
uid: uid,
|
||||||
name: path.basename(pathname),
|
name: name,
|
||||||
pathname: pathname,
|
pathname: pathname,
|
||||||
items: []
|
items: []
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,8 @@
|
|||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"is-valid-path": "^0.1.1",
|
"is-valid-path": "^0.1.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nanoid": "3.3.4"
|
"nanoid": "3.3.4",
|
||||||
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^21.1.1",
|
"electron": "^21.1.1",
|
||||||
|
@ -1,8 +1,52 @@
|
|||||||
const { uuid } = require('../utils/common');
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const { dialog, ipcMain } = require('electron');
|
const { dialog, ipcMain } = require('electron');
|
||||||
|
const Yup = require('yup');
|
||||||
const { isDirectory, normalizeAndResolvePath } = require('../utils/filesystem');
|
const { isDirectory, normalizeAndResolvePath } = require('../utils/filesystem');
|
||||||
|
|
||||||
const openCollection = async (win, watcher) => {
|
const uidSchema = Yup.string()
|
||||||
|
.length(21, 'uid must be 21 characters in length')
|
||||||
|
.matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric')
|
||||||
|
.required('uid is required')
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
const configSchema = Yup.object({
|
||||||
|
uid: uidSchema,
|
||||||
|
name: Yup.string().nullable().max(256, 'name must be 256 characters or less'),
|
||||||
|
type: Yup.string().oneOf(['collection']).required('type is required'),
|
||||||
|
version: Yup.string().oneOf(['1']).required('type is required')
|
||||||
|
}).noUnknown(true).strict();
|
||||||
|
|
||||||
|
const readConfigFile = async (pathname) => {
|
||||||
|
try {
|
||||||
|
const jsonData = fs.readFileSync(pathname, 'utf8');
|
||||||
|
return JSON.parse(jsonData);
|
||||||
|
} catch(err) {
|
||||||
|
return Promise.reject(new Error("Unable to parse json in bruno.json"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateSchema = async (config) => {
|
||||||
|
try {
|
||||||
|
await configSchema.validate(config);
|
||||||
|
} catch(err) {
|
||||||
|
return Promise.reject(new Error("bruno.json format is invalid"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCollectionConfigFile = async (pathname) => {
|
||||||
|
const configFilePath = path.join(pathname, 'bruno.json');
|
||||||
|
if (!fs.existsSync(configFilePath)){
|
||||||
|
throw new Error(`The collection is not valid (bruno.json not found)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await readConfigFile(configFilePath);
|
||||||
|
await validateSchema(config);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openCollectionDialog = async (win, watcher) => {
|
||||||
const { filePaths } = await dialog.showOpenDialog(win, {
|
const { filePaths } = await dialog.showOpenDialog(win, {
|
||||||
properties: ['openDirectory', 'createDirectory']
|
properties: ['openDirectory', 'createDirectory']
|
||||||
});
|
});
|
||||||
@ -10,19 +54,37 @@ const openCollection = async (win, watcher) => {
|
|||||||
if (filePaths && filePaths[0]) {
|
if (filePaths && filePaths[0]) {
|
||||||
const resolvedPath = normalizeAndResolvePath(filePaths[0]);
|
const resolvedPath = normalizeAndResolvePath(filePaths[0]);
|
||||||
if (isDirectory(resolvedPath)) {
|
if (isDirectory(resolvedPath)) {
|
||||||
if(!watcher.hasWatcher(resolvedPath)) {
|
openCollection(win, watcher, resolvedPath);
|
||||||
const uid = uuid();
|
|
||||||
win.webContents.send('main:collection-opened', resolvedPath, uid);
|
|
||||||
ipcMain.emit('main:collection-opened', win, resolvedPath, uid);
|
|
||||||
} else {
|
|
||||||
win.webContents.send('main:collection-already-opened', resolvedPath);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.error(`[ERROR] Cannot open unknown folder: "${resolvedPath}"`);
|
console.error(`[ERROR] Cannot open unknown folder: "${resolvedPath}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openCollection = async (win, watcher, collectionPath, options = {}) => {
|
||||||
|
if(!watcher.hasWatcher(collectionPath)) {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
uid,
|
||||||
|
name
|
||||||
|
} = await getCollectionConfigFile(collectionPath);
|
||||||
|
|
||||||
|
console.log(uid);
|
||||||
|
console.log(name);
|
||||||
|
|
||||||
|
win.webContents.send('main:collection-opened', collectionPath, uid, name);
|
||||||
|
ipcMain.emit('main:collection-opened', win, collectionPath, uid);
|
||||||
|
} catch(err) {
|
||||||
|
if(!options.dontSendDisplayErrors) {
|
||||||
|
win.webContents.send('main:display-error', err.message || 'An error occured while opening the local collection');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
win.webContents.send('main:collection-already-opened', collectionPath);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
openCollection
|
openCollection,
|
||||||
|
openCollectionDialog
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,7 @@ const {
|
|||||||
createDirectory
|
createDirectory
|
||||||
} = require('../utils/filesystem');
|
} = require('../utils/filesystem');
|
||||||
const { uuid, stringifyJson, parseJson } = require('../utils/common');
|
const { uuid, stringifyJson, parseJson } = require('../utils/common');
|
||||||
const { openCollection } = require('../app/collections');
|
const { openCollectionDialog, openCollection } = require('../app/collections');
|
||||||
|
|
||||||
const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {
|
const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {
|
||||||
// browse directory
|
// browse directory
|
||||||
@ -41,14 +41,14 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
|
|
||||||
const uid = uuid();
|
const uid = uuid();
|
||||||
const content = await stringifyJson({
|
const content = await stringifyJson({
|
||||||
version: '1.0',
|
version: '1',
|
||||||
uid: uid,
|
uid: uid,
|
||||||
name: collectionName,
|
name: collectionName,
|
||||||
type: 'collection'
|
type: 'collection'
|
||||||
});
|
});
|
||||||
await writeFile(path.join(dirPath, 'bruno.json'), content);
|
await writeFile(path.join(dirPath, 'bruno.json'), content);
|
||||||
|
|
||||||
mainWindow.webContents.send('main:collection-opened', dirPath, uid);
|
mainWindow.webContents.send('main:collection-opened', dirPath, uid, collectionName);
|
||||||
ipcMain.emit('main:collection-opened', mainWindow, dirPath, uid);
|
ipcMain.emit('main:collection-opened', mainWindow, dirPath, uid);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -149,7 +149,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
|
|
||||||
ipcMain.handle('renderer:open-collection', () => {
|
ipcMain.handle('renderer:open-collection', () => {
|
||||||
if(watcher && mainWindow) {
|
if(watcher && mainWindow) {
|
||||||
openCollection(mainWindow, watcher);
|
openCollectionDialog(mainWindow, watcher);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -168,9 +168,9 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
if(lastOpened && lastOpened.length) {
|
if(lastOpened && lastOpened.length) {
|
||||||
for(let collectionPath of lastOpened) {
|
for(let collectionPath of lastOpened) {
|
||||||
if(isDirectory(collectionPath)) {
|
if(isDirectory(collectionPath)) {
|
||||||
const uid = uuid();
|
openCollection(mainWindow, watcher, collectionPath, {
|
||||||
mainWindow.webContents.send('main:collection-opened', collectionPath, uid);
|
dontSendDisplayErrors: true
|
||||||
ipcMain.emit('main:collection-opened', mainWindow, collectionPath, uid);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {
|
const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {
|
||||||
ipcMain.on('main:open-collection', () => {
|
ipcMain.on('main:open-collection', () => {
|
||||||
if(watcher && mainWindow) {
|
if(watcher && mainWindow) {
|
||||||
openCollection(mainWindow, watcher);
|
openCollectionDialog(mainWindow, watcher);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user