From 4f115b06fb2625fc39ccca76969e26fd1ece3291 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com> Date: Wed, 22 May 2024 14:04:52 +0530 Subject: [PATCH] Now added the ability to import and export js and json values. changes made in item schema. (#2296) * Now added the ability to import and export js values. changes made in item schema. * Improvements upon review * Fixes. * refactor: removed the copyRequest function and wrote the logic directly inside the copyItems function. * refactor: Update getBrunoJsonConfig function to remove unnecessary parameter * refactor: Update getBrunoJsonConfig function to remove unnecessary parameter and handle auth object dynamically * refactor: Update OAuth2 grantType handling in transformCollectionToSaveToExportAsFile function * refactor: Update getBrunoJsonConfig function to remove unnecessary async --- .../bruno-app/src/utils/collections/index.js | 187 +++++++++++------- packages/bruno-electron/src/ipc/collection.js | 33 +++- .../bruno-schema/src/collections/index.js | 10 +- 3 files changed, 147 insertions(+), 83 deletions(-) diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index f59d9e2d5..3ca18bd95 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -267,6 +267,10 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} const copyItems = (sourceItems, destItems) => { each(sourceItems, (si) => { + if (!isItemAFolder(si) && !isItemARequest(si) && si.type !== 'js') { + return; + } + const di = { uid: si.uid, type: si.type, @@ -274,80 +278,109 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} seq: si.seq }; - // if items is draft, then take data from draft to save - // The condition "!options.ignoreDraft" may appear confusing - // When saving a collection, this option allows the caller to specify to ignore any draft changes while still saving rest of the collection. - // This is useful for performing rename request/collections while still leaving changes in draft not making its way into the indexeddb - if (si.draft && !options.ignoreDraft) { - if (si.draft.request) { - di.request = { - url: si.draft.request.url, - method: si.draft.request.method, - headers: copyHeaders(si.draft.request.headers), - params: copyQueryParams(si.draft.request.params), - body: { - mode: si.draft.request.body.mode, - json: si.draft.request.body.json, - text: si.draft.request.body.text, - xml: si.draft.request.body.xml, - graphql: si.draft.request.body.graphql, - sparql: si.draft.request.body.sparql, - formUrlEncoded: copyFormUrlEncodedParams(si.draft.request.body.formUrlEncoded), - multipartForm: copyMultipartFormParams(si.draft.request.body.multipartForm) - }, - auth: { - mode: get(si.draft.request, 'auth.mode', 'none'), - basic: { - username: get(si.draft.request, 'auth.basic.username', ''), - password: get(si.draft.request, 'auth.basic.password', '') - }, - bearer: { - token: get(si.draft.request, 'auth.bearer.token', '') - } - }, - script: si.draft.request.script, - vars: si.draft.request.vars, - assertions: si.draft.request.assertions, - tests: si.draft.request.tests - }; - } - } else { - if (si.request) { - di.request = { - url: si.request.url, - method: si.request.method, - headers: copyHeaders(si.request.headers), - params: copyQueryParams(si.request.params), - body: { - mode: si.request.body.mode, - json: si.request.body.json, - text: si.request.body.text, - xml: si.request.body.xml, - graphql: si.request.body.graphql, - sparql: si.request.body.sparql, - formUrlEncoded: copyFormUrlEncodedParams(si.request.body.formUrlEncoded), - multipartForm: copyMultipartFormParams(si.request.body.multipartForm) - }, - auth: { - mode: get(si.request, 'auth.mode', 'none'), - basic: { - username: get(si.request, 'auth.basic.username', ''), - password: get(si.request, 'auth.basic.password', '') - }, - bearer: { - token: get(si.request, 'auth.bearer.token', '') - } - }, - script: si.request.script, - vars: si.request.vars, - assertions: si.request.assertions, - tests: si.request.tests - }; - } - } + if (si.request) { + di.request = { + url: si.request.url, + method: si.request.method, + headers: copyHeaders(si.request.headers), + params: copyQueryParams(si.request.params), + body: { + mode: si.request.body.mode, + json: si.request.body.json, + text: si.request.body.text, + xml: si.request.body.xml, + graphql: si.request.body.graphql, + sparql: si.request.body.sparql, + formUrlEncoded: copyFormUrlEncodedParams(si.request.body.formUrlEncoded), + multipartForm: copyMultipartFormParams(si.request.body.multipartForm) + }, + script: si.request.script, + vars: si.request.vars, + assertions: si.request.assertions, + tests: si.request.tests + }; - if (di.request && di.request.body.mode === 'json') { - di.request.body.json = replaceTabsWithSpaces(di.request.body.json); + // Handle auth object dynamically + di.request.auth = { + mode: get(si.request, 'auth.mode', 'none') + }; + + switch (di.request.auth.mode) { + case 'awsv4': + di.request.auth.awsv4 = { + accessKeyId: get(si.request, 'auth.awsv4.accessKeyId', ''), + secretAccessKey: get(si.request, 'auth.awsv4.secretAccessKey', ''), + sessionToken: get(si.request, 'auth.awsv4.sessionToken', ''), + service: get(si.request, 'auth.awsv4.service', ''), + region: get(si.request, 'auth.awsv4.region', ''), + profileName: get(si.request, 'auth.awsv4.profileName', '') + }; + break; + case 'basic': + di.request.auth.basic = { + username: get(si.request, 'auth.basic.username', ''), + password: get(si.request, 'auth.basic.password', '') + }; + break; + case 'bearer': + di.request.auth.bearer = { + token: get(si.request, 'auth.bearer.token', '') + }; + break; + case 'digest': + di.request.auth.digest = { + username: get(si.request, 'auth.digest.username', ''), + password: get(si.request, 'auth.digest.password', '') + }; + break; + case 'oauth2': + let grantType = get(si.request, 'auth.oauth2.grantType', ''); + switch (grantType) { + case 'password': + di.request.auth.oauth2 = { + grantType: grantType, + accessTokenUrl: get(si.request, 'auth.oauth2.accessTokenUrl', ''), + username: get(si.request, 'auth.oauth2.username', ''), + password: get(si.request, 'auth.oauth2.password', ''), + clientId: get(si.request, 'auth.oauth2.clientId', ''), + clientSecret: get(si.request, 'auth.oauth2.clientSecret', ''), + scope: get(si.request, 'auth.oauth2.scope', '') + }; + break; + case 'authorization_code': + di.request.auth.oauth2 = { + grantType: grantType, + callbackUrl: get(si.request, 'auth.oauth2.callbackUrl', ''), + authorizationUrl: get(si.request, 'auth.oauth2.authorizationUrl', ''), + accessTokenUrl: get(si.request, 'auth.oauth2.accessTokenUrl', ''), + clientId: get(si.request, 'auth.oauth2.clientId', ''), + clientSecret: get(si.request, 'auth.oauth2.clientSecret', ''), + scope: get(si.request, 'auth.oauth2.scope', ''), + pkce: get(si.request, 'auth.oauth2.pkce', false) + }; + break; + case 'client_credentials': + di.request.auth.oauth2 = { + grantType: grantType, + accessTokenUrl: get(si.request, 'auth.oauth2.accessTokenUrl', ''), + clientId: get(si.request, 'auth.oauth2.clientId', ''), + clientSecret: get(si.request, 'auth.oauth2.clientSecret', ''), + scope: get(si.request, 'auth.oauth2.scope', '') + }; + break; + } + break; + default: + break; + } + + if (si.type === 'js') { + di.fileContent = si.raw; + } + + if (di.request.body.mode === 'json') { + di.request.body.json = replaceTabsWithSpaces(di.request.body.json); + } } destItems.push(di); @@ -369,8 +402,14 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} collectionToSave.activeEnvironmentUid = collection.activeEnvironmentUid; collectionToSave.environments = collection.environments || []; - copyItems(collection.items, collectionToSave.items); + collectionToSave.brunoConfig = cloneDeep(collection?.brunoConfig); + // delete proxy password if present + if (collectionToSave?.brunoConfig?.proxy?.auth?.password) { + delete collectionToSave.brunoConfig.proxy.auth.password; + } + + copyItems(collection.items, collectionToSave.items); return collectionToSave; }; diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index ff570ca9f..bdba1bc42 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -428,6 +428,11 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection parseCollectionItems(item.items, folderPath); } } + // Handle items of type 'js' + if (item.type === 'js') { + const filePath = path.join(currentPath, `${item.name}.js`); + fs.writeFileSync(filePath, item.raw); + } }); }; @@ -444,17 +449,29 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection }); }; + const getBrunoJsonConfig = (collection) => { + let brunoConfig = collection.brunoConfig; + + if (!brunoConfig) { + brunoConfig = { + version: '1', + name: collection.name, + type: 'collection', + ignore: ['node_modules', '.git'] + }; + } + + return brunoConfig; + }; + await createDirectory(collectionPath); const uid = generateUidBasedOnHash(collectionPath); - const brunoConfig = { - version: '1', - name: collectionName, - type: 'collection', - ignore: ['node_modules', '.git'] - }; - const content = await stringifyJson(brunoConfig); - await writeFile(path.join(collectionPath, 'bruno.json'), content); + const brunoConfig = getBrunoJsonConfig(collection); + const stringifiedBrunoConfig = await stringifyJson(brunoConfig); + + // Write the Bruno configuration to a file + await writeFile(path.join(collectionPath, 'bruno.json'), stringifiedBrunoConfig); mainWindow.webContents.send('main:collection-opened', collectionPath, uid, brunoConfig); ipcMain.emit('main:collection-opened', mainWindow, collectionPath, uid, brunoConfig); diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index 033e68277..6bf8dd2e4 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -217,13 +217,21 @@ const requestSchema = Yup.object({ const itemSchema = Yup.object({ uid: uidSchema, - type: Yup.string().oneOf(['http-request', 'graphql-request', 'folder']).required('type is required'), + type: Yup.string().oneOf(['http-request', 'graphql-request', 'folder', 'js']).required('type is required'), seq: Yup.number().min(1), name: Yup.string().min(1, 'name must be at least 1 character').required('name is required'), request: requestSchema.when('type', { is: (type) => ['http-request', 'graphql-request'].includes(type), then: (schema) => schema.required('request is required when item-type is request') }), + fileContent: Yup.string().when('type', { + // If the type is 'js', the fileContent field is expected to be a string. + // This can include an empty string, indicating that the JS file may not have any content. + is: 'js', + then: Yup.string(), + // For all other types, the fileContent field is not required and can be null. + otherwise: Yup.string().nullable() + }), items: Yup.lazy(() => Yup.array().of(itemSchema)), filename: Yup.string().nullable(), pathname: Yup.string().nullable()