mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
feat: opencollection actions
This commit is contained in:
16
package-lock.json
generated
16
package-lock.json
generated
@@ -26,6 +26,7 @@
|
||||
"@eslint/compat": "^1.3.2",
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@jest/globals": "^29.2.0",
|
||||
"@opencollection/types": "0.3.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@stylistic/eslint-plugin": "^5.3.1",
|
||||
@@ -5680,6 +5681,13 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencollection/types": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.3.0.tgz",
|
||||
"integrity": "sha512-kw+co3sM4ATDQI85lgy5UmOilEHFVdNYvOjZXmnSw6PUDUTAGBiaZNdwdSXwp//o4IwKbTQb8/0I3UdjLKh+qA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
|
||||
@@ -33626,7 +33634,6 @@
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.22.0",
|
||||
"@babel/preset-typescript": "^7.22.0",
|
||||
"@opencollection/types": "0.2.0",
|
||||
"@rollup/plugin-commonjs": "^23.0.2",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
@@ -33646,13 +33653,6 @@
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
},
|
||||
"packages/bruno-filestore/node_modules/@opencollection/types": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.2.0.tgz",
|
||||
"integrity": "sha512-Lucjjoy+ZzfdjL0/9HF6PFlNSDG/m11VZBiR2K5XU6ChJ2XXfJyKocRB2g0tm7e5zQNMoVL3oUoDJ2gexx6xyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/bruno-filestore/node_modules/@rollup/plugin-typescript": {
|
||||
"version": "12.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@eslint/compat": "^1.3.2",
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@jest/globals": "^29.2.0",
|
||||
"@opencollection/types": "0.3.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@stylistic/eslint-plugin": "^5.3.1",
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.22.0",
|
||||
"@babel/preset-typescript": "^7.22.0",
|
||||
"@opencollection/types": "0.2.0",
|
||||
"@usebruno/schema-types": "0.0.1",
|
||||
"@rollup/plugin-commonjs": "^23.0.2",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
@@ -31,6 +29,7 @@
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^24.1.0",
|
||||
"@usebruno/schema-types": "0.0.1",
|
||||
"babel-jest": "^29.7.0",
|
||||
"jest": "^29.2.0",
|
||||
"nanoid": "3.3.8",
|
||||
|
||||
78
packages/bruno-filestore/src/formats/yml/common/actions.ts
Normal file
78
packages/bruno-filestore/src/formats/yml/common/actions.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { Action, ActionSetVariable, ActionVariableScope } from '@opencollection/types/common/actions';
|
||||
import type { Variable as BrunoVariable, Variables as BrunoVariables } from '@usebruno/schema-types/common/variables';
|
||||
import { uuid } from '../../../utils';
|
||||
|
||||
/**
|
||||
* 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?.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 toBrunoPostResponseVariables = (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: false
|
||||
};
|
||||
|
||||
if (setVarAction.description) {
|
||||
variable.description = typeof setVarAction.description === 'string'
|
||||
? setVarAction.description
|
||||
: (setVarAction.description as any)?.content || '';
|
||||
}
|
||||
|
||||
resVars.push(variable);
|
||||
}
|
||||
});
|
||||
|
||||
return resVars;
|
||||
};
|
||||
@@ -3,36 +3,28 @@ import { FolderRequest as BrunoFolderRequest } from '@usebruno/schema-types/coll
|
||||
import { Variable as BrunoVariable, Variables as BrunoVariables } from '@usebruno/schema-types/common/variables';
|
||||
import { uuid } from '../../../utils';
|
||||
|
||||
/**
|
||||
* Convert Bruno pre-request variables to OpenCollection variables format.
|
||||
* Note: Post-response variables are now converted to actions (see actions.ts).
|
||||
*/
|
||||
export const toOpenCollectionVariables = (variables: BrunoFolderRequest['vars'] | BrunoVariables | null | undefined): Variable[] | undefined => {
|
||||
// Handle folder variables (has req/res structure)
|
||||
// Handle folder variables (has req/res structure) - only use req vars
|
||||
const hasReqRes = variables && 'req' in variables;
|
||||
const reqVars = hasReqRes ? variables.req : variables as BrunoVariables;
|
||||
const resVars = hasReqRes && 'res' in variables ? variables.res : [];
|
||||
|
||||
const reqVarsArray = Array.isArray(reqVars) ? reqVars : [];
|
||||
const resVarsArray = Array.isArray(resVars) ? resVars : [];
|
||||
|
||||
const allVars = [...reqVarsArray, ...resVarsArray];
|
||||
|
||||
if (!allVars.length) {
|
||||
if (!reqVarsArray.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ocVariables: Variable[] = allVars.map((v: BrunoVariable, index: number): Variable => {
|
||||
const isResVar = index >= reqVarsArray.length;
|
||||
const ocVariables: Variable[] = reqVarsArray.map((v: BrunoVariable): Variable => {
|
||||
const variable: Variable = {
|
||||
name: v.name || '',
|
||||
value: v.value || ''
|
||||
};
|
||||
|
||||
if (isResVar) {
|
||||
const scopeMarker = '[post-response]';
|
||||
if (v?.description?.trim().length) {
|
||||
variable.description = `${scopeMarker} ${v.description}`;
|
||||
} else {
|
||||
variable.description = scopeMarker;
|
||||
}
|
||||
} else if (v?.description?.trim().length) {
|
||||
if (v?.description?.trim().length) {
|
||||
variable.description = v.description;
|
||||
}
|
||||
|
||||
@@ -45,18 +37,18 @@ export const toOpenCollectionVariables = (variables: BrunoFolderRequest['vars']
|
||||
return ocVariables.length > 0 ? ocVariables : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert OpenCollection variables to Bruno pre-request variables format.
|
||||
* Note: Post-response variables come from actions (see actions.ts).
|
||||
*/
|
||||
export const toBrunoVariables = (variables: Variable[] | null | undefined): { req: BrunoVariables; res: BrunoVariables } => {
|
||||
if (!variables?.length) {
|
||||
return { req: [], res: [] };
|
||||
}
|
||||
|
||||
const scopeMarker = '[post-response]';
|
||||
const reqVars: BrunoVariables = [];
|
||||
const resVars: BrunoVariables = [];
|
||||
|
||||
variables.forEach((v: Variable) => {
|
||||
const isPostResponse = typeof v.description === 'string' && v.description.startsWith(scopeMarker);
|
||||
|
||||
const variable: BrunoVariable = {
|
||||
uid: uuid(),
|
||||
name: v.name || '',
|
||||
@@ -65,19 +57,12 @@ export const toBrunoVariables = (variables: Variable[] | null | undefined): { re
|
||||
local: false
|
||||
};
|
||||
|
||||
if (isPostResponse) {
|
||||
const cleanDesc = (v.description as string).substring(scopeMarker.length).trim();
|
||||
if (cleanDesc) {
|
||||
variable.description = cleanDesc;
|
||||
}
|
||||
resVars.push(variable);
|
||||
} else {
|
||||
if (v.description) {
|
||||
variable.description = typeof v.description === 'string' ? v.description : (v.description as any)?.content || '';
|
||||
}
|
||||
reqVars.push(variable);
|
||||
if (v.description) {
|
||||
variable.description = typeof v.description === 'string' ? v.description : (v.description as any)?.content || '';
|
||||
}
|
||||
|
||||
reqVars.push(variable);
|
||||
});
|
||||
|
||||
return { req: reqVars, res: resVars };
|
||||
return { req: reqVars, res: [] };
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { toBrunoAuth } from '../common/auth';
|
||||
import { toBrunoHttpHeaders } from '../common/headers';
|
||||
import { toBrunoParams } from '../common/params';
|
||||
import { toBrunoVariables } from '../common/variables';
|
||||
import { toBrunoPostResponseVariables } from '../common/actions';
|
||||
import { toBrunoScripts } from '../common/scripts';
|
||||
import { toBrunoAssertions } from '../common/assertions';
|
||||
import { uuid } from '../../../utils';
|
||||
@@ -61,9 +62,13 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
|
||||
brunoRequest.tests = scripts.tests;
|
||||
}
|
||||
|
||||
// variables
|
||||
// variables (pre-request from variables, post-response from actions)
|
||||
const variables = toBrunoVariables(runtime?.variables);
|
||||
brunoRequest.vars = variables;
|
||||
const postResponseVars = toBrunoPostResponseVariables(runtime?.actions);
|
||||
brunoRequest.vars = {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
};
|
||||
|
||||
// assertions
|
||||
const assertions = toBrunoAssertions(runtime?.assertions);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { toBrunoHttpHeaders } from '../common/headers';
|
||||
import { toBrunoParams } from '../common/params';
|
||||
import { toBrunoBody } from '../common/body';
|
||||
import { toBrunoVariables } from '../common/variables';
|
||||
import { toBrunoPostResponseVariables } from '../common/actions';
|
||||
import { toBrunoScripts } from '../common/scripts';
|
||||
import { toBrunoAssertions } from '../common/assertions';
|
||||
import { uuid } from '../../../utils';
|
||||
@@ -59,9 +60,13 @@ const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
|
||||
brunoRequest.tests = scripts.tests;
|
||||
}
|
||||
|
||||
// variables
|
||||
// variables (pre-request from variables, post-response from actions)
|
||||
const variables = toBrunoVariables(runtime?.variables);
|
||||
brunoRequest.vars = variables;
|
||||
const postResponseVars = toBrunoPostResponseVariables(runtime?.actions);
|
||||
brunoRequest.vars = {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
};
|
||||
|
||||
// assertions
|
||||
const assertions = toBrunoAssertions(runtime?.assertions);
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { Auth } from '@opencollection/types/common/auth';
|
||||
import type { Scripts } from '@opencollection/types/common/scripts';
|
||||
import type { Variable } from '@opencollection/types/common/variables';
|
||||
import type { Assertion } from '@opencollection/types/common/assertions';
|
||||
import type { Action } from '@opencollection/types/common/actions';
|
||||
import type { HttpRequestParam, HttpRequestHeader } from '@opencollection/types/requests/http';
|
||||
import { stringifyYml } from '../utils';
|
||||
import { isNonEmptyString, isNumber } from '../../../utils';
|
||||
@@ -12,6 +13,7 @@ import { toOpenCollectionAuth } from '../common/auth';
|
||||
import { toOpenCollectionHttpHeaders } from '../common/headers';
|
||||
import { toOpenCollectionParams } from '../common/params';
|
||||
import { toOpenCollectionVariables } from '../common/variables';
|
||||
import { toOpenCollectionActions } from '../common/actions';
|
||||
import { toOpenCollectionScripts } from '../common/scripts';
|
||||
import { toOpenCollectionAssertions } from '../common/assertions';
|
||||
|
||||
@@ -98,6 +100,14 @@ const stringifyGraphQLRequest = (item: BrunoItem): string => {
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
// actions (from post-response variables)
|
||||
const resVars = brunoRequest.vars?.res;
|
||||
const actions: Action[] | undefined = toOpenCollectionActions(resVars);
|
||||
if (actions) {
|
||||
runtime.actions = actions;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
// auth
|
||||
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
|
||||
if (auth) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { Auth } from '@opencollection/types/common/auth';
|
||||
import type { Scripts } from '@opencollection/types/common/scripts';
|
||||
import type { Variable } from '@opencollection/types/common/variables';
|
||||
import type { Assertion } from '@opencollection/types/common/assertions';
|
||||
import type { Action } from '@opencollection/types/common/actions';
|
||||
import type { HttpRequestParam, HttpRequestBody } from '@opencollection/types/requests/http';
|
||||
import { stringifyYml } from '../utils';
|
||||
import { toOpenCollectionAuth } from '../common/auth';
|
||||
@@ -12,6 +13,7 @@ import { toOpenCollectionHttpHeaders, toOpenCollectionResponseHeaders } from '..
|
||||
import { toOpenCollectionParams } from '../common/params';
|
||||
import { toOpenCollectionBody } from '../common/body';
|
||||
import { toOpenCollectionVariables } from '../common/variables';
|
||||
import { toOpenCollectionActions } from '../common/actions';
|
||||
import { toOpenCollectionScripts } from '../common/scripts';
|
||||
import { toOpenCollectionAssertions } from '../common/assertions';
|
||||
import { isNumber, isNonEmptyString } from '../../../utils';
|
||||
@@ -85,6 +87,14 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
// actions (from post-response variables)
|
||||
const resVars = brunoRequest.vars?.res;
|
||||
const actions: Action[] | undefined = toOpenCollectionActions(resVars);
|
||||
if (actions) {
|
||||
runtime.actions = actions;
|
||||
hasRuntime = true;
|
||||
}
|
||||
|
||||
// auth
|
||||
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
|
||||
if (auth) {
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
"paths": {
|
||||
"@usebruno/schema-types": ["packages/bruno-schema-types/dist/index.d.ts"],
|
||||
"@usebruno/schema-types/*": ["packages/bruno-schema-types/dist/*"],
|
||||
"@opencollection/types": ["packages/bruno-filestore/node_modules/@opencollection/types/dist/opencollection.d.ts"],
|
||||
"@opencollection/types/*": ["packages/bruno-filestore/node_modules/@opencollection/types/dist/*"]
|
||||
"@opencollection/types": ["node_modules/@opencollection/types/dist/opencollection.d.ts"],
|
||||
"@opencollection/types/*": ["node_modules/@opencollection/types/dist/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.d.ts"],
|
||||
|
||||
Reference in New Issue
Block a user