feat: collection variables

This commit is contained in:
Anoop M D 2023-02-01 17:56:13 +05:30
parent 37b1c043eb
commit 6f6dedbb9c
12 changed files with 86 additions and 36 deletions

View File

@ -8,7 +8,7 @@
import React from 'react'; import React from 'react';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import MD from 'markdown-it'; import MD from 'markdown-it';
import { getEnvironmentVariables } from 'utils/collections'; import { getAllVariables } from 'utils/collections';
import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror'; import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
@ -43,7 +43,7 @@ export default class QueryEditor extends React.Component {
mode: 'graphql', mode: 'graphql',
// mode: 'brunovariables', // mode: 'brunovariables',
brunoVarInfo: { brunoVarInfo: {
variables: getEnvironmentVariables(this.props.collection), variables: getAllVariables(this.props.collection),
}, },
theme: this.props.editorTheme || 'graphiql', theme: this.props.editorTheme || 'graphiql',
theme: this.props.theme === 'dark' ? 'monokai' : 'default', theme: this.props.theme === 'dark' ? 'monokai' : 'default',
@ -160,7 +160,7 @@ export default class QueryEditor extends React.Component {
if (this.props.theme !== prevProps.theme && this.editor) { if (this.props.theme !== prevProps.theme && this.editor) {
this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default'); this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default');
} }
let variables = getEnvironmentVariables(this.props.collection); let variables = getAllVariables(this.props.collection);
if (!isEqual(variables, this.variables)) { if (!isEqual(variables, this.variables)) {
this.editor.options.brunoVarInfo.variables = variables; this.editor.options.brunoVarInfo.variables = variables;
this.addOverlay(); this.addOverlay();
@ -180,7 +180,7 @@ export default class QueryEditor extends React.Component {
// Todo: Overlay is messing up with schema hint // Todo: Overlay is messing up with schema hint
// Fix this // Fix this
addOverlay = () => { addOverlay = () => {
// let variables = getEnvironmentVariables(this.props.collection); // let variables = getAllVariables(this.props.collection);
// this.variables = variables; // this.variables = variables;
// defineCodeMirrorBrunoVariablesMode(variables, 'graphql'); // defineCodeMirrorBrunoVariablesMode(variables, 'graphql');

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import { getEnvironmentVariables } from 'utils/collections'; import { getAllVariables } from 'utils/collections';
import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror'; import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
@ -29,7 +29,7 @@ class SingleLineEditor extends Component {
theme: this.props.theme === 'dark' ? 'monokai' : 'default', theme: this.props.theme === 'dark' ? 'monokai' : 'default',
mode: "brunovariables", mode: "brunovariables",
brunoVarInfo: { brunoVarInfo: {
variables: getEnvironmentVariables(this.props.collection), variables: getAllVariables(this.props.collection),
}, },
extraKeys: { extraKeys: {
"Enter": () => { "Enter": () => {
@ -92,7 +92,7 @@ class SingleLineEditor extends Component {
// event loop. // event loop.
this.ignoreChangeEvent = true; this.ignoreChangeEvent = true;
let variables = getEnvironmentVariables(this.props.collection); let variables = getAllVariables(this.props.collection);
if (!isEqual(variables, this.variables)) { if (!isEqual(variables, this.variables)) {
this.editor.options.brunoVarInfo.variables = variables; this.editor.options.brunoVarInfo.variables = variables;
this.addOverlay(); this.addOverlay();
@ -112,7 +112,7 @@ class SingleLineEditor extends Component {
} }
addOverlay = () => { addOverlay = () => {
let variables = getEnvironmentVariables(this.props.collection); let variables = getAllVariables(this.props.collection);
this.variables = variables; this.variables = variables;
defineCodeMirrorBrunoVariablesMode(variables, "text/plain"); defineCodeMirrorBrunoVariablesMode(variables, "text/plain");

View File

@ -20,7 +20,6 @@ import {
} from 'utils/collections'; } from 'utils/collections';
import { collectionSchema, itemSchema, environmentSchema, environmentsSchema } from '@usebruno/schema'; import { collectionSchema, itemSchema, environmentSchema, environmentsSchema } from '@usebruno/schema';
import { waitForNextTick } from 'utils/common'; import { waitForNextTick } from 'utils/common';
import { saveCollectionToIdb } from 'utils/idb';
import { sendNetworkRequest, cancelNetworkRequest } from 'utils/network'; import { sendNetworkRequest, cancelNetworkRequest } from 'utils/network';
import { import {
@ -100,7 +99,7 @@ export const sendRequest = (item, collectionUid) => (dispatch, getState) => {
const environment = findEnvironmentInCollection(collectionCopy, collection.activeEnvironmentUid); const environment = findEnvironmentInCollection(collectionCopy, collection.activeEnvironmentUid);
sendNetworkRequest(itemCopy, collection, environment) sendNetworkRequest(itemCopy, collection, environment, collectionCopy.collectionVariables)
.then((response) => { .then((response) => {
return dispatch( return dispatch(
responseReceived({ responseReceived({
@ -716,7 +715,8 @@ export const openCollectionEvent = (uid, pathname, name) => (dispatch, getState)
name: name, name: name,
pathname: pathname, pathname: pathname,
items: [], items: [],
showRunner: false showRunner: false,
collectionVariables: {}
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -176,7 +176,7 @@ export const collectionsSlice = createSlice({
} }
}, },
scriptEnvironmentUpdateEvent: (state, action) => { scriptEnvironmentUpdateEvent: (state, action) => {
const { collectionUid, environment } = action.payload; const { collectionUid, environment, collectionVariables } = action.payload;
const collection = findCollectionByUid(state.collections, collectionUid); const collection = findCollectionByUid(state.collections, collectionUid);
if (collection) { if (collection) {
@ -192,6 +192,9 @@ export const collectionsSlice = createSlice({
} }
}); });
} }
collection.collectionVariables = collectionVariables;
} }
}, },
requestCancelled: (state, action) => { requestCancelled: (state, action) => {

View File

@ -517,3 +517,12 @@ export const getEnvironmentVariables = (collection) => {
return variables; return variables;
} }
export const getAllVariables = (collection) => {
const environmentVariables = getEnvironmentVariables(collection);
return {
...environmentVariables,
...collection.collectionVariables
};
}

View File

@ -1,8 +1,8 @@
export const sendNetworkRequest = async (item, collection, environment) => { export const sendNetworkRequest = async (item, collection, environment, collectionVariables) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (['http-request', 'graphql-request'].includes(item.type)) { if (['http-request', 'graphql-request'].includes(item.type)) {
const timeStart = Date.now(); const timeStart = Date.now();
sendHttpRequest(item, collection, environment) sendHttpRequest(item, collection, environment, collectionVariables)
.then((response) => { .then((response) => {
const timeEnd = Date.now(); const timeEnd = Date.now();
resolve({ resolve({
@ -20,12 +20,12 @@ export const sendNetworkRequest = async (item, collection, environment) => {
}); });
}; };
const sendHttpRequest = async (item, collection, environment) => { const sendHttpRequest = async (item, collection, environment, collectionVariables) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const { ipcRenderer } = window; const { ipcRenderer } = window;
ipcRenderer ipcRenderer
.invoke('send-http-request', item, collection.uid, collection.pathname, environment) .invoke('send-http-request', item, collection.uid, collection.pathname, environment, collectionVariables)
.then(resolve) .then(resolve)
.catch(reject); .catch(reject);
}); });

View File

@ -69,7 +69,7 @@ const getSize = (data) => {
const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => { const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
// handler for sending http request // handler for sending http request
ipcMain.handle('send-http-request', async (event, item, collectionUid, collectionPath, environment) => { ipcMain.handle('send-http-request', async (event, item, collectionUid, collectionPath, environment, collectionVariables) => {
const cancelTokenUid = uuid(); const cancelTokenUid = uuid();
try { try {
@ -102,15 +102,16 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
if(request.script && request.script.length) { if(request.script && request.script.length) {
let script = request.script + '\n if (typeof onRequest === "function") {onRequest(__brunoRequest);}'; let script = request.script + '\n if (typeof onRequest === "function") {onRequest(__brunoRequest);}';
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runRequestScript(script, request, envVars, collectionPath); const result = scriptRuntime.runRequestScript(script, request, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,
collectionVariables: result.collectionVariables,
collectionUid collectionUid
}); });
} }
interpolateVars(request, envVars); interpolateVars(request, envVars, collectionVariables);
// todo: // todo:
// i have no clue why electron can't send the request object // i have no clue why electron can't send the request object
@ -132,10 +133,11 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
if(request.script && request.script.length) { if(request.script && request.script.length) {
let script = request.script + '\n if (typeof onResponse === "function") {onResponse(__brunoResponse);}'; let script = request.script + '\n if (typeof onResponse === "function") {onResponse(__brunoResponse);}';
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runResponseScript(script, response, envVars, collectionPath); const result = scriptRuntime.runResponseScript(script, response, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,
collectionVariables: result.collectionVariables,
collectionUid collectionUid
}); });
} }
@ -143,7 +145,7 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
const testFile = get(item, 'request.tests'); const testFile = get(item, 'request.tests');
if(testFile && testFile.length) { if(testFile && testFile.length) {
const testRuntime = new TestRuntime(); const testRuntime = new TestRuntime();
const result = testRuntime.runTests(testFile, request, response, envVars, collectionPath); const result = testRuntime.runTests(testFile, request, response, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:test-results', { mainWindow.webContents.send('main:test-results', {
results: result.results, results: result.results,
@ -275,7 +277,19 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
request.data = form; request.data = form;
} }
interpolateVars(request, envVars); if(request.script && request.script.length) {
let script = request.script + '\n if (typeof onRequest === "function") {onRequest(__brunoRequest);}';
const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runRequestScript(script, request, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment,
collectionVariables: result.collectionVariables,
collectionUid
});
}
interpolateVars(request, envVars, collectionVariables);
// todo: // todo:
// i have no clue why electron can't send the request object // i have no clue why electron can't send the request object
@ -298,10 +312,11 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
if(request.script && request.script.length) { if(request.script && request.script.length) {
let script = request.script + '\n if (typeof onResponse === "function") {onResponse(__brunoResponse);}'; let script = request.script + '\n if (typeof onResponse === "function") {onResponse(__brunoResponse);}';
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runResponseScript(script, response, envVars, collectionPath); const result = scriptRuntime.runResponseScript(script, response, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,
collectionVariables: result.collectionVariables,
collectionUid collectionUid
}); });
} }
@ -309,7 +324,7 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
const testFile = get(item, 'request.tests'); const testFile = get(item, 'request.tests');
if(testFile && testFile.length) { if(testFile && testFile.length) {
const testRuntime = new TestRuntime(); const testRuntime = new TestRuntime();
const result = testRuntime.runTests(testFile, request, response, envVars, collectionPath); const result = testRuntime.runTests(testFile, request, response, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:run-folder-event', { mainWindow.webContents.send('main:run-folder-event', {
type: 'test-results', type: 'test-results',

View File

@ -6,13 +6,19 @@ Mustache.escape = function (value) {
return value; return value;
}; };
const interpolateVars = (request, envVars = {}) => { const interpolateVars = (request, envVars = {}, collectionVariables ={}) => {
const interpolate = (str) => { const interpolate = (str) => {
if(!str || !str.length || typeof str !== "string") { if(!str || !str.length || typeof str !== "string") {
return str; return str;
} }
return Mustache.render(str, envVars); // collectionVariables take precedence over envVars
const combinedVars = {
...envVars,
...collectionVariables
};
return Mustache.render(str, combinedVars);
}; };
request.url = interpolate(request.url); request.url = interpolate(request.url);

View File

@ -9,8 +9,9 @@ const nanoid = require('nanoid');
const CryptoJS = require('crypto-js'); const CryptoJS = require('crypto-js');
class Bru { class Bru {
constructor(environment) { constructor(environment, collectionVariables) {
this._environment = environment; this._environment = environment;
this._collectionVariables = collectionVariables;
} }
require(module) { require(module) {
@ -50,6 +51,18 @@ class Bru {
this._environment[key] = value; this._environment[key] = value;
} }
setVar(key, value) {
if(!key) {
throw new Error('Key is required');
}
this._collectionVariables[key] = value;
}
getVar(key) {
return this._collectionVariables[key];
}
} }
module.exports = Bru; module.exports = Bru;

View File

@ -8,8 +8,8 @@ class ScriptRuntime {
constructor() { constructor() {
} }
runRequestScript(script, request, environment, collectionPath) { runRequestScript(script, request, environment, collectionVariables, collectionPath) {
const bru = new Bru(environment); const bru = new Bru(environment, collectionVariables);
const __brunoRequest = new BrunoRequest(request); const __brunoRequest = new BrunoRequest(request);
const context = { const context = {
@ -29,12 +29,13 @@ class ScriptRuntime {
return { return {
request, request,
environment environment,
collectionVariables
}; };
} }
runResponseScript(script, response, environment, collectionPath) { runResponseScript(script, response, environment, collectionVariables, collectionPath) {
const bru = new Bru(environment); const bru = new Bru(environment, collectionVariables);
const __brunoResponse = new BrunoResponse(response); const __brunoResponse = new BrunoResponse(response);
const context = { const context = {
@ -54,7 +55,8 @@ class ScriptRuntime {
return { return {
response, response,
environment environment,
collectionVariables
}; };
} }
} }

View File

@ -11,8 +11,8 @@ class TestRuntime {
constructor() { constructor() {
} }
runTests(testsFile, request, response, environment, collectionPath) { runTests(testsFile, request, response, environment, collectionVariables, collectionPath) {
const bru = new Bru(environment); const bru = new Bru(environment, collectionVariables);
const req = new BrunoRequest(request); const req = new BrunoRequest(request);
const res = new BrunoResponse(response); const res = new BrunoResponse(response);
@ -45,6 +45,7 @@ class TestRuntime {
request, request,
response, response,
environment, environment,
collectionVariables,
results: __brunoTestResults.getResults() results: __brunoTestResults.getResults()
}; };
} }

View File

@ -89,7 +89,8 @@ const collectionSchema = Yup.object({
showRunner: Yup.boolean(), showRunner: Yup.boolean(),
runnerResult: Yup.object({ runnerResult: Yup.object({
items: Yup.array() items: Yup.array()
}) }),
collectionVariables: Yup.object()
}).noUnknown(true).strict(); }).noUnknown(true).strict();