From d9ef1692fee6a6a7af03ea2f7b44e5bab0253a48 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Wed, 1 Feb 2023 08:00:10 +0530 Subject: [PATCH] feat: generic key val line parser --- packages/bruno-lang/src/body-tag.js | 37 +-- packages/bruno-lang/src/env-vars-tag.js | 38 +-- packages/bruno-lang/src/headers-tag.js | 36 +-- packages/bruno-lang/src/key-val-lines.js | 64 +++++ packages/bruno-lang/src/params-tag.js | 37 +-- .../bruno-lang/tests/key-val-lines.spec.js | 230 ++++++++++++++++++ 6 files changed, 308 insertions(+), 134 deletions(-) create mode 100644 packages/bruno-lang/src/key-val-lines.js create mode 100644 packages/bruno-lang/tests/key-val-lines.spec.js diff --git a/packages/bruno-lang/src/body-tag.js b/packages/bruno-lang/src/body-tag.js index 5a79dfae..de4dbbc8 100644 --- a/packages/bruno-lang/src/body-tag.js +++ b/packages/bruno-lang/src/body-tag.js @@ -1,16 +1,9 @@ const { between, regex, - everyCharUntil, - digit, - whitespace, - optionalWhitespace, - endOfInput, - choice, - many, - sepBy, - sequenceOf + everyCharUntil } = require("arcsecond"); +const keyvalLines = require('./key-val-lines'); // body(type=json) const bodyJsonBegin = regex(/^body\s*\(\s*type\s*=\s*json\s*\)\s*\r?\n/); @@ -60,32 +53,6 @@ const bodyXmlTag = between(bodyXmlBegin)(bodyEnd)(everyCharUntil(bodyEnd)).map(( } }); -// generic key value parser -const newline = regex(/^\r?\n/); -const newLineOrEndOfInput = choice([newline, endOfInput]); -const wordWithoutWhitespace = regex(/^[^\s\t\r?\n]+/g); -const wordWithWhitespace = regex(/^[^\r?\n]+/g); - -const line = sequenceOf([ - optionalWhitespace, - digit, - whitespace, - wordWithoutWhitespace, - whitespace, - wordWithWhitespace, - newLineOrEndOfInput -]).map(([_, enabled, __, key, ___, value]) => { - return { - "enabled": Number(enabled) ? true : false, - "name": key, - "value": value - }; -}); - -const lines = many(line); -const keyvalLines = sepBy(newline)(lines); - - /** * We have deprecated form-url-encoded type in body tag, it was a misspelling on my part * The new type is form-urlencoded diff --git a/packages/bruno-lang/src/env-vars-tag.js b/packages/bruno-lang/src/env-vars-tag.js index d2192799..1ae9d9cd 100644 --- a/packages/bruno-lang/src/env-vars-tag.js +++ b/packages/bruno-lang/src/env-vars-tag.js @@ -1,44 +1,18 @@ const { - sequenceOf, - whitespace, - optionalWhitespace, - choice, - endOfInput, between, - digit, - many, regex, - sepBy } = require("arcsecond"); - -const newline = regex(/^\r?\n/); -const newLineOrEndOfInput = choice([newline, endOfInput]); +const { each } = require('lodash'); +const keyValLines = require('./key-val-lines'); const begin = regex(/^vars\s*\r?\n/); const end = regex(/^[\r?\n]*\/vars\s*[\r?\n]*/); -const wordWithoutWhitespace = regex(/^[^\s\r?\t\n]+/g); -const wordWithWhitespace = regex(/^[^\r?\n]+/g); -const line = sequenceOf([ - optionalWhitespace, - digit, - whitespace, - wordWithoutWhitespace, - whitespace, - wordWithWhitespace, - newLineOrEndOfInput -]).map(([_, enabled, __, key, ___, value]) => { - return { - "enabled": Number(enabled) ? true : false, - "name": key, - "value": value, - "type": "text" - }; -}); +const envVarsTag = between(begin)(end)(keyValLines).map(([variables]) => { + each(variables, (variable) => { + variable.type = "text" + }); -const lines = many(line); -const envVarsLines = sepBy(newline)(lines); -const envVarsTag = between(begin)(end)(envVarsLines).map(([variables]) => { return { variables }; diff --git a/packages/bruno-lang/src/headers-tag.js b/packages/bruno-lang/src/headers-tag.js index 8f28f8a0..f40bcb81 100644 --- a/packages/bruno-lang/src/headers-tag.js +++ b/packages/bruno-lang/src/headers-tag.js @@ -1,43 +1,13 @@ const { - sequenceOf, - whitespace, - optionalWhitespace, - choice, - endOfInput, between, - digit, - many, - regex, - sepBy + regex } = require("arcsecond"); - -const newline = regex(/^\r?\n/); -const newLineOrEndOfInput = choice([newline, endOfInput]); +const keyValLines = require('./key-val-lines'); const begin = regex(/^headers\s*\r?\n/); const end = regex(/^[\r?\n]*\/headers\s*[\r?\n]*/); -const wordWithoutWhitespace = regex(/^[^\s\t\n]+/g); -const wordWithWhitespace = regex(/^[^\r?\n]+/g); -const line = sequenceOf([ - optionalWhitespace, - digit, - whitespace, - wordWithoutWhitespace, - whitespace, - wordWithWhitespace, - newLineOrEndOfInput -]).map(([_, enabled, __, key, ___, value]) => { - return { - "enabled": Number(enabled) ? true : false, - "name": key, - "value": value - }; -}); - -const lines = many(line); -const headersLines = sepBy(newline)(lines); -const headersTag = between(begin)(end)(headersLines).map(([headers]) => { +const headersTag = between(begin)(end)(keyValLines).map(([headers]) => { return { headers }; diff --git a/packages/bruno-lang/src/key-val-lines.js b/packages/bruno-lang/src/key-val-lines.js new file mode 100644 index 00000000..b6ecf966 --- /dev/null +++ b/packages/bruno-lang/src/key-val-lines.js @@ -0,0 +1,64 @@ +const { + sequenceOf, + whitespace, + optionalWhitespace, + choice, + digit, + many, + regex, + sepBy, +} = require("arcsecond"); + +const newline = regex(/^\r?\n/); +const wordWithoutWhitespace = regex(/^[^\s\r?\t\n]+/g); +const wordWithWhitespace = regex(/^[^\r?\n]+/g); + +// matching lines like: 1 key value +const line = sequenceOf([ + optionalWhitespace, + digit, + whitespace, + wordWithoutWhitespace, + whitespace, + wordWithWhitespace +]).map(([_, enabled, __, key, ___, value]) => { + return { + "enabled": Number(enabled) ? true : false, + "name": key ? key.trim() : "", + "value": value ? value.trim() : "" + }; +}); + +// matching lines like: 1 key follows by [whitespaces] and a newline +const line2 = sequenceOf([ + optionalWhitespace, + digit, + whitespace, + wordWithoutWhitespace, + regex(/^\s*\r?\n/) +]).map(([_, enabled, __, key]) => { + return { + "enabled": Number(enabled) ? true : false, + "name": key, + "value": "" + }; +}); + +// matching lines like: 1 followed by [whitespaces] and a newline +const line3 = sequenceOf([ + optionalWhitespace, + digit, + regex(/^\s*\r?\n/) +]).map(([_, enabled]) => { + return { + "enabled": Number(enabled) ? true : false, + "name": "", + "value": "" + }; +}); + +const lines = many(choice([line3, line2, line])); + +const keyValLines = sepBy(newline)(lines); + +module.exports = keyValLines; \ No newline at end of file diff --git a/packages/bruno-lang/src/params-tag.js b/packages/bruno-lang/src/params-tag.js index fa505a99..39867e5f 100644 --- a/packages/bruno-lang/src/params-tag.js +++ b/packages/bruno-lang/src/params-tag.js @@ -1,44 +1,13 @@ const { - sequenceOf, - whitespace, - optionalWhitespace, - choice, - endOfInput, - everyCharUntil, between, - digit, - many, - regex, - sepBy + regex } = require("arcsecond"); - -const newline = regex(/^\r?\n/); -const newLineOrEndOfInput = choice([newline, endOfInput]); +const keyValLines = require('./key-val-lines'); const begin = regex(/^params\s*\r?\n/); const end = regex(/^[\r?\n]*\/params\s*[\r?\n]*/); -const wordWithoutWhitespace = regex(/^[^\s\t\r?\n]+/g); -const wordWithWhitespace = regex(/^[^\r?\n]+/g); -const line = sequenceOf([ - optionalWhitespace, - digit, - whitespace, - wordWithoutWhitespace, - whitespace, - wordWithWhitespace, - newLineOrEndOfInput -]).map(([_, enabled, __, key, ___, value]) => { - return { - "enabled": Number(enabled) ? true : false, - "name": key, - "value": value - }; -}); - -const lines = many(line); -const paramsLines = sepBy(newline)(lines); -const paramsTag = between(begin)(end)(paramsLines).map(([params]) => { +const paramsTag = between(begin)(end)(keyValLines).map(([params]) => { return { params }; diff --git a/packages/bruno-lang/tests/key-val-lines.spec.js b/packages/bruno-lang/tests/key-val-lines.spec.js new file mode 100644 index 00000000..88a2d6ad --- /dev/null +++ b/packages/bruno-lang/tests/key-val-lines.spec.js @@ -0,0 +1,230 @@ +const { + between, + regex, + anyChar, + many, + choice +} = require("arcsecond"); +const _ = require('lodash'); + +const keyValLines = require('../src/key-val-lines'); + +const begin = regex(/^vars\s*\r?\n/); +const end = regex(/^[\r?\n]*\/vars\s*[\r?\n]*/); + +const varsTag = between(begin)(end)(keyValLines).map(([variables]) => { + return { + variables + }; +}); + +const toJson = (fileContents) => { + const parser = many(choice([ + varsTag, + anyChar + ])); + + const parsed = parser + .run(fileContents) + .result + .reduce((acc, item) => _.merge(acc, item), {}); + + const json = { + variables: parsed.variables || [] + }; + + return json; +}; + +describe('bool-key-val', () => { + it('should parse bool-key-val - case 1', () => { + const file = ` +vars + 1 host https://www.google.com +/vars +`; + + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: 'https://www.google.com' + }] + }); + }); + + it('should parse bool-key-val - case 2', () => { + const file = ` +vars + 1 host https://www.google.com + 1 auth jwt secret +/vars +`; + + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: 'https://www.google.com' + }, { + enabled: true, + name: 'auth', + value: 'jwt secret' + }] + }); + }); + + // following test cases are for edge cases + + // one line with just enabled flag + it('should parse bool-key-val - case 3', () => { + const file = ` +vars + 1 +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: '', + value: '' + }] + }); + }); + + // one line with just enabled flag and a space + it('should parse bool-key-val - case 4', () => { + const file = ` +vars + 1 +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: '', + value: '' + }] + }); + }); + + // one line with just enabled flag and a space and a name + it('should parse bool-key-val - case 5', () => { + const file = ` +vars + 1 host +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: '' + }] + }); + }); + + // one line with just enabled flag and a space and a name and a space + it('should parse bool-key-val - case 6', () => { + const file = ` +vars + 1 host +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: '' + }] + }); + }); + + // three lines, second line with just enabled flag + it('should parse bool-key-val - case 7', () => { + const file = ` +vars + 1 host https://www.google.com + 1 + 0 Content-type application/json +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: 'https://www.google.com' + }, { + enabled: true, + name: '', + value: '' + }, { + enabled: false, + name: 'Content-type', + value: 'application/json' + }] + }); + }); + + // three lines, second line with just enabled flag and a space + it('should parse bool-key-val - case 8', () => { + const file = ` +vars + 1 host https://www.google.com + 1 + 0 Content-type application/json +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: 'https://www.google.com' + }, { + enabled: true, + name: '', + value: '' + }, { + enabled: false, + name: 'Content-type', + value: 'application/json' + }] + }); + }); + + // three lines, second line with just enabled flag and a space and a name + it('should parse bool-key-val - case 9', () => { + const file = ` +vars + 1 host https://www.google.com + 1 auth + 0 Content-type application/json +/vars +`; + const result = toJson(file); + expect(result).toEqual({ + variables: [{ + enabled: true, + name: 'host', + value: 'https://www.google.com' + }, { + enabled: true, + name: 'auth', + value: '' + }, { + enabled: false, + name: 'Content-type', + value: 'application/json' + }] + }); + }); +}); +