feat: yaml lang - parseRequest and stringifyRequest

This commit is contained in:
Anoop M D
2025-02-06 19:41:38 +05:30
parent 4598acd068
commit 0b19b26ce7
11 changed files with 1195 additions and 2 deletions

View File

@@ -0,0 +1,346 @@
const _ = require('lodash');
const getMeta = (json) => {
const sequence = _.get(json, 'seq');
const meta = {
name: _.get(json, 'name')
};
const description = _.get(json, 'description');
if (description) {
meta.description = description;
}
meta.seq = !isNaN(sequence) ? Number(sequence) : 1;
return meta;
};
const getParams = (req) => {
return {
query: _.map(_.filter(req?.params || [], param => param.type === 'query'), (param) => {
const paramObj = {
name: param.name,
value: param.value,
type: param.type
};
if (param.description) {
paramObj.description = param.description;
}
if (param.enabled === false) {
paramObj.disabled = true;
}
return paramObj;
}),
path: _.map(_.filter(req?.params || [], param => param.type === 'path'), (param) => {
const paramObj = {
name: param.name,
value: param.value,
type: param.type
};
if (param.description) {
paramObj.description = param.description;
}
if (param.enabled === false) {
paramObj.disabled = true;
}
return paramObj;
})
}
};
const getHeaders = (req) => {
return _.map(_.get(req, 'headers', []), (header) => {
const headerObj = {
name: header.name,
value: header.value,
};
if (header.description) {
headerObj.description = header.description;
}
if (header.enabled === false) {
headerObj.disabled = true;
}
return headerObj;
});
};
const getBody = (req) => {
const body = _.get(req, 'body', {});
const mode = _.get(body, 'mode', 'none');
if (mode === 'none') {
return null;
}
if (mode === 'graphql') {
return {
type: 'graphql',
query: _.get(body, 'graphql.query', ''),
variables: _.get(body, 'graphql.variables', '')
};
}
if (mode === 'sparql') {
return {
type: 'sparql',
query: _.get(body, 'sparql', '')
};
}
if (mode === 'formUrlEncoded') {
return {
type: 'form-urlencoded',
data: _.map(_.get(body, 'formUrlEncoded', []), (param) => {
const paramObj = {
name: param.name,
value: param.value
};
if (param.description) {
paramObj.description = param.description;
}
if (param.enabled === false) {
paramObj.disabled = true;
}
return paramObj;
})
};
}
if (mode === 'multipartForm') {
return {
type: 'multipart-form',
data: _.map(_.get(body, 'multipartForm', []), (param) => {
const paramObj = {
name: param.name,
value: param.value,
type: param.type
};
if (param.description) {
paramObj.description = param.description;
}
if (param.enabled === false) {
paramObj.disabled = true;
}
if (param.contentType) {
paramObj.content_type = param.contentType;
}
return paramObj;
})
};
}
let data = '';
switch(mode) {
case 'json':
data = _.get(body, 'json', '');
break;
case 'text':
data = _.get(body, 'text', '');
break;
case 'xml':
data = _.get(body, 'xml', '');
break;
}
return {
type: mode,
data
};
};
const getAuth = (req) => {
const auth = {};
const mode = _.get(req, 'auth.mode', 'none');
if (req?.auth?.awsv4) {
auth.awsv4 = {
access_key_id: req?.auth?.awsv4?.accessKeyId,
secret_access_key: req?.auth?.awsv4?.secretAccessKey,
session_token: req?.auth?.awsv4?.sessionToken,
service: req?.auth?.awsv4?.service,
region: req?.auth?.awsv4?.region,
profile_name: req?.auth?.awsv4?.profileName
};
}
if (req?.auth?.basic) {
auth.basic = {
username: req?.auth?.basic?.username,
password: req?.auth?.basic?.password
};
}
if (req?.auth?.bearer) {
auth.bearer = {
token: req?.auth?.bearer?.token
};
}
if (req?.auth?.digest) {
auth.digest = {
username: req?.auth?.digest?.username,
password: req?.auth?.digest?.password
};
}
if (req?.auth?.ntlm) {
auth.ntlm = {
username: req?.auth?.ntlm?.username,
password: req?.auth?.ntlm?.password,
domain: req?.auth?.ntlm?.domain
};
}
if (req?.auth?.oauth2) {
auth.oauth2 = {};
if (req?.auth?.oauth2?.grantType === 'password') {
auth.oauth2 = {
grant_type: 'password',
access_token_url: req?.auth?.oauth2?.accessTokenUrl || '',
username: req?.auth?.oauth2?.username || '',
password: req?.auth?.oauth2?.password || '',
client_id: req?.auth?.oauth2?.clientId || '',
client_secret: req?.auth?.oauth2?.clientSecret || '',
scope: req?.auth?.oauth2?.scope || ''
};
}
if (req?.auth?.oauth2?.grantType === 'authorization_code') {
auth.oauth2 = {
grant_type: 'authorization_code',
callback_url: req?.auth?.oauth2?.callbackUrl || '',
authorization_url: req?.auth?.oauth2?.authorizationUrl || '',
access_token_url: req?.auth?.oauth2?.accessTokenUrl || '',
client_id: req?.auth?.oauth2?.clientId || '',
client_secret: req?.auth?.oauth2?.clientSecret || '',
scope: req?.auth?.oauth2?.scope || '',
state: req?.auth?.oauth2?.state || '',
pkce: req?.auth?.oauth2?.pkce || false
};
}
if (req?.auth?.oauth2?.grantType === 'client_credentials') {
auth.oauth2 = {
grant_type: 'client_credentials',
access_token_url: req?.auth?.oauth2?.accessTokenUrl || '',
client_id: req?.auth?.oauth2?.clientId || '',
client_secret: req?.auth?.oauth2?.clientSecret || '',
scope: req?.auth?.oauth2?.scope || ''
};
}
}
if (req?.auth?.apikey) {
auth.apikey = {
key: req?.auth?.apikey?.key,
value: req?.auth?.apikey?.value,
placement: req?.auth?.apikey?.placement
};
}
return {
type: mode,
...auth
};
};
const getVars = (req) => {
const preRequest = _.map(_.get(req, 'vars.req', []), (variable) => {
const varObj = {
name: variable.name,
value: variable.value
};
if (variable.description) {
varObj.description = variable.description;
}
if (variable.enabled === false) {
varObj.disabled = true;
}
return varObj;
});
const postResponse = _.map(_.get(req, 'vars.res', []), (variable) => {
const varObj = {
name: variable.name,
value: variable.value
};
if (variable.description) {
varObj.description = variable.description;
}
if (variable.enabled === false) {
varObj.disabled = true;
}
return varObj;
});
const vars = {};
if(preRequest.length) {
vars['pre-request'] = preRequest;
}
if(postResponse.length) {
vars['post-response'] = postResponse;
}
return !_.isEmpty(vars) ? vars : null;
};
const getScripts = (req) => {
const preRequestScript = _.get(req, 'script.req', '');
const postResponseScript = _.get(req, 'script.res', '');
const scripts = {};
if (preRequestScript) {
scripts['pre-request'] = preRequestScript;
}
if (postResponseScript) {
scripts['post-response'] = postResponseScript;
}
return !_.isEmpty(scripts) ? scripts : null;
};
const getTests = (req) => {
return _.get(req, 'tests', '');
};
const getDocs = (req) => {
return _.get(req, 'docs', '');
};
module.exports = {
getMeta,
getParams,
getHeaders,
getBody,
getAuth,
getVars,
getScripts,
getTests,
getDocs
};

