feat: bruno.json validation for local collections

This commit is contained in:
Anoop M D 2022-10-18 02:34:22 +05:30
parent 8a96a0ce71
commit ff87586a1d
5 changed files with 93 additions and 24 deletions

View File

@ -21,9 +21,9 @@ const useLocalCollectionTreeSync = () => {
const { ipcRenderer } = window;
const _openCollection = (pathname, uid) => {
console.log(`collection uid: ${uid}, pathname: ${pathname}`);
dispatch(openLocalCollectionEvent(uid, pathname));
const _openCollection = (pathname, uid, name) => {
console.log(`collection uid: ${uid}, pathname: ${pathname}, name: ${name}`);
dispatch(openLocalCollectionEvent(uid, pathname, name));
};
const _collectionTreeUpdated = (type, val) => {
@ -60,16 +60,22 @@ const useLocalCollectionTreeSync = () => {
toast.success('Collection is already opened under local collections');
};
const _displayError = (message) => {
toast.error(message || 'Something went wrong!');
};
ipcRenderer.invoke('renderer:ready');
const removeListener1 = ipcRenderer.on('main:collection-opened', _openCollection);
const removeListener2 = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated);
const removeListener3 = ipcRenderer.on('main:collection-already-opened', _collectionAlreadyOpened);
const removeListener4 = ipcRenderer.on('main:display-error', _displayError);
return () => {
removeListener1();
removeListener2();
removeListener3();
removeListener4();
};
}, [isElectron]);
};

View File

@ -57,11 +57,11 @@ export const loadCollectionsFromIdb = () => (dispatch) => {
.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 = {
version: "1",
uid: uid,
name: path.basename(pathname),
name: name,
pathname: pathname,
items: []
};

View File

@ -22,7 +22,8 @@
"fs-extra": "^10.1.0",
"is-valid-path": "^0.1.1",
"lodash": "^4.17.21",
"nanoid": "3.3.4"
"nanoid": "3.3.4",
"yup": "^0.32.11"
},
"devDependencies": {
"electron": "^21.1.1",

View File

@ -1,8 +1,52 @@
const { uuid } = require('../utils/common');
const fs = require('fs');
const path = require('path');
const { dialog, ipcMain } = require('electron');
const Yup = require('yup');
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, {
properties: ['openDirectory', 'createDirectory']
});
@ -10,19 +54,37 @@ const openCollection = async (win, watcher) => {
if (filePaths && filePaths[0]) {
const resolvedPath = normalizeAndResolvePath(filePaths[0]);
if (isDirectory(resolvedPath)) {
if(!watcher.hasWatcher(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);
}
openCollection(win, watcher, resolvedPath);
} else {
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 = {
openCollection
openCollection,
openCollectionDialog
};

View File

@ -11,7 +11,7 @@ const {
createDirectory
} = require('../utils/filesystem');
const { uuid, stringifyJson, parseJson } = require('../utils/common');
const { openCollection } = require('../app/collections');
const { openCollectionDialog, openCollection } = require('../app/collections');
const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {
// browse directory
@ -41,14 +41,14 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
const uid = uuid();
const content = await stringifyJson({
version: '1.0',
version: '1',
uid: uid,
name: collectionName,
type: 'collection'
});
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);
return;
@ -149,7 +149,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
ipcMain.handle('renderer:open-collection', () => {
if(watcher && mainWindow) {
openCollection(mainWindow, watcher);
openCollectionDialog(mainWindow, watcher);
}
});
@ -168,9 +168,9 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
if(lastOpened && lastOpened.length) {
for(let collectionPath of lastOpened) {
if(isDirectory(collectionPath)) {
const uid = uuid();
mainWindow.webContents.send('main:collection-opened', collectionPath, uid);
ipcMain.emit('main:collection-opened', mainWindow, collectionPath, uid);
openCollection(mainWindow, watcher, collectionPath, {
dontSendDisplayErrors: true
});
}
}
}
@ -180,7 +180,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {
ipcMain.on('main:open-collection', () => {
if(watcher && mainWindow) {
openCollection(mainWindow, watcher);
openCollectionDialog(mainWindow, watcher);
}
});