mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-07 14:08:38 +00:00
export & import in opencollection format (#6329)
This commit is contained in:
@@ -26,6 +26,21 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share-button {
|
||||
display: flex;
|
||||
border-radius: ${(props) => props.theme.border.radius.base};
|
||||
padding: 10px;
|
||||
border: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
|
||||
background-color: ${(props) => props.theme.sidebar.bg};
|
||||
color: ${(props) => props.theme.text};
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.listItem.hoverBg};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import { IconUpload, IconLoader2, IconAlertTriangle } from '@tabler/icons';
|
||||
import { IconUpload, IconLoader2, IconAlertTriangle, IconFileExport } from '@tabler/icons';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import Bruno from 'components/Bruno';
|
||||
import exportBrunoCollection from 'utils/collections/export';
|
||||
import exportPostmanCollection from 'utils/exporters/postman-collection';
|
||||
import exportOpenCollection from 'utils/exporters/opencollection';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { transformCollectionToSaveToExportAsFile } from 'utils/collections/index';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -48,6 +49,12 @@ const ShareCollection = ({ onClose, collectionUid }) => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleExportOpenCollection = () => {
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
exportOpenCollection(transformCollectionToSaveToExportAsFile(collectionCopy));
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="md"
|
||||
@@ -60,10 +67,10 @@ const ShareCollection = ({ onClose, collectionUid }) => {
|
||||
<StyledWrapper className="flex flex-col h-full w-[500px]">
|
||||
<div className="space-y-2">
|
||||
<div
|
||||
className={`flex border border-gray-200 dark:border-gray-600 items-center p-3 rounded-lg transition-colors ${
|
||||
className={`share-button ${
|
||||
isCollectionLoading
|
||||
? 'opacity-50 cursor-not-allowed'
|
||||
: 'hover:bg-gray-100 dark:hover:bg-gray-500/10 cursor-pointer'
|
||||
: 'cursor-pointer'
|
||||
}`}
|
||||
onClick={isCollectionLoading ? undefined : handleExportBrunoCollection}
|
||||
>
|
||||
@@ -77,10 +84,31 @@ const ShareCollection = ({ onClose, collectionUid }) => {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`flex flex-col border border-gray-200 dark:border-gray-600 items-center rounded-lg transition-colors ${
|
||||
className={`share-button ${
|
||||
isCollectionLoading
|
||||
? 'opacity-50 cursor-not-allowed'
|
||||
: 'hover:bg-gray-100 dark:hover:bg-gray-500/10 cursor-pointer'
|
||||
: 'cursor-pointer'
|
||||
}`}
|
||||
onClick={isCollectionLoading ? undefined : handleExportOpenCollection}
|
||||
>
|
||||
<div className="mr-3 p-1 rounded-full">
|
||||
{isCollectionLoading ? (
|
||||
<IconLoader2 size={28} className="animate-spin" />
|
||||
) : (
|
||||
<IconFileExport size={28} strokeWidth={1} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">OpenCollection</div>
|
||||
<div className="text-xs">{isCollectionLoading ? 'Loading collection...' : 'Export in OpenCollection format'}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`flex !flex-col share-button ${
|
||||
isCollectionLoading
|
||||
? 'opacity-50 cursor-not-allowed'
|
||||
: 'cursor-pointer'
|
||||
}`}
|
||||
onClick={isCollectionLoading ? undefined : handleExportPostmanCollection}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isInsomniaCollection } from 'utils/importers/insomnia-collection';
|
||||
import { isOpenApiSpec } from 'utils/importers/openapi-collection';
|
||||
import { isWSDLCollection } from 'utils/importers/wsdl-collection';
|
||||
import { isBrunoCollection } from 'utils/importers/bruno-collection';
|
||||
import { isOpenCollection } from 'utils/importers/opencollection';
|
||||
import FullscreenLoader from './FullscreenLoader/index';
|
||||
|
||||
const convertFileToObject = async (file) => {
|
||||
@@ -72,6 +73,8 @@ const ImportCollection = ({ onClose, handleSubmit }) => {
|
||||
type = 'postman';
|
||||
} else if (isInsomniaCollection(data)) {
|
||||
type = 'insomnia';
|
||||
} else if (isOpenCollection(data)) {
|
||||
type = 'opencollection';
|
||||
} else if (isBrunoCollection(data)) {
|
||||
type = 'bruno';
|
||||
} else {
|
||||
@@ -159,7 +162,7 @@ const ImportCollection = ({ onClose, handleSubmit }) => {
|
||||
</button>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Supports Bruno, Postman, Insomnia, OpenAPI v3, and WSDL formats
|
||||
Supports Bruno, OpenCollection, Postman, Insomnia, OpenAPI v3, and WSDL formats
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { postmanToBruno } from 'utils/importers/postman-collection';
|
||||
import { convertInsomniaToBruno } from 'utils/importers/insomnia-collection';
|
||||
import { convertOpenapiToBruno } from 'utils/importers/openapi-collection';
|
||||
import { processBrunoCollection } from 'utils/importers/bruno-collection';
|
||||
import { processOpenCollection } from 'utils/importers/opencollection';
|
||||
import { wsdlToBruno } from '@usebruno/converters';
|
||||
import { toastError } from 'utils/common/error';
|
||||
import Modal from 'components/Modal';
|
||||
@@ -37,6 +38,8 @@ const getCollectionName = (format, rawData) => {
|
||||
return rawData.name || 'Insomnia Collection';
|
||||
case 'bruno':
|
||||
return rawData.name || 'Bruno Collection';
|
||||
case 'opencollection':
|
||||
return rawData.info?.name || 'OpenCollection';
|
||||
case 'wsdl':
|
||||
return 'WSDL Collection';
|
||||
default:
|
||||
@@ -65,6 +68,9 @@ const convertCollection = async (format, rawData, groupingType) => {
|
||||
case 'bruno':
|
||||
collection = await processBrunoCollection(rawData);
|
||||
break;
|
||||
case 'opencollection':
|
||||
collection = await processOpenCollection(rawData);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown collection format');
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ const transformCollection = async (collection, type) => {
|
||||
const { convertOpenapiToBruno } = await import('utils/importers/openapi-collection');
|
||||
return convertOpenapiToBruno(collection);
|
||||
}
|
||||
case 'opencollection': {
|
||||
const { processOpenCollection } = await import('utils/importers/opencollection');
|
||||
return processOpenCollection(collection);
|
||||
}
|
||||
case 'wsdl': {
|
||||
const { wsdlToBruno } = await import('@usebruno/converters');
|
||||
return wsdlToBruno(collection);
|
||||
|
||||
23
packages/bruno-app/src/utils/exporters/opencollection.js
Normal file
23
packages/bruno-app/src/utils/exporters/opencollection.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as FileSaver from 'file-saver';
|
||||
import jsyaml from 'js-yaml';
|
||||
import { brunoToOpenCollection } from '@usebruno/converters';
|
||||
import { sanitizeName } from 'utils/common/regex';
|
||||
|
||||
export const exportCollection = (collection) => {
|
||||
const openCollection = brunoToOpenCollection(collection);
|
||||
|
||||
const yamlContent = jsyaml.dump(openCollection, {
|
||||
indent: 2,
|
||||
lineWidth: -1,
|
||||
noRefs: true,
|
||||
sortKeys: false
|
||||
});
|
||||
|
||||
const sanitizedName = sanitizeName(collection.name);
|
||||
const fileName = `${sanitizedName}.yml`;
|
||||
const fileBlob = new Blob([yamlContent], { type: 'application/x-yaml' });
|
||||
|
||||
FileSaver.saveAs(fileBlob, fileName);
|
||||
};
|
||||
|
||||
export default exportCollection;
|
||||
81
packages/bruno-app/src/utils/importers/opencollection.js
Normal file
81
packages/bruno-app/src/utils/importers/opencollection.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import each from 'lodash/each';
|
||||
import { uuid } from 'utils/common';
|
||||
import { BrunoError } from 'utils/common/error';
|
||||
import { validateSchema, updateUidsInCollection, hydrateSeqInCollection } from './common';
|
||||
import { openCollectionToBruno } from '@usebruno/converters';
|
||||
|
||||
const addUidsToRoot = (collection) => {
|
||||
if (collection.root?.request?.headers) {
|
||||
each(collection.root.request.headers, (header) => {
|
||||
header.uid = uuid();
|
||||
});
|
||||
}
|
||||
if (collection.root?.request?.vars?.req) {
|
||||
each(collection.root.request.vars.req, (v) => {
|
||||
v.uid = uuid();
|
||||
});
|
||||
}
|
||||
if (collection.root?.request?.vars?.res) {
|
||||
each(collection.root.request.vars.res, (v) => {
|
||||
v.uid = uuid();
|
||||
});
|
||||
}
|
||||
|
||||
const addUidsToFolderRoot = (items) => {
|
||||
each(items, (item) => {
|
||||
if (item.type === 'folder') {
|
||||
if (item.root?.request?.headers) {
|
||||
each(item.root.request.headers, (header) => {
|
||||
header.uid = uuid();
|
||||
});
|
||||
}
|
||||
if (item.root?.request?.vars?.req) {
|
||||
each(item.root.request.vars.req, (v) => {
|
||||
v.uid = uuid();
|
||||
});
|
||||
}
|
||||
if (item.root?.request?.vars?.res) {
|
||||
each(item.root.request.vars.res, (v) => {
|
||||
v.uid = uuid();
|
||||
});
|
||||
}
|
||||
if (item.items?.length) {
|
||||
addUidsToFolderRoot(item.items);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
addUidsToFolderRoot(collection.items);
|
||||
return collection;
|
||||
};
|
||||
|
||||
export const processOpenCollection = async (jsonData) => {
|
||||
try {
|
||||
let collection = openCollectionToBruno(jsonData);
|
||||
collection = hydrateSeqInCollection(collection);
|
||||
collection = updateUidsInCollection(collection);
|
||||
collection = addUidsToRoot(collection);
|
||||
await validateSchema(collection);
|
||||
return collection;
|
||||
} catch (err) {
|
||||
console.error('Error processing OpenCollection:', err);
|
||||
throw new BrunoError('Import OpenCollection failed');
|
||||
}
|
||||
};
|
||||
|
||||
export const isOpenCollection = (data) => {
|
||||
if (typeof data !== 'object' || data === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.opencollection !== 'string' || !data.opencollection.trim()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.info !== 'object' || data.info === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -4,7 +4,6 @@
|
||||
"license": "MIT",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/esm/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
@@ -29,6 +28,8 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.4",
|
||||
"@opencollection/types": "0.3.0",
|
||||
"@usebruno/schema-types": "0.0.1",
|
||||
"@rollup/plugin-alias": "^5.1.0",
|
||||
"@rollup/plugin-commonjs": "^23.0.2",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
|
||||
@@ -1,43 +1,89 @@
|
||||
const { nodeResolve } = require('@rollup/plugin-node-resolve');
|
||||
const commonjs = require('@rollup/plugin-commonjs');
|
||||
const typescript = require('@rollup/plugin-typescript');
|
||||
const { terser } = require('rollup-plugin-terser');
|
||||
const peerDepsExternal = require('rollup-plugin-peer-deps-external');
|
||||
const { copy } = require('@web/rollup-plugin-copy');
|
||||
const path = require('path');
|
||||
|
||||
const packageJson = require('./package.json');
|
||||
const alias = require('@rollup/plugin-alias');
|
||||
const path = require('path');
|
||||
|
||||
const externalDeps = [
|
||||
'@usebruno/schema',
|
||||
'@usebruno/schema-types',
|
||||
/@usebruno\/schema-types\/.*/,
|
||||
'@opencollection/types',
|
||||
/@opencollection\/types\/.*/,
|
||||
// Runtime dependencies
|
||||
'lodash',
|
||||
'lodash/each',
|
||||
'lodash/get',
|
||||
'lodash/cloneDeep',
|
||||
'lodash/map',
|
||||
'js-yaml',
|
||||
'jscodeshift',
|
||||
'nanoid',
|
||||
'xml2js',
|
||||
// Node built-ins
|
||||
'path',
|
||||
'fs'
|
||||
];
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
input: 'src/index.js',
|
||||
output: [
|
||||
{
|
||||
dir: path.dirname(packageJson.main),
|
||||
format: 'cjs',
|
||||
sourcemap: true
|
||||
},
|
||||
{
|
||||
dir: path.dirname(packageJson.module),
|
||||
format: 'esm',
|
||||
sourcemap: true
|
||||
}
|
||||
],
|
||||
output: {
|
||||
dir: path.dirname(packageJson.main),
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
entryFileNames: 'index.js'
|
||||
},
|
||||
plugins: [
|
||||
peerDepsExternal(),
|
||||
nodeResolve({
|
||||
preferBuiltins: true,
|
||||
extensions: ['.js', '.css'] // Resolve .js files
|
||||
extensions: ['.js', '.ts', '.tsx', '.json']
|
||||
}),
|
||||
commonjs(),
|
||||
terser(),
|
||||
alias({
|
||||
entries: [{ find: 'src', replacement: path.resolve(__dirname, 'src') }]
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
sourceMap: true,
|
||||
outDir: path.dirname(packageJson.main)
|
||||
}),
|
||||
terser(),
|
||||
copy({
|
||||
patterns: 'src/workers/scripts/**/*',
|
||||
rootDir: '.'
|
||||
})
|
||||
]
|
||||
],
|
||||
external: externalDeps
|
||||
},
|
||||
{
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
dir: path.dirname(packageJson.module),
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
entryFileNames: 'index.js'
|
||||
},
|
||||
plugins: [
|
||||
peerDepsExternal(),
|
||||
nodeResolve({
|
||||
extensions: ['.js', '.ts', '.tsx', '.json']
|
||||
}),
|
||||
commonjs(),
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
sourceMap: true,
|
||||
outDir: path.dirname(packageJson.module)
|
||||
}),
|
||||
terser(),
|
||||
copy({
|
||||
patterns: 'src/workers/scripts/**/*',
|
||||
rootDir: '.'
|
||||
})
|
||||
],
|
||||
external: externalDeps
|
||||
}
|
||||
];
|
||||
|
||||
@@ -5,3 +5,5 @@ export { default as openApiToBruno } from './openapi/openapi-to-bruno.js';
|
||||
export { default as insomniaToBruno } from './insomnia/insomnia-to-bruno.js';
|
||||
export { default as wsdlToBruno } from './wsdl/wsdl-to-bruno.js';
|
||||
export { default as postmanTranslation } from './postman/postman-translations.js';
|
||||
export { openCollectionToBruno } from './opencollection/opencollection-to-bruno.js';
|
||||
export { brunoToOpenCollection } from './opencollection/bruno-to-opencollection.js';
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import { toOpenCollectionAuth, toOpenCollectionHeaders, toOpenCollectionScripts, toOpenCollectionVariables } from "./common";
|
||||
import { toOpenCollectionEnvironments } from "./environment";
|
||||
import { toOpenCollectionFolder } from "./folder";
|
||||
import { toOpenCollectionItems } from "./items";
|
||||
import { BrunoCollection, BrunoCollectionRoot, BrunoConfig, ClientCertificate, CollectionConfig, OpenCollection, PemCertificate, Pkcs12Certificate, Protobuf } from "./types";
|
||||
|
||||
const toOpenCollectionConfig = (brunoConfig: BrunoConfig | undefined): CollectionConfig | undefined => {
|
||||
if (!brunoConfig) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const config: CollectionConfig = {};
|
||||
|
||||
if (brunoConfig.protobuf?.protoFiles?.length || brunoConfig.protobuf?.importPaths?.length) {
|
||||
config.protobuf = {} as Protobuf;
|
||||
|
||||
if (brunoConfig.protobuf.protoFiles?.length) {
|
||||
config.protobuf.protoFiles = brunoConfig.protobuf.protoFiles.map((f) => ({
|
||||
type: 'file' as const,
|
||||
path: f.path
|
||||
}));
|
||||
}
|
||||
|
||||
if (brunoConfig.protobuf.importPaths?.length) {
|
||||
config.protobuf.importPaths = brunoConfig.protobuf.importPaths.map((p) => {
|
||||
const importPath: { path: string; disabled?: boolean } = { path: p.path };
|
||||
if (p.disabled) {
|
||||
importPath.disabled = true;
|
||||
}
|
||||
return importPath;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (brunoConfig.proxy?.enabled) {
|
||||
if (brunoConfig.proxy.enabled === 'global') {
|
||||
config.proxy = 'inherit';
|
||||
} else {
|
||||
config.proxy = {
|
||||
protocol: brunoConfig.proxy.protocol || 'http',
|
||||
hostname: brunoConfig.proxy.hostname || '',
|
||||
port: brunoConfig.proxy.port || 0
|
||||
};
|
||||
|
||||
if (brunoConfig.proxy.auth?.enabled) {
|
||||
config.proxy.auth = {
|
||||
username: brunoConfig.proxy.auth.username || '',
|
||||
password: brunoConfig.proxy.auth.password || ''
|
||||
};
|
||||
}
|
||||
|
||||
if (brunoConfig.proxy.bypassProxy) {
|
||||
config.proxy.bypassProxy = brunoConfig.proxy.bypassProxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (brunoConfig.clientCertificates?.certs?.length) {
|
||||
config.clientCertificates = brunoConfig.clientCertificates.certs
|
||||
.map((cert): ClientCertificate | null => {
|
||||
if (cert.type === 'pem') {
|
||||
const pemCert: PemCertificate = {
|
||||
domain: cert.domain || '',
|
||||
type: 'pem',
|
||||
certificateFilePath: cert.certFilePath || '',
|
||||
privateKeyFilePath: cert.keyFilePath || ''
|
||||
};
|
||||
if (cert.passphrase) {
|
||||
pemCert.passphrase = cert.passphrase;
|
||||
}
|
||||
return pemCert;
|
||||
} else if (cert.type === 'pkcs12') {
|
||||
const pkcs12Cert: Pkcs12Certificate = {
|
||||
domain: cert.domain || '',
|
||||
type: 'pkcs12',
|
||||
pkcs12FilePath: cert.pfxFilePath || ''
|
||||
};
|
||||
if (cert.passphrase) {
|
||||
pkcs12Cert.passphrase = cert.passphrase;
|
||||
}
|
||||
return pkcs12Cert;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((cert): cert is ClientCertificate => cert !== null);
|
||||
}
|
||||
|
||||
return Object.keys(config).length > 0 ? config : undefined;
|
||||
};
|
||||
|
||||
const hasRequestDefaults = (root: BrunoCollectionRoot | undefined): boolean => {
|
||||
const request = root?.request;
|
||||
return Boolean(
|
||||
request?.headers?.length ||
|
||||
request?.vars?.req?.length ||
|
||||
request?.script?.req ||
|
||||
request?.script?.res ||
|
||||
request?.tests ||
|
||||
(request?.auth && request.auth.mode !== 'none')
|
||||
);
|
||||
};
|
||||
|
||||
export const brunoToOpenCollection = (collection: BrunoCollection): OpenCollection => {
|
||||
const openCollection: OpenCollection = {
|
||||
opencollection: '1.0.0',
|
||||
info: {
|
||||
name: collection.name || 'Untitled Collection'
|
||||
}
|
||||
};
|
||||
|
||||
const config = toOpenCollectionConfig(collection.brunoConfig as BrunoConfig);
|
||||
if (config) {
|
||||
openCollection.config = config;
|
||||
}
|
||||
|
||||
const environments = toOpenCollectionEnvironments(collection.environments ?? undefined);
|
||||
if (environments?.length) {
|
||||
if (!openCollection.config) {
|
||||
openCollection.config = {};
|
||||
}
|
||||
openCollection.config.environments = environments;
|
||||
}
|
||||
|
||||
const items = toOpenCollectionItems(collection.items, toOpenCollectionFolder);
|
||||
if (items.length) {
|
||||
openCollection.items = items as OpenCollection['items'];
|
||||
}
|
||||
|
||||
if (hasRequestDefaults(collection.root as BrunoCollectionRoot)) {
|
||||
const request = (collection.root as BrunoCollectionRoot)?.request;
|
||||
openCollection.request = {};
|
||||
|
||||
const headers = toOpenCollectionHeaders(request?.headers);
|
||||
if (headers) {
|
||||
openCollection.request.headers = headers;
|
||||
}
|
||||
|
||||
const auth = toOpenCollectionAuth(request?.auth);
|
||||
if (auth) {
|
||||
openCollection.request.auth = auth;
|
||||
}
|
||||
|
||||
const variables = toOpenCollectionVariables(request?.vars);
|
||||
if (variables) {
|
||||
openCollection.request.variables = variables;
|
||||
}
|
||||
|
||||
const scripts = toOpenCollectionScripts(request as any);
|
||||
if (scripts) {
|
||||
openCollection.request.scripts = scripts;
|
||||
}
|
||||
}
|
||||
|
||||
if ((collection.root as BrunoCollectionRoot)?.docs) {
|
||||
openCollection.docs = {
|
||||
content: (collection.root as BrunoCollectionRoot).docs!,
|
||||
type: 'text/markdown'
|
||||
};
|
||||
}
|
||||
|
||||
openCollection.bundled = true;
|
||||
|
||||
const extensions: { ignore?: string[] } = {};
|
||||
if ((collection.brunoConfig as BrunoConfig)?.ignore?.length) {
|
||||
extensions.ignore = (collection.brunoConfig as BrunoConfig).ignore;
|
||||
}
|
||||
if (Object.keys(extensions).length > 0) {
|
||||
openCollection.extensions = extensions;
|
||||
}
|
||||
|
||||
return openCollection;
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import type {
|
||||
Action,
|
||||
ActionSetVariable,
|
||||
ActionVariableScope,
|
||||
BrunoVariable,
|
||||
BrunoVariables
|
||||
} from '../types';
|
||||
|
||||
/**
|
||||
* Convert Bruno post-response variables to OpenCollection actions.
|
||||
* Post-response variables in Bruno are converted to 'set-variable' actions
|
||||
* with phase 'after-response'.
|
||||
*/
|
||||
export const toOpenCollectionActions = (resVariables: BrunoVariables | null | undefined): Action[] | undefined => {
|
||||
if (!resVariables?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const actions: Action[] = resVariables.map((v: BrunoVariable): ActionSetVariable => {
|
||||
const action: ActionSetVariable = {
|
||||
type: 'set-variable',
|
||||
phase: 'after-response',
|
||||
selector: {
|
||||
expression: v.value || '',
|
||||
method: 'jsonq'
|
||||
},
|
||||
variable: {
|
||||
name: v.name || '',
|
||||
scope: v.local ? 'request' : 'runtime' as ActionVariableScope
|
||||
}
|
||||
};
|
||||
|
||||
if (v.description && typeof v.description === 'string' && v.description.trim().length) {
|
||||
action.description = v.description;
|
||||
}
|
||||
|
||||
if (v.enabled === false) {
|
||||
action.disabled = true;
|
||||
}
|
||||
|
||||
return action;
|
||||
});
|
||||
|
||||
return actions.length > 0 ? actions : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert OpenCollection actions to Bruno post-response variables.
|
||||
* Only 'set-variable' actions with phase 'after-response' are converted.
|
||||
*/
|
||||
export const fromOpenCollectionActions = (actions: Action[] | null | undefined): BrunoVariables => {
|
||||
if (!actions?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const resVars: BrunoVariables = [];
|
||||
|
||||
actions.forEach((action: Action) => {
|
||||
// Only process 'set-variable' actions with 'after-response' phase
|
||||
if (action.type === 'set-variable' && action.phase === 'after-response') {
|
||||
const setVarAction = action as ActionSetVariable;
|
||||
|
||||
const variable: BrunoVariable = {
|
||||
uid: uuid(),
|
||||
name: setVarAction.variable?.name || '',
|
||||
value: setVarAction.selector?.expression || '',
|
||||
enabled: setVarAction.disabled !== true,
|
||||
local: setVarAction.variable?.scope === 'request'
|
||||
};
|
||||
|
||||
if (setVarAction.description) {
|
||||
variable.description = typeof setVarAction.description === 'string'
|
||||
? setVarAction.description
|
||||
: (setVarAction.description as { content?: string })?.content || '';
|
||||
}
|
||||
|
||||
resVars.push(variable);
|
||||
}
|
||||
});
|
||||
|
||||
return resVars;
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import type {
|
||||
Assertion,
|
||||
BrunoKeyValue
|
||||
} from '../types';
|
||||
|
||||
export const fromOpenCollectionAssertions = (assertions: Assertion[] | undefined): BrunoKeyValue[] => {
|
||||
if (!assertions?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return assertions.map((a): BrunoKeyValue => ({
|
||||
uid: uuid(),
|
||||
name: a.expression || '',
|
||||
value: `${a.operator || 'eq'} ${a.value || ''}`.trim(),
|
||||
description: typeof a.description === 'string' ? a.description : a.description?.content || null,
|
||||
enabled: a.disabled !== true
|
||||
}));
|
||||
};
|
||||
|
||||
export const toOpenCollectionAssertions = (assertions: BrunoKeyValue[] | null | undefined): Assertion[] | undefined => {
|
||||
if (!assertions?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return assertions.map((a): Assertion => {
|
||||
const valueStr = a.value || '';
|
||||
const parts = valueStr.split(' ');
|
||||
const operator = parts[0] || 'eq';
|
||||
const value = parts.slice(1).join(' ');
|
||||
|
||||
const ocAssertion: Assertion = {
|
||||
expression: a.name || '',
|
||||
operator,
|
||||
value
|
||||
};
|
||||
|
||||
if (a.enabled === false) {
|
||||
ocAssertion.disabled = true;
|
||||
}
|
||||
|
||||
if (a.description && typeof a.description === 'string' && a.description.trim().length) {
|
||||
ocAssertion.description = a.description;
|
||||
}
|
||||
|
||||
return ocAssertion;
|
||||
});
|
||||
};
|
||||
470
packages/bruno-converters/src/opencollection/common/auth.ts
Normal file
470
packages/bruno-converters/src/opencollection/common/auth.ts
Normal file
@@ -0,0 +1,470 @@
|
||||
import type {
|
||||
Auth,
|
||||
AuthBasic,
|
||||
AuthBearer,
|
||||
AuthDigest,
|
||||
AuthNTLM,
|
||||
AuthAwsV4,
|
||||
AuthApiKey,
|
||||
AuthWsse,
|
||||
AuthOAuth2,
|
||||
BrunoAuth,
|
||||
BrunoOAuth2
|
||||
} from '../types';
|
||||
|
||||
const fromOpenCollectionOAuth2 = (auth: AuthOAuth2): BrunoAuth => {
|
||||
const getTokenPlacement = (tokenConfig: AuthOAuth2['tokenConfig']): string => {
|
||||
if (tokenConfig?.placement && 'query' in tokenConfig.placement) {
|
||||
return 'query';
|
||||
}
|
||||
return 'header';
|
||||
};
|
||||
|
||||
const getTokenHeaderPrefix = (tokenConfig: AuthOAuth2['tokenConfig']): string => {
|
||||
if (tokenConfig?.placement && 'header' in tokenConfig.placement) {
|
||||
return tokenConfig.placement.header;
|
||||
}
|
||||
return 'Bearer';
|
||||
};
|
||||
|
||||
const getTokenQueryKey = (tokenConfig: AuthOAuth2['tokenConfig']): string => {
|
||||
if (tokenConfig?.placement && 'query' in tokenConfig.placement) {
|
||||
return tokenConfig.placement.query;
|
||||
}
|
||||
return 'access_token';
|
||||
};
|
||||
|
||||
const getCredentialsPlacement = (credentials: AuthOAuth2['credentials']): 'body' | 'basic_auth_header' => {
|
||||
if (credentials && 'placement' in credentials && credentials.placement === 'basic_auth_header') {
|
||||
return 'basic_auth_header';
|
||||
}
|
||||
return 'body';
|
||||
};
|
||||
|
||||
const buildOAuth2Config = (base: Partial<BrunoOAuth2>): BrunoAuth => {
|
||||
const brunoAuth: BrunoAuth = {
|
||||
mode: 'oauth2',
|
||||
awsv4: null,
|
||||
basic: null,
|
||||
bearer: null,
|
||||
digest: null,
|
||||
ntlm: null,
|
||||
oauth2: {
|
||||
grantType: base.grantType || 'client_credentials',
|
||||
username: base.username || null,
|
||||
password: base.password || null,
|
||||
callbackUrl: base.callbackUrl || null,
|
||||
authorizationUrl: base.authorizationUrl || null,
|
||||
accessTokenUrl: base.accessTokenUrl || null,
|
||||
clientId: base.clientId || null,
|
||||
clientSecret: base.clientSecret || null,
|
||||
scope: base.scope || null,
|
||||
state: base.state || null,
|
||||
pkce: base.pkce || null,
|
||||
credentialsPlacement: base.credentialsPlacement || null,
|
||||
credentialsId: base.credentialsId || null,
|
||||
tokenPlacement: base.tokenPlacement || null,
|
||||
tokenHeaderPrefix: base.tokenHeaderPrefix || null,
|
||||
tokenQueryKey: base.tokenQueryKey || null,
|
||||
refreshTokenUrl: base.refreshTokenUrl || null,
|
||||
autoRefreshToken: base.autoRefreshToken || null,
|
||||
autoFetchToken: base.autoFetchToken || null,
|
||||
additionalParameters: null
|
||||
},
|
||||
wsse: null,
|
||||
apikey: null
|
||||
};
|
||||
return brunoAuth;
|
||||
};
|
||||
|
||||
switch (auth.flow) {
|
||||
case 'client_credentials':
|
||||
return buildOAuth2Config({
|
||||
grantType: 'client_credentials',
|
||||
accessTokenUrl: auth.accessTokenUrl || null,
|
||||
refreshTokenUrl: auth.refreshTokenUrl || null,
|
||||
clientId: auth.credentials?.clientId || null,
|
||||
clientSecret: auth.credentials?.clientSecret || null,
|
||||
scope: auth.scope || null,
|
||||
credentialsPlacement: getCredentialsPlacement(auth.credentials),
|
||||
credentialsId: auth.tokenConfig?.id || 'credentials',
|
||||
tokenPlacement: getTokenPlacement(auth.tokenConfig),
|
||||
tokenHeaderPrefix: getTokenHeaderPrefix(auth.tokenConfig),
|
||||
tokenQueryKey: getTokenQueryKey(auth.tokenConfig),
|
||||
autoFetchToken: auth.settings?.autoFetchToken !== false,
|
||||
autoRefreshToken: auth.settings?.autoRefreshToken !== false
|
||||
});
|
||||
|
||||
case 'resource_owner_password_credentials':
|
||||
return buildOAuth2Config({
|
||||
grantType: 'password',
|
||||
accessTokenUrl: auth.accessTokenUrl || null,
|
||||
refreshTokenUrl: auth.refreshTokenUrl || null,
|
||||
clientId: auth.credentials?.clientId || null,
|
||||
clientSecret: auth.credentials?.clientSecret || null,
|
||||
username: auth.resourceOwner?.username || null,
|
||||
password: auth.resourceOwner?.password || null,
|
||||
scope: auth.scope || null,
|
||||
credentialsPlacement: getCredentialsPlacement(auth.credentials),
|
||||
credentialsId: auth.tokenConfig?.id || 'credentials',
|
||||
tokenPlacement: getTokenPlacement(auth.tokenConfig),
|
||||
tokenHeaderPrefix: getTokenHeaderPrefix(auth.tokenConfig),
|
||||
tokenQueryKey: getTokenQueryKey(auth.tokenConfig),
|
||||
autoFetchToken: auth.settings?.autoFetchToken !== false,
|
||||
autoRefreshToken: auth.settings?.autoRefreshToken !== false
|
||||
});
|
||||
|
||||
case 'authorization_code':
|
||||
return buildOAuth2Config({
|
||||
grantType: 'authorization_code',
|
||||
authorizationUrl: auth.authorizationUrl || null,
|
||||
accessTokenUrl: auth.accessTokenUrl || null,
|
||||
refreshTokenUrl: auth.refreshTokenUrl || null,
|
||||
callbackUrl: auth.callbackUrl || null,
|
||||
clientId: auth.credentials?.clientId || null,
|
||||
clientSecret: auth.credentials?.clientSecret || null,
|
||||
scope: auth.scope || null,
|
||||
pkce: auth.pkce?.enabled || null,
|
||||
credentialsPlacement: getCredentialsPlacement(auth.credentials),
|
||||
credentialsId: auth.tokenConfig?.id || 'credentials',
|
||||
tokenPlacement: getTokenPlacement(auth.tokenConfig),
|
||||
tokenHeaderPrefix: getTokenHeaderPrefix(auth.tokenConfig),
|
||||
tokenQueryKey: getTokenQueryKey(auth.tokenConfig),
|
||||
autoFetchToken: auth.settings?.autoFetchToken !== false,
|
||||
autoRefreshToken: auth.settings?.autoRefreshToken !== false
|
||||
});
|
||||
|
||||
case 'implicit':
|
||||
return buildOAuth2Config({
|
||||
grantType: 'implicit',
|
||||
authorizationUrl: auth.authorizationUrl || null,
|
||||
callbackUrl: auth.callbackUrl || null,
|
||||
clientId: auth.credentials?.clientId || null,
|
||||
scope: auth.scope || null,
|
||||
state: auth.state || null,
|
||||
credentialsId: auth.tokenConfig?.id || 'credentials',
|
||||
tokenPlacement: getTokenPlacement(auth.tokenConfig),
|
||||
tokenHeaderPrefix: getTokenHeaderPrefix(auth.tokenConfig),
|
||||
tokenQueryKey: getTokenQueryKey(auth.tokenConfig),
|
||||
autoFetchToken: auth.settings?.autoFetchToken !== false
|
||||
});
|
||||
|
||||
default:
|
||||
return {
|
||||
mode: 'none',
|
||||
awsv4: null,
|
||||
basic: null,
|
||||
bearer: null,
|
||||
digest: null,
|
||||
ntlm: null,
|
||||
oauth2: null,
|
||||
wsse: null,
|
||||
apikey: null
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const fromOpenCollectionAuth = (auth: Auth | undefined): BrunoAuth => {
|
||||
const defaultAuth: BrunoAuth = {
|
||||
mode: 'none',
|
||||
awsv4: null,
|
||||
basic: null,
|
||||
bearer: null,
|
||||
digest: null,
|
||||
ntlm: null,
|
||||
oauth2: null,
|
||||
wsse: null,
|
||||
apikey: null
|
||||
};
|
||||
|
||||
if (!auth) {
|
||||
return defaultAuth;
|
||||
}
|
||||
|
||||
if (auth === 'inherit') {
|
||||
return { ...defaultAuth, mode: 'inherit' };
|
||||
}
|
||||
|
||||
switch (auth.type) {
|
||||
case 'basic': {
|
||||
const basicAuth = auth as AuthBasic;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'basic',
|
||||
basic: {
|
||||
username: basicAuth.username || null,
|
||||
password: basicAuth.password || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'bearer': {
|
||||
const bearerAuth = auth as AuthBearer;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'bearer',
|
||||
bearer: {
|
||||
token: bearerAuth.token || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'digest': {
|
||||
const digestAuth = auth as AuthDigest;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'digest',
|
||||
digest: {
|
||||
username: digestAuth.username || null,
|
||||
password: digestAuth.password || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'ntlm': {
|
||||
const ntlmAuth = auth as AuthNTLM;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'ntlm',
|
||||
ntlm: {
|
||||
username: ntlmAuth.username || null,
|
||||
password: ntlmAuth.password || null,
|
||||
domain: ntlmAuth.domain || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'awsv4': {
|
||||
const awsAuth = auth as AuthAwsV4;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'awsv4',
|
||||
awsv4: {
|
||||
accessKeyId: awsAuth.accessKeyId || null,
|
||||
secretAccessKey: awsAuth.secretAccessKey || null,
|
||||
sessionToken: awsAuth.sessionToken || null,
|
||||
service: awsAuth.service || null,
|
||||
region: awsAuth.region || null,
|
||||
profileName: awsAuth.profileName || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'apikey': {
|
||||
const apiKeyAuth = auth as AuthApiKey;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'apikey',
|
||||
apikey: {
|
||||
key: apiKeyAuth.key || null,
|
||||
value: apiKeyAuth.value || null,
|
||||
placement: apiKeyAuth.placement === 'query' ? 'queryparams' : (apiKeyAuth.placement === 'header' ? 'header' : null)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'wsse': {
|
||||
const wsseAuth = auth as AuthWsse;
|
||||
return {
|
||||
...defaultAuth,
|
||||
mode: 'wsse',
|
||||
wsse: {
|
||||
username: wsseAuth.username || null,
|
||||
password: wsseAuth.password || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case 'oauth2':
|
||||
return fromOpenCollectionOAuth2(auth as AuthOAuth2);
|
||||
|
||||
default:
|
||||
return defaultAuth;
|
||||
}
|
||||
};
|
||||
|
||||
const toOpenCollectionOAuth2 = (oauth2: BrunoOAuth2 | null | undefined): AuthOAuth2 | undefined => {
|
||||
if (!oauth2) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const base = { type: 'oauth2' as const };
|
||||
|
||||
switch (oauth2.grantType) {
|
||||
case 'client_credentials':
|
||||
return {
|
||||
...base,
|
||||
flow: 'client_credentials',
|
||||
accessTokenUrl: oauth2.accessTokenUrl || '',
|
||||
refreshTokenUrl: oauth2.refreshTokenUrl || '',
|
||||
credentials: {
|
||||
clientId: oauth2.clientId || '',
|
||||
clientSecret: oauth2.clientSecret || '',
|
||||
placement: oauth2.credentialsPlacement === 'basic_auth_header' ? 'basic_auth_header' : 'body'
|
||||
},
|
||||
scope: oauth2.scope || '',
|
||||
tokenConfig: {
|
||||
id: oauth2.credentialsId || 'credentials',
|
||||
placement: oauth2.tokenPlacement === 'query'
|
||||
? { query: oauth2.tokenQueryKey || 'access_token' }
|
||||
: { header: oauth2.tokenHeaderPrefix || 'Bearer' }
|
||||
},
|
||||
settings: {
|
||||
autoFetchToken: oauth2.autoFetchToken !== false,
|
||||
autoRefreshToken: oauth2.autoRefreshToken !== false
|
||||
}
|
||||
};
|
||||
|
||||
case 'password':
|
||||
return {
|
||||
...base,
|
||||
flow: 'resource_owner_password_credentials',
|
||||
accessTokenUrl: oauth2.accessTokenUrl || '',
|
||||
refreshTokenUrl: oauth2.refreshTokenUrl || '',
|
||||
credentials: {
|
||||
clientId: oauth2.clientId || '',
|
||||
clientSecret: oauth2.clientSecret || '',
|
||||
placement: oauth2.credentialsPlacement === 'basic_auth_header' ? 'basic_auth_header' : 'body'
|
||||
},
|
||||
resourceOwner: {
|
||||
username: oauth2.username || '',
|
||||
password: oauth2.password || ''
|
||||
},
|
||||
scope: oauth2.scope || '',
|
||||
tokenConfig: {
|
||||
id: oauth2.credentialsId || 'credentials',
|
||||
placement: oauth2.tokenPlacement === 'query'
|
||||
? { query: oauth2.tokenQueryKey || 'access_token' }
|
||||
: { header: oauth2.tokenHeaderPrefix || 'Bearer' }
|
||||
},
|
||||
settings: {
|
||||
autoFetchToken: oauth2.autoFetchToken !== false,
|
||||
autoRefreshToken: oauth2.autoRefreshToken !== false
|
||||
}
|
||||
};
|
||||
|
||||
case 'authorization_code':
|
||||
return {
|
||||
...base,
|
||||
flow: 'authorization_code',
|
||||
authorizationUrl: oauth2.authorizationUrl || '',
|
||||
accessTokenUrl: oauth2.accessTokenUrl || '',
|
||||
refreshTokenUrl: oauth2.refreshTokenUrl || '',
|
||||
callbackUrl: oauth2.callbackUrl || '',
|
||||
credentials: {
|
||||
clientId: oauth2.clientId || '',
|
||||
clientSecret: oauth2.clientSecret || '',
|
||||
placement: oauth2.credentialsPlacement === 'basic_auth_header' ? 'basic_auth_header' : 'body'
|
||||
},
|
||||
scope: oauth2.scope || '',
|
||||
pkce: oauth2.pkce ? { enabled: true, method: 'S256' } : undefined,
|
||||
tokenConfig: {
|
||||
id: oauth2.credentialsId || 'credentials',
|
||||
placement: oauth2.tokenPlacement === 'query'
|
||||
? { query: oauth2.tokenQueryKey || 'access_token' }
|
||||
: { header: oauth2.tokenHeaderPrefix || 'Bearer' }
|
||||
},
|
||||
settings: {
|
||||
autoFetchToken: oauth2.autoFetchToken !== false,
|
||||
autoRefreshToken: oauth2.autoRefreshToken !== false
|
||||
}
|
||||
};
|
||||
|
||||
case 'implicit':
|
||||
return {
|
||||
...base,
|
||||
flow: 'implicit',
|
||||
authorizationUrl: oauth2.authorizationUrl || '',
|
||||
callbackUrl: oauth2.callbackUrl || '',
|
||||
credentials: {
|
||||
clientId: oauth2.clientId || ''
|
||||
},
|
||||
scope: oauth2.scope || '',
|
||||
state: oauth2.state || '',
|
||||
tokenConfig: {
|
||||
id: oauth2.credentialsId || 'credentials',
|
||||
placement: oauth2.tokenPlacement === 'query'
|
||||
? { query: oauth2.tokenQueryKey || 'access_token' }
|
||||
: { header: oauth2.tokenHeaderPrefix || 'Bearer' }
|
||||
},
|
||||
settings: {
|
||||
autoFetchToken: oauth2.autoFetchToken !== false
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const toOpenCollectionAuth = (auth: BrunoAuth | null | undefined): Auth | undefined => {
|
||||
if (!auth || auth.mode === 'none') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (auth.mode === 'inherit') {
|
||||
return 'inherit';
|
||||
}
|
||||
|
||||
switch (auth.mode) {
|
||||
case 'basic':
|
||||
return {
|
||||
type: 'basic',
|
||||
username: auth.basic?.username || '',
|
||||
password: auth.basic?.password || ''
|
||||
};
|
||||
|
||||
case 'bearer':
|
||||
return {
|
||||
type: 'bearer',
|
||||
token: auth.bearer?.token || ''
|
||||
};
|
||||
|
||||
case 'digest':
|
||||
return {
|
||||
type: 'digest',
|
||||
username: auth.digest?.username || '',
|
||||
password: auth.digest?.password || ''
|
||||
};
|
||||
|
||||
case 'ntlm':
|
||||
return {
|
||||
type: 'ntlm',
|
||||
username: auth.ntlm?.username || '',
|
||||
password: auth.ntlm?.password || '',
|
||||
domain: auth.ntlm?.domain || ''
|
||||
};
|
||||
|
||||
case 'awsv4':
|
||||
return {
|
||||
type: 'awsv4',
|
||||
accessKeyId: auth.awsv4?.accessKeyId || '',
|
||||
secretAccessKey: auth.awsv4?.secretAccessKey || '',
|
||||
sessionToken: auth.awsv4?.sessionToken || '',
|
||||
service: auth.awsv4?.service || '',
|
||||
region: auth.awsv4?.region || '',
|
||||
profileName: auth.awsv4?.profileName || ''
|
||||
};
|
||||
|
||||
case 'apikey':
|
||||
return {
|
||||
type: 'apikey',
|
||||
key: auth.apikey?.key || '',
|
||||
value: auth.apikey?.value || '',
|
||||
placement: auth.apikey?.placement === 'queryparams' ? 'query' : 'header'
|
||||
};
|
||||
|
||||
case 'wsse':
|
||||
return {
|
||||
type: 'wsse',
|
||||
username: auth.wsse?.username || '',
|
||||
password: auth.wsse?.password || ''
|
||||
};
|
||||
|
||||
case 'oauth2':
|
||||
return toOpenCollectionOAuth2(auth.oauth2);
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
227
packages/bruno-converters/src/opencollection/common/body.ts
Normal file
227
packages/bruno-converters/src/opencollection/common/body.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import type {
|
||||
HttpRequestBody,
|
||||
RawBody,
|
||||
FormUrlEncodedBody,
|
||||
FormUrlEncodedEntry,
|
||||
MultipartFormBody,
|
||||
MultipartFormEntry,
|
||||
FileBody,
|
||||
FileBodyVariant,
|
||||
GraphQLBody,
|
||||
BrunoHttpRequestBody,
|
||||
BrunoKeyValue,
|
||||
BrunoMultipartFormEntry,
|
||||
BrunoFileEntry,
|
||||
BrunoGraphqlBody
|
||||
} from '../types';
|
||||
|
||||
export const fromOpenCollectionBody = (body: HttpRequestBody | GraphQLBody | undefined, requestType: string = 'http'): BrunoHttpRequestBody => {
|
||||
const defaultBody: BrunoHttpRequestBody = {
|
||||
mode: 'none',
|
||||
json: null,
|
||||
text: null,
|
||||
xml: null,
|
||||
sparql: null,
|
||||
formUrlEncoded: [],
|
||||
multipartForm: [],
|
||||
graphql: null,
|
||||
file: []
|
||||
};
|
||||
|
||||
if (!body) {
|
||||
return defaultBody;
|
||||
}
|
||||
|
||||
if (requestType === 'graphql') {
|
||||
const gqlBody = body as GraphQLBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'graphql',
|
||||
graphql: {
|
||||
query: gqlBody.query || '',
|
||||
variables: gqlBody.variables || ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const httpBody = body as HttpRequestBody;
|
||||
|
||||
if ('type' in httpBody) {
|
||||
switch (httpBody.type) {
|
||||
case 'json': {
|
||||
const rawBody = httpBody as RawBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'json',
|
||||
json: rawBody.data || ''
|
||||
};
|
||||
}
|
||||
|
||||
case 'text': {
|
||||
const rawBody = httpBody as RawBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'text',
|
||||
text: rawBody.data || ''
|
||||
};
|
||||
}
|
||||
|
||||
case 'xml': {
|
||||
const rawBody = httpBody as RawBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'xml',
|
||||
xml: rawBody.data || ''
|
||||
};
|
||||
}
|
||||
|
||||
case 'sparql': {
|
||||
const rawBody = httpBody as RawBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'sparql',
|
||||
sparql: rawBody.data || ''
|
||||
};
|
||||
}
|
||||
|
||||
case 'form-urlencoded': {
|
||||
const formBody = httpBody as FormUrlEncodedBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'formUrlEncoded',
|
||||
formUrlEncoded: (formBody.data || []).map((field): BrunoKeyValue => ({
|
||||
uid: uuid(),
|
||||
name: field.name || '',
|
||||
value: field.value || '',
|
||||
description: typeof field.description === 'string' ? field.description : field.description?.content || null,
|
||||
enabled: field.disabled !== true
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
case 'multipart-form': {
|
||||
const multipartBody = httpBody as MultipartFormBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'multipartForm',
|
||||
multipartForm: (multipartBody.data || []).map((field): BrunoMultipartFormEntry => ({
|
||||
uid: uuid(),
|
||||
type: field.type || 'text',
|
||||
name: field.name || '',
|
||||
value: Array.isArray(field.value) ? field.value : (field.value || ''),
|
||||
description: typeof field.description === 'string' ? field.description : field.description?.content || null,
|
||||
contentType: null,
|
||||
enabled: field.disabled !== true
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
case 'file': {
|
||||
const fileBody = httpBody as FileBody;
|
||||
return {
|
||||
...defaultBody,
|
||||
mode: 'file',
|
||||
file: (fileBody.data || []).map((file): BrunoFileEntry => ({
|
||||
uid: uuid(),
|
||||
filePath: file.filePath || '',
|
||||
contentType: file.contentType || '',
|
||||
selected: file.selected !== false
|
||||
}))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultBody;
|
||||
};
|
||||
|
||||
export const toOpenCollectionBody = (body: BrunoHttpRequestBody | null | undefined): HttpRequestBody | undefined => {
|
||||
if (!body || body.mode === 'none') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (body.mode) {
|
||||
case 'json':
|
||||
return { type: 'json', data: body.json || '' };
|
||||
|
||||
case 'text':
|
||||
return { type: 'text', data: body.text || '' };
|
||||
|
||||
case 'xml':
|
||||
return { type: 'xml', data: body.xml || '' };
|
||||
|
||||
case 'sparql':
|
||||
return { type: 'sparql', data: body.sparql || '' };
|
||||
|
||||
case 'formUrlEncoded': {
|
||||
const formData: FormUrlEncodedEntry[] = (body.formUrlEncoded || []).map((field): FormUrlEncodedEntry => {
|
||||
const entry: FormUrlEncodedEntry = {
|
||||
name: field.name || '',
|
||||
value: field.value || ''
|
||||
};
|
||||
|
||||
if (field.description && typeof field.description === 'string' && field.description.trim().length) {
|
||||
entry.description = field.description;
|
||||
}
|
||||
|
||||
if (field.enabled === false) {
|
||||
entry.disabled = true;
|
||||
}
|
||||
|
||||
return entry;
|
||||
});
|
||||
|
||||
return { type: 'form-urlencoded', data: formData };
|
||||
}
|
||||
|
||||
case 'multipartForm': {
|
||||
const multipartData: MultipartFormEntry[] = (body.multipartForm || []).map((field): MultipartFormEntry => {
|
||||
const entry: MultipartFormEntry = {
|
||||
name: field.name || '',
|
||||
type: field.type || 'text',
|
||||
value: field.value || ''
|
||||
};
|
||||
|
||||
if (field.description && typeof field.description === 'string' && field.description.trim().length) {
|
||||
entry.description = field.description;
|
||||
}
|
||||
|
||||
if (field.enabled === false) {
|
||||
entry.disabled = true;
|
||||
}
|
||||
|
||||
return entry;
|
||||
});
|
||||
|
||||
return { type: 'multipart-form', data: multipartData };
|
||||
}
|
||||
|
||||
case 'file': {
|
||||
const fileData: FileBodyVariant[] = (body.file || []).map((file): FileBodyVariant => ({
|
||||
filePath: file.filePath || '',
|
||||
contentType: file.contentType || '',
|
||||
selected: file.selected !== false
|
||||
}));
|
||||
|
||||
return { type: 'file', data: fileData };
|
||||
}
|
||||
|
||||
case 'graphql':
|
||||
return undefined;
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const toOpenCollectionGraphqlBody = (body: BrunoHttpRequestBody | null | undefined): GraphQLBody | undefined => {
|
||||
if (!body || body.mode !== 'graphql' || !body.graphql) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
query: body.graphql.query || '',
|
||||
variables: body.graphql.variables || ''
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import type {
|
||||
HttpRequestHeader,
|
||||
BrunoKeyValue
|
||||
} from '../types';
|
||||
|
||||
export const fromOpenCollectionHeaders = (headers: HttpRequestHeader[] | undefined): BrunoKeyValue[] => {
|
||||
if (!headers?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return headers.map((header): BrunoKeyValue => ({
|
||||
uid: uuid(),
|
||||
name: header.name || '',
|
||||
value: header.value || '',
|
||||
description: typeof header.description === 'string' ? header.description : header.description?.content || null,
|
||||
enabled: header.disabled !== true
|
||||
}));
|
||||
};
|
||||
|
||||
export const toOpenCollectionHeaders = (headers: BrunoKeyValue[] | null | undefined): HttpRequestHeader[] | undefined => {
|
||||
if (!headers?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ocHeaders = headers.map((header): HttpRequestHeader => {
|
||||
const httpHeader: HttpRequestHeader = {
|
||||
name: header.name || '',
|
||||
value: header.value || ''
|
||||
};
|
||||
|
||||
if (header.description && typeof header.description === 'string' && header.description.trim().length) {
|
||||
httpHeader.description = header.description;
|
||||
}
|
||||
|
||||
if (header.enabled === false) {
|
||||
httpHeader.disabled = true;
|
||||
}
|
||||
|
||||
return httpHeader;
|
||||
});
|
||||
|
||||
return ocHeaders.length ? ocHeaders : undefined;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
export { fromOpenCollectionAuth, toOpenCollectionAuth } from './auth';
|
||||
export { fromOpenCollectionHeaders, toOpenCollectionHeaders } from './headers';
|
||||
export { fromOpenCollectionParams, toOpenCollectionParams } from './params';
|
||||
export { fromOpenCollectionBody, toOpenCollectionBody, toOpenCollectionGraphqlBody } from './body';
|
||||
export { fromOpenCollectionVariables, toOpenCollectionVariables } from './variables';
|
||||
export { fromOpenCollectionActions, toOpenCollectionActions } from './actions';
|
||||
export { fromOpenCollectionScripts, toOpenCollectionScripts } from './scripts';
|
||||
export { fromOpenCollectionAssertions, toOpenCollectionAssertions } from './assertions';
|
||||
@@ -0,0 +1,47 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import type {
|
||||
HttpRequestParam,
|
||||
BrunoHttpRequestParam,
|
||||
BrunoHttpRequestParamType
|
||||
} from '../types';
|
||||
|
||||
export const fromOpenCollectionParams = (params: HttpRequestParam[] | undefined): BrunoHttpRequestParam[] => {
|
||||
if (!params?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return params.map((param): BrunoHttpRequestParam => ({
|
||||
uid: uuid(),
|
||||
name: param.name || '',
|
||||
value: param.value || '',
|
||||
description: typeof param.description === 'string' ? param.description : param.description?.content || null,
|
||||
type: (param.type || 'query') as BrunoHttpRequestParamType,
|
||||
enabled: param.disabled !== true
|
||||
}));
|
||||
};
|
||||
|
||||
export const toOpenCollectionParams = (params: BrunoHttpRequestParam[] | null | undefined): HttpRequestParam[] | undefined => {
|
||||
if (!params?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ocParams = params.map((param): HttpRequestParam => {
|
||||
const httpParam: HttpRequestParam = {
|
||||
name: param.name || '',
|
||||
value: param.value || '',
|
||||
type: param.type || 'query'
|
||||
};
|
||||
|
||||
if (param.description && typeof param.description === 'string' && param.description.trim().length) {
|
||||
httpParam.description = param.description;
|
||||
}
|
||||
|
||||
if (param.enabled === false) {
|
||||
httpParam.disabled = true;
|
||||
}
|
||||
|
||||
return httpParam;
|
||||
});
|
||||
|
||||
return ocParams.length ? ocParams : undefined;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { Scripts, Script } from '@opencollection/types/common/scripts';
|
||||
import type { FolderRequest as BrunoFolderRequest } from '@usebruno/schema-types/collection/folder';
|
||||
import type { HttpRequest as BrunoHttpRequest } from '@usebruno/schema-types/requests/http';
|
||||
import type { WebSocketRequest as BrunoWebSocketRequest } from '@usebruno/schema-types/requests/websocket';
|
||||
import type { GrpcRequest as BrunoGrpcRequest } from '@usebruno/schema-types/requests/grpc';
|
||||
|
||||
export const toOpenCollectionScripts = (request: BrunoFolderRequest | BrunoHttpRequest | BrunoWebSocketRequest | BrunoGrpcRequest | null | undefined): Scripts | undefined => {
|
||||
const ocScripts: Scripts = [];
|
||||
|
||||
if (request?.script?.req?.trim().length) {
|
||||
ocScripts.push({
|
||||
type: 'before-request',
|
||||
code: request.script.req.trim()
|
||||
});
|
||||
}
|
||||
if (request?.script?.res?.trim().length) {
|
||||
ocScripts.push({
|
||||
type: 'after-response',
|
||||
code: request.script.res.trim()
|
||||
});
|
||||
}
|
||||
if (request?.tests?.trim().length) {
|
||||
ocScripts.push({
|
||||
type: 'tests',
|
||||
code: request.tests.trim()
|
||||
});
|
||||
}
|
||||
|
||||
return ocScripts.length > 0 ? ocScripts : undefined;
|
||||
};
|
||||
|
||||
export const fromOpenCollectionScripts = (scripts: Scripts | null | undefined): {
|
||||
script?: { req?: string | null; res?: string | null };
|
||||
tests?: string | null;
|
||||
} | undefined => {
|
||||
if (!scripts || !Array.isArray(scripts) || scripts.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const brunoScripts: {
|
||||
script?: { req?: string | null; res?: string | null };
|
||||
tests?: string | null;
|
||||
} = {};
|
||||
|
||||
for (const script of scripts) {
|
||||
if (script.type === 'before-request' && script.code) {
|
||||
if (!brunoScripts.script) {
|
||||
brunoScripts.script = {};
|
||||
}
|
||||
brunoScripts.script.req = script.code;
|
||||
}
|
||||
if (script.type === 'after-response' && script.code) {
|
||||
if (!brunoScripts.script) {
|
||||
brunoScripts.script = {};
|
||||
}
|
||||
brunoScripts.script.res = script.code;
|
||||
}
|
||||
if (script.type === 'tests' && script.code) {
|
||||
brunoScripts.tests = script.code;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(brunoScripts).length > 0 ? brunoScripts : undefined;
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import type {
|
||||
Variable,
|
||||
BrunoVariable,
|
||||
BrunoVariables
|
||||
} from '../types';
|
||||
|
||||
interface BrunoVars {
|
||||
req: BrunoVariables;
|
||||
res: BrunoVariables;
|
||||
}
|
||||
|
||||
export const fromOpenCollectionVariables = (variables: Variable[] | undefined): BrunoVars => {
|
||||
if (!variables?.length) {
|
||||
return { req: [], res: [] };
|
||||
}
|
||||
|
||||
const reqVars: BrunoVariable[] = [];
|
||||
|
||||
variables.forEach((v: Variable) => {
|
||||
let value = '';
|
||||
if (typeof v.value === 'string') {
|
||||
value = v.value;
|
||||
} else if (v.value && typeof v.value === 'object' && 'data' in v.value) {
|
||||
value = (v.value as { data: string }).data || '';
|
||||
}
|
||||
|
||||
const variable: BrunoVariable = {
|
||||
uid: uuid(),
|
||||
name: v.name || '',
|
||||
value,
|
||||
enabled: v.disabled !== true,
|
||||
local: false
|
||||
};
|
||||
|
||||
if (v.description) {
|
||||
variable.description = typeof v.description === 'string' ? v.description : (v.description as { content?: string })?.content || '';
|
||||
}
|
||||
|
||||
reqVars.push(variable);
|
||||
});
|
||||
|
||||
return { req: reqVars, res: [] };
|
||||
};
|
||||
|
||||
export const toOpenCollectionVariables = (vars: BrunoVars | { req?: BrunoVariables; res?: BrunoVariables } | null | undefined): Variable[] | undefined => {
|
||||
// Handle folder variables (has req/res structure) - only use req vars
|
||||
const hasReqRes = vars && 'req' in vars;
|
||||
const reqVars = hasReqRes ? vars.req : vars as BrunoVariables;
|
||||
|
||||
const reqVarsArray = Array.isArray(reqVars) ? reqVars : [];
|
||||
|
||||
if (!reqVarsArray.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ocVariables: Variable[] = reqVarsArray.map((v: BrunoVariable): Variable => {
|
||||
const variable: Variable = {
|
||||
name: v.name || '',
|
||||
value: v.value || ''
|
||||
};
|
||||
|
||||
if (v.description && typeof v.description === 'string' && v.description.trim().length) {
|
||||
variable.description = v.description;
|
||||
}
|
||||
|
||||
if (v.enabled === false) {
|
||||
variable.disabled = true;
|
||||
}
|
||||
|
||||
return variable;
|
||||
});
|
||||
|
||||
return ocVariables.length > 0 ? ocVariables : undefined;
|
||||
};
|
||||
79
packages/bruno-converters/src/opencollection/environment.ts
Normal file
79
packages/bruno-converters/src/opencollection/environment.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { uuid } from '../common/index.js';
|
||||
import type {
|
||||
Environment,
|
||||
Variable,
|
||||
BrunoEnvironment,
|
||||
BrunoEnvironmentVariable
|
||||
} from './types';
|
||||
|
||||
interface OCVariable extends Omit<Variable, 'value'> {
|
||||
name: string;
|
||||
value?: string | { data: string };
|
||||
secret?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const fromOpenCollectionEnvironments = (environments: Environment[] | undefined): BrunoEnvironment[] => {
|
||||
if (!environments?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return environments.map((env): BrunoEnvironment => ({
|
||||
uid: uuid(),
|
||||
name: env.name || 'Untitled Environment',
|
||||
variables: (env.variables || []).map((v): BrunoEnvironmentVariable => {
|
||||
const variable = v as OCVariable;
|
||||
const isSecret = variable.secret === true;
|
||||
|
||||
let value = '';
|
||||
if (!isSecret && variable.value !== undefined) {
|
||||
if (typeof variable.value === 'string') {
|
||||
value = variable.value;
|
||||
} else if (variable.value && typeof variable.value === 'object' && 'data' in variable.value) {
|
||||
value = variable.value.data;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uid: uuid(),
|
||||
name: variable.name || '',
|
||||
value,
|
||||
type: 'text',
|
||||
enabled: variable.disabled !== true,
|
||||
secret: isSecret
|
||||
};
|
||||
})
|
||||
}));
|
||||
};
|
||||
|
||||
export const toOpenCollectionEnvironments = (environments: BrunoEnvironment[] | undefined): Environment[] | undefined => {
|
||||
if (!environments?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return environments.map((env): Environment => {
|
||||
const ocEnv: Environment = {
|
||||
name: env.name || 'Untitled Environment',
|
||||
variables: (env.variables || []).map((v): OCVariable => {
|
||||
const ocVar: OCVariable = {
|
||||
name: v.name || '',
|
||||
value: typeof v.value === 'string' ? v.value : String(v.value ?? '')
|
||||
};
|
||||
|
||||
if (v.secret) {
|
||||
ocVar.secret = true;
|
||||
// Secret variables don't include the value in export
|
||||
delete ocVar.value;
|
||||
}
|
||||
|
||||
if (v.enabled === false) {
|
||||
ocVar.disabled = true;
|
||||
}
|
||||
|
||||
return ocVar;
|
||||
}) as Variable[]
|
||||
};
|
||||
|
||||
return ocEnv;
|
||||
});
|
||||
};
|
||||
135
packages/bruno-converters/src/opencollection/folder.ts
Normal file
135
packages/bruno-converters/src/opencollection/folder.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { uuid } from '../common/index.js';
|
||||
import {
|
||||
fromOpenCollectionHeaders,
|
||||
toOpenCollectionHeaders,
|
||||
fromOpenCollectionAuth,
|
||||
toOpenCollectionAuth,
|
||||
fromOpenCollectionScripts,
|
||||
toOpenCollectionScripts,
|
||||
fromOpenCollectionVariables,
|
||||
toOpenCollectionVariables
|
||||
} from './common';
|
||||
import { fromOpenCollectionItems, toOpenCollectionItems } from './items';
|
||||
import type {
|
||||
Folder,
|
||||
FolderInfo,
|
||||
RequestDefaults,
|
||||
Auth,
|
||||
BrunoItem,
|
||||
BrunoFolderRoot,
|
||||
BrunoKeyValue
|
||||
} from './types';
|
||||
|
||||
export const fromOpenCollectionFolder = (folder: Folder): BrunoItem => {
|
||||
const info = folder.info || {};
|
||||
|
||||
const brunoFolder: BrunoItem = {
|
||||
uid: uuid(),
|
||||
type: 'folder',
|
||||
name: info.name || 'Untitled Folder',
|
||||
seq: info.seq || 1
|
||||
};
|
||||
|
||||
if (folder.request || folder.docs) {
|
||||
const root: BrunoFolderRoot = {};
|
||||
|
||||
if (folder.request) {
|
||||
const scripts = fromOpenCollectionScripts(folder.request.scripts);
|
||||
root.request = {
|
||||
headers: fromOpenCollectionHeaders(folder.request.headers),
|
||||
auth: fromOpenCollectionAuth(folder.request.auth as Auth),
|
||||
script: scripts?.script,
|
||||
vars: fromOpenCollectionVariables(folder.request.variables),
|
||||
tests: scripts?.tests
|
||||
};
|
||||
}
|
||||
|
||||
if (folder.docs) {
|
||||
if (typeof folder.docs === 'string') {
|
||||
root.docs = folder.docs;
|
||||
} else if (folder.docs && typeof folder.docs === 'object' && 'content' in folder.docs) {
|
||||
root.docs = folder.docs.content || '';
|
||||
}
|
||||
}
|
||||
|
||||
root.meta = {
|
||||
name: info.name || 'Untitled Folder',
|
||||
seq: info.seq || 1
|
||||
};
|
||||
|
||||
brunoFolder.root = root;
|
||||
}
|
||||
|
||||
if (info.tags?.length) {
|
||||
brunoFolder.tags = info.tags;
|
||||
}
|
||||
|
||||
if (folder.items?.length) {
|
||||
brunoFolder.items = fromOpenCollectionItems(folder.items, fromOpenCollectionFolder as (f: unknown) => BrunoItem);
|
||||
}
|
||||
|
||||
return brunoFolder;
|
||||
};
|
||||
|
||||
export const toOpenCollectionFolder = (folder: BrunoItem): Folder => {
|
||||
const info: FolderInfo = {
|
||||
name: folder.name || 'Untitled Folder',
|
||||
type: 'folder'
|
||||
};
|
||||
|
||||
if (folder.seq) {
|
||||
info.seq = folder.seq;
|
||||
}
|
||||
|
||||
if (folder.tags?.length) {
|
||||
info.tags = folder.tags;
|
||||
}
|
||||
|
||||
const ocFolder: Folder = {
|
||||
info
|
||||
};
|
||||
|
||||
if (folder.root) {
|
||||
const folderRequest = folder.root.request || {};
|
||||
|
||||
const headers = toOpenCollectionHeaders(folderRequest.headers as BrunoKeyValue[]);
|
||||
const auth = toOpenCollectionAuth(folderRequest.auth);
|
||||
const scripts = toOpenCollectionScripts(folderRequest as { script?: { req: string | null; res: string | null } | null; tests?: string | null });
|
||||
const variables = toOpenCollectionVariables(folderRequest.vars);
|
||||
|
||||
if (headers || auth || scripts || variables) {
|
||||
const request: RequestDefaults = {};
|
||||
|
||||
if (headers) {
|
||||
request.headers = headers;
|
||||
}
|
||||
|
||||
if (auth) {
|
||||
request.auth = auth;
|
||||
}
|
||||
|
||||
if (scripts) {
|
||||
request.scripts = scripts;
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
request.variables = variables;
|
||||
}
|
||||
|
||||
ocFolder.request = request;
|
||||
}
|
||||
|
||||
if (folder.root.docs) {
|
||||
ocFolder.docs = {
|
||||
content: folder.root.docs,
|
||||
type: 'text/markdown'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (folder.items?.length) {
|
||||
ocFolder.items = toOpenCollectionItems(folder.items, toOpenCollectionFolder as (f: BrunoItem) => unknown) as Folder['items'];
|
||||
}
|
||||
|
||||
return ocFolder;
|
||||
};
|
||||
37
packages/bruno-converters/src/opencollection/index.ts
Normal file
37
packages/bruno-converters/src/opencollection/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export { openCollectionToBruno } from './opencollection-to-bruno';
|
||||
export { brunoToOpenCollection } from './bruno-to-opencollection';
|
||||
export { fromOpenCollectionFolder, toOpenCollectionFolder } from './folder';
|
||||
export { fromOpenCollectionEnvironments, toOpenCollectionEnvironments } from './environment';
|
||||
|
||||
export {
|
||||
fromOpenCollectionItem,
|
||||
toOpenCollectionItem,
|
||||
fromOpenCollectionItems,
|
||||
toOpenCollectionItems,
|
||||
fromOpenCollectionHttpItem,
|
||||
toOpenCollectionHttpItem,
|
||||
fromOpenCollectionGraphqlItem,
|
||||
toOpenCollectionGraphqlItem,
|
||||
fromOpenCollectionGrpcItem,
|
||||
toOpenCollectionGrpcItem,
|
||||
fromOpenCollectionWebsocketItem,
|
||||
toOpenCollectionWebsocketItem
|
||||
} from './items';
|
||||
|
||||
export {
|
||||
fromOpenCollectionAuth,
|
||||
toOpenCollectionAuth,
|
||||
fromOpenCollectionHeaders,
|
||||
toOpenCollectionHeaders,
|
||||
fromOpenCollectionParams,
|
||||
toOpenCollectionParams,
|
||||
fromOpenCollectionBody,
|
||||
toOpenCollectionBody,
|
||||
toOpenCollectionGraphqlBody,
|
||||
fromOpenCollectionVariables,
|
||||
toOpenCollectionVariables,
|
||||
fromOpenCollectionScripts,
|
||||
toOpenCollectionScripts,
|
||||
fromOpenCollectionAssertions,
|
||||
toOpenCollectionAssertions
|
||||
} from './common';
|
||||
192
packages/bruno-converters/src/opencollection/items/graphql.ts
Normal file
192
packages/bruno-converters/src/opencollection/items/graphql.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import {
|
||||
fromOpenCollectionHeaders,
|
||||
toOpenCollectionHeaders,
|
||||
fromOpenCollectionParams,
|
||||
toOpenCollectionParams,
|
||||
fromOpenCollectionBody,
|
||||
toOpenCollectionGraphqlBody,
|
||||
fromOpenCollectionAuth,
|
||||
toOpenCollectionAuth,
|
||||
fromOpenCollectionScripts,
|
||||
toOpenCollectionScripts,
|
||||
fromOpenCollectionVariables,
|
||||
toOpenCollectionVariables,
|
||||
fromOpenCollectionActions,
|
||||
toOpenCollectionActions,
|
||||
fromOpenCollectionAssertions,
|
||||
toOpenCollectionAssertions
|
||||
} from '../common';
|
||||
import type {
|
||||
GraphQLRequest,
|
||||
GraphQLRequestInfo,
|
||||
GraphQLRequestDetails,
|
||||
GraphQLRequestRuntime,
|
||||
GraphQLRequestSettings,
|
||||
GraphQLBody,
|
||||
GraphQLBodyVariant,
|
||||
Auth,
|
||||
BrunoItem,
|
||||
BrunoKeyValue,
|
||||
BrunoHttpRequestParam
|
||||
} from '../types';
|
||||
|
||||
const getGraphqlBody = (body: GraphQLBody | GraphQLBodyVariant[] | undefined): GraphQLBody | undefined => {
|
||||
if (!body) return undefined;
|
||||
if (Array.isArray(body)) {
|
||||
const selected = body.find((v) => v.selected);
|
||||
return selected?.body || body[0]?.body;
|
||||
}
|
||||
return body;
|
||||
};
|
||||
|
||||
export const fromOpenCollectionGraphqlItem = (item: GraphQLRequest): BrunoItem => {
|
||||
const info = item.info || {};
|
||||
const graphql = item.graphql || {};
|
||||
const runtime = item.runtime || {};
|
||||
|
||||
const scripts = fromOpenCollectionScripts(runtime.scripts);
|
||||
const graphqlBody = getGraphqlBody(graphql.body);
|
||||
|
||||
// variables (pre-request from variables, post-response from actions)
|
||||
const variables = fromOpenCollectionVariables(runtime.variables);
|
||||
const postResponseVars = fromOpenCollectionActions(runtime.actions);
|
||||
|
||||
const brunoItem: BrunoItem = {
|
||||
uid: uuid(),
|
||||
type: 'graphql-request',
|
||||
name: info.name || 'Untitled Request',
|
||||
seq: info.seq || 1,
|
||||
request: {
|
||||
url: graphql.url || '',
|
||||
method: graphql.method || 'POST',
|
||||
headers: fromOpenCollectionHeaders(graphql.headers),
|
||||
params: fromOpenCollectionParams(graphql.params),
|
||||
body: fromOpenCollectionBody(graphqlBody, 'graphql'),
|
||||
auth: fromOpenCollectionAuth(runtime.auth as Auth),
|
||||
script: scripts?.script,
|
||||
vars: {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
},
|
||||
assertions: fromOpenCollectionAssertions(runtime.assertions),
|
||||
tests: scripts?.tests,
|
||||
docs: item.docs || ''
|
||||
}
|
||||
};
|
||||
|
||||
const settings = item.settings;
|
||||
if (settings) {
|
||||
brunoItem.settings = {};
|
||||
if (settings.encodeUrl !== undefined) {
|
||||
(brunoItem.settings as Record<string, unknown>).encodeUrl = settings.encodeUrl;
|
||||
}
|
||||
if (settings.timeout !== undefined) {
|
||||
(brunoItem.settings as Record<string, unknown>).timeout = settings.timeout;
|
||||
}
|
||||
if (settings.followRedirects !== undefined) {
|
||||
(brunoItem.settings as Record<string, unknown>).followRedirects = settings.followRedirects;
|
||||
}
|
||||
if (settings.maxRedirects !== undefined) {
|
||||
(brunoItem.settings as Record<string, unknown>).maxRedirects = settings.maxRedirects;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.tags?.length) {
|
||||
brunoItem.tags = info.tags;
|
||||
}
|
||||
|
||||
return brunoItem;
|
||||
};
|
||||
|
||||
export const toOpenCollectionGraphqlItem = (item: BrunoItem): GraphQLRequest => {
|
||||
const request = (item.request || {}) as Record<string, unknown>;
|
||||
const brunoSettings = (item.settings || {}) as Record<string, unknown>;
|
||||
|
||||
const info: GraphQLRequestInfo = {
|
||||
name: item.name || 'Untitled Request',
|
||||
type: 'graphql'
|
||||
};
|
||||
|
||||
if (item.seq) {
|
||||
info.seq = item.seq;
|
||||
}
|
||||
|
||||
if (item.tags?.length) {
|
||||
info.tags = item.tags;
|
||||
}
|
||||
|
||||
const graphql: GraphQLRequestDetails = {
|
||||
url: request.url as string || '',
|
||||
method: request.method as string || 'POST'
|
||||
};
|
||||
|
||||
const headers = toOpenCollectionHeaders(request.headers as BrunoKeyValue[]);
|
||||
if (headers) {
|
||||
graphql.headers = headers;
|
||||
}
|
||||
|
||||
const params = toOpenCollectionParams(request.params as BrunoHttpRequestParam[]);
|
||||
if (params) {
|
||||
graphql.params = params;
|
||||
}
|
||||
|
||||
const body = toOpenCollectionGraphqlBody(request.body as Parameters<typeof toOpenCollectionGraphqlBody>[0]);
|
||||
if (body) {
|
||||
graphql.body = body;
|
||||
}
|
||||
|
||||
const ocRequest: GraphQLRequest = {
|
||||
info,
|
||||
graphql
|
||||
};
|
||||
|
||||
const auth = toOpenCollectionAuth(request.auth as Parameters<typeof toOpenCollectionAuth>[0]);
|
||||
const scripts = toOpenCollectionScripts(request as Parameters<typeof toOpenCollectionScripts>[0]);
|
||||
const variables = toOpenCollectionVariables(request.vars as Parameters<typeof toOpenCollectionVariables>[0]);
|
||||
const assertions = toOpenCollectionAssertions(request.assertions as BrunoKeyValue[]);
|
||||
|
||||
// actions (from post-response variables)
|
||||
const vars = request.vars as { req?: unknown[]; res?: unknown[] } | undefined;
|
||||
const actions = toOpenCollectionActions(vars?.res as Parameters<typeof toOpenCollectionActions>[0]);
|
||||
|
||||
if (auth || scripts || variables || assertions || actions) {
|
||||
const runtime: GraphQLRequestRuntime = {};
|
||||
|
||||
if (auth) {
|
||||
runtime.auth = auth;
|
||||
}
|
||||
|
||||
if (scripts) {
|
||||
runtime.scripts = scripts;
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
runtime.variables = variables;
|
||||
}
|
||||
|
||||
if (assertions) {
|
||||
runtime.assertions = assertions;
|
||||
}
|
||||
|
||||
if (actions) {
|
||||
runtime.actions = actions;
|
||||
}
|
||||
|
||||
ocRequest.runtime = runtime;
|
||||
}
|
||||
|
||||
const settings: GraphQLRequestSettings = {
|
||||
encodeUrl: typeof brunoSettings.encodeUrl === 'boolean' ? brunoSettings.encodeUrl : true,
|
||||
timeout: typeof brunoSettings.timeout === 'number' ? brunoSettings.timeout : 0,
|
||||
followRedirects: typeof brunoSettings.followRedirects === 'boolean' ? brunoSettings.followRedirects : true,
|
||||
maxRedirects: typeof brunoSettings.maxRedirects === 'number' ? brunoSettings.maxRedirects : 5
|
||||
};
|
||||
ocRequest.settings = settings;
|
||||
|
||||
if (request.docs) {
|
||||
ocRequest.docs = request.docs as string;
|
||||
}
|
||||
|
||||
return ocRequest;
|
||||
};
|
||||
214
packages/bruno-converters/src/opencollection/items/grpc.ts
Normal file
214
packages/bruno-converters/src/opencollection/items/grpc.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import {
|
||||
fromOpenCollectionAuth,
|
||||
toOpenCollectionAuth,
|
||||
fromOpenCollectionScripts,
|
||||
toOpenCollectionScripts,
|
||||
fromOpenCollectionVariables,
|
||||
toOpenCollectionVariables,
|
||||
fromOpenCollectionActions,
|
||||
toOpenCollectionActions,
|
||||
fromOpenCollectionAssertions,
|
||||
toOpenCollectionAssertions
|
||||
} from '../common';
|
||||
import type {
|
||||
GrpcRequest,
|
||||
GrpcRequestInfo,
|
||||
GrpcRequestDetails,
|
||||
GrpcRequestRuntime,
|
||||
GrpcMetadata,
|
||||
GrpcMessageVariant,
|
||||
GrpcMessagePayload,
|
||||
Auth,
|
||||
BrunoItem,
|
||||
BrunoGrpcMessage,
|
||||
BrunoKeyValue
|
||||
} from '../types';
|
||||
|
||||
const fromGrpcMetadata = (metadata: GrpcMetadata[] | undefined): BrunoKeyValue[] => {
|
||||
if (!metadata?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return metadata.map((m): BrunoKeyValue => ({
|
||||
uid: uuid(),
|
||||
name: m.name || '',
|
||||
value: m.value || '',
|
||||
description: typeof m.description === 'string' ? m.description : (m.description as { content?: string } | undefined)?.content || null,
|
||||
enabled: m.disabled !== true
|
||||
}));
|
||||
};
|
||||
|
||||
export const fromOpenCollectionGrpcItem = (item: GrpcRequest): BrunoItem => {
|
||||
const info = item.info || {};
|
||||
const grpc = item.grpc || {};
|
||||
const runtime = item.runtime || {};
|
||||
|
||||
const grpcMessages: BrunoGrpcMessage[] = [];
|
||||
|
||||
if (grpc.message) {
|
||||
if (typeof grpc.message === 'string') {
|
||||
grpcMessages.push({ name: 'message 1', content: grpc.message });
|
||||
} else if (Array.isArray(grpc.message)) {
|
||||
grpc.message.forEach((msg, index) => {
|
||||
grpcMessages.push({
|
||||
name: msg.title || `message ${index + 1}`,
|
||||
content: typeof msg.message === 'string' ? msg.message : ''
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const scripts = fromOpenCollectionScripts(runtime.scripts);
|
||||
|
||||
// variables (pre-request from variables, post-response from actions)
|
||||
const variables = fromOpenCollectionVariables(runtime.variables);
|
||||
const postResponseVars = fromOpenCollectionActions((runtime as { actions?: Parameters<typeof fromOpenCollectionActions>[0] }).actions);
|
||||
|
||||
const brunoItem: BrunoItem = {
|
||||
uid: uuid(),
|
||||
type: 'grpc-request',
|
||||
name: info.name || 'Untitled Request',
|
||||
seq: info.seq || 1,
|
||||
request: {
|
||||
url: grpc.url || '',
|
||||
method: grpc.method || '',
|
||||
headers: fromGrpcMetadata(grpc.metadata),
|
||||
body: {
|
||||
mode: 'grpc',
|
||||
grpc: grpcMessages
|
||||
},
|
||||
auth: fromOpenCollectionAuth(runtime.auth as Auth),
|
||||
script: scripts?.script,
|
||||
vars: {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
},
|
||||
assertions: fromOpenCollectionAssertions(runtime.assertions),
|
||||
tests: scripts?.tests,
|
||||
docs: ''
|
||||
}
|
||||
};
|
||||
|
||||
// Add grpc-specific properties
|
||||
if (grpc.methodType) {
|
||||
(brunoItem.request as unknown as Record<string, unknown>).methodType = grpc.methodType;
|
||||
}
|
||||
if (grpc.protoFilePath) {
|
||||
(brunoItem.request as unknown as Record<string, unknown>).protoPath = grpc.protoFilePath;
|
||||
}
|
||||
|
||||
if (info.tags?.length) {
|
||||
brunoItem.tags = info.tags;
|
||||
}
|
||||
|
||||
return brunoItem;
|
||||
};
|
||||
|
||||
export const toOpenCollectionGrpcItem = (item: BrunoItem): GrpcRequest => {
|
||||
const request = (item.request || {}) as Record<string, unknown>;
|
||||
|
||||
const info: GrpcRequestInfo = {
|
||||
name: item.name || 'Untitled Request',
|
||||
type: 'grpc'
|
||||
};
|
||||
|
||||
if (item.seq) {
|
||||
info.seq = item.seq;
|
||||
}
|
||||
|
||||
if (item.tags?.length) {
|
||||
info.tags = item.tags;
|
||||
}
|
||||
|
||||
const grpc: GrpcRequestDetails = {
|
||||
url: request.url as string || '',
|
||||
method: request.method as string || ''
|
||||
};
|
||||
|
||||
if (request.methodType) {
|
||||
grpc.methodType = request.methodType as GrpcRequestDetails['methodType'];
|
||||
}
|
||||
if (request.protoPath) {
|
||||
grpc.protoFilePath = request.protoPath as string;
|
||||
}
|
||||
|
||||
const headers = request.headers as BrunoKeyValue[] | undefined;
|
||||
if (headers?.length) {
|
||||
grpc.metadata = headers.map((h): GrpcMetadata => {
|
||||
const metadata: GrpcMetadata = {
|
||||
name: h.name || '',
|
||||
value: h.value || ''
|
||||
};
|
||||
|
||||
if (h.description && typeof h.description === 'string' && h.description.trim().length) {
|
||||
metadata.description = h.description;
|
||||
}
|
||||
|
||||
if (h.enabled === false) {
|
||||
metadata.disabled = true;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
});
|
||||
}
|
||||
|
||||
const body = request.body as { grpc?: BrunoGrpcMessage[] } | undefined;
|
||||
if (body?.grpc?.length) {
|
||||
const messages = body.grpc;
|
||||
if (messages.length === 1) {
|
||||
grpc.message = messages[0].content || '';
|
||||
} else {
|
||||
grpc.message = messages.map((msg): GrpcMessageVariant => ({
|
||||
title: msg.name || 'Untitled',
|
||||
message: msg.content || ''
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const ocRequest: GrpcRequest = {
|
||||
info,
|
||||
grpc
|
||||
};
|
||||
|
||||
const auth = toOpenCollectionAuth(request.auth as Parameters<typeof toOpenCollectionAuth>[0]);
|
||||
const scripts = toOpenCollectionScripts(request as Parameters<typeof toOpenCollectionScripts>[0]);
|
||||
const variables = toOpenCollectionVariables(request.vars as Parameters<typeof toOpenCollectionVariables>[0]);
|
||||
const assertions = toOpenCollectionAssertions(request.assertions as BrunoKeyValue[]);
|
||||
|
||||
// actions (from post-response variables)
|
||||
const vars = request.vars as { req?: unknown[]; res?: unknown[] } | undefined;
|
||||
const actions = toOpenCollectionActions(vars?.res as Parameters<typeof toOpenCollectionActions>[0]);
|
||||
|
||||
if (auth || scripts || variables || assertions || actions) {
|
||||
const runtime: GrpcRequestRuntime = {};
|
||||
|
||||
if (auth) {
|
||||
runtime.auth = auth;
|
||||
}
|
||||
|
||||
if (scripts) {
|
||||
runtime.scripts = scripts;
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
runtime.variables = variables;
|
||||
}
|
||||
|
||||
if (assertions) {
|
||||
runtime.assertions = assertions;
|
||||
}
|
||||
|
||||
if (actions) {
|
||||
(runtime as { actions?: typeof actions }).actions = actions;
|
||||
}
|
||||
|
||||
ocRequest.runtime = runtime;
|
||||
}
|
||||
|
||||
if (request.docs) {
|
||||
ocRequest.docs = request.docs as string;
|
||||
}
|
||||
|
||||
return ocRequest;
|
||||
};
|
||||
295
packages/bruno-converters/src/opencollection/items/http.ts
Normal file
295
packages/bruno-converters/src/opencollection/items/http.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import {
|
||||
fromOpenCollectionHeaders,
|
||||
toOpenCollectionHeaders,
|
||||
fromOpenCollectionParams,
|
||||
toOpenCollectionParams,
|
||||
fromOpenCollectionBody,
|
||||
toOpenCollectionBody,
|
||||
fromOpenCollectionAuth,
|
||||
toOpenCollectionAuth,
|
||||
fromOpenCollectionScripts,
|
||||
toOpenCollectionScripts,
|
||||
fromOpenCollectionVariables,
|
||||
toOpenCollectionVariables,
|
||||
fromOpenCollectionActions,
|
||||
toOpenCollectionActions,
|
||||
fromOpenCollectionAssertions,
|
||||
toOpenCollectionAssertions
|
||||
} from '../common';
|
||||
import type {
|
||||
HttpRequest,
|
||||
HttpRequestSettings,
|
||||
HttpRequestExample,
|
||||
HttpRequestInfo,
|
||||
HttpRequestDetails,
|
||||
HttpRequestRuntime,
|
||||
HttpRequestHeader,
|
||||
HttpRequestBody,
|
||||
Auth,
|
||||
BrunoItem,
|
||||
BrunoKeyValue,
|
||||
BrunoHttpRequestParam,
|
||||
BrunoExample,
|
||||
BrunoHttpRequest
|
||||
} from '../types';
|
||||
import type { HttpItemSettings as BrunoHttpItemSettings } from '@usebruno/schema-types/collection/item';
|
||||
|
||||
const getHttpBody = (body: HttpRequestBody | Array<{ title: string; selected?: boolean; body: HttpRequestBody }> | undefined): HttpRequestBody | undefined => {
|
||||
if (!body) return undefined;
|
||||
if (Array.isArray(body)) {
|
||||
const selected = body.find((v) => v.selected);
|
||||
return selected?.body || body[0]?.body;
|
||||
}
|
||||
return body;
|
||||
};
|
||||
|
||||
export const fromOpenCollectionHttpItem = (ocRequest: HttpRequest): BrunoItem => {
|
||||
const info = ocRequest.info;
|
||||
const http = ocRequest.http;
|
||||
const runtime = ocRequest.runtime;
|
||||
|
||||
const scripts = fromOpenCollectionScripts(runtime?.scripts);
|
||||
const httpBody = getHttpBody(http?.body as HttpRequestBody);
|
||||
|
||||
// variables (pre-request from variables, post-response from actions)
|
||||
const variables = fromOpenCollectionVariables(runtime?.variables);
|
||||
const postResponseVars = fromOpenCollectionActions(runtime?.actions);
|
||||
|
||||
const brunoRequest: BrunoHttpRequest = {
|
||||
url: http?.url || '',
|
||||
method: http?.method || 'GET',
|
||||
headers: fromOpenCollectionHeaders(http?.headers) || [],
|
||||
params: fromOpenCollectionParams(http?.params) || [],
|
||||
body: fromOpenCollectionBody(httpBody) || {
|
||||
mode: 'none',
|
||||
json: null,
|
||||
text: null,
|
||||
xml: null,
|
||||
sparql: null,
|
||||
formUrlEncoded: [],
|
||||
multipartForm: [],
|
||||
graphql: null,
|
||||
file: []
|
||||
},
|
||||
auth: fromOpenCollectionAuth(runtime?.auth as Auth),
|
||||
script: {
|
||||
req: scripts?.script?.req || null,
|
||||
res: scripts?.script?.res || null
|
||||
},
|
||||
vars: {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
},
|
||||
assertions: fromOpenCollectionAssertions(runtime?.assertions) || [],
|
||||
tests: scripts?.tests || null,
|
||||
docs: ocRequest.docs || null
|
||||
};
|
||||
|
||||
const brunoItem: BrunoItem = {
|
||||
uid: uuid(),
|
||||
type: 'http-request',
|
||||
seq: info?.seq || 1,
|
||||
name: info?.name || 'Untitled Request',
|
||||
tags: info?.tags || [],
|
||||
request: brunoRequest,
|
||||
settings: null,
|
||||
fileContent: null,
|
||||
root: null,
|
||||
items: [],
|
||||
examples: [],
|
||||
filename: null,
|
||||
pathname: null
|
||||
};
|
||||
|
||||
if (ocRequest.settings) {
|
||||
const settings: BrunoHttpItemSettings = {
|
||||
encodeUrl: typeof ocRequest.settings.encodeUrl === 'boolean' ? ocRequest.settings.encodeUrl : true,
|
||||
timeout: typeof ocRequest.settings.timeout === 'number' ? ocRequest.settings.timeout : 0,
|
||||
followRedirects: typeof ocRequest.settings.followRedirects === 'boolean' ? ocRequest.settings.followRedirects : true,
|
||||
maxRedirects: typeof ocRequest.settings.maxRedirects === 'number' ? ocRequest.settings.maxRedirects : 5
|
||||
};
|
||||
brunoItem.settings = settings;
|
||||
}
|
||||
|
||||
if (ocRequest.examples?.length) {
|
||||
brunoItem.examples = ocRequest.examples.map((example): BrunoExample => ({
|
||||
uid: uuid(),
|
||||
itemUid: brunoItem.uid,
|
||||
name: example.name || 'Untitled Example',
|
||||
description: typeof example.description === 'string' ? example.description : (example.description as { content?: string })?.content || null,
|
||||
type: 'http-request',
|
||||
request: {
|
||||
url: example.request?.url || '',
|
||||
method: example.request?.method || 'GET',
|
||||
headers: fromOpenCollectionHeaders(example.request?.headers) || [],
|
||||
params: fromOpenCollectionParams(example.request?.params) || [],
|
||||
body: fromOpenCollectionBody(example.request?.body) || null
|
||||
},
|
||||
response: example.response ? {
|
||||
status: String(example.response.status || 200),
|
||||
statusText: example.response.statusText || 'OK',
|
||||
headers: fromOpenCollectionHeaders(example.response.headers as HttpRequestHeader[]) || [],
|
||||
body: example.response.body ? {
|
||||
type: example.response.body.type || 'text',
|
||||
content: example.response.body.data || ''
|
||||
} : null
|
||||
} : null
|
||||
}));
|
||||
}
|
||||
|
||||
return brunoItem;
|
||||
};
|
||||
|
||||
export const toOpenCollectionHttpItem = (item: BrunoItem): HttpRequest => {
|
||||
const ocRequest: HttpRequest = {};
|
||||
const brunoRequest = item.request as BrunoHttpRequest;
|
||||
const brunoSettings = item.settings as BrunoHttpItemSettings | undefined;
|
||||
|
||||
const info: HttpRequestInfo = {
|
||||
name: item.name || 'Untitled Request',
|
||||
type: 'http'
|
||||
};
|
||||
if (item.seq) {
|
||||
info.seq = item.seq;
|
||||
}
|
||||
if (item.tags?.length) {
|
||||
info.tags = item.tags;
|
||||
}
|
||||
ocRequest.info = info;
|
||||
|
||||
const http: HttpRequestDetails = {
|
||||
method: brunoRequest?.method || 'GET',
|
||||
url: brunoRequest?.url || ''
|
||||
};
|
||||
|
||||
const headers = toOpenCollectionHeaders(brunoRequest?.headers as BrunoKeyValue[]);
|
||||
if (headers) {
|
||||
http.headers = headers;
|
||||
}
|
||||
|
||||
const params = toOpenCollectionParams(brunoRequest?.params as BrunoHttpRequestParam[]);
|
||||
if (params) {
|
||||
http.params = params;
|
||||
}
|
||||
|
||||
const body = toOpenCollectionBody(brunoRequest?.body);
|
||||
if (body) {
|
||||
http.body = body;
|
||||
}
|
||||
|
||||
ocRequest.http = http;
|
||||
|
||||
const runtime: HttpRequestRuntime = {};
|
||||
let hasRuntime = false;
|
||||
|
||||
const variables = toOpenCollectionVariables(brunoRequest?.vars);
|
||||
if (variables) {
|
||||
runtime.variables = variables;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
const scripts = toOpenCollectionScripts(brunoRequest);
|
||||
if (scripts) {
|
||||
runtime.scripts = scripts;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
const assertions = toOpenCollectionAssertions(brunoRequest?.assertions as BrunoKeyValue[]);
|
||||
if (assertions) {
|
||||
runtime.assertions = assertions;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
// actions (from post-response variables)
|
||||
const resVars = brunoRequest?.vars?.res;
|
||||
const actions = toOpenCollectionActions(resVars);
|
||||
if (actions) {
|
||||
runtime.actions = actions;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
const auth = toOpenCollectionAuth(brunoRequest?.auth);
|
||||
if (auth) {
|
||||
runtime.auth = auth;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
if (hasRuntime) {
|
||||
ocRequest.runtime = runtime;
|
||||
}
|
||||
|
||||
const settings: HttpRequestSettings = {
|
||||
encodeUrl: typeof brunoSettings?.encodeUrl === 'boolean' ? brunoSettings.encodeUrl : true,
|
||||
timeout: typeof brunoSettings?.timeout === 'number' ? brunoSettings.timeout : 0,
|
||||
followRedirects: typeof brunoSettings?.followRedirects === 'boolean' ? brunoSettings.followRedirects : true,
|
||||
maxRedirects: typeof brunoSettings?.maxRedirects === 'number' ? brunoSettings.maxRedirects : 5
|
||||
};
|
||||
ocRequest.settings = settings;
|
||||
|
||||
if (brunoRequest?.docs) {
|
||||
ocRequest.docs = brunoRequest.docs;
|
||||
}
|
||||
|
||||
if (item.examples?.length) {
|
||||
ocRequest.examples = item.examples.map((example): HttpRequestExample => {
|
||||
const ocExample: HttpRequestExample = {
|
||||
name: example.name || 'Untitled Example'
|
||||
};
|
||||
|
||||
if (example.description) {
|
||||
ocExample.description = example.description;
|
||||
}
|
||||
|
||||
if (example.request) {
|
||||
ocExample.request = {
|
||||
url: example.request.url || '',
|
||||
method: example.request.method || 'GET'
|
||||
};
|
||||
|
||||
const exampleHeaders = toOpenCollectionHeaders(example.request.headers as BrunoKeyValue[]);
|
||||
if (exampleHeaders) {
|
||||
ocExample.request.headers = exampleHeaders;
|
||||
}
|
||||
|
||||
const exampleParams = toOpenCollectionParams(example.request.params as BrunoHttpRequestParam[]);
|
||||
if (exampleParams) {
|
||||
ocExample.request.params = exampleParams;
|
||||
}
|
||||
|
||||
const exampleBody = toOpenCollectionBody(example.request.body);
|
||||
if (exampleBody) {
|
||||
ocExample.request.body = exampleBody;
|
||||
}
|
||||
}
|
||||
|
||||
if (example.response) {
|
||||
ocExample.response = {};
|
||||
|
||||
if (example.response.status !== undefined) {
|
||||
ocExample.response.status = Number(example.response.status);
|
||||
}
|
||||
|
||||
if (example.response.statusText) {
|
||||
ocExample.response.statusText = example.response.statusText;
|
||||
}
|
||||
|
||||
const responseHeaders = toOpenCollectionHeaders(example.response.headers as BrunoKeyValue[]);
|
||||
if (responseHeaders) {
|
||||
ocExample.response.headers = responseHeaders;
|
||||
}
|
||||
|
||||
if (example.response.body) {
|
||||
ocExample.response.body = {
|
||||
type: (example.response.body.type as 'json' | 'text' | 'xml' | 'html' | 'binary') || 'text',
|
||||
data: String(example.response.body.content || '')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return ocExample;
|
||||
});
|
||||
}
|
||||
|
||||
return ocRequest;
|
||||
};
|
||||
125
packages/bruno-converters/src/opencollection/items/index.ts
Normal file
125
packages/bruno-converters/src/opencollection/items/index.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import { fromOpenCollectionHttpItem, toOpenCollectionHttpItem } from './http';
|
||||
import { fromOpenCollectionGraphqlItem, toOpenCollectionGraphqlItem } from './graphql';
|
||||
import { fromOpenCollectionGrpcItem, toOpenCollectionGrpcItem } from './grpc';
|
||||
import { fromOpenCollectionWebsocketItem, toOpenCollectionWebsocketItem } from './websocket';
|
||||
import type {
|
||||
BrunoItem
|
||||
} from '../types';
|
||||
|
||||
interface OCItem {
|
||||
info?: {
|
||||
type?: string;
|
||||
name?: string;
|
||||
seq?: number;
|
||||
};
|
||||
http?: unknown;
|
||||
graphql?: unknown;
|
||||
grpc?: unknown;
|
||||
websocket?: unknown;
|
||||
items?: unknown[];
|
||||
script?: string;
|
||||
}
|
||||
|
||||
const getItemType = (item: OCItem): string => {
|
||||
if (item.info?.type) {
|
||||
return item.info.type;
|
||||
}
|
||||
|
||||
if ('items' in item && item.items) {
|
||||
return 'folder';
|
||||
}
|
||||
|
||||
if ('http' in item && item.http) {
|
||||
return 'http';
|
||||
}
|
||||
|
||||
if ('graphql' in item && item.graphql) {
|
||||
return 'graphql';
|
||||
}
|
||||
|
||||
if ('grpc' in item && item.grpc) {
|
||||
return 'grpc';
|
||||
}
|
||||
|
||||
if ('websocket' in item && item.websocket) {
|
||||
return 'websocket';
|
||||
}
|
||||
|
||||
if ('script' in item && typeof item.script === 'string') {
|
||||
return 'script';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
export const fromOpenCollectionItem = (item: unknown, parseFolder: (folder: unknown) => BrunoItem): BrunoItem | null => {
|
||||
const ocItem = item as OCItem;
|
||||
const itemType = getItemType(ocItem);
|
||||
|
||||
switch (itemType) {
|
||||
case 'http':
|
||||
return fromOpenCollectionHttpItem(item as Parameters<typeof fromOpenCollectionHttpItem>[0]);
|
||||
case 'graphql':
|
||||
return fromOpenCollectionGraphqlItem(item as Parameters<typeof fromOpenCollectionGraphqlItem>[0]);
|
||||
case 'grpc':
|
||||
return fromOpenCollectionGrpcItem(item as Parameters<typeof fromOpenCollectionGrpcItem>[0]);
|
||||
case 'websocket':
|
||||
return fromOpenCollectionWebsocketItem(item as Parameters<typeof fromOpenCollectionWebsocketItem>[0]);
|
||||
case 'folder':
|
||||
return parseFolder(item);
|
||||
case 'script': {
|
||||
const scriptItem = item as { script?: string; info?: { name?: string } };
|
||||
return {
|
||||
uid: uuid(),
|
||||
type: 'js',
|
||||
name: scriptItem.info?.name || 'script.js',
|
||||
fileContent: scriptItem.script || ''
|
||||
};
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const toOpenCollectionItem = (item: BrunoItem, stringifyFolder: (folder: BrunoItem) => unknown): unknown | null => {
|
||||
switch (item.type) {
|
||||
case 'http-request':
|
||||
return toOpenCollectionHttpItem(item);
|
||||
case 'graphql-request':
|
||||
return toOpenCollectionGraphqlItem(item);
|
||||
case 'grpc-request':
|
||||
return toOpenCollectionGrpcItem(item);
|
||||
case 'ws-request':
|
||||
return toOpenCollectionWebsocketItem(item);
|
||||
case 'folder':
|
||||
return stringifyFolder(item);
|
||||
case 'js':
|
||||
return {
|
||||
info: {
|
||||
name: item.name || 'script.js',
|
||||
type: 'script'
|
||||
},
|
||||
script: item.fileContent || ''
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const fromOpenCollectionItems = (items: unknown[] | undefined, parseFolder: (folder: unknown) => BrunoItem): BrunoItem[] => {
|
||||
return (items || [])
|
||||
.map((item) => fromOpenCollectionItem(item, parseFolder))
|
||||
.filter((item): item is BrunoItem => item !== null);
|
||||
};
|
||||
|
||||
export const toOpenCollectionItems = (items: BrunoItem[] | undefined | null, stringifyFolder: (folder: BrunoItem) => unknown): unknown[] => {
|
||||
return (items || [])
|
||||
.map((item) => toOpenCollectionItem(item, stringifyFolder))
|
||||
.filter((item): item is unknown => item !== null);
|
||||
};
|
||||
|
||||
export { fromOpenCollectionHttpItem, toOpenCollectionHttpItem } from './http';
|
||||
export { fromOpenCollectionGraphqlItem, toOpenCollectionGraphqlItem } from './graphql';
|
||||
export { fromOpenCollectionGrpcItem, toOpenCollectionGrpcItem } from './grpc';
|
||||
export { fromOpenCollectionWebsocketItem, toOpenCollectionWebsocketItem } from './websocket';
|
||||
176
packages/bruno-converters/src/opencollection/items/websocket.ts
Normal file
176
packages/bruno-converters/src/opencollection/items/websocket.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import { uuid } from '../../common/index.js';
|
||||
import {
|
||||
fromOpenCollectionHeaders,
|
||||
toOpenCollectionHeaders,
|
||||
fromOpenCollectionAuth,
|
||||
toOpenCollectionAuth,
|
||||
fromOpenCollectionScripts,
|
||||
toOpenCollectionScripts,
|
||||
fromOpenCollectionVariables,
|
||||
toOpenCollectionVariables,
|
||||
fromOpenCollectionActions,
|
||||
toOpenCollectionActions
|
||||
} from '../common';
|
||||
import type {
|
||||
WebSocketRequest,
|
||||
WebSocketRequestInfo,
|
||||
WebSocketRequestDetails,
|
||||
WebSocketRequestRuntime,
|
||||
WebSocketMessage,
|
||||
WebSocketMessageVariant,
|
||||
Auth,
|
||||
BrunoItem,
|
||||
BrunoWsMessage,
|
||||
BrunoKeyValue,
|
||||
BrunoWebSocketRequestBody
|
||||
} from '../types';
|
||||
|
||||
export const fromOpenCollectionWebsocketItem = (item: WebSocketRequest): BrunoItem => {
|
||||
const info = item.info || {};
|
||||
const websocket = item.websocket || {};
|
||||
const runtime = item.runtime || {};
|
||||
|
||||
const wsMessages: BrunoWsMessage[] = [];
|
||||
|
||||
if (websocket.message) {
|
||||
if ('type' in websocket.message && 'data' in websocket.message) {
|
||||
const msg = websocket.message as WebSocketMessage;
|
||||
wsMessages.push({
|
||||
name: 'message 1',
|
||||
type: msg.type || 'json',
|
||||
content: msg.data || ''
|
||||
});
|
||||
} else if (Array.isArray(websocket.message)) {
|
||||
websocket.message.forEach((m, index) => {
|
||||
wsMessages.push({
|
||||
name: m.title || `message ${index + 1}`,
|
||||
type: m.message?.type || 'json',
|
||||
content: m.message?.data || ''
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const scripts = fromOpenCollectionScripts(runtime.scripts);
|
||||
|
||||
// variables (pre-request from variables, post-response from actions)
|
||||
const variables = fromOpenCollectionVariables(runtime.variables);
|
||||
const postResponseVars = fromOpenCollectionActions((runtime as { actions?: Parameters<typeof fromOpenCollectionActions>[0] }).actions);
|
||||
|
||||
const wsBody: BrunoWebSocketRequestBody = {
|
||||
mode: 'ws',
|
||||
ws: wsMessages
|
||||
};
|
||||
|
||||
const brunoItem: BrunoItem = {
|
||||
uid: uuid(),
|
||||
type: 'ws-request',
|
||||
name: info.name || 'Untitled Request',
|
||||
seq: info.seq || 1,
|
||||
request: {
|
||||
url: websocket.url || '',
|
||||
headers: fromOpenCollectionHeaders(websocket.headers),
|
||||
body: wsBody,
|
||||
auth: fromOpenCollectionAuth(runtime.auth as Auth),
|
||||
script: scripts?.script,
|
||||
vars: {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
},
|
||||
tests: scripts?.tests,
|
||||
docs: item.docs || ''
|
||||
}
|
||||
};
|
||||
|
||||
if (info.tags?.length) {
|
||||
brunoItem.tags = info.tags;
|
||||
}
|
||||
|
||||
return brunoItem;
|
||||
};
|
||||
|
||||
export const toOpenCollectionWebsocketItem = (item: BrunoItem): WebSocketRequest => {
|
||||
const request = (item.request || {}) as Record<string, unknown>;
|
||||
|
||||
const info: WebSocketRequestInfo = {
|
||||
name: item.name || 'Untitled Request',
|
||||
type: 'websocket'
|
||||
};
|
||||
|
||||
if (item.seq) {
|
||||
info.seq = item.seq;
|
||||
}
|
||||
|
||||
if (item.tags?.length) {
|
||||
info.tags = item.tags;
|
||||
}
|
||||
|
||||
const websocket: WebSocketRequestDetails = {
|
||||
url: request.url as string || ''
|
||||
};
|
||||
|
||||
const headers = toOpenCollectionHeaders(request.headers as BrunoKeyValue[]);
|
||||
if (headers) {
|
||||
websocket.headers = headers;
|
||||
}
|
||||
|
||||
const body = request.body as { ws?: BrunoWsMessage[] } | undefined;
|
||||
if (body?.ws?.length) {
|
||||
const messages = body.ws;
|
||||
if (messages.length === 1) {
|
||||
websocket.message = {
|
||||
type: (messages[0].type as WebSocketMessage['type']) || 'json',
|
||||
data: messages[0].content || ''
|
||||
};
|
||||
} else {
|
||||
websocket.message = messages.map((msg): WebSocketMessageVariant => ({
|
||||
title: msg.name || 'Untitled',
|
||||
message: {
|
||||
type: (msg.type as WebSocketMessage['type']) || 'json',
|
||||
data: msg.content || ''
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const ocRequest: WebSocketRequest = {
|
||||
info,
|
||||
websocket
|
||||
};
|
||||
|
||||
const auth = toOpenCollectionAuth(request.auth as Parameters<typeof toOpenCollectionAuth>[0]);
|
||||
const scripts = toOpenCollectionScripts(request as Parameters<typeof toOpenCollectionScripts>[0]);
|
||||
const variables = toOpenCollectionVariables(request.vars as Parameters<typeof toOpenCollectionVariables>[0]);
|
||||
|
||||
// actions (from post-response variables)
|
||||
const vars = request.vars as { req?: unknown[]; res?: unknown[] } | undefined;
|
||||
const actions = toOpenCollectionActions(vars?.res as Parameters<typeof toOpenCollectionActions>[0]);
|
||||
|
||||
if (auth || scripts || variables || actions) {
|
||||
const runtime: WebSocketRequestRuntime = {};
|
||||
|
||||
if (auth) {
|
||||
runtime.auth = auth;
|
||||
}
|
||||
|
||||
if (scripts) {
|
||||
runtime.scripts = scripts;
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
runtime.variables = variables;
|
||||
}
|
||||
|
||||
if (actions) {
|
||||
(runtime as { actions?: typeof actions }).actions = actions;
|
||||
}
|
||||
|
||||
ocRequest.runtime = runtime;
|
||||
}
|
||||
|
||||
if (request.docs) {
|
||||
ocRequest.docs = request.docs as string;
|
||||
}
|
||||
|
||||
return ocRequest;
|
||||
};
|
||||
@@ -0,0 +1,133 @@
|
||||
import { OpenCollection } from "@opencollection/types";
|
||||
import { BrunoCollection, BrunoCollectionRoot, BrunoConfig, PemCertificate, Pkcs12Certificate } from "./types";
|
||||
import { fromOpenCollectionAuth, fromOpenCollectionHeaders, fromOpenCollectionScripts, fromOpenCollectionVariables } from "./common";
|
||||
import { uuid } from "../common";
|
||||
import { fromOpenCollectionItems } from "./items";
|
||||
import { fromOpenCollectionFolder } from "./folder";
|
||||
import { fromOpenCollectionEnvironments } from "./environment";
|
||||
|
||||
const fromOpenCollectionConfig = (oc: OpenCollection): BrunoConfig => {
|
||||
const extensions = oc.extensions;
|
||||
const ignoreList = extensions && Array.isArray(extensions.ignore)
|
||||
? extensions.ignore as string[]
|
||||
: ['node_modules', '.git'];
|
||||
|
||||
const brunoConfig: BrunoConfig = {
|
||||
version: '1',
|
||||
name: oc.info?.name || 'Untitled Collection',
|
||||
type: 'collection',
|
||||
ignore: ignoreList
|
||||
};
|
||||
|
||||
const config = oc.config;
|
||||
if (!config) {
|
||||
return brunoConfig;
|
||||
}
|
||||
|
||||
if (config.protobuf) {
|
||||
brunoConfig.protobuf = {
|
||||
protoFiles: config.protobuf.protoFiles?.map((f) => ({
|
||||
path: f.path
|
||||
})),
|
||||
importPaths: config.protobuf.importPaths?.map((p) => ({
|
||||
path: p.path,
|
||||
disabled: p.disabled || false
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
if (config.proxy && typeof config.proxy !== 'boolean') {
|
||||
if (config.proxy === 'inherit') {
|
||||
brunoConfig.proxy = { enabled: 'global' };
|
||||
} else {
|
||||
const proxyConfig = config.proxy;
|
||||
brunoConfig.proxy = {
|
||||
enabled: true,
|
||||
protocol: proxyConfig.protocol || 'http',
|
||||
hostname: proxyConfig.hostname || '',
|
||||
port: proxyConfig.port || 0
|
||||
};
|
||||
|
||||
if (proxyConfig.auth) {
|
||||
brunoConfig.proxy.auth = {
|
||||
enabled: true,
|
||||
username: proxyConfig.auth.username || '',
|
||||
password: proxyConfig.auth.password || ''
|
||||
};
|
||||
}
|
||||
|
||||
if (proxyConfig.bypassProxy) {
|
||||
brunoConfig.proxy.bypassProxy = proxyConfig.bypassProxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.clientCertificates?.length) {
|
||||
brunoConfig.clientCertificates = {
|
||||
certs: config.clientCertificates.map((cert) => {
|
||||
if (cert.type === 'pem') {
|
||||
const pemCert = cert as PemCertificate;
|
||||
return {
|
||||
domain: pemCert.domain || '',
|
||||
type: 'pem' as const,
|
||||
certFilePath: pemCert.certificateFilePath || '',
|
||||
keyFilePath: pemCert.privateKeyFilePath || '',
|
||||
passphrase: pemCert.passphrase || ''
|
||||
};
|
||||
} else if (cert.type === 'pkcs12') {
|
||||
const pkcs12Cert = cert as Pkcs12Certificate;
|
||||
return {
|
||||
domain: pkcs12Cert.domain || '',
|
||||
type: 'pkcs12' as const,
|
||||
pfxFilePath: pkcs12Cert.pkcs12FilePath || '',
|
||||
passphrase: pkcs12Cert.passphrase || ''
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}).filter((cert): cert is NonNullable<typeof cert> => cert !== null)
|
||||
};
|
||||
}
|
||||
|
||||
return brunoConfig;
|
||||
};
|
||||
|
||||
const fromOpenCollectionRoot = (oc: OpenCollection): BrunoCollectionRoot => {
|
||||
const root: BrunoCollectionRoot = {};
|
||||
|
||||
if (oc.request) {
|
||||
const scripts = fromOpenCollectionScripts(oc.request.scripts);
|
||||
root.request = {
|
||||
headers: fromOpenCollectionHeaders(oc.request.headers),
|
||||
auth: fromOpenCollectionAuth(oc.request.auth),
|
||||
script: scripts?.script,
|
||||
vars: fromOpenCollectionVariables(oc.request.variables),
|
||||
tests: scripts?.tests
|
||||
};
|
||||
}
|
||||
|
||||
if (oc.docs) {
|
||||
root.docs = typeof oc.docs === 'string'
|
||||
? oc.docs
|
||||
: oc.docs.content || '';
|
||||
}
|
||||
|
||||
root.meta = {
|
||||
name: oc.info?.name || 'Untitled Collection'
|
||||
};
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
export const openCollectionToBruno = (openCollection: OpenCollection): BrunoCollection => {
|
||||
const brunoCollection: BrunoCollection = {
|
||||
uid: uuid(),
|
||||
name: openCollection.info?.name || 'Untitled Collection',
|
||||
version: '1',
|
||||
items: fromOpenCollectionItems(openCollection.items, (folder: unknown) => fromOpenCollectionFolder(folder as Parameters<typeof fromOpenCollectionFolder>[0])),
|
||||
environments: fromOpenCollectionEnvironments(openCollection.config?.environments),
|
||||
brunoConfig: fromOpenCollectionConfig(openCollection) as Record<string, unknown>,
|
||||
root: fromOpenCollectionRoot(openCollection)
|
||||
};
|
||||
|
||||
return brunoCollection;
|
||||
};
|
||||
208
packages/bruno-converters/src/opencollection/types.ts
Normal file
208
packages/bruno-converters/src/opencollection/types.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
// OpenCollection types - main module
|
||||
export type { OpenCollection, Extensions } from '@opencollection/types';
|
||||
|
||||
// OpenCollection collection/item types
|
||||
export type { Item, Folder, FolderInfo } from '@opencollection/types/collection/item';
|
||||
|
||||
// OpenCollection HTTP request types
|
||||
export type {
|
||||
HttpRequest,
|
||||
HttpRequestHeader,
|
||||
HttpResponseHeader,
|
||||
HttpRequestParam,
|
||||
HttpRequestBody,
|
||||
HttpRequestSettings,
|
||||
HttpRequestExample,
|
||||
HttpRequestExampleRequest,
|
||||
HttpRequestExampleResponse,
|
||||
HttpRequestExampleResponseBody,
|
||||
HttpRequestBodyVariant,
|
||||
HttpRequestInfo,
|
||||
HttpRequestDetails,
|
||||
HttpRequestRuntime,
|
||||
RawBody,
|
||||
FormUrlEncodedBody,
|
||||
FormUrlEncodedEntry,
|
||||
MultipartFormBody,
|
||||
MultipartFormEntry,
|
||||
FileBody,
|
||||
FileBodyVariant
|
||||
} from '@opencollection/types/requests/http';
|
||||
|
||||
// OpenCollection GraphQL request types
|
||||
export type {
|
||||
GraphQLRequest,
|
||||
GraphQLRequestSettings,
|
||||
GraphQLRequestInfo,
|
||||
GraphQLRequestDetails,
|
||||
GraphQLRequestRuntime,
|
||||
GraphQLBody,
|
||||
GraphQLBodyVariant
|
||||
} from '@opencollection/types/requests/graphql';
|
||||
|
||||
// OpenCollection gRPC request types
|
||||
export type {
|
||||
GrpcRequest,
|
||||
GrpcRequestInfo,
|
||||
GrpcRequestDetails,
|
||||
GrpcRequestRuntime,
|
||||
GrpcMetadata,
|
||||
GrpcMessage,
|
||||
GrpcMessageVariant,
|
||||
GrpcMessagePayload,
|
||||
GrpcMethodType
|
||||
} from '@opencollection/types/requests/grpc';
|
||||
|
||||
// OpenCollection WebSocket request types
|
||||
export type {
|
||||
WebSocketRequest,
|
||||
WebSocketRequestInfo,
|
||||
WebSocketRequestDetails,
|
||||
WebSocketRequestRuntime,
|
||||
WebSocketMessage,
|
||||
WebSocketMessageVariant,
|
||||
WebSocketPayload,
|
||||
WebSocketMessageType
|
||||
} from '@opencollection/types/requests/websocket';
|
||||
|
||||
// OpenCollection config types
|
||||
export type { Environment, CollectionConfig } from '@opencollection/types/config/environments';
|
||||
export type { Protobuf, ProtoFileItem, ProtoFileImportPath } from '@opencollection/types/config/protobuf';
|
||||
export type { Proxy, ProxyAuth } from '@opencollection/types/config/proxy';
|
||||
export type { ClientCertificate, PemCertificate, Pkcs12Certificate } from '@opencollection/types/config/certificates';
|
||||
|
||||
// OpenCollection common types
|
||||
export type { RequestDefaults, RequestSettings } from '@opencollection/types/common/request-defaults';
|
||||
export type { Documentation } from '@opencollection/types/common/documentation';
|
||||
export type { Description } from '@opencollection/types/common/description';
|
||||
export type { Info, Author } from '@opencollection/types/common/info';
|
||||
export type {
|
||||
Variable,
|
||||
VariableValueVariant
|
||||
} from '@opencollection/types/common/variables';
|
||||
export type { Scripts } from '@opencollection/types/common/scripts';
|
||||
export type { Assertion } from '@opencollection/types/common/assertions';
|
||||
export type { Tag } from '@opencollection/types/common/tags';
|
||||
export type {
|
||||
Action,
|
||||
ActionSetVariable,
|
||||
ActionPhase,
|
||||
ActionVariableScope,
|
||||
SetVariableActionSelector,
|
||||
SetVariableActionTarget
|
||||
} from '@opencollection/types/common/actions';
|
||||
|
||||
// OpenCollection auth types
|
||||
export type {
|
||||
Auth,
|
||||
AuthBasic,
|
||||
AuthBearer,
|
||||
AuthDigest,
|
||||
AuthNTLM,
|
||||
AuthAwsV4,
|
||||
AuthApiKey,
|
||||
AuthWsse
|
||||
} from '@opencollection/types/common/auth';
|
||||
|
||||
export type { AuthOAuth2 } from '@opencollection/types/common/auth-oauth2';
|
||||
|
||||
// Bruno types - collection
|
||||
export type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
|
||||
export type {
|
||||
FolderRoot as BrunoFolderRoot,
|
||||
FolderRequest as BrunoFolderRequest,
|
||||
FolderMeta as BrunoFolderMeta
|
||||
} from '@usebruno/schema-types/collection/folder';
|
||||
export type { Collection as BrunoCollection } from '@usebruno/schema-types/collection/collection';
|
||||
export type {
|
||||
Environment as BrunoEnvironment,
|
||||
EnvironmentVariable as BrunoEnvironmentVariable
|
||||
} from '@usebruno/schema-types/collection/environment';
|
||||
export type {
|
||||
Example as BrunoExample,
|
||||
ExampleRequest as BrunoExampleRequest,
|
||||
ExampleResponse as BrunoExampleResponse,
|
||||
ExampleResponseBody as BrunoExampleResponseBody
|
||||
} from '@usebruno/schema-types/collection/examples';
|
||||
|
||||
// Bruno types - common
|
||||
export type { KeyValue as BrunoKeyValue } from '@usebruno/schema-types/common/key-value';
|
||||
export type { Variable as BrunoVariable, Variables as BrunoVariables } from '@usebruno/schema-types/common/variables';
|
||||
export type { Script as BrunoScript } from '@usebruno/schema-types/common/scripts';
|
||||
export type {
|
||||
Auth as BrunoAuth,
|
||||
AuthMode as BrunoAuthMode,
|
||||
AuthAwsV4 as BrunoAuthAwsV4,
|
||||
AuthBasic as BrunoAuthBasic,
|
||||
AuthBearer as BrunoAuthBearer,
|
||||
AuthDigest as BrunoAuthDigest,
|
||||
AuthNTLM as BrunoAuthNTLM,
|
||||
AuthWsse as BrunoAuthWsse,
|
||||
AuthApiKey as BrunoAuthApiKey,
|
||||
OAuth2 as BrunoOAuth2
|
||||
} from '@usebruno/schema-types/common/auth';
|
||||
export type { MultipartFormEntry as BrunoMultipartFormEntry, MultipartForm as BrunoMultipartForm } from '@usebruno/schema-types/common/multipart-form';
|
||||
export type { FileEntry as BrunoFileEntry, FileList as BrunoFileList } from '@usebruno/schema-types/common/file';
|
||||
export type { GraphqlBody as BrunoGraphqlBody } from '@usebruno/schema-types/common/graphql';
|
||||
|
||||
// Bruno types - requests
|
||||
export type {
|
||||
HttpRequest as BrunoHttpRequest,
|
||||
HttpRequestBody as BrunoHttpRequestBody,
|
||||
HttpRequestBodyMode as BrunoHttpRequestBodyMode,
|
||||
HttpRequestParam as BrunoHttpRequestParam,
|
||||
HttpRequestParamType as BrunoHttpRequestParamType
|
||||
} from '@usebruno/schema-types/requests/http';
|
||||
export type {
|
||||
GrpcRequest as BrunoGrpcRequest,
|
||||
GrpcRequestBody as BrunoGrpcRequestBody,
|
||||
GrpcMessage as BrunoGrpcMessage,
|
||||
GrpcMethodType as BrunoGrpcMethodType
|
||||
} from '@usebruno/schema-types/requests/grpc';
|
||||
export type {
|
||||
WebSocketRequest as BrunoWebSocketRequest,
|
||||
WebSocketRequestBody as BrunoWebSocketRequestBody,
|
||||
WebSocketMessage as BrunoWsMessage
|
||||
} from '@usebruno/schema-types/requests/websocket';
|
||||
|
||||
export interface BrunoConfig {
|
||||
version?: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
ignore?: string[];
|
||||
protobuf?: {
|
||||
protoFiles?: { path: string }[];
|
||||
importPaths?: { path: string; disabled?: boolean }[];
|
||||
};
|
||||
proxy?: {
|
||||
enabled?: boolean | 'global';
|
||||
protocol?: string;
|
||||
hostname?: string;
|
||||
port?: number;
|
||||
auth?: {
|
||||
enabled?: boolean;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
bypassProxy?: string;
|
||||
};
|
||||
clientCertificates?: {
|
||||
certs?: Array<{
|
||||
domain?: string;
|
||||
type?: 'pem' | 'pkcs12';
|
||||
certFilePath?: string;
|
||||
keyFilePath?: string;
|
||||
pfxFilePath?: string;
|
||||
passphrase?: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BrunoCollectionRoot {
|
||||
request?: any;
|
||||
docs?: string;
|
||||
meta?: {
|
||||
name?: string;
|
||||
seq?: number;
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@ const generateUID = () => {
|
||||
return result;
|
||||
};
|
||||
|
||||
import { get, each, filter } from 'lodash';
|
||||
import { get, each } from 'lodash';
|
||||
import { collectionSchema } from '@usebruno/schema';
|
||||
|
||||
// --- Inlined from src/common/index.js ---
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react",
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"declaration": true,
|
||||
"declarationDir": "types",
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"emitDeclarationOnly": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"types": ["node"],
|
||||
"lib": ["ES2020"],
|
||||
"typeRoots": ["../../node_modules/@types", "./node_modules/@types"],
|
||||
"baseUrl": "../..",
|
||||
"paths": {
|
||||
"@usebruno/schema-types": ["packages/bruno-schema-types/dist/index.d.ts"],
|
||||
"@usebruno/schema-types/*": ["packages/bruno-schema-types/dist/*"],
|
||||
"@opencollection/types": ["node_modules/@opencollection/types/dist/opencollection.d.ts"],
|
||||
"@opencollection/types/*": ["node_modules/@opencollection/types/dist/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["dist", "node_modules", "tests"]
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user