View File

@@ -0,0 +1,8 @@
const _ = require('lodash');
const stringifyRequest = require('./stringifyRequest');
const parseRequest = require('./parseRequest');
module.exports = {
parseRequest,
stringifyRequest,
};

View File

@@ -0,0 +1,235 @@
const yaml = require('js-yaml');
const _ = require('lodash');
const parseRequest = (yamlContent) => {
const yamlData = yaml.load(yamlContent);
const isHttp = !!yamlData.http;
const request = isHttp ? yamlData.http : yamlData.graphql;
const item = {
name: yamlData.meta.name,
description: yamlData.meta.description || '',
seq: yamlData.meta.seq,
type: isHttp ? 'http-request' : 'graphql-request'
};
item.request = {
method: request.method.toUpperCase(),
url: request.url,
headers: _.map(request.headers || [], header => ({
name: header.name,
value: header.value,
description: header.description || '',
enabled: !header.disabled
})),
params: _.flatMap(request.params || {}, (params, type) => {
return _.map(params, param => ({
name: param.name,
value: param.value,
type,
description: param.description || '',
enabled: !param.disabled
}));
}),
body: {
mode: 'none',
json: null,
text: null,
xml: null,
sparql: null,
formUrlEncoded: [],
multipartForm: [],
graphql: null
},
auth: {
mode: 'none',
awsv4: null,
basic: null,
bearer: null,
ntlm: null,
digest: null,
oauth2: null,
wsse: null,
apikey: null
},
vars: {
req: [],
res: []
},
script: {
req: '',
res: ''
},
tests: '',
docs: ''
};
// Handle body
if (request.body) {
item.request.body.mode = request.body.type;
switch(request.body.type) {
case 'json':
item.request.body.json = request.body.data;
break;
case 'text':
item.request.body.text = request.body.data;
break;
case 'xml':
item.request.body.xml = request.body.data;
break;
case 'sparql':
item.request.body.sparql = request.body.data;
break;
case 'formUrlEncoded':
item.request.body.formUrlEncoded = _.map(request.body.data, formItem => ({
name: formItem.name,
value: formItem.value,
description: formItem.description || '',
enabled: !formItem.disabled
}));
break;
case 'multipartForm':
item.request.body.multipartForm = _.map(request.body.data, formItem => ({
name: formItem.name,
value: formItem.value,
description: formItem.description || '',
enabled: !formItem.disabled,
contentType: formItem.content_type || ''
}));
break;
case 'graphql':
item.request.body.graphql = {
query: request.body.data.query || '',
variables: request.body.data.variables || ''
};
break;
}
}
// Handle auth
if (request.auth) {
item.request.auth.mode = request.auth.type;
switch(request.auth.type) {
case 'awsv4':
item.request.auth.awsv4 = {
accessKeyId: request.auth.access_key_id,
secretAccessKey: request.auth.secret_access_key,
sessionToken: request.auth.session_token,
service: request.auth.service,
region: request.auth.region,
profileName: request.auth.profile_name
}
break;
case 'basic':
item.request.auth.basic = {
username: request.auth.username,
password: request.auth.password
}
break;
case 'bearer':
item.request.auth.bearer = {
token: request.auth.token
}
break;
case 'digest':
item.request.auth.digest = {
username: request.auth.username,
password: request.auth.password
}
break;
case 'ntlm':
item.request.auth.ntlm = {
username: request.auth.username,
password: request.auth.password,
domain: request.auth.domain
}
break;
case 'wsse':
item.request.auth.wsse = {
username: request.auth.username,
password: request.auth.password
}
break;
case 'apikey':
item.request.auth.apikey = {
key: request.auth.key,
value: request.auth.value,
placement: request.auth.placement
}
break;
case 'oauth2':
if (request.auth.grant_type === 'password') {
item.request.auth.oauth2 = {
grantType: 'password',
accessTokenUrl: request.auth.access_token_url || '',
clientId: request.auth.client_id || '',
clientSecret: request.auth.client_secret || '',
scope: request.auth.scope || '',
username: request.auth.username || '',
password: request.auth.password || ''
};
} else if (request.auth.grant_type === 'authorization_code') {
item.request.auth.oauth2 = {
grantType: 'authorization_code',
accessTokenUrl: request.auth.access_token_url || '',
clientId: request.auth.client_id || '',
clientSecret: request.auth.client_secret || '',
scope: request.auth.scope || '',
callbackUrl: request.auth.callback_url || '',
authorizationUrl: request.auth.authorization_url || '',
state: request.auth.state || '',
pkce: request.auth.pkce || false
};
} else if (request.auth.grant_type === 'client_credentials') {
item.request.auth.oauth2 = {
grantType: 'client_credentials',
accessTokenUrl: request.auth.access_token_url || '',
clientId: request.auth.client_id || '',
clientSecret: request.auth.client_secret || '',
scope: request.auth.scope || ''
};
}
break;
}
}
// Handle vars
if (yamlData.vars) {
item.request.vars = {
req: _.map(yamlData.vars['pre-request'] || [], v => ({
name: v.name,
value: v.value,
description: v.description || '',
enabled: !v.disabled
})),
res: _.map(yamlData.vars['post-response'] || [], v => ({
name: v.name,
value: v.value,
description: v.description || '',
enabled: !v.disabled
}))
};
}
// Handle scripts
if (yamlData.scripts) {
item.request.script = {
req: yamlData.scripts['pre-request'] || '',
res: yamlData.scripts['post-response'] || ''
};
}
// Handle tests and docs
if (yamlData.tests) {
item.request.tests = yamlData.tests;
}
if (yamlData.docs) {
item.request.docs = yamlData.docs;
}
return item;
};
module.exports = parseRequest;

