feat: changes to incorporate oc schema updates

This commit is contained in:
Anoop M D
2025-12-07 05:34:54 +05:30
parent 4ffb447c53
commit 8a2cfd1963
22 changed files with 464 additions and 304 deletions

16
package-lock.json generated
View File

@@ -5669,13 +5669,6 @@
"node": ">= 8"
}
},
"node_modules/@opencollection/types": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.1.0.tgz",
"integrity": "sha512-/v64ShE+KyDUAfAlO6Qd5wBwPArd603VC44eife/CdmrtPUSIiFBYcZ9gxAD7LlW99J36wb5IkMpKFDvViINiA==",
"dev": true,
"license": "MIT"
},
"node_modules/@parcel/watcher": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
@@ -31912,7 +31905,7 @@
"devDependencies": {
"@babel/preset-env": "^7.22.0",
"@babel/preset-typescript": "^7.22.0",
"@opencollection/types": "0.1.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",
@@ -31932,6 +31925,13 @@
"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",

View File

@@ -22,7 +22,7 @@
"devDependencies": {
"@babel/preset-env": "^7.22.0",
"@babel/preset-typescript": "^7.22.0",
"@opencollection/types": "0.1.0",
"@opencollection/types": "0.2.0",
"@usebruno/schema-types": "0.0.1",
"@rollup/plugin-commonjs": "^23.0.2",
"@rollup/plugin-json": "^6.1.0",

View File

@@ -163,7 +163,7 @@ const buildClientCredentialsFlow = (oauth: BrunoOAuth2): OAuth2ClientCredentials
const buildResourceOwnerPasswordFlow = (oauth: BrunoOAuth2): OAuth2ResourceOwnerPasswordFlow => {
const flow: OAuth2ResourceOwnerPasswordFlow = {
type: 'oauth2',
flow: 'resource_owner_password'
flow: 'resource_owner_password_credentials'
};
isNonEmptyString(oauth.accessTokenUrl) && (flow.accessTokenUrl = oauth.accessTokenUrl);
@@ -394,7 +394,7 @@ export const toBrunoOAuth2 = (oauth: AuthOAuth2 | null | undefined): BrunoOAuth2
}
break;
case 'resource_owner_password':
case 'resource_owner_password_credentials':
brunoOAuth.grantType = 'password';
if (oauth.accessTokenUrl) brunoOAuth.accessTokenUrl = oauth.accessTokenUrl;
if (oauth.refreshTokenUrl) brunoOAuth.refreshTokenUrl = oauth.refreshTokenUrl;

View File

@@ -1,15 +1,15 @@
import type { FolderRequest as BrunoFolderRequest } from '@usebruno/schema-types/collection/folder';
import type { KeyValue as BrunoKeyValue } from '@usebruno/schema-types/common/key-value';
import type { HttpHeader } from '@opencollection/types/requests/http';
import type { HttpRequestHeader, HttpResponseHeader } from '@opencollection/types/requests/http';
import { uuid } from '../../../utils';
export const toOpenCollectionHttpHeaders = (headers: BrunoFolderRequest['headers']): HttpHeader[] | undefined => {
export const toOpenCollectionHttpHeaders = (headers: BrunoFolderRequest['headers']): HttpRequestHeader[] | undefined => {
if (!headers?.length) {
return undefined;
}
const ocHeaders = headers.map((header: BrunoKeyValue): HttpHeader => {
const httpHeader: HttpHeader = {
const ocHeaders = headers.map((header: BrunoKeyValue): HttpRequestHeader => {
const httpHeader: HttpRequestHeader = {
name: header.name || '',
value: header.value || ''
};
@@ -25,17 +25,30 @@ export const toOpenCollectionHttpHeaders = (headers: BrunoFolderRequest['headers
return ocHeaders.length ? ocHeaders : undefined;
};
export const toBrunoHttpHeaders = (headers: HttpHeader[] | null | undefined): BrunoKeyValue[] | undefined => {
export const toOpenCollectionResponseHeaders = (headers: BrunoFolderRequest['headers']): HttpResponseHeader[] | undefined => {
if (!headers?.length) {
return undefined;
}
const brunoHeaders = headers.map((header: HttpHeader): BrunoKeyValue => {
const ocHeaders = headers.map((header: BrunoKeyValue): HttpResponseHeader => ({
name: header.name || '',
value: header.value || ''
}));
return ocHeaders.length ? ocHeaders : undefined;
};
export const toBrunoHttpHeaders = (headers: HttpRequestHeader[] | HttpResponseHeader[] | null | undefined): BrunoKeyValue[] | undefined => {
if (!headers?.length) {
return undefined;
}
const brunoHeaders = headers.map((header): BrunoKeyValue => {
const brunoHeader: BrunoKeyValue = {
uid: uuid(),
name: header.name || '',
value: header.value || '',
enabled: header.disabled !== true
enabled: ('disabled' in header) ? header.disabled !== true : true
};
return brunoHeader;

View File

@@ -1,30 +1,39 @@
import { Scripts } from '@opencollection/types/common/scripts';
import { FolderRequest as BrunoFolderRequest } from '@usebruno/schema-types/collection/folder';
import { HttpRequest as BrunoHttpRequest } from '@usebruno/schema-types/requests/http';
import { WebSocketRequest as BrunoWebSocketRequest } from '@usebruno/schema-types/requests/websocket';
import { GrpcRequest as BrunoGrpcRequest } from '@usebruno/schema-types/requests/grpc';
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 = {};
const ocScripts: Scripts = [];
if (request?.script?.req?.trim().length) {
ocScripts.preRequest = request.script.req.trim();
ocScripts.push({
type: 'before-request',
code: request.script.req.trim()
});
}
if (request?.script?.res?.trim().length) {
ocScripts.postResponse = request.script.res.trim();
ocScripts.push({
type: 'after-response',
code: request.script.res.trim()
});
}
if (request?.tests?.trim().length) {
ocScripts.tests = request.tests.trim();
ocScripts.push({
type: 'tests',
code: request.tests.trim()
});
}
return Object.keys(ocScripts).length > 0 ? ocScripts : undefined;
return ocScripts.length > 0 ? ocScripts : undefined;
};
export const toBrunoScripts = (scripts: Scripts | null | undefined): {
script?: { req?: string; res?: string };
tests?: string;
} | undefined => {
if (!scripts) {
if (!scripts || !Array.isArray(scripts) || scripts.length === 0) {
return undefined;
}
@@ -33,18 +42,22 @@ export const toBrunoScripts = (scripts: Scripts | null | undefined): {
tests?: string;
} = {};
if (scripts.preRequest || scripts.postResponse) {
brunoScripts.script = {};
if (scripts.preRequest) {
brunoScripts.script.req = scripts.preRequest;
for (const script of scripts) {
if (script.type === 'before-request' && script.code) {
if (!brunoScripts.script) {
brunoScripts.script = {};
}
brunoScripts.script.req = script.code;
}
if (scripts.postResponse) {
brunoScripts.script.res = scripts.postResponse;
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;
}
}
if (scripts.tests) {
brunoScripts.tests = scripts.tests;
}
return Object.keys(brunoScripts).length > 0 ? brunoScripts : undefined;

View File

@@ -10,12 +10,16 @@ import { toBrunoAssertions } from '../common/assertions';
import { uuid } from '../../../utils';
const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
const info = ocRequest.info;
const graphql = ocRequest.graphql;
const runtime = ocRequest.runtime;
const brunoRequest: BrunoHttpRequest = {
url: ocRequest.url || '',
method: ocRequest.method || 'POST',
headers: toBrunoHttpHeaders(ocRequest.headers) || [],
params: toBrunoParams(ocRequest.params) || [],
auth: toBrunoAuth(ocRequest.auth),
url: graphql?.url || '',
method: graphql?.method || 'POST',
headers: toBrunoHttpHeaders(graphql?.headers) || [],
params: toBrunoParams(graphql?.params) || [],
auth: toBrunoAuth(runtime?.auth),
body: {
mode: 'graphql',
json: null,
@@ -25,8 +29,8 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
formUrlEncoded: [],
multipartForm: [],
graphql: {
query: (ocRequest.body as GraphQLBody)?.query || null,
variables: (ocRequest.body as GraphQLBody)?.variables || null
query: (graphql?.body as GraphQLBody)?.query || null,
variables: (graphql?.body as GraphQLBody)?.variables || null
},
file: []
},
@@ -44,7 +48,7 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
};
// scripts
const scripts = toBrunoScripts(ocRequest.scripts);
const scripts = toBrunoScripts(runtime?.scripts);
if (scripts?.script && brunoRequest.script) {
if (scripts.script.req) {
brunoRequest.script.req = scripts.script.req;
@@ -58,11 +62,11 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
}
// variables
const variables = toBrunoVariables(ocRequest.variables);
const variables = toBrunoVariables(runtime?.variables);
brunoRequest.vars = variables;
// assertions
const assertions = toBrunoAssertions(ocRequest.assertions);
const assertions = toBrunoAssertions(runtime?.assertions);
if (assertions) {
brunoRequest.assertions = assertions;
}
@@ -76,9 +80,9 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
const brunoItem: BrunoItem = {
uid: uuid(),
type: 'graphql-request',
seq: ocRequest.seq || 1,
name: ocRequest.name || 'Untitled Request',
tags: ocRequest.tags || [],
seq: info?.seq || 1,
name: info?.name || 'Untitled Request',
tags: info?.tags || [],
request: brunoRequest,
settings: null,
fileContent: null,

View File

@@ -28,13 +28,17 @@ const toBrunoGrpcMetadata = (metadata: GrpcMetadata[] | null | undefined): Bruno
};
const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
const info = ocRequest.info;
const grpc = ocRequest.grpc;
const runtime = ocRequest.runtime;
const brunoRequest: BrunoGrpcRequest = {
url: ocRequest.url || '',
method: ocRequest.method || '',
methodType: ocRequest.methodType || '',
protoPath: ocRequest.protoFilePath || null,
headers: toBrunoGrpcMetadata(ocRequest.metadata) || [],
auth: toBrunoAuth(ocRequest.auth),
url: grpc?.url || '',
method: grpc?.method || '',
methodType: grpc?.methodType || '',
protoPath: grpc?.protoFilePath || null,
headers: toBrunoGrpcMetadata(grpc?.metadata) || [],
auth: toBrunoAuth(runtime?.auth),
body: {
mode: 'grpc',
grpc: []
@@ -53,15 +57,15 @@ const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
};
// message
if (isNonEmptyString(ocRequest.message)) {
if (isNonEmptyString(grpc?.message)) {
brunoRequest.body.grpc = [{
name: '',
content: ocRequest.message
content: grpc?.message as string
}];
}
// scripts
const scripts = toBrunoScripts(ocRequest.scripts);
const scripts = toBrunoScripts(runtime?.scripts);
if (scripts?.script && brunoRequest.script) {
if (scripts.script.req) {
brunoRequest.script.req = scripts.script.req;
@@ -75,11 +79,11 @@ const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
}
// variables
const variables = toBrunoVariables(ocRequest.variables);
const variables = toBrunoVariables(runtime?.variables);
brunoRequest.vars = variables;
// assertions
const assertions = toBrunoAssertions(ocRequest.assertions);
const assertions = toBrunoAssertions(runtime?.assertions);
if (assertions) {
brunoRequest.assertions = assertions;
}
@@ -93,9 +97,9 @@ const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
const brunoItem: BrunoItem = {
uid: uuid(),
type: 'grpc-request',
seq: ocRequest.seq || 1,
name: ocRequest.name || 'Untitled Request',
tags: ocRequest.tags || [],
seq: info?.seq || 1,
name: info?.name || 'Untitled Request',
tags: info?.tags || [],
request: brunoRequest,
settings: {},
fileContent: null,

View File

@@ -11,13 +11,17 @@ import { toBrunoAssertions } from '../common/assertions';
import { uuid } from '../../../utils';
const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
const info = ocRequest.info;
const http = ocRequest.http;
const runtime = ocRequest.runtime;
const brunoRequest: BrunoHttpRequest = {
url: ocRequest.url || '',
method: ocRequest.method || 'GET',
headers: toBrunoHttpHeaders(ocRequest.headers) || [],
params: toBrunoParams(ocRequest.params) || [],
auth: toBrunoAuth(ocRequest.auth),
body: toBrunoBody(ocRequest.body as HttpRequestBody) || {
url: http?.url || '',
method: http?.method || 'GET',
headers: toBrunoHttpHeaders(http?.headers) || [],
params: toBrunoParams(http?.params) || [],
auth: toBrunoAuth(runtime?.auth),
body: toBrunoBody(http?.body as HttpRequestBody) || {
mode: 'none',
json: null,
text: null,
@@ -42,7 +46,7 @@ const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
};
// scripts
const scripts = toBrunoScripts(ocRequest.scripts);
const scripts = toBrunoScripts(runtime?.scripts);
if (scripts?.script && brunoRequest.script) {
if (scripts.script.req) {
brunoRequest.script.req = scripts.script.req;
@@ -56,11 +60,11 @@ const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
}
// variables
const variables = toBrunoVariables(ocRequest.variables);
const variables = toBrunoVariables(runtime?.variables);
brunoRequest.vars = variables;
// assertions
const assertions = toBrunoAssertions(ocRequest.assertions);
const assertions = toBrunoAssertions(runtime?.assertions);
if (assertions) {
brunoRequest.assertions = assertions;
}
@@ -74,9 +78,9 @@ const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
const brunoItem: BrunoItem = {
uid: uuid(),
type: 'http-request',
seq: ocRequest.seq || 1,
name: ocRequest.name || 'Untitled Request',
tags: ocRequest.tags || [],
seq: info?.seq || 1,
name: info?.name || 'Untitled Request',
tags: info?.tags || [],
request: brunoRequest,
settings: null,
fileContent: null,

View File

@@ -1,8 +1,8 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { Script } from '@opencollection/types/collection/item';
import type { ScriptFile } from '@opencollection/types/collection/item';
import { uuid } from '../../../utils';
const parseScript = (ocScript: Script): BrunoItem => {
const parseScript = (ocScript: ScriptFile): BrunoItem => {
const brunoItem: BrunoItem = {
uid: uuid(),
type: 'js',

View File

@@ -15,10 +15,14 @@ interface WebSocketRequestWithSettings extends WebSocketRequest {
}
const parseWebsocketRequest = (ocRequest: WebSocketRequestWithSettings): BrunoItem => {
const info = ocRequest.info;
const websocket = ocRequest.websocket;
const runtime = ocRequest.runtime;
const brunoRequest: BrunoWebSocketRequest = {
url: ocRequest.url || '',
headers: toBrunoHttpHeaders(ocRequest.headers) || [],
auth: toBrunoAuth(ocRequest.auth),
url: websocket?.url || '',
headers: toBrunoHttpHeaders(websocket?.headers) || [],
auth: toBrunoAuth(runtime?.auth),
body: {
mode: 'ws',
ws: []
@@ -37,8 +41,8 @@ const parseWebsocketRequest = (ocRequest: WebSocketRequestWithSettings): BrunoIt
};
// message
if (ocRequest.message) {
const message = ocRequest.message as WebSocketMessage;
if (websocket?.message) {
const message = websocket.message as WebSocketMessage;
if (message.data?.trim().length) {
brunoRequest.body.ws = [{
name: '',
@@ -49,7 +53,7 @@ const parseWebsocketRequest = (ocRequest: WebSocketRequestWithSettings): BrunoIt
}
// scripts
const scripts = toBrunoScripts(ocRequest.scripts);
const scripts = toBrunoScripts(runtime?.scripts);
if (scripts?.script && brunoRequest.script) {
if (scripts.script.req) {
brunoRequest.script.req = scripts.script.req;
@@ -63,7 +67,7 @@ const parseWebsocketRequest = (ocRequest: WebSocketRequestWithSettings): BrunoIt
}
// variables
const variables = toBrunoVariables(ocRequest.variables);
const variables = toBrunoVariables(runtime?.variables);
brunoRequest.vars = variables;
// docs
@@ -90,9 +94,9 @@ const parseWebsocketRequest = (ocRequest: WebSocketRequestWithSettings): BrunoIt
const brunoItem: BrunoItem = {
uid: uuid(),
type: 'ws-request',
seq: ocRequest.seq || 1,
name: ocRequest.name || 'Untitled Request',
tags: ocRequest.tags || [],
seq: info?.seq || 1,
name: info?.name || 'Untitled Request',
tags: info?.tags || [],
request: brunoRequest,
settings: wsSettings as any,
fileContent: null,

View File

@@ -1,11 +1,11 @@
import type { Item as BrunoItem, HttpItemSettings as BrunoHttpItemSettings } from '@usebruno/schema-types/collection/item';
import type { HttpRequest as BrunoHttpRequest } from '@usebruno/schema-types/requests/http';
import type { GraphQLRequest, GraphQLRequestSettings, GraphQLBody } from '@opencollection/types/requests/graphql';
import type { GraphQLRequest, GraphQLRequestSettings, GraphQLBody, GraphQLRequestInfo, GraphQLRequestDetails, GraphQLRequestRuntime } from '@opencollection/types/requests/graphql';
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 { HttpRequestParam, HttpHeader } from '@opencollection/types/requests/http';
import type { HttpRequestParam, HttpRequestHeader } from '@opencollection/types/requests/http';
import { stringifyYml } from '../utils';
import { isNonEmptyString, isNumber } from '../../../utils';
import { toOpenCollectionAuth } from '../common/auth';
@@ -17,32 +17,38 @@ import { toOpenCollectionAssertions } from '../common/assertions';
const stringifyGraphQLRequest = (item: BrunoItem): string => {
try {
const ocRequest: GraphQLRequest = {
const ocRequest: GraphQLRequest = {};
const brunoRequest = item.request as BrunoHttpRequest;
// info block
const info: GraphQLRequestInfo = {
name: isNonEmptyString(item.name) ? item.name : 'Untitled Request',
type: 'graphql'
};
ocRequest.name = isNonEmptyString(item.name) ? item.name : 'Untitled Request';
// sequence
if (item.seq) {
ocRequest.seq = item.seq;
info.seq = item.seq;
}
if (item.tags?.length) {
info.tags = item.tags;
}
ocRequest.info = info;
const brunoRequest = item.request as BrunoHttpRequest;
// url and method
ocRequest.url = isNonEmptyString(brunoRequest.url) ? brunoRequest.url : '';
ocRequest.method = isNonEmptyString(brunoRequest.method) ? brunoRequest.method : 'POST';
// graphql block
const graphql: GraphQLRequestDetails = {
method: isNonEmptyString(brunoRequest.method) ? brunoRequest.method : 'POST',
url: isNonEmptyString(brunoRequest.url) ? brunoRequest.url : ''
};
// headers
const headers: HttpHeader[] | undefined = toOpenCollectionHttpHeaders(brunoRequest.headers);
const headers: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(brunoRequest.headers);
if (headers) {
ocRequest.headers = headers;
graphql.headers = headers;
}
// params
const params: HttpRequestParam[] | undefined = toOpenCollectionParams(brunoRequest.params);
if (params) {
ocRequest.params = params;
graphql.params = params;
}
// body
@@ -61,83 +67,86 @@ const stringifyGraphQLRequest = (item: BrunoItem): string => {
}
if (hasBody) {
ocRequest.body = graphqlBody;
graphql.body = graphqlBody;
}
}
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
ocRequest.auth = auth;
ocRequest.graphql = graphql;
// runtime block
const runtime: GraphQLRequestRuntime = {};
let hasRuntime = false;
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
runtime.variables = variables;
hasRuntime = true;
}
// scripts
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
if (scripts) {
ocRequest.scripts = scripts;
}
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
ocRequest.variables = variables;
runtime.scripts = scripts;
hasRuntime = true;
}
// assertions
const assertions: Assertion[] | undefined = toOpenCollectionAssertions(brunoRequest.assertions);
if (assertions) {
ocRequest.assertions = assertions;
runtime.assertions = assertions;
hasRuntime = true;
}
// docs
if (isNonEmptyString(brunoRequest.docs)) {
ocRequest.docs = brunoRequest.docs;
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
runtime.auth = auth;
hasRuntime = true;
}
if (hasRuntime) {
ocRequest.runtime = runtime;
}
// settings
const httpSettings = item.settings as BrunoHttpItemSettings | undefined;
ocRequest.settings = {} as GraphQLRequestSettings;
const settings: GraphQLRequestSettings = {};
if (httpSettings?.encodeUrl === true) {
ocRequest.settings.encodeUrl = true;
settings.encodeUrl = true;
} else if (httpSettings?.encodeUrl === false) {
ocRequest.settings.encodeUrl = false;
settings.encodeUrl = false;
} else {
// todo: we are defaulting to true for now as bruno config does not yet support inherit for encodeUrl
// update this when bruno config supports inherit for encodeUrl
ocRequest.settings.encodeUrl = true;
settings.encodeUrl = true;
}
const timeout = httpSettings?.timeout;
if (isNumber(timeout)) {
ocRequest.settings.timeout = timeout;
settings.timeout = timeout;
} else {
// todo: we are defaulting to 0 for now as bruno config does not yet support inherit for timeout
// update this when bruno config supports inherit for timeout
ocRequest.settings.timeout = 0;
settings.timeout = 0;
}
if (httpSettings?.followRedirects === true) {
ocRequest.settings.followRedirects = true;
settings.followRedirects = true;
} else if (httpSettings?.followRedirects === false) {
ocRequest.settings.followRedirects = false;
settings.followRedirects = false;
} else {
// todo: we are defaulting to true for now as bruno config does not yet support inherit for followRedirects
// update this when bruno config supports inherit for followRedirects
ocRequest.settings.followRedirects = true;
settings.followRedirects = true;
}
const maxRedirects = httpSettings?.maxRedirects;
if (isNumber(maxRedirects)) {
ocRequest.settings.maxRedirects = maxRedirects;
settings.maxRedirects = maxRedirects;
} else {
// todo: we are defaulting to 5 for now as bruno config does not yet support inherit for maxRedirects
// update this when bruno config supports inherit for maxRedirects
ocRequest.settings.maxRedirects = 5;
settings.maxRedirects = 5;
}
// tags
if (item.tags?.length) {
ocRequest.tags = item.tags;
ocRequest.settings = settings;
// docs
if (isNonEmptyString(brunoRequest.docs)) {
ocRequest.docs = brunoRequest.docs;
}
return stringifyYml(ocRequest);

View File

@@ -1,7 +1,7 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { KeyValue as BrunoKeyValue } from '@usebruno/schema-types/common/key-value';
import type { GrpcRequest as BrunoGrpcRequest } from '@usebruno/schema-types/requests/grpc';
import type { GrpcRequest, GrpcMetadata, GrpcMessage, GrpcMessageVariant } from '@opencollection/types/requests/grpc';
import type { GrpcRequest, GrpcMetadata, GrpcMessage, GrpcRequestInfo, GrpcRequestDetails, GrpcRequestRuntime } from '@opencollection/types/requests/grpc';
import type { Auth } from '@opencollection/types/common/auth';
import type { Scripts } from '@opencollection/types/common/scripts';
import type { Variable } from '@opencollection/types/common/variables';
@@ -15,30 +15,36 @@ import { toOpenCollectionAssertions } from '../common/assertions';
const stringifyGrpcRequest = (item: BrunoItem): string => {
try {
const ocRequest: GrpcRequest = {
const ocRequest: GrpcRequest = {};
const brunoRequest = item.request as BrunoGrpcRequest;
// info block
const info: GrpcRequestInfo = {
name: isNonEmptyString(item.name) ? item.name : 'Untitled Request',
type: 'grpc'
};
ocRequest.name = isNonEmptyString(item.name) ? item.name : 'Untitled Request';
// sequence
if (item.seq) {
ocRequest.seq = item.seq;
info.seq = item.seq;
}
if (item.tags?.length) {
info.tags = item.tags;
}
ocRequest.info = info;
const brunoRequest = item.request as BrunoGrpcRequest;
// url and method
ocRequest.url = isNonEmptyString(brunoRequest.url) ? brunoRequest.url : '';
ocRequest.method = isNonEmptyString(brunoRequest.method) ? brunoRequest.method : '';
// grpc block
const grpc: GrpcRequestDetails = {
url: isNonEmptyString(brunoRequest.url) ? brunoRequest.url : '',
method: isNonEmptyString(brunoRequest.method) ? brunoRequest.method : ''
};
// method type
if (brunoRequest.methodType) {
ocRequest.methodType = brunoRequest.methodType;
grpc.methodType = brunoRequest.methodType;
}
// proto file path
if (isNonEmptyString(brunoRequest.protoPath)) {
ocRequest.protoFilePath = brunoRequest.protoPath;
grpc.protoFilePath = brunoRequest.protoPath;
}
// metadata
@@ -61,7 +67,7 @@ const stringifyGrpcRequest = (item: BrunoItem): string => {
});
if (metadata.length) {
ocRequest.metadata = metadata;
grpc.metadata = metadata;
}
}
@@ -74,33 +80,47 @@ const stringifyGrpcRequest = (item: BrunoItem): string => {
if (messages.length) {
const message: GrpcMessage = messages[0].content || '';
if (message.trim().length) {
ocRequest.message = message;
grpc.message = message;
}
}
}
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
ocRequest.auth = auth;
ocRequest.grpc = grpc;
// runtime block
const runtime: GrpcRequestRuntime = {};
let hasRuntime = false;
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
runtime.variables = variables;
hasRuntime = true;
}
// scripts
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
if (scripts) {
ocRequest.scripts = scripts;
}
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
ocRequest.variables = variables;
runtime.scripts = scripts;
hasRuntime = true;
}
// assertions
const assertions: Assertion[] | undefined = toOpenCollectionAssertions(brunoRequest.assertions);
if (assertions) {
ocRequest.assertions = assertions;
runtime.assertions = assertions;
hasRuntime = true;
}
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
runtime.auth = auth;
hasRuntime = true;
}
if (hasRuntime) {
ocRequest.runtime = runtime;
}
// docs
@@ -108,11 +128,6 @@ const stringifyGrpcRequest = (item: BrunoItem): string => {
ocRequest.docs = brunoRequest.docs;
}
// tags
if (item.tags?.length) {
ocRequest.tags = item.tags;
}
return stringifyYml(ocRequest);
} catch (error) {
console.error('Error stringifying gRPC request:', error);

View File

@@ -1,14 +1,14 @@
import type { Item as BrunoItem, HttpItemSettings as BrunoHttpItemSettings } from '@usebruno/schema-types/collection/item';
import type { HttpRequest as BrunoHttpRequest } from '@usebruno/schema-types/requests/http';
import type { HttpRequest, HttpRequestSettings, HttpRequestExample } from '@opencollection/types/requests/http';
import type { HttpRequest, HttpRequestSettings, HttpRequestExample, HttpRequestInfo, HttpRequestDetails, HttpRequestRuntime, HttpRequestHeader } from '@opencollection/types/requests/http';
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 { HttpRequestParam, HttpHeader, HttpRequestBody } from '@opencollection/types/requests/http';
import type { HttpRequestParam, HttpRequestBody } from '@opencollection/types/requests/http';
import { stringifyYml } from '../utils';
import { toOpenCollectionAuth } from '../common/auth';
import { toOpenCollectionHttpHeaders } from '../common/headers';
import { toOpenCollectionHttpHeaders, toOpenCollectionResponseHeaders } from '../common/headers';
import { toOpenCollectionParams } from '../common/params';
import { toOpenCollectionBody } from '../common/body';
import { toOpenCollectionVariables } from '../common/variables';
@@ -18,114 +18,118 @@ import { isNumber, isNonEmptyString } from '../../../utils';
const stringifyHttpRequest = (item: BrunoItem): string => {
try {
const ocRequest: HttpRequest = {
const ocRequest: HttpRequest = {};
const brunoRequest = item.request as BrunoHttpRequest;
// info block
const info: HttpRequestInfo = {
name: isNonEmptyString(item.name) ? item.name : 'Untitled Request',
type: 'http'
};
ocRequest.name = isNonEmptyString(item.name) ? item.name : 'Untitled Request';
// sequence
if (item.seq) {
ocRequest.seq = item.seq;
info.seq = item.seq;
}
if (item.tags?.length) {
info.tags = item.tags;
}
ocRequest.info = info;
const brunoRequest = item.request as BrunoHttpRequest;
// url and method
ocRequest.url = isNonEmptyString(brunoRequest.url) ? brunoRequest.url : '';
ocRequest.method = isNonEmptyString(brunoRequest.method) ? brunoRequest.method : 'GET';
// http block
const http: HttpRequestDetails = {
method: isNonEmptyString(brunoRequest.method) ? brunoRequest.method : 'GET',
url: isNonEmptyString(brunoRequest.url) ? brunoRequest.url : ''
};
// headers
const headers: HttpHeader[] | undefined = toOpenCollectionHttpHeaders(brunoRequest.headers);
const headers: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(brunoRequest.headers);
if (headers) {
ocRequest.headers = headers;
http.headers = headers;
}
// params
const params: HttpRequestParam[] | undefined = toOpenCollectionParams(brunoRequest.params);
if (params) {
ocRequest.params = params;
http.params = params;
}
// body
const body: HttpRequestBody | undefined = toOpenCollectionBody(brunoRequest.body);
if (body) {
ocRequest.body = body;
http.body = body;
}
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
ocRequest.auth = auth;
ocRequest.http = http;
// runtime block
const runtime: HttpRequestRuntime = {};
let hasRuntime = false;
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
runtime.variables = variables;
hasRuntime = true;
}
// scripts
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
if (scripts) {
ocRequest.scripts = scripts;
}
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
ocRequest.variables = variables;
runtime.scripts = scripts;
hasRuntime = true;
}
// assertions
const assertions: Assertion[] | undefined = toOpenCollectionAssertions(brunoRequest.assertions);
if (assertions) {
ocRequest.assertions = assertions;
runtime.assertions = assertions;
hasRuntime = true;
}
// docs
if (isNonEmptyString(brunoRequest.docs)) {
ocRequest.docs = brunoRequest.docs;
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
runtime.auth = auth;
hasRuntime = true;
}
if (hasRuntime) {
ocRequest.runtime = runtime;
}
// settings
const httpSettings = item.settings as BrunoHttpItemSettings | undefined;
ocRequest.settings = {} as HttpRequestSettings;
const settings: HttpRequestSettings = {};
if (httpSettings?.encodeUrl === true) {
ocRequest.settings.encodeUrl = true;
settings.encodeUrl = true;
} else if (httpSettings?.encodeUrl === false) {
ocRequest.settings.encodeUrl = false;
settings.encodeUrl = false;
} else {
// todo: we are defaulting to true for now as bruno config does not yet support inherit for encodeUrl
// update this when bruno config supports inherit for encodeUrl
ocRequest.settings.encodeUrl = true;
settings.encodeUrl = true;
}
const timeout = httpSettings?.timeout;
if (isNumber(timeout)) {
ocRequest.settings.timeout = timeout;
settings.timeout = timeout;
} else {
// todo: we are defaulting to 0 for now as bruno config does not yet support inherit for timeout
// update this when bruno config supports inherit for timeout
ocRequest.settings.timeout = 0;
settings.timeout = 0;
}
if (httpSettings?.followRedirects === true) {
ocRequest.settings.followRedirects = true;
settings.followRedirects = true;
} else if (httpSettings?.followRedirects === false) {
ocRequest.settings.followRedirects = false;
settings.followRedirects = false;
} else {
// todo: we are defaulting to true for now as bruno config does not yet support inherit for followRedirects
// update this when bruno config supports inherit for followRedirects
ocRequest.settings.followRedirects = true;
settings.followRedirects = true;
}
const maxRedirects = httpSettings?.maxRedirects;
if (isNumber(maxRedirects)) {
ocRequest.settings.maxRedirects = maxRedirects;
settings.maxRedirects = maxRedirects;
} else {
// todo: we are defaulting to 5 for now as bruno config does not yet support inherit for maxRedirects
// update this when bruno config supports inherit for maxRedirects
ocRequest.settings.maxRedirects = 5;
settings.maxRedirects = 5;
}
// tags
if (item.tags?.length) {
ocRequest.tags = item.tags;
}
ocRequest.settings = settings;
// examples
if (item.examples?.length) {
@@ -169,7 +173,7 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
ocExample.response.statusText = example.response.statusText;
}
const responseHeaders = toOpenCollectionHttpHeaders(example.response.headers);
const responseHeaders = toOpenCollectionResponseHeaders(example.response.headers);
if (responseHeaders) {
ocExample.response.headers = responseHeaders;
}
@@ -185,12 +189,16 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
return ocExample;
});
// examples
if (examples?.length) {
ocRequest.examples = examples;
}
}
// docs
if (isNonEmptyString(brunoRequest.docs)) {
ocRequest.docs = brunoRequest.docs;
}
return stringifyYml(ocRequest);
} catch (error) {
console.error('Error stringifying HTTP request:', error);

View File

@@ -1,10 +1,10 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { Script } from '@opencollection/types/collection/item';
import type { ScriptFile } from '@opencollection/types/collection/item';
import { stringifyYml } from '../utils';
const stringifyScript = (item: BrunoItem): string => {
try {
const ocScript: Script = {
const ocScript: ScriptFile = {
type: 'script'
};

View File

@@ -1,10 +1,10 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { WebSocketRequest as BrunoWebSocketRequest } from '@usebruno/schema-types/requests/websocket';
import type { WebSocketRequest, WebSocketMessage, WebSocketMessageVariant } from '@opencollection/types/requests/websocket';
import type { WebSocketRequest, WebSocketMessage, WebSocketRequestInfo, WebSocketRequestDetails, WebSocketRequestRuntime } from '@opencollection/types/requests/websocket';
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 { HttpHeader } from '@opencollection/types/requests/http';
import type { HttpRequestHeader } from '@opencollection/types/requests/http';
import { stringifyYml } from '../utils';
import { isNonEmptyString } from '../../../utils';
import { toOpenCollectionAuth } from '../common/auth';
@@ -21,25 +21,31 @@ interface WebSocketRequestWithSettings extends WebSocketRequest {
const stringifyWebsocketRequest = (item: BrunoItem): string => {
try {
const ocRequest: WebSocketRequestWithSettings = {
const ocRequest: WebSocketRequestWithSettings = {};
const brunoRequest = item.request as BrunoWebSocketRequest;
// info block
const info: WebSocketRequestInfo = {
name: isNonEmptyString(item.name) ? item.name : 'Untitled Request',
type: 'websocket'
};
ocRequest.name = isNonEmptyString(item.name) ? item.name : 'Untitled Request';
// sequence
if (item.seq) {
ocRequest.seq = item.seq;
info.seq = item.seq;
}
if (item.tags?.length) {
info.tags = item.tags;
}
ocRequest.info = info;
const brunoRequest = item.request as BrunoWebSocketRequest;
// url
ocRequest.url = isNonEmptyString(brunoRequest.url) ? brunoRequest.url : '';
// websocket block
const websocket: WebSocketRequestDetails = {
url: isNonEmptyString(brunoRequest.url) ? brunoRequest.url : ''
};
// headers
const headers: HttpHeader[] | undefined = toOpenCollectionHttpHeaders(brunoRequest.headers);
const headers: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(brunoRequest.headers);
if (headers) {
ocRequest.headers = headers;
websocket.headers = headers;
}
// message
@@ -55,37 +61,40 @@ const stringifyWebsocketRequest = (item: BrunoItem): string => {
data: msg.content || ''
};
if (message.data.trim().length) {
ocRequest.message = message;
websocket.message = message;
}
}
}
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
ocRequest.auth = auth;
ocRequest.websocket = websocket;
// runtime block
const runtime: WebSocketRequestRuntime = {};
let hasRuntime = false;
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
runtime.variables = variables;
hasRuntime = true;
}
// scripts
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
if (scripts) {
ocRequest.scripts = scripts;
runtime.scripts = scripts;
hasRuntime = true;
}
// variables
const variables: Variable[] | undefined = toOpenCollectionVariables(brunoRequest.vars);
if (variables) {
ocRequest.variables = variables;
// auth
const auth: Auth | undefined = toOpenCollectionAuth(brunoRequest.auth);
if (auth) {
runtime.auth = auth;
hasRuntime = true;
}
// docs
if (isNonEmptyString(brunoRequest.docs)) {
ocRequest.docs = brunoRequest.docs;
}
// tags
if (item.tags?.length) {
ocRequest.tags = item.tags;
if (hasRuntime) {
ocRequest.runtime = runtime;
}
// settings
@@ -98,6 +107,11 @@ const stringifyWebsocketRequest = (item: BrunoItem): string => {
ocRequest.settings.keepAliveInterval = !isNaN(keepAliveInterval) ? keepAliveInterval : 0;
}
// docs
if (isNonEmptyString(brunoRequest.docs)) {
ocRequest.docs = brunoRequest.docs;
}
return stringifyYml(ocRequest);
} catch (error) {
console.error('Error stringifying WebSocket request:', error);

View File

@@ -1,22 +1,36 @@
import type { Environment as BrunoEnvironment, EnvironmentVariable as BrunoEnvironmentVariable } from '@usebruno/schema-types/collection/environment';
import type { Environment } from '@opencollection/types/config/environments';
import type { Variable } from '@opencollection/types/common/variables';
import type { Variable, SecretVariable } from '@opencollection/types/common/variables';
import { parseYml } from './utils';
import { uuid } from '../../utils';
const toBrunoEnvironmentVariables = (variables: Variable[] | null | undefined): BrunoEnvironmentVariable[] => {
const isSecretVariable = (v: Variable | SecretVariable): v is SecretVariable => {
return 'secret' in v && v.secret === true;
};
const toBrunoEnvironmentVariables = (variables: (Variable | SecretVariable)[] | null | undefined): BrunoEnvironmentVariable[] => {
if (!variables?.length) {
return [];
}
return variables.map((v: Variable): BrunoEnvironmentVariable => {
return variables.map((v): BrunoEnvironmentVariable => {
if (isSecretVariable(v)) {
return {
uid: uuid(),
name: v.name || '',
value: '',
type: 'text',
enabled: v.disabled !== true,
secret: true
};
}
const variable: BrunoEnvironmentVariable = {
uid: uuid(),
name: v.name || '',
value: v.value as string || '',
value: (typeof v.value === 'string' ? v.value : '') || '',
type: 'text',
enabled: v.disabled !== true,
secret: v.transient === true
secret: false
};
return variable;
});

View File

@@ -11,10 +11,12 @@ const parseFolder = (ymlString: string): FolderRoot => {
try {
const ocFolder: Folder = parseYml(ymlString);
const info = ocFolder.info;
const folderRoot: FolderRoot = {
meta: {
name: ocFolder.name || 'Untitled Folder',
seq: ocFolder.seq || 1
name: info?.name || 'Untitled Folder',
seq: info?.seq || 1
},
request: null,
docs: null
@@ -67,9 +69,13 @@ const parseFolder = (ymlString: string): FolderRoot => {
}
}
// docs
if (isNonEmptyString(ocFolder.docs)) {
folderRoot.docs = ocFolder.docs;
// docs (now at root level)
if (ocFolder.docs) {
if (typeof ocFolder.docs === 'string' && ocFolder.docs.trim().length) {
folderRoot.docs = ocFolder.docs;
} else if (typeof ocFolder.docs === 'object' && ocFolder.docs.content?.trim().length) {
folderRoot.docs = ocFolder.docs.content;
}
}
return folderRoot;

View File

@@ -1,5 +1,9 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { Item } from '@opencollection/types/collection/item';
import type { Item, ScriptFile } from '@opencollection/types/collection/item';
import type { HttpRequest } from '@opencollection/types/requests/http';
import type { GraphQLRequest } from '@opencollection/types/requests/graphql';
import type { GrpcRequest } from '@opencollection/types/requests/grpc';
import type { WebSocketRequest } from '@opencollection/types/requests/websocket';
import { parseYml } from './utils';
import parseHttpRequest from './items/parseHttpRequest';
import parseGraphQLRequest from './items/parseGraphQLRequest';
@@ -7,35 +11,48 @@ import parseGrpcRequest from './items/parseGrpcRequest';
import parseWebsocketRequest from './items/parseWebsocketRequest';
import parseScript from './items/parseScript';
// Helper to get the type from an item (now in info block)
const getItemType = (item: Item): string | undefined => {
if ('info' in item && item.info && 'type' in item.info) {
return item.info.type;
}
// For ScriptFile which still has type at root
if ('type' in item) {
return (item as ScriptFile).type;
}
return undefined;
};
const parseItem = (ymlString: string): BrunoItem => {
try {
const ocItem: Item = parseYml(ymlString);
const itemType = getItemType(ocItem);
if (!ocItem || !ocItem.type) {
if (!ocItem || !itemType) {
throw new Error('Invalid item: missing type');
}
switch (ocItem.type) {
switch (itemType) {
case 'http':
return parseHttpRequest(ocItem);
return parseHttpRequest(ocItem as HttpRequest);
case 'graphql':
return parseGraphQLRequest(ocItem);
return parseGraphQLRequest(ocItem as GraphQLRequest);
case 'grpc':
return parseGrpcRequest(ocItem);
return parseGrpcRequest(ocItem as GrpcRequest);
case 'websocket':
return parseWebsocketRequest(ocItem);
return parseWebsocketRequest(ocItem as WebSocketRequest);
case 'script':
return parseScript(ocItem);
return parseScript(ocItem as ScriptFile);
case 'folder':
throw new Error('Folder items should be handled separately using parseFolder');
default:
throw new Error(`Unsupported item type: ${(ocItem as any).type}`);
throw new Error(`Unsupported item type: ${itemType}`);
}
} catch (error) {
console.error('Error parsing item:', error);

View File

@@ -1,6 +1,6 @@
import type { OpenCollection } from '@opencollection/types';
import type { ProtoFileItem, ProtoFileImportPath } from '@opencollection/types/config/protobuf';
import type { HttpHeader } from '@opencollection/types/requests/http';
import type { HttpRequestHeader } from '@opencollection/types/requests/http';
import type { ClientCertificate, PemCertificate, Pkcs12Certificate } from '@opencollection/types/config/certificates';
import type { Variable } from '@opencollection/types/common/variables';
import type { Scripts } from '@opencollection/types/common/scripts';
@@ -128,7 +128,7 @@ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
// headers
if (collectionRoot.request?.headers?.length) {
const ocHeaders: HttpHeader[] | undefined = toOpenCollectionHttpHeaders(collectionRoot.request?.headers);
const ocHeaders: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(collectionRoot.request?.headers);
if (ocHeaders) {
oc.request.headers = ocHeaders;
}

View File

@@ -1,20 +1,31 @@
import type { Environment as BrunoEnvironment, EnvironmentVariable as BrunoEnvironmentVariable } from '@usebruno/schema-types/collection/environment';
import type { Environment } from '@opencollection/types/config/environments';
import type { Variable } from '@opencollection/types/common/variables';
import type { Variable, SecretVariable } from '@opencollection/types/common/variables';
import { stringifyYml } from './utils';
const toOpenCollectionEnvironmentVariables = (variables: BrunoEnvironmentVariable[]): Variable[] | undefined => {
const toOpenCollectionEnvironmentVariables = (variables: BrunoEnvironmentVariable[]): (Variable | SecretVariable)[] | undefined => {
if (!variables?.length) {
return undefined;
}
const ocVariables: Variable[] = variables
const ocVariables: (Variable | SecretVariable)[] = variables
.filter((v: BrunoEnvironmentVariable) => {
// todo: currently neithwe bru lang nor bruno app supports non-string values
// todo: currently neither bru lang nor bruno app supports non-string values
// update this when bruno app supports non-string values
return typeof v.value === 'string';
})
.map((v: BrunoEnvironmentVariable): Variable => {
.map((v: BrunoEnvironmentVariable): Variable | SecretVariable => {
if (v.secret === true) {
const secretVar: SecretVariable = {
secret: true,
name: v.name || ''
};
if (v.enabled === false) {
secretVar.disabled = true;
}
return secretVar;
}
const variable: Variable = {
name: v.name || '',
value: v.value as string
@@ -24,10 +35,6 @@ const toOpenCollectionEnvironmentVariables = (variables: BrunoEnvironmentVariabl
variable.disabled = true;
}
if (v.secret === true) {
variable.transient = true;
}
return variable;
});

View File

@@ -1,8 +1,9 @@
import type { FolderRoot } from '@usebruno/schema-types/collection/folder';
import type { Folder } from '@opencollection/types/collection/item';
import type { Folder, FolderInfo } from '@opencollection/types/collection/item';
import type { Variable } from '@opencollection/types/common/variables';
import type { Scripts } from '@opencollection/types/common/scripts';
import type { Auth, HttpHeader } from '@opencollection/types/requests/http';
import type { Auth } from '@opencollection/types/common/auth';
import type { HttpRequestHeader } from '@opencollection/types/requests/http';
import type { RequestDefaults } from '@opencollection/types/common/request-defaults';
import { toOpenCollectionAuth } from './common/auth';
import { toOpenCollectionHttpHeaders } from './common/headers';
@@ -31,12 +32,15 @@ const hasRequestScripts = (folderRoot: FolderRoot): boolean => {
const stringifyFolder = (folderRoot: FolderRoot): string => {
try {
const ocFolder: Folder = {
type: 'folder'
};
const ocFolder: Folder = {};
ocFolder.name = folderRoot.meta?.name || 'Untitled Folder';
ocFolder.seq = folderRoot.meta?.seq || 1;
// info block
const info: FolderInfo = {
name: folderRoot.meta?.name || 'Untitled Folder',
type: 'folder',
seq: folderRoot.meta?.seq || 1
};
ocFolder.info = info;
// request defaults
if (hasRequestDefaults(folderRoot)) {
@@ -44,7 +48,7 @@ const stringifyFolder = (folderRoot: FolderRoot): string => {
// headers
if (folderRoot.request?.headers?.length) {
const ocHeaders: HttpHeader[] | undefined = toOpenCollectionHttpHeaders(folderRoot.request?.headers);
const ocHeaders: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(folderRoot.request?.headers);
if (ocHeaders) {
ocFolder.request.headers = ocHeaders;
}

View File

@@ -1,12 +1,36 @@
import * as YAML from 'yaml';
// Top-level keys that should have a blank line before them
const BLOCK_KEYS = ['info', 'http', 'graphql', 'grpc', 'websocket', 'runtime', 'settings', 'examples', 'docs', 'items', 'request'];
export const stringifyYml = (obj: any): string => {
return YAML.stringify(obj, {
const yamlStr = YAML.stringify(obj, {
lineWidth: 0,
indent: 2,
minContentWidth: 0,
defaultStringType: 'PLAIN'
});
// Add blank lines before major blocks (only at the top level, not indented)
const lines = yamlStr.split('\n');
const result: string[] = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Check if this is a top-level key (no leading whitespace) that should have a blank line
if (i > 0 && !line.startsWith(' ') && !line.startsWith('\t')) {
const key = line.split(':')[0];
if (BLOCK_KEYS.includes(key)) {
// Add blank line before this block (if previous line isn't already blank)
if (result.length > 0 && result[result.length - 1].trim() !== '') {
result.push('');
}
}
}
result.push(line);
}
return result.join('\n');
};
export const parseYml = (ymlString: string): any => {