Files
bruno/packages/bruno-lang/v2/src/bruToJson.js
2023-02-17 13:55:23 +05:30

424 lines
9.3 KiB
JavaScript

const ohm = require("ohm-js");
const _ = require('lodash');
const {
outdentString
} = require('../../v1/src/utils');
/**
* A Bru file is made up of blocks.
* There are two types of blocks
*
* 1. Dictionary Blocks - These are blocks that have key value pairs
* ex:
* headers {
* content-type: application/json
* }
*
* 2. Text Blocks - These are blocks that have text
* ex:
* body:json {
* {
* "username": "John Nash",
* "password": "governingdynamics
* }
*
*/
const grammar = ohm.grammar(`Bru {
BruFile = (meta | http | query | headers | bodies | varsandassert | script | tests | docs)*
bodies = bodyjson | bodytext | bodyxml | bodygraphql | bodygraphqlvars | bodyforms | body
bodyforms = bodyformurlencoded | bodymultipart
nl = "\\r"? "\\n"
st = " " | "\\t"
stnl = st | nl
tagend = nl "}"
optionalnl = ~tagend nl
keychar = ~(tagend | st | nl | ":") any
valuechar = ~(nl | tagend) any
// Dictionary Blocks
dictionary = st* "{" pairlist? tagend
pairlist = optionalnl* pair (~tagend stnl* pair)* (~tagend space)*
pair = st* key st* ":" st* value st*
key = keychar*
value = valuechar*
// Dictionary for Assert Block
assertdictionary = st* "{" assertpairlist? tagend
assertpairlist = optionalnl* assertpair (~tagend stnl* assertpair)* (~tagend space)*
assertpair = st* assertkey st* ":" st* value st*
assertkey = ~tagend assertkeychar*
assertkeychar = ~(tagend | nl | ":") any
// Text Blocks
textblock = textline (~tagend nl textline)*
textline = textchar*
textchar = ~nl any
meta = "meta" dictionary
http = get | post | put | delete | patch | options | head | connect | trace
get = "get" dictionary
post = "post" dictionary
put = "put" dictionary
delete = "delete" dictionary
patch = "patch" dictionary
options = "options" dictionary
head = "head" dictionary
connect = "connect" dictionary
trace = "trace" dictionary
headers = "headers" dictionary
query = "query" dictionary
varsandassert = varsreq | varsres | assert
varsreq = "vars:pre-request" dictionary
varsres = "vars:post-response" dictionary
assert = "assert" assertdictionary
body = "body" st* "{" nl* textblock tagend
bodyjson = "body:json" st* "{" nl* textblock tagend
bodytext = "body:text" st* "{" nl* textblock tagend
bodyxml = "body:xml" st* "{" nl* textblock tagend
bodygraphql = "body:graphql" st* "{" nl* textblock tagend
bodygraphqlvars = "body:graphql:vars" st* "{" nl* textblock tagend
bodyformurlencoded = "body:form-urlencoded" dictionary
bodymultipart = "body:multipart-form" dictionary
script = scriptreq | scriptres
scriptreq = "script:pre-request" st* "{" nl* textblock tagend
scriptres = "script:post-response" st* "{" nl* textblock tagend
tests = "tests" st* "{" nl* textblock tagend
docs = "docs" st* "{" nl* textblock tagend
}`);
const mapPairListToKeyValPairs = (pairList = []) => {
if(!pairList.length) {
return [];
}
return _.map(pairList[0], pair => {
let name = _.keys(pair)[0];
let value = pair[name];
let enabled = true;
if (name && name.length && name.charAt(0) === "~") {
name = name.slice(1);
enabled = false;
}
return {
name,
value,
enabled
};
});
};
const concatArrays = (objValue, srcValue) => {
if (_.isArray(objValue) && _.isArray(srcValue)) {
return objValue.concat(srcValue);
}
};
const mapPairListToKeyValPair = (pairList = []) => {
if(!pairList || !pairList.length) {
return {};
}
return _.merge({}, ...pairList[0]);
}
const sem = grammar.createSemantics().addAttribute('ast', {
BruFile(tags) {
if(!tags || !tags.ast || !tags.ast.length) {
return {};
}
return _.reduce(tags.ast, (result, item) => {
return _.mergeWith(result, item, concatArrays);
}, {});
},
dictionary(_1, _2, pairlist, _3) {
return pairlist.ast;
},
pairlist(_1, pair, _2, rest, _3) {
return [pair.ast, ...rest.ast];
},
pair(_1, key, _2, _3, _4, value, _5) {
let res = {};
res[key.ast] = value.ast ? value.ast.trim() : '';
return res;
},
key(chars) {
return chars.sourceString ? chars.sourceString.trim() : '';
},
value(chars) {
return chars.sourceString ? chars.sourceString.trim() : '';
},
assertdictionary(_1, _2, pairlist, _3) {
return pairlist.ast;
},
assertpairlist(_1, pair, _2, rest, _3) {
return [pair.ast, ...rest.ast];
},
assertpair(_1, key, _2, _3, _4, value, _5) {
let res = {};
res[key.ast] = value.ast ? value.ast.trim() : '';
return res;
},
assertkey(chars) {
return chars.sourceString ? chars.sourceString.trim() : '';
},
textblock(line, _1, rest) {
return [line.ast, ...rest.ast].join('\n');
},
textline(chars) {
return chars.sourceString;
},
textchar(char) {
return char.sourceString;
},
nl(_1, _2) {
return '';
},
st(_) {
return '';
},
tagend(_1 ,_2) {
return '';
},
_iter(...elements) {
return elements.map(e => e.ast);
},
meta(_1, dictionary) {
let meta = mapPairListToKeyValPair(dictionary.ast);
if(!meta.seq) {
meta.seq = 1;
}
if(!meta.type) {
meta.type = 'http';
}
return {
meta
};
},
get(_1, dictionary) {
return {
http: {
method: 'get',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
post(_1, dictionary) {
return {
http: {
method: 'post',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
put(_1, dictionary) {
return {
http: {
method: 'put',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
delete(_1, dictionary) {
return {
http: {
method: 'delete',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
patch(_1, dictionary) {
return {
http: {
method: 'patch',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
options(_1, dictionary) {
return {
http: {
method: 'options',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
head(_1, dictionary) {
return {
http: {
method: 'head',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
connect(_1, dictionary) {
return {
http: {
method: 'connect',
...mapPairListToKeyValPair(dictionary.ast)
}
};
},
query(_1, dictionary) {
return {
query: mapPairListToKeyValPairs(dictionary.ast)
};
},
headers(_1, dictionary) {
return {
headers: mapPairListToKeyValPairs(dictionary.ast)
};
},
bodyformurlencoded(_1, dictionary) {
return {
body: {
formUrlEncoded: mapPairListToKeyValPairs(dictionary.ast)
}
};
},
bodymultipart(_1, dictionary) {
return {
body: {
multipartForm: mapPairListToKeyValPairs(dictionary.ast)
}
};
},
body(_1, _2, _3, _4, textblock, _5) {
return {
http: {
body: 'json'
},
body: {
json: outdentString(textblock.sourceString)
}
};
},
bodyjson(_1, _2, _3, _4, textblock, _5) {
return {
body: {
json: outdentString(textblock.sourceString)
}
};
},
bodytext(_1, _2, _3, _4, textblock, _5) {
return {
body: {
text: outdentString(textblock.sourceString)
}
};
},
bodyxml(_1, _2, _3, _4, textblock, _5) {
return {
body: {
xml: outdentString(textblock.sourceString)
}
};
},
bodygraphql(_1, _2, _3, _4, textblock, _5) {
return {
body: {
graphql: {
query: outdentString(textblock.sourceString)
}
}
};
},
bodygraphqlvars(_1, _2, _3, _4, textblock, _5) {
return {
body: {
graphql: {
variables: outdentString(textblock.sourceString)
}
}
};
},
varsreq(_1, dictionary) {
const vars = mapPairListToKeyValPairs(dictionary.ast);
_.each(vars, (v) => {
let name = v.name;
if (name && name.length && name.charAt(0) === "@") {
v.name = name.slice(1);
v.local = true;
} else {
v.local = false;
}
});
return {
vars: {
req: vars
}
};
},
varsres(_1, dictionary) {
const vars = mapPairListToKeyValPairs(dictionary.ast);
_.each(vars, (v) => {
let name = v.name;
if (name && name.length && name.charAt(0) === "@") {
v.name = name.slice(1);
v.local = true;
} else {
v.local = false;
}
});
return {
vars: {
res: vars
}
};
},
assert(_1, dictionary) {
return {
assert: mapPairListToKeyValPairs(dictionary.ast)
};
},
scriptreq(_1, _2, _3, _4, textblock, _5) {
return {
script: {
req: outdentString(textblock.sourceString)
}
};
},
scriptres(_1, _2, _3, _4, textblock, _5) {
return {
script: {
res: outdentString(textblock.sourceString)
}
};
},
tests(_1, _2, _3, _4, textblock, _5) {
return {
tests: outdentString(textblock.sourceString)
};;
},
docs(_1, _2, _3, _4, textblock, _5) {
return {
docs: outdentString(textblock.sourceString)
};
}
});
const parser = (input) => {
const match = grammar.match(input);
if(match.succeeded()) {
return sem(match).ast;
} else {
throw new Error(match.message);
}
}
module.exports = parser;