Merge branch 'main' of https://github.com/sthagen/usebruno-bruno into feature/export-to-postman-collection

This commit is contained in:
Miroslav Kapa 2023-10-12 00:31:23 +02:00
commit d4e07d2028
14 changed files with 99 additions and 67 deletions

28
package-lock.json generated
View File

@ -5600,6 +5600,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/chai-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz",
"integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==",
"peerDependencies": {
"chai": "^4.1.2"
}
},
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@ -16691,7 +16699,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@usebruno/js": "0.8.0", "@usebruno/js": "0.8.0",
"@usebruno/lang": "0.7.0", "@usebruno/lang": "0.8.0",
"axios": "^1.5.1", "axios": "^1.5.1",
"chai": "^4.3.7", "chai": "^4.3.7",
"chalk": "^3.0.0", "chalk": "^3.0.0",
@ -16771,14 +16779,15 @@
}, },
"packages/bruno-electron": { "packages/bruno-electron": {
"name": "bruno", "name": "bruno",
"version": "v0.22.0", "version": "v0.22.1",
"dependencies": { "dependencies": {
"@usebruno/js": "0.8.0", "@usebruno/js": "0.8.0",
"@usebruno/lang": "0.7.0", "@usebruno/lang": "0.8.0",
"@usebruno/schema": "0.5.0", "@usebruno/schema": "0.5.0",
"about-window": "^1.15.2", "about-window": "^1.15.2",
"axios": "^1.5.1", "axios": "^1.5.1",
"chai": "^4.3.7", "chai": "^4.3.7",
"chai-string": "^1.5.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"decomment": "^0.9.5", "decomment": "^0.9.5",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
@ -17021,7 +17030,7 @@
}, },
"packages/bruno-lang": { "packages/bruno-lang": {
"name": "@usebruno/lang", "name": "@usebruno/lang",
"version": "0.7.0", "version": "0.8.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"arcsecond": "^5.0.0", "arcsecond": "^5.0.0",
@ -20023,7 +20032,7 @@
"version": "file:packages/bruno-cli", "version": "file:packages/bruno-cli",
"requires": { "requires": {
"@usebruno/js": "0.8.0", "@usebruno/js": "0.8.0",
"@usebruno/lang": "0.7.0", "@usebruno/lang": "0.8.0",
"axios": "^1.5.1", "axios": "^1.5.1",
"chai": "^4.3.7", "chai": "^4.3.7",
"chalk": "^3.0.0", "chalk": "^3.0.0",
@ -21155,11 +21164,12 @@
"version": "file:packages/bruno-electron", "version": "file:packages/bruno-electron",
"requires": { "requires": {
"@usebruno/js": "0.8.0", "@usebruno/js": "0.8.0",
"@usebruno/lang": "0.7.0", "@usebruno/lang": "0.8.0",
"@usebruno/schema": "0.5.0", "@usebruno/schema": "0.5.0",
"about-window": "^1.15.2", "about-window": "^1.15.2",
"axios": "^1.5.1", "axios": "^1.5.1",
"chai": "^4.3.7", "chai": "^4.3.7",
"chai-string": "^1.5.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"decomment": "^0.9.5", "decomment": "^0.9.5",
"dmg-license": "^1.0.11", "dmg-license": "^1.0.11",
@ -21547,6 +21557,12 @@
"type-detect": "^4.0.5" "type-detect": "^4.0.5"
} }
}, },
"chai-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz",
"integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==",
"requires": {}
},
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",

View File

@ -1,8 +1,14 @@
import React from 'react'; import React from 'react';
import { IconSend } from '@tabler/icons'; import { IconSend } from '@tabler/icons';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
import { isMacOS } from 'utils/common/platform';
const Placeholder = () => { const Placeholder = () => {
const isMac = isMacOS();
const sendRequestShortcut = isMac ? 'Cmd + Enter' : 'Ctrl + Enter';
const newRequestShortcut = isMac ? 'Cmd + B' : 'Ctrl + B';
const editEnvironmentShortcut = isMac ? 'Cmd + E' : 'Ctrl + E';
return ( return (
<StyledWrapper> <StyledWrapper>
<div className="send-icon flex justify-center" style={{ fontSize: 200 }}> <div className="send-icon flex justify-center" style={{ fontSize: 200 }}>
@ -15,9 +21,9 @@ const Placeholder = () => {
<div className="px-1 py-2">Edit Environments</div> <div className="px-1 py-2">Edit Environments</div>
</div> </div>
<div className="flex flex-1 flex-col px-1"> <div className="flex flex-1 flex-col px-1">
<div className="px-1 py-2">Cmd + Enter</div> <div className="px-1 py-2">{sendRequestShortcut}</div>
<div className="px-1 py-2">Cmd + B</div> <div className="px-1 py-2">{newRequestShortcut}</div>
<div className="px-1 py-2">Cmd + E</div> <div className="px-1 py-2">{editEnvironmentShortcut}</div>
</div> </div>
</div> </div>
</StyledWrapper> </StyledWrapper>

View File

@ -45,6 +45,10 @@ const QueryResult = ({ item, collection, data, width, disableRunEventListener, h
return safeStringifyJSON(data); return safeStringifyJSON(data);
} }
if (mode.includes('image')) {
return item.requestSent.url;
}
// final fallback // final fallback
if (typeof data === 'string') { if (typeof data === 'string') {
return data; return data;
@ -103,6 +107,8 @@ const QueryResult = ({ item, collection, data, width, disableRunEventListener, h
className="h-full bg-white" className="h-full bg-white"
/> />
); );
} else if (mode.includes('image')) {
return <img src={item.requestSent.url} alt="image" />;
} }
return <CodeEditor collection={collection} theme={storedTheme} onRun={onRun} value={value} mode={mode} readOnly />; return <CodeEditor collection={collection} theme={storedTheme} onRun={onRun} value={value} mode={mode} readOnly />;

View File

@ -105,7 +105,7 @@ const Sidebar = () => {
Star Star
</GitHubButton> </GitHubButton>
</div> </div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v0.22.1</div> <div className="flex flex-grow items-center justify-end text-xs mr-2">v0.23.0</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -60,6 +60,8 @@ export const getCodeMirrorModeBasedOnContentType = (contentType) => {
return 'application/xml'; return 'application/xml';
} else if (contentType.includes('yaml')) { } else if (contentType.includes('yaml')) {
return 'application/yaml'; return 'application/yaml';
} else if (contentType.includes('image')) {
return 'application/image';
} else { } else {
return 'application/text'; return 'application/text';
} }

View File

@ -1,3 +1,5 @@
import { safeStringifyJSON } from 'utils/common';
export const sendNetworkRequest = async (item, collection, environment, collectionVariables) => { 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)) {
@ -7,7 +9,7 @@ export const sendNetworkRequest = async (item, collection, environment, collecti
state: 'success', state: 'success',
data: response.data, data: response.data,
headers: Object.entries(response.headers), headers: Object.entries(response.headers),
size: response.headers['content-length'] || 0, size: getResponseSize(response),
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
duration: response.duration duration: response.duration
@ -29,6 +31,10 @@ const sendHttpRequest = async (item, collection, environment, collectionVariable
}); });
}; };
const getResponseSize = (response) => {
return response.headers['content-length'] || Buffer.byteLength(safeStringifyJSON(response.data)) || 0;
};
export const fetchGqlSchema = async (endpoint, environment, request, collection) => { export const fetchGqlSchema = async (endpoint, environment, request, collection) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const { ipcRenderer } = window; const { ipcRenderer } = window;

View File

@ -25,7 +25,7 @@
], ],
"dependencies": { "dependencies": {
"@usebruno/js": "0.8.0", "@usebruno/js": "0.8.0",
"@usebruno/lang": "0.7.0", "@usebruno/lang": "0.8.0",
"axios": "^1.5.1", "axios": "^1.5.1",
"chai": "^4.3.7", "chai": "^4.3.7",
"chalk": "^3.0.0", "chalk": "^3.0.0",

View File

@ -1,5 +1,5 @@
{ {
"version": "v0.22.1", "version": "v0.23.0",
"name": "bruno", "name": "bruno",
"description": "Opensource API Client for Exploring and Testing APIs", "description": "Opensource API Client for Exploring and Testing APIs",
"homepage": "https://www.usebruno.com", "homepage": "https://www.usebruno.com",
@ -15,11 +15,12 @@
}, },
"dependencies": { "dependencies": {
"@usebruno/js": "0.8.0", "@usebruno/js": "0.8.0",
"@usebruno/lang": "0.7.0", "@usebruno/lang": "0.8.0",
"@usebruno/schema": "0.5.0", "@usebruno/schema": "0.5.0",
"about-window": "^1.15.2", "about-window": "^1.15.2",
"axios": "^1.5.1", "axios": "^1.5.1",
"chai": "^4.3.7", "chai": "^4.3.7",
"chai-string": "^1.5.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"decomment": "^0.9.5", "decomment": "^0.9.5",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",

View File

@ -75,7 +75,7 @@ const getSize = (data) => {
} }
if (typeof data === 'object') { if (typeof data === 'object') {
return Buffer.byteLength(JSON.stringify(data), 'utf8'); return Buffer.byteLength(safeStringifyJSON(data), 'utf8');
} }
return 0; return 0;
@ -493,7 +493,8 @@ const registerNetworkIpc = (mainWindow) => {
ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment, request, collection) => { ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment, request, collection) => {
try { try {
const envVars = getEnvVars(environment); const envVars = getEnvVars(environment);
const preparedRequest = prepareGqlIntrospectionRequest(endpoint, envVars, request); const collectionRoot = get(collection, 'root', {});
const preparedRequest = prepareGqlIntrospectionRequest(endpoint, envVars, request, collectionRoot);
const preferences = getPreferences(); const preferences = getPreferences();
const sslVerification = get(preferences, 'request.sslVerification', true); const sslVerification = get(preferences, 'request.sslVerification', true);
@ -711,14 +712,14 @@ const registerNetworkIpc = (mainWindow) => {
if (socksEnabled) { if (socksEnabled) {
const socksProxyAgent = new SocksProxyAgent(proxyUri); const socksProxyAgent = new SocksProxyAgent(proxyUri);
request.httpsAgent = socksProxyAgent; request.httpsAgent = socksProxyAgent;
request.httpAgent = socksProxyAgent; request.httpAgent = socksProxyAgent;
} else { } else {
request.httpsAgent = new HttpsProxyAgent(proxyUri, { request.httpsAgent = new HttpsProxyAgent(proxyUri, {
rejectUnauthorized: sslVerification rejectUnauthorized: sslVerification
}); });
request.httpAgent = new HttpProxyAgent(proxyUri); request.httpAgent = new HttpProxyAgent(proxyUri);
} }
} else if (!sslVerification) { } else if (!sslVerification) {

View File

@ -1,15 +1,14 @@
const Handlebars = require('handlebars'); const Handlebars = require('handlebars');
const { getIntrospectionQuery } = require('graphql'); const { getIntrospectionQuery } = require('graphql');
const { get } = require('lodash'); const { setAuthHeaders } = require('./prepare-request');
const prepareGqlIntrospectionRequest = (endpoint, envVars, request) => { const prepareGqlIntrospectionRequest = (endpoint, envVars, request, collectionRoot) => {
if (endpoint && endpoint.length) { if (endpoint && endpoint.length) {
endpoint = Handlebars.compile(endpoint, { noEscape: true })(envVars); endpoint = Handlebars.compile(endpoint, { noEscape: true })(envVars);
} }
const introspectionQuery = getIntrospectionQuery();
const queryParams = { const queryParams = {
query: introspectionQuery query: getIntrospectionQuery()
}; };
let axiosRequest = { let axiosRequest = {
@ -23,20 +22,7 @@ const prepareGqlIntrospectionRequest = (endpoint, envVars, request) => {
data: JSON.stringify(queryParams) data: JSON.stringify(queryParams)
}; };
if (request.auth) { return setAuthHeaders(axiosRequest, request, collectionRoot);
if (request.auth.mode === 'basic') {
axiosRequest.auth = {
username: get(request, 'auth.basic.username'),
password: get(request, 'auth.basic.password')
};
}
if (request.auth.mode === 'bearer') {
axiosRequest.headers.authorization = `Bearer ${get(request, 'auth.bearer.token')}`;
}
}
return axiosRequest;
}; };
const mapHeaders = (headers) => { const mapHeaders = (headers) => {

View File

@ -1,6 +1,41 @@
const { get, each, filter } = require('lodash'); const { get, each, filter } = require('lodash');
const decomment = require('decomment'); const decomment = require('decomment');
// Authentication
// A request can override the collection auth with another auth
// But it cannot override the collection auth with no auth
// We will provide support for disabling the auth via scripting in the future
const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
const collectionAuth = get(collectionRoot, 'request.auth');
if (collectionAuth) {
if (collectionAuth.mode === 'basic') {
axiosRequest.auth = {
username: get(collectionAuth, 'basic.username'),
password: get(collectionAuth, 'basic.password')
};
}
if (collectionAuth.mode === 'bearer') {
axiosRequest.headers['authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
}
}
if (request.auth) {
if (request.auth.mode === 'basic') {
axiosRequest.auth = {
username: get(request, 'auth.basic.username'),
password: get(request, 'auth.basic.password')
};
}
if (request.auth.mode === 'bearer') {
axiosRequest.headers['authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
}
}
return axiosRequest;
};
const prepareRequest = (request, collectionRoot) => { const prepareRequest = (request, collectionRoot) => {
const headers = {}; const headers = {};
let contentTypeDefined = false; let contentTypeDefined = false;
@ -30,36 +65,7 @@ const prepareRequest = (request, collectionRoot) => {
headers: headers headers: headers
}; };
// Authentication axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot);
// A request can override the collection auth with another auth
// But it cannot override the collection auth with no auth
// We will provide support for disabling the auth via scripting in the future
const collectionAuth = get(collectionRoot, 'request.auth');
if (collectionAuth) {
if (collectionAuth.mode === 'basic') {
axiosRequest.auth = {
username: get(collectionAuth, 'basic.username'),
password: get(collectionAuth, 'basic.password')
};
}
if (collectionAuth.mode === 'bearer') {
axiosRequest.headers['authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
}
}
if (request.auth) {
if (request.auth.mode === 'basic') {
axiosRequest.auth = {
username: get(request, 'auth.basic.username'),
password: get(request, 'auth.basic.password')
};
}
if (request.auth.mode === 'bearer') {
axiosRequest.headers['authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
}
}
if (request.body.mode === 'json') { if (request.body.mode === 'json') {
if (!contentTypeDefined) { if (!contentTypeDefined) {
@ -132,3 +138,4 @@ const prepareRequest = (request, collectionRoot) => {
}; };
module.exports = prepareRequest; module.exports = prepareRequest;
module.exports.setAuthHeaders = setAuthHeaders;

View File

@ -6,6 +6,7 @@ const BrunoRequest = require('../bruno-request');
const { evaluateJsTemplateLiteral, evaluateJsExpression, createResponseParser } = require('../utils'); const { evaluateJsTemplateLiteral, evaluateJsExpression, createResponseParser } = require('../utils');
const { expect } = chai; const { expect } = chai;
chai.use(require('chai-string'));
chai.use(function (chai, utils) { chai.use(function (chai, utils) {
// Custom assertion for checking if a variable is JSON // Custom assertion for checking if a variable is JSON
chai.Assertion.addProperty('json', function () { chai.Assertion.addProperty('json', function () {

View File

@ -1,6 +1,6 @@
{ {
"name": "@usebruno/lang", "name": "@usebruno/lang",
"version": "0.7.0", "version": "0.8.0",
"license": "MIT", "license": "MIT",
"main": "src/index.js", "main": "src/index.js",
"files": [ "files": [