diff --git a/packages/bruno-app/next.config.js b/packages/bruno-app/next.config.js index 8c5064b4c..920ab22bc 100644 --- a/packages/bruno-app/next.config.js +++ b/packages/bruno-app/next.config.js @@ -1,5 +1,5 @@ module.exports = { - reactStrictMode: true, + reactStrictMode: false, publicRuntimeConfig: { CI: process.env.CI, PLAYWRIGHT: process.env.PLAYWRIGHT diff --git a/packages/bruno-app/src/components/Sidebar/Collections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/index.js index eb5847e29..e06eae57e 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/index.js @@ -30,7 +30,7 @@ const Collections = ({ searchText }) => { ? collectionToDisplay.map((c) => { return ( - ; + ); }) diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index f01a02677..44cd2ed3f 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -1,3 +1,4 @@ +import reckon from 'reckonjs'; import get from 'lodash/get'; import each from 'lodash/each'; import find from 'lodash/find'; @@ -413,7 +414,6 @@ export const interpolateEnvironmentVars = (item, variables) => { }; request.url = interpolate(request.url); - console.log(request.url); each(request.headers, (header) => { header.value = interpolate(header.value); diff --git a/packages/bruno-app/src/utils/common/platform.js b/packages/bruno-app/src/utils/common/platform.js index 19b27925a..be5af1110 100644 --- a/packages/bruno-app/src/utils/common/platform.js +++ b/packages/bruno-app/src/utils/common/platform.js @@ -14,7 +14,7 @@ export const isLocalCollection = (collection) => { }; export const resolveRequestFilename = (name) => { - return `${trim(name)}.json`; + return `${trim(name)}.bru`; }; export const getSubdirectoriesFromRoot = (rootPath, pathname) => { diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index a9de762d1..2389bc311 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -13,6 +13,8 @@ "pack-app": "electron-builder --dir" }, "dependencies": { + "@usebruno/bruno-lang": "0.1.0", + "@usebruno/schema": "0.1.0", "axios": "^0.26.0", "chokidar": "^3.5.3", "electron-is-dev": "^2.0.0", diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index 58340aec7..1a53c4977 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -1,7 +1,14 @@ +const _ = require('lodash'); const fs = require('fs'); const path = require('path'); const chokidar = require('chokidar'); -const { hasJsonExtension } = require('../utils/filesystem'); +const { hasJsonExtension, hasBruExtension, writeFile } = require('../utils/filesystem'); +const { + bruToJson, + jsonToBru +} = require('@usebruno/bruno-lang'); +const { itemSchema } = require('@usebruno/schema'); +const { generateUidBasedOnHash, uuid } = require('../utils/common'); const isEnvironmentConfig = (pathname, collectionPath) => { const dirname = path.dirname(pathname); @@ -10,6 +17,22 @@ const isEnvironmentConfig = (pathname, collectionPath) => { return dirname === collectionPath && basename === 'environments.json'; } +const hydrateRequestWithUuid = (request, pathname) => { + request.uid = generateUidBasedOnHash(pathname); + + const params = _.get(request, 'request.params', []); + const headers = _.get(request, 'request.headers', []); + const bodyFormUrlEncoded = _.get(request, 'request.body.formUrlEncoded', []); + const bodyMultipartForm = _.get(request, 'request.body.multipartForm', []); + + params.forEach((param) => param.uid = uuid()); + headers.forEach((header) => header.uid = uuid()); + bodyFormUrlEncoded.forEach((param) => param.uid = uuid()); + bodyMultipartForm.forEach((param) => param.uid = uuid()); + + return request; +} + const addEnvironmentFile = async (win, pathname, collectionUid) => { try { const file = { @@ -71,7 +94,30 @@ const add = async (win, pathname, collectionUid, collectionPath) => { if(isEnvironmentConfig(pathname, collectionPath)) { return addEnvironmentFile(win, pathname, collectionUid); } + } + // migrate old json files to bru + if(hasJsonExtension(pathname)) { + try { + const json = fs.readFileSync(pathname, 'utf8'); + const jsonData = JSON.parse(json); + + await itemSchema.validate(jsonData); + + const content = jsonToBru(jsonData); + + const re = /(.*)\.json$/; + const subst = `$1.bru`; + const bruFilename = pathname.replace(re, subst); + + await writeFile(bruFilename, content); + await fs.unlinkSync(pathname); + } catch (err) { + // do nothing + } + } + + if(hasBruExtension(pathname)) { const file = { meta: { collectionUid, @@ -81,8 +127,9 @@ const add = async (win, pathname, collectionUid, collectionPath) => { } try { - const jsonData = fs.readFileSync(pathname, 'utf8'); - file.data = JSON.parse(jsonData); + const bru = fs.readFileSync(pathname, 'utf8'); + file.data = bruToJson(bru); + hydrateRequestWithUuid(file.data, pathname); win.webContents.send('main:collection-tree-updated', 'addFile', file); } catch (err) { console.error(err) @@ -104,25 +151,30 @@ const addDirectory = (win, pathname, collectionUid) => { const change = async (win, pathname, collectionUid, collectionPath) => { console.log(`watcher change: ${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); - } catch (err) { - console.error(err) + if(isEnvironmentConfig(pathname, collectionPath)) { + return changeEnvironmentFile(win, pathname, collectionUid); } + + if(hasBruExtension(pathname)) { + try { + const file = { + meta: { + collectionUid, + pathname, + name: path.basename(pathname), + } + }; + + const bru = fs.readFileSync(pathname, 'utf8'); + file.data = bruToJson(bru); + hydrateRequestWithUuid(file.data, pathname); + win.webContents.send('main:collection-tree-updated', 'change', file); + } catch (err) { + console.error(err) + } + } + }; const unlink = (win, pathname, collectionUid, collectionPath) => { @@ -130,19 +182,19 @@ const unlink = (win, pathname, collectionUid, collectionPath) => { return unlinkEnvironmentFile(win, pathname, collectionUid); } - console.log(`watcher unlink: ${pathname}`); - const file = { - meta: { - collectionUid, - pathname, - name: path.basename(pathname) - } - }; - win.webContents.send('main:collection-tree-updated', 'unlink', file); + if(hasBruExtension(pathname)) { + const file = { + meta: { + collectionUid, + pathname, + name: path.basename(pathname) + } + }; + win.webContents.send('main:collection-tree-updated', 'unlink', file); + } } const unlinkDir = (win, pathname, collectionUid) => { - console.log(`watcher unlinkDir: ${pathname}`); const directory = { meta: { collectionUid, @@ -163,6 +215,11 @@ class Watcher { this.watchers[watchPath].close(); } + // todo + // enable this in a future release + // once we can confirm all older json based files have been auto migrated to .bru format + // watchPath = path.join(watchPath, '**/*.bru'); + const self = this; setTimeout(() => { const watcher = chokidar.watch(watchPath, { diff --git a/packages/bruno-electron/src/ipc/local-collection.js b/packages/bruno-electron/src/ipc/local-collection.js index 94f575c5e..9cae43ef3 100644 --- a/packages/bruno-electron/src/ipc/local-collection.js +++ b/packages/bruno-electron/src/ipc/local-collection.js @@ -2,15 +2,19 @@ const _ = require('lodash'); const fs = require('fs'); const path = require('path'); const { ipcMain } = require('electron'); +const { + jsonToBru, + bruToJson, +} = require('@usebruno/bruno-lang'); const { isValidPathname, writeFile, - hasJsonExtension, + hasBruExtension, isDirectory, browseDirectory, createDirectory } = require('../utils/filesystem'); -const { uuid, stringifyJson, parseJson } = require('../utils/common'); +const { uuid, stringifyJson } = require('../utils/common'); const { openCollectionDialog, openCollection } = require('../app/collections'); const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollections) => { @@ -64,7 +68,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection throw new Error(`path: ${pathname} already exists`); } - const content = await stringifyJson(request); + const content = jsonToBru(request); await writeFile(pathname, content); } catch (error) { return Promise.reject(error); @@ -78,7 +82,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection throw new Error(`path: ${pathname} does not exist`); } - const content = await stringifyJson(request); + const content = jsonToBru(request); await writeFile(pathname, content); } catch (error) { return Promise.reject(error); @@ -112,18 +116,18 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection return fs.renameSync(oldPath, newPath); } - const isJson = hasJsonExtension(oldPath); - if(!isJson) { - throw new Error(`path: ${oldPath} is not a json file`); + const isBru = hasBruExtension(oldPath); + if(!isBru) { + throw new Error(`path: ${oldPath} is not a bru file`); } // update name in file and save new copy, then delete old copy const data = fs.readFileSync(oldPath, 'utf8'); - const jsonData = await parseJson(data); + const jsonData = bruToJson(data); jsonData.name = newName; - const content = await stringifyJson(jsonData); + const content = jsonToBru(jsonData); await writeFile(newPath, content); await fs.unlinkSync(oldPath); } catch (error) { diff --git a/packages/bruno-electron/src/utils/common.js b/packages/bruno-electron/src/utils/common.js index ceef5e0b6..39324fede 100644 --- a/packages/bruno-electron/src/utils/common.js +++ b/packages/bruno-electron/src/utils/common.js @@ -25,8 +25,26 @@ const parseJson = async (obj) => { } } +const simpleHash = (str) => { + let hash = 0; + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash &= hash; // Convert to 32bit integer + } + return new Uint32Array([hash])[0].toString(36); +}; + +const generateUidBasedOnHash = (str) => { + const hash = simpleHash(str); + + return `${hash}`.padEnd(21, '0'); +} + module.exports = { uuid, stringifyJson, - parseJson + parseJson, + simpleHash, + generateUidBasedOnHash }; diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index cb5018bde..322ff2e00 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -65,6 +65,11 @@ const hasJsonExtension = filename => { return ['json'].some(ext => filename.toLowerCase().endsWith(`.${ext}`)) } +const hasBruExtension = filename => { + if (!filename || typeof filename !== 'string') return false + return ['bru'].some(ext => filename.toLowerCase().endsWith(`.${ext}`)) +} + const createDirectory = async (dir) => { if(!dir) { throw new Error(`directory: path is null`); @@ -99,6 +104,7 @@ module.exports = { normalizeAndResolvePath, writeFile, hasJsonExtension, + hasBruExtension, createDirectory, browseDirectory };