View File

@@ -0,0 +1,54 @@
const yaml = require('js-yaml');
const _ = require('lodash');
const { getMeta, getParams, getHeaders, getVars, getScripts, getBody, getAuth, getTests, getDocs } = require('./common');
const stringifyRequest = (json) => {
const request = json?.request;
const requestBody = getBody(request);
const requestAuth = getAuth(request);
const isGraphql = requestBody?.type === 'graphql';
const requestData = {
method: _.lowerCase(_.get(json, 'request.method')),
url: _.get(json, 'request.url'),
params: getParams(request),
headers: getHeaders(request),
};
if (requestBody && requestBody.type !== 'none') {
requestData.body = requestBody;
}
if (requestAuth && requestAuth.mode !== 'none') {
requestData.auth = requestAuth;
}
const finalJson = {
meta: getMeta(json),
[isGraphql ? 'graphql' : 'http']: requestData
};
const vars = getVars(request);
if (vars) {
finalJson.vars = vars;
}
const scripts = getScripts(request);
if (scripts) {
finalJson.scripts = scripts;
}
const tests = getTests(request);
if (tests) {
finalJson.tests = tests;
}
const docs = getDocs(request);
if (docs) {
finalJson.docs = docs;
}
return yaml.dump(finalJson);
};
module.exports = stringifyRequest;

