diff --git a/packages/bruno-lang/v1/src/index.js b/packages/bruno-lang/v1/src/index.js index 877e8ce3..ccd90a24 100644 --- a/packages/bruno-lang/v1/src/index.js +++ b/packages/bruno-lang/v1/src/index.js @@ -6,8 +6,7 @@ const { const _ = require('lodash'); const { indentString, - outdentString, - get + outdentString } = require('./utils'); const inlineTag = require('./inline-tag'); @@ -64,7 +63,7 @@ const bruToJson = (fileContents) => { } }; - const body = get(json, 'request.body'); + const body = _.get(json, 'request.body'); if(body && body.text) { body.text = outdentString(body.text); diff --git a/packages/bruno-lang/v1/src/utils.js b/packages/bruno-lang/v1/src/utils.js index fed6fdf4..7a0ebb55 100644 --- a/packages/bruno-lang/v1/src/utils.js +++ b/packages/bruno-lang/v1/src/utils.js @@ -23,21 +23,7 @@ const outdentString = (str) => { return str.split("\n").map(line => line.replace(/^ /, '')).join("\n"); }; -// implement lodash _.get functionality -const get = (obj, path, defaultValue) => { - const pathParts = path.split('.'); - let current = obj; - for(let i = 0; i < pathParts.length; i++) { - if(current[pathParts[i]] === undefined) { - return defaultValue; - } - current = current[pathParts[i]]; - } - return current; -}; - module.exports = { - get, safeParseJson, indentString, outdentString diff --git a/packages/bruno-lang/v1/tests/utils.spec.js b/packages/bruno-lang/v1/tests/utils.spec.js index 1d0a32b1..d8103648 100644 --- a/packages/bruno-lang/v1/tests/utils.spec.js +++ b/packages/bruno-lang/v1/tests/utils.spec.js @@ -35,16 +35,4 @@ describe('utils', () => { expect(outdentString(input)).toBe(expectedOutput); }); }); - - describe('get', () => { - it('returns the value at the given path', () => { - const input = { a: { b: { c: 1 } } }; - expect(get(input, 'a.b.c')).toBe(1); - }); - - it('returns the defaultValue if the path does not exist', () => { - const input = { a: { b: { c: 1 } } }; - expect(get(input, 'a.b.d', 2)).toBe(2); - }); - }); }); \ No newline at end of file diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index b21459cc..289e9b5b 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -1,5 +1,8 @@ const ohm = require("ohm-js"); const _ = require('lodash'); +const { + outdentString +} = require('../../v1/src/utils'); /** * A Bru file is made up of blocks. @@ -21,7 +24,7 @@ const _ = require('lodash'); * */ const grammar = ohm.grammar(`Bru { - BruFile = (meta | http | querydisabled | query | headersdisabled | headers | bodies | varsandassert | script | test | docs)* + BruFile = (meta | http | querydisabled | query | headersdisabled | headers | bodies | varsandassert | script | tests | docs)* bodies = bodyjson | bodytext | bodyxml | bodygraphql | bodygraphqlvars | bodyforms bodyforms = bodyformurlencodeddisabled | bodyformurlencoded | bodymultipartdisabled | bodymultipart @@ -81,7 +84,7 @@ const grammar = ohm.grammar(`Bru { bodymultipartdisabled = "body:multipart-form:disabled" dictionary script = "script" st* "{" nl* textblock tagend - test = "test" st* "{" nl* textblock tagend + tests = "tests" st* "{" nl* textblock tagend docs = "docs" st* "{" nl* textblock tagend }`); @@ -148,7 +151,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { get(_1, dictionary) { return { http: { - method: 'GET', + method: 'get', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -156,7 +159,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { post(_1, dictionary) { return { http: { - method: 'POST', + method: 'post', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -164,7 +167,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { put(_1, dictionary) { return { http: { - method: 'PUT', + method: 'put', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -172,7 +175,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { delete(_1, dictionary) { return { http: { - method: 'DELETE', + method: 'delete', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -180,7 +183,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { options(_1, dictionary) { return { http: { - method: 'OPTIONS', + method: 'options', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -188,7 +191,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { head(_1, dictionary) { return { http: { - method: 'HEAD', + method: 'head', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -196,7 +199,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { connect(_1, dictionary) { return { http: { - method: 'CONNECT', + method: 'connect', ...mapPairListToKeyValPair(dictionary.ast) } }; @@ -252,21 +255,21 @@ const sem = grammar.createSemantics().addAttribute('ast', { bodyjson(_1, _2, _3, _4, textblock, _5) { return { body: { - json: textblock.sourceString + json: outdentString(textblock.sourceString) } }; }, bodytext(_1, _2, _3, _4, textblock, _5) { return { body: { - text: textblock.sourceString + text: outdentString(textblock.sourceString) } }; }, bodyxml(_1, _2, _3, _4, textblock, _5) { return { body: { - xml: textblock.sourceString + xml: outdentString(textblock.sourceString) } }; }, @@ -274,7 +277,7 @@ const sem = grammar.createSemantics().addAttribute('ast', { return { body: { graphql: { - query: textblock.sourceString + query: outdentString(textblock.sourceString) } } }; @@ -283,29 +286,37 @@ const sem = grammar.createSemantics().addAttribute('ast', { return { body: { graphql: { - variables: textblock.sourceString + variables: outdentString(textblock.sourceString) } } }; }, vars(_1, dictionary) { + const vars = mapPairListToKeyValPairs(dictionary.ast); + _.each(vars, (val) => { val.local = false; }); return { - vars: mapPairListToKeyValPairs(dictionary.ast) + vars }; }, varsdisabled(_1, dictionary) { + const vars = mapPairListToKeyValPairs(dictionary.ast, false); + _.each(vars, (val) => { val.local = false; }); return { - vars: mapPairListToKeyValPairs(dictionary.ast, false) + vars }; }, varslocal(_1, dictionary) { + const varsLocal = mapPairListToKeyValPairs(dictionary.ast); + _.each(varsLocal, (val) => { val.local = true; }); return { - varsLocal: mapPairListToKeyValPairs(dictionary.ast) + vars: varsLocal }; }, varslocaldisabled(_1, dictionary) { + const varsLocal = mapPairListToKeyValPairs(dictionary.ast, false); + _.each(varsLocal, (val) => { val.local = true; }); return { - varsLocal: mapPairListToKeyValPairs(dictionary.ast, false) + vars: varsLocal }; }, assert(_1, dictionary) { @@ -320,17 +331,17 @@ const sem = grammar.createSemantics().addAttribute('ast', { }, script(_1, _2, _3, _4, textblock, _5) { return { - script: textblock.sourceString + script: outdentString(textblock.sourceString) }; }, - test(_1, _2, _3, _4, textblock, _5) { + tests(_1, _2, _3, _4, textblock, _5) { return { - test: textblock.sourceString + tests: outdentString(textblock.sourceString) };; }, docs(_1, _2, _3, _4, textblock, _5) { return { - docs: textblock.sourceString + docs: outdentString(textblock.sourceString) }; }, textblock(line, _1, rest) { diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js new file mode 100644 index 00000000..71387e30 --- /dev/null +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -0,0 +1,243 @@ +const _ = require('lodash'); + +const { + indentString, +} = require('../../v1/src/utils'); + +const enabled = (items = []) => items.filter(item => item.enabled); +const disabled = (items = []) => items.filter(item => !item.enabled); + +// remove the last line if two new lines are found +const stripLastLine = (text) => { + if(!text || !text.length) return text; + + return text.replace(/(\r?\n)$/, ''); +}; + +const jsonToBru = (json) => { + const { + meta, + http, + query, + headers, + body, + script, + tests, + vars, + assert, + docs + } = json; + + let bru = ''; + + if(meta) { + bru += 'meta {\n'; + for (const key in meta) { + bru += ` ${key}: ${meta[key]}\n`; + } + bru += '}\n\n'; + } + + if(http && http.method) { + bru += `${http.method} { + url: ${http.url}`; + + if(http.body && http.body.length) { + bru += ` + body: ${http.body}`; + } + + bru += ` +} + +`; + } + + if(query && query.length && enabled(query).length) { + bru += `query { +${indentString(enabled(query).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(query && query.length && disabled(query).length) { + bru += `query:disabled { +${indentString(disabled(query).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(headers && headers.length && enabled(headers).length) { + bru += `headers { +${indentString(enabled(headers).map(header => `${header.name}: ${header.value}`).join('\n'))} +} + +`; + } + + if(headers && headers.length && disabled(headers).length) { + bru += `headers:disabled { +${indentString(disabled(headers).map(header => `${header.name}: ${header.value}`).join('\n'))} +} + +`; + } + + if(body && body.json && body.json.length) { + bru += `body:json { +${indentString(body.json)} +} + +`; + } + + if(body && body.text && body.text.length) { + bru += `body:text { +${indentString(body.text)} +} + +`; + } + + if(body && body.xml && body.xml.length) { + bru += `body:xml { +${indentString(body.xml)} +} + +`; + } + + if(body && body.formUrlEncoded && enabled(body.formUrlEncoded).length) { + bru += `body:form-urlencoded { +${indentString(enabled(body.formUrlEncoded).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(body && body.formUrlEncoded && disabled(body.formUrlEncoded).length) { + bru += `body:form-urlencoded:disabled { +${indentString(disabled(body.formUrlEncoded).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(body && body.multipartForm && enabled(body.multipartForm).length) { + bru += `body:multipart-form { +${indentString(enabled(body.multipartForm).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(body && body.multipartForm && disabled(body.multipartForm).length) { + bru += `body:multipart-form:disabled { +${indentString(disabled(body.multipartForm).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(body && body.graphql && body.graphql.query) { + bru += `body:graphql { +${indentString(body.graphql.query)} +} + +`; + } + + if(body && body.graphql && body.graphql.variables) { + bru += `body:graphql:vars { +${indentString(body.graphql.variables)} +} + +`; + } + + if(vars && vars.length) { + const varsEnabled = _.filter(vars, (v) => v.enabled && !v.local); + const varsDisabled = _.filter(vars, (v) => !v.enabled && !v.local); + const varsLocalEnabled = _.filter(vars, (v) => v.enabled && v.local); + const varsLocalDisabled = _.filter(vars, (v) => !v.enabled && v.local); + + if(varsEnabled.length) { + bru += `vars { +${indentString(varsEnabled.map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(varsDisabled.length) { + bru += `vars:disabled { +${indentString(varsDisabled.map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(varsLocalEnabled.length) { + bru += `vars:local { +${indentString(varsLocalEnabled.map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(varsLocalDisabled.length) { + bru += `vars:local:disabled { +${indentString(varsLocalDisabled.map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + } + + if(assert && enabled(assert).length) { + bru += `assert { +${indentString(enabled(assert).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(assert && disabled(assert).length) { + bru += `assert:disabled { +${indentString(disabled(assert).map(item => `${item.name}: ${item.value}`).join('\n'))} +} + +`; + } + + if(script && script.length) { + bru += `script { +${indentString(script)} +} + +`; + } + + if(tests && tests.length) { + bru += `tests { +${indentString(tests)} +} + +`; + } + + if(docs && docs.length) { + bru += `docs { +${indentString(docs)} +} + +`; + } + + + return stripLastLine(bru); +}; + +module.exports = jsonToBru; diff --git a/packages/bruno-lang/v2/tests/fixtures/request.bru b/packages/bruno-lang/v2/tests/fixtures/request.bru index 9db94d4d..103a44f7 100644 --- a/packages/bruno-lang/v2/tests/fixtures/request.bru +++ b/packages/bruno-lang/v2/tests/fixtures/request.bru @@ -27,6 +27,23 @@ headers:disabled { transaction-id: {{transactionId}} } +body:json { + { + "hello": "world" + } +} + +body:text { + This is a text body +} + +body:xml { + + John + 30 + +} + body:form-urlencoded { apikey: secret numbers: +91998877665 @@ -45,23 +62,6 @@ body:multipart-form:disabled { message: hello } -body:json { - { - "hello": "world" - } -} - -body:text { - This is a text body -} - -body:xml { - - John - 30 - -} - body:graphql { { launchesPast { @@ -103,7 +103,11 @@ assert:disabled { $res.body.message: success } -test { +script { + const foo = 'bar'; +} + +tests { function onResponse(request, response) { expect(response.status).to.equal(200); } @@ -111,4 +115,4 @@ test { docs { This request needs auth token to be set in the headers. -} \ No newline at end of file +} diff --git a/packages/bruno-lang/v2/tests/fixtures/request.json b/packages/bruno-lang/v2/tests/fixtures/request.json index 167af5f6..d5f7672d 100644 --- a/packages/bruno-lang/v2/tests/fixtures/request.json +++ b/packages/bruno-lang/v2/tests/fixtures/request.json @@ -5,7 +5,7 @@ "seq": "1" }, "http": { - "method": "GET", + "method": "get", "url": "https://api.textlocal.in/send/", "body": "json" }, @@ -40,12 +40,12 @@ } ], "body": { - "json": " {\n \"hello\": \"world\"\n }", - "text": " This is a text body", - "xml": " \n John\n 30\n ", + "json": "{\n \"hello\": \"world\"\n}", + "text": "This is a text body", + "xml": "\n John\n 30\n", "graphql": { - "query": " {\n launchesPast {\n launch_site {\n site_name\n }\n launch_success\n }\n }", - "variables": " {\n \"limit\": 5\n }" + "query": "{\n launchesPast {\n launch_site {\n site_name\n }\n launch_success\n }\n}", + "variables": "{\n \"limit\": 5\n}" }, "formUrlEncoded": [ { @@ -86,23 +86,25 @@ { "name": "token", "value": "$res.body.token", + "local": false, "enabled": true }, { "name": "petId", "value": "$res.body.id", + "local": false, "enabled": false - } - ], - "varsLocal": [ + }, { "name": "orderNumber", "value": "$res.body.orderNumber", + "local": true, "enabled": true }, { "name": "transactionId", "value": "$res.body.transactionId", + "local": true, "enabled": false } ], @@ -118,6 +120,7 @@ "enabled": false } ], - "test": " function onResponse(request, response) {\n expect(response.status).to.equal(200);\n }", - "docs": " This request needs auth token to be set in the headers." + "script": "const foo = 'bar';", + "tests": "function onResponse(request, response) {\n expect(response.status).to.equal(200);\n}", + "docs": "This request needs auth token to be set in the headers." } \ No newline at end of file diff --git a/packages/bruno-lang/v2/tests/index.spec.js b/packages/bruno-lang/v2/tests/index.spec.js index 764a95da..02c88fe1 100644 --- a/packages/bruno-lang/v2/tests/index.spec.js +++ b/packages/bruno-lang/v2/tests/index.spec.js @@ -1,14 +1,24 @@ const fs = require("fs"); const path = require("path"); -const parser = require("../src/bruToJson"); +const bruToJson = require("../src/bruToJson"); +const jsonToBru = require("../src/jsonToBru"); -describe("parser", () => { +describe("bruToJson", () => { it("should parse the bru file", () => { const input = fs.readFileSync(path.join(__dirname, 'fixtures', 'request.bru'), 'utf8'); const expected = require("./fixtures/request.json"); - const output = parser(input); + const output = bruToJson(input); + + expect(output).toEqual(expected); + }); +}); + +describe("jsonToBru", () => { + it("should parse the bru file", () => { + const input = require("./fixtures/request.json"); + const expected = fs.readFileSync(path.join(__dirname, 'fixtures', 'request.bru'), 'utf8'); + const output = jsonToBru(input); - // console.log(JSON.stringify(output, null, 2)); expect(output).toEqual(expected); }); }); diff --git a/packages/bruno-lang/v2/tests/text.spec.js b/packages/bruno-lang/v2/tests/text.spec.js index 4eeb87ce..50959dc3 100644 --- a/packages/bruno-lang/v2/tests/text.spec.js +++ b/packages/bruno-lang/v2/tests/text.spec.js @@ -14,7 +14,7 @@ script { `; const output = parser(input); - const expected = " function onResponse(request, response) {\n expect(response.status).to.equal(200);\n }"; + const expected = "function onResponse(request, response) {\n expect(response.status).to.equal(200);\n}"; expect(output.script).toEqual(expected); }); });