View File

@@ -0,0 +1,38 @@
{
"name": "Get User GraphQL",
"seq": 1,
"request": {
"method": "POST",
"url": "https://api.example.com/graphql",
"headers": [
{
"name": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "graphql",
"graphql": {
"query": "query GetUser($id: ID!) {\n user(id: $id) {\n id\n name\n email\n }\n}",
"variables": "{\n \"id\": \"123\"\n}"
}
},
"auth": {
"mode": "bearer",
"bearer": {
"token": "jwt-token"
}
},
"vars": {
"req": [
{
"name": "userId",
"value": "123"
}
]
},
"script": {
"req": "// Pre-request script for GraphQL"
}
}
}

View File

@@ -0,0 +1,36 @@
meta:
name: Get User GraphQL
seq: 1
graphql:
method: post
url: https://api.example.com/graphql
params:
query: []
path: []
headers:
- name: Content-Type
value: application/json
body:
type: graphql
query: |-
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
variables: |-
{
"id": "123"
}
auth:
type: bearer
bearer:
token: jwt-token
vars:
pre-request:
- name: userId
value: '123'
scripts:
pre-request: // Pre-request script for GraphQL

View File

@@ -0,0 +1,70 @@
{
"name": "HTTP Methods",
"seq": 1,
"request": {
"method": "POST",
"url": "https://api.example.com/users",
"headers": [
{
"name": "Content-Type",
"value": "application/json",
"description": "Content type header",
"enabled": true
},
{
"name": "Accept",
"value": "application/json",
"enabled": false
}
],
"params": [
{
"name": "userId",
"value": "123",
"type": "path",
"description": "User ID parameter",
"enabled": true
},
{
"name": "filter",
"value": "active",
"type": "query",
"enabled": false
}
],
"body": {
"mode": "json",
"json": "{\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\"\n}"
},
"auth": {
"mode": "basic",
"basic": {
"username": "admin",
"password": "secret"
}
},
"vars": {
"req": [
{
"name": "userId",
"value": "123",
"description": "User ID variable",
"enabled": true
}
],
"res": [
{
"name": "token",
"value": "response.token",
"enabled": false
}
]
},
"script": {
"req": "// Pre-request script\nconsole.log('pre-request');",
"res": "// Post-response script\nconsole.log('post-response');"
},
"tests": "// Test script\nassert.response.status === 200;",
"docs": "# User Creation API\nThis endpoint creates a new user."
}
}

View File

@@ -0,0 +1,58 @@
meta:
name: HTTP Methods
seq: 1
http:
method: post
url: https://api.example.com/users
params:
query:
- name: filter
value: active
type: query
disabled: true
path:
- name: userId
value: '123'
type: path
description: User ID parameter
headers:
- name: Content-Type
value: application/json
description: Content type header
- name: Accept
value: application/json
disabled: true
body:
type: json
data: |-
{
"name": "John Doe",
"email": "john@example.com"
}
auth:
type: basic
basic:
username: admin
password: secret
vars:
pre-request:
- name: userId
value: '123'
description: User ID variable
post-response:
- name: token
value: response.token
disabled: true
scripts:
pre-request: |-
// Pre-request script
console.log('pre-request');
post-response: |-
// Post-response script
console.log('post-response');
tests: |-
// Test script
assert.response.status === 200;
docs: |-
# User Creation API
This endpoint creates a new user.

View File

@@ -0,0 +1,103 @@
const { stringifyRequest } = require('../../src');
const yaml = require('js-yaml');
const path = require('path');
const fs = require('fs');
describe('GraphQL Request Handling', () => {
const loadFixture = (filename) => {
const filePath = path.join(__dirname, '__fixtures__', filename);
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
};
it('should generate exact YAML matching the fixture', () => {
const json = loadFixture('graphql-request.json');
const expectedYaml = fs.readFileSync(
path.join(__dirname, '__fixtures__', 'graphql-request.yml'),
'utf8'
);
const generatedYaml = stringifyRequest(json);
// Normalize line endings and whitespace for comparison
const normalizeString = (str) => str.replace(/\r\n/g, '\n').trim();
expect(normalizeString(generatedYaml)).toBe(normalizeString(expectedYaml));
});
it('should correctly format GraphQL request', () => {
const json = loadFixture('graphql-request.json');
const result = yaml.load(stringifyRequest(json));
// Verify GraphQL section exists instead of HTTP
expect(result.graphql).toBeDefined();
expect(result.http).toBeUndefined();
// Verify basic properties
expect(result.graphql.method).toBe('post');
expect(result.graphql.url).toBe('https://api.example.com/graphql');
// Verify headers
expect(result.graphql.headers).toHaveLength(1);
expect(result.graphql.headers[0]).toEqual({
name: 'Content-Type',
value: 'application/json'
});
// Verify body
expect(result.graphql.body).toEqual({
type: 'graphql',
query: 'query GetUser($id: ID!) {\n user(id: $id) {\n id\n name\n email\n }\n}',
variables: '{\n \"id\": \"123\"\n}'
});
// Verify auth
expect(result.graphql.auth).toEqual({
type: 'bearer',
bearer: {
token: 'jwt-token'
}
});
// Verify vars
expect(result.vars['pre-request']).toHaveLength(1);
expect(result.vars['pre-request'][0]).toEqual({
name: 'userId',
value: '123'
});
// Verify scripts
expect(result.scripts['pre-request']).toBe('// Pre-request script for GraphQL');
});
it('should handle GraphQL request without variables', () => {
const json = loadFixture('graphql-request.json');
json.request.body.graphql.variables = '';
const result = yaml.load(stringifyRequest(json));
expect(result.graphql.body.variables).toBe('');
});
it('should handle GraphQL request without optional components', () => {
const json = loadFixture('graphql-request.json');
// Remove optional components
delete json.request.auth;
delete json.request.vars;
delete json.request.script;
const result = yaml.load(stringifyRequest(json));
expect(result.graphql.auth).toEqual({ type: 'none' });
expect(result.vars).toBeUndefined();
expect(result.scripts).toBeUndefined();
});
it('should detect GraphQL request based on body mode', () => {
const json = loadFixture('graphql-request.json');
// Change URL but keep GraphQL body
json.request.url = 'https://api.example.com/not-graphql';
const result = yaml.load(stringifyRequest(json));
expect(result.graphql).toBeDefined();
expect(result.http).toBeUndefined();
});
});

View File

@@ -0,0 +1,244 @@
const { stringifyRequest } = require('../../src');
const yaml = require('js-yaml');
const path = require('path');
const fs = require('fs');
describe('HTTP Request Handling', () => {
const loadFixture = (filename) => {
const filePath = path.join(__dirname, '__fixtures__', filename);
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
};
it('should generate exact YAML matching the fixture', () => {
const json = loadFixture('http-request.json');
const expectedYaml = fs.readFileSync(
path.join(__dirname, '__fixtures__', 'http-request.yml'),
'utf8'
);
const generatedYaml = stringifyRequest(json);
// Normalize line endings and whitespace for comparison
const normalizeString = (str) => str.replace(/\r\n/g, '\n').trim();
expect(normalizeString(generatedYaml)).toBe(normalizeString(expectedYaml));
});
it('should correctly format basic HTTP request', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
// Verify HTTP section exists instead of GraphQL
expect(result.http).toBeDefined();
expect(result.graphql).toBeUndefined();
// Verify basic properties
expect(result.meta.name).toBe('HTTP Methods');
expect(result.meta.seq).toBe(1);
expect(result.http.method).toBe('post');
expect(result.http.url).toBe('https://api.example.com/users');
});
it('should handle request with query parameters', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.http.params.query).toEqual([
{
name: 'filter',
value: 'active',
type: 'query',
disabled: true
}
]);
});
it('should handle request with path parameters', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.http.params.path).toEqual([
{
name: 'userId',
value: '123',
type: 'path',
description: 'User ID parameter'
}
]);
});
it('should handle request with headers', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.http.headers).toEqual([
{
name: 'Content-Type',
value: 'application/json',
description: 'Content type header'
},
{
name: 'Accept',
value: 'application/json',
disabled: true
}
]);
});
describe('Body Handling', () => {
it('should handle JSON body', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.http.body).toEqual({
type: 'json',
data: '{\n "name": "John Doe",\n "email": "john@example.com"\n}'
});
});
it('should handle form-urlencoded body', () => {
const json = loadFixture('http-request.json');
json.request.body = {
mode: 'formUrlEncoded',
formUrlEncoded: [
{ name: 'username', value: 'johndoe', description: 'Username field', enabled: true },
{ name: 'password', value: 'secret', enabled: false }
]
};
const result = yaml.load(stringifyRequest(json));
expect(result.http.body).toEqual({
type: 'form-urlencoded',
data: [
{ name: 'username', value: 'johndoe', description: 'Username field' },
{ name: 'password', value: 'secret', disabled: true }
]
});
});
it('should handle multipart-form body', () => {
const json = loadFixture('http-request.json');
json.request.body = {
mode: 'multipartForm',
multipartForm: [
{ name: 'file', value: ['path/to/file'], type: 'file', enabled: true },
{ name: 'description', value: 'profile photo', type: 'text', enabled: true }
]
};
const result = yaml.load(stringifyRequest(json));
expect(result.http.body).toEqual({
type: 'multipart-form',
data: [
{ name: 'file', value: ['path/to/file'], type: 'file' },
{ name: 'description', value: 'profile photo', type: 'text' }
]
});
});
});
describe('Auth Handling', () => {
it('should handle basic auth', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.http.auth).toEqual({
type: 'basic',
basic: {
username: 'admin',
password: 'secret'
}
});
});
it('should handle bearer auth', () => {
const json = loadFixture('http-request.json');
json.request.auth = {
mode: 'bearer',
bearer: { token: 'xyz123' }
};
const result = yaml.load(stringifyRequest(json));
expect(result.http.auth).toEqual({
type: 'bearer',
bearer: {
token: 'xyz123'
}
});
});
it('should handle oauth2 password grant', () => {
const json = loadFixture('http-request.json');
json.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'password',
accessTokenUrl: 'https://api.example.com/oauth/token',
username: 'user',
password: 'pass',
clientId: 'client123',
clientSecret: 'secret123',
scope: 'read write'
}
};
const result = yaml.load(stringifyRequest(json));
expect(result.http.auth).toEqual({
type: 'oauth2',
oauth2: {
grant_type: 'password',
access_token_url: 'https://api.example.com/oauth/token',
username: 'user',
password: 'pass',
client_id: 'client123',
client_secret: 'secret123',
scope: 'read write'
}
});
});
});
describe('Variables and Scripts', () => {
it('should handle pre-request variables', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.vars['pre-request']).toEqual([
{ name: 'userId', value: '123', description: 'User ID variable' }
]);
});
it('should handle post-response variables', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.vars['post-response']).toEqual([
{ name: 'token', value: 'response.token', disabled: true }
]);
});
it('should handle scripts', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.scripts['pre-request']).toBe('// Pre-request script\nconsole.log(\'pre-request\');');
expect(result.scripts['post-response']).toBe('// Post-response script\nconsole.log(\'post-response\');');
});
});
it('should handle tests and docs', () => {
const json = loadFixture('http-request.json');
const result = yaml.load(stringifyRequest(json));
expect(result.tests).toBe('// Test script\nassert.response.status === 200;');
expect(result.docs).toBe('# User Creation API\nThis endpoint creates a new user.');
});
it('should handle disabled components', () => {
const json = loadFixture('http-request.json');
json.request.headers[0].enabled = false;
json.request.params[0].enabled = false;
const result = yaml.load(stringifyRequest(json));
expect(result.http.headers[0].disabled).toBe(true);
expect(result.http.params.query[0].disabled).toBe(true);
});
});

View File

@@ -41,7 +41,7 @@ const varsSchema = Yup.object({
// todo
// anoop(4 feb 2023) - nobody uses this, and it needs to be removed
local: Yup.boolean()
local: Yup.boolean().optional()
})
.noUnknown(true)
.strict();
@@ -298,9 +298,10 @@ const folderRootSchema = Yup.object({
const itemSchema = Yup.object({
uid: uidSchema,
name: Yup.string().min(1, 'name must be at least 1 character').required('name is required'),
description: Yup.string().nullable(),
type: Yup.string().oneOf(['http-request', 'graphql-request', 'folder', 'js']).required('type is required'),
seq: Yup.number().min(1),
name: Yup.string().min(1, 'name must be at least 1 character').required('name is required'),
request: requestSchema.when('type', {
is: (type) => ['http-request', 'graphql-request'].includes(type),
then: (schema) => schema.required('request is required when item-type is request')