Created
October 28, 2014 22:24
-
-
Save TGOlson/2a3df929270564b3b44e to your computer and use it in GitHub Desktop.
Field Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var Parser = {}; | |
| Parser.parseFields = function(fields) { | |
| var parser = new LoadTierParser(), | |
| tokenizer = new Tokenizer(fields); | |
| return parser.parseTier(tokenizer, false); | |
| }; | |
| function LoadTierParser(parent) { | |
| this.parent = parent; | |
| this.loadDescObj = {}; | |
| } | |
| LoadTierParser.prototype.parseTier = function(tokenizer, bracketed) { | |
| while (true) { | |
| var pname = tokenizer.next(), | |
| nexttok; | |
| if (!pname) { | |
| throw new Error('Property name expected'); | |
| } | |
| if (pname === '*') { | |
| this.loadDescObj.$all = true; | |
| nexttok = tokenizer.next(); | |
| } | |
| else { | |
| nexttok = tokenizer.next(); | |
| if (nexttok == ':') { | |
| this.loadDescObj[pname] = this.parseNested(tokenizer); | |
| } | |
| else { | |
| this.loadDescObj[pname] = true; | |
| tokenizer.back(); | |
| } | |
| // pick up the optional page size phrase | |
| // note: there's a future sort phrase coming later | |
| nexttok = tokenizer.next(); | |
| if (nexttok == '*') { | |
| var pageSize = tokenizer.next(); | |
| nexttok = tokenizer.next(); | |
| if (typeof this.loadDescObj[pname] != 'object') { | |
| this.loadDescObj[pname] = {}; | |
| } | |
| this.loadDescObj[pname].$pageSize = pageSize; | |
| } | |
| } | |
| if (nexttok == ',') continue; | |
| if (bracketed) { | |
| if (nexttok != ']') throw new Error('] expected'); | |
| } | |
| else { | |
| tokenizer.back(); | |
| } | |
| return this.loadDescObj; | |
| } | |
| }; | |
| LoadTierParser.prototype.parseNested = function(tokenizer) { | |
| var nexttok = tokenizer.next(); | |
| if (nexttok == '[') { | |
| return new LoadTierParser(this).parseTier(tokenizer, true); | |
| } else { | |
| throw new Error('[ expected'); | |
| } | |
| }; | |
| function Tokenizer(text) { | |
| this.tokens = []; | |
| this.offset = 0; | |
| while (true) { | |
| var match = this.tokenPattern.exec(text); | |
| if (!match) break; | |
| if (match[2]) | |
| this.tokens.push(Number(match[2])); | |
| else | |
| if (!match[1]) | |
| this.tokens.push(match[0]); | |
| } | |
| } | |
| Tokenizer.prototype.tokenPattern = /(\s+)|(\d+(\.\d*)?)|([\w@]+)|(.)/g; | |
| Tokenizer.prototype.next = function() { | |
| var o = this.offset++; | |
| if (o < this.tokens.length) { | |
| return this.tokens[o]; | |
| } | |
| return null; | |
| }; | |
| Tokenizer.prototype.back = function() { | |
| this.offset--; | |
| }; | |
| module.exports = Parser; | |
| // expose additional functions when testing | |
| if (process.env.NODE_ENV === 'test') { | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var Parser = require('./parser'); | |
| ddescribe('parseFields helper', function(){ | |
| it('should parse simple non-nested field parameters', function(){ | |
| var fields = 'endDate,status,creator'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| status: true, | |
| creator: true | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should throw an error for invalid formatting', function(){ | |
| var fields = 'endDate,status,creator,', | |
| parseFields = Parser.parseFields.bind(Parser, fields); | |
| expect(parseFields).toThrow('Property name expected'); | |
| }); | |
| describe('nested property expression parsing', function() { | |
| it('should parse field parameters with a single nested property expression', function(){ | |
| var fields = 'endDate,creator:[mbox,name]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| creator: { | |
| mbox: true, | |
| name: true | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should throw an error when a nested property segment is not properly opened', function() { | |
| var fields = 'endDate,creator:mbox,name]', | |
| parseFields = Parser.parseFields.bind(Parser, fields); | |
| expect(parseFields).toThrow('[ expected'); | |
| }); | |
| it('should throw an error when a nested property segment is not properly closed', function() { | |
| var fields = 'endDate,creator:[mbox,name', | |
| parseFields = Parser.parseFields.bind(Parser, fields); | |
| expect(parseFields).toThrow('] expected'); | |
| }); | |
| it('should parse field parameters with deeply nested property expressions', function(){ | |
| var fields = 'endDate,creator:[dataSpace:[tenant]]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| creator: { | |
| dataSpace: { | |
| tenant: true | |
| }, | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with multiple nested property expressions', function(){ | |
| var fields = 'endDate,creator:[mbox,name],ideas:[title]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| creator: { | |
| mbox: true, | |
| name: true | |
| }, | |
| ideas: { | |
| title: true | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with multiple deeply nested property expressions', function(){ | |
| var fields = 'endDate,creator:[dataSpace:[tenant]],ideas:[creator:[name]]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| creator: { | |
| dataSpace: { | |
| tenant: true | |
| }, | |
| }, | |
| ideas: { | |
| creator: { | |
| name: true | |
| } | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| }); | |
| describe('page size parsing', function() { | |
| it('should parse field parameters with a declared page size', function(){ | |
| var fields = 'endDate,ideas*10'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| $pageSize: 10 | |
| }, | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with a property that has nested property expressions and a declared page size', function(){ | |
| var fields = 'endDate,ideas:[title, description]*10'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| title: true, | |
| description: true, | |
| $pageSize: 10 | |
| }, | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with a multiple declared page sizes', function(){ | |
| var fields = 'endDate,ideas:[title, description]*10,votes*50'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| title: true, | |
| description: true, | |
| $pageSize: 10 | |
| }, | |
| votes: { | |
| $pageSize: 50 | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with nested page sizes', function(){ | |
| var fields = 'endDate,ideas:[title, votes*2]*10'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| title: true, | |
| $pageSize: 10, | |
| votes: { | |
| $pageSize: 2 | |
| } | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with multiple nested page sizes', function(){ | |
| var fields = 'endDate,ideas:[title, votes*2]*10,votes:[scores*10]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| title: true, | |
| $pageSize: 10, | |
| votes: { | |
| $pageSize: 2 | |
| } | |
| }, | |
| votes: { | |
| scores: { | |
| $pageSize: 10 | |
| } | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| }); | |
| describe('wildcard parsing', function() { | |
| it('should parse field parameters with a wildcard parameter', function(){ | |
| var fields = 'endDate,ideas:[*]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| $all: true | |
| }, | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with a nested wildcard parameter', function(){ | |
| var fields = 'endDate,ideas:[creator:[*]]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| creator: { | |
| $all: true | |
| } | |
| }, | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with a nested property expression and a wildcard parameter', function(){ | |
| var fields = 'endDate,ideas:[title, creator:[*]]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| title: true, | |
| creator: { | |
| $all: true | |
| } | |
| }, | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse field parameters with nested wildcard parameters', function(){ | |
| var fields = 'endDate,ideas:[creator:[*]],votes:[*]'; | |
| var expectedParsedFields = { | |
| endDate: true, | |
| ideas: { | |
| creator: { | |
| $all: true | |
| } | |
| }, | |
| votes: { | |
| $all: true | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| }); | |
| describe('complex parsing', function() { | |
| it('should parse complex field parameters', function() { | |
| var fields = 'title,inquiries:[title,creator:[name,mbox],ideaSet:[ideas*30]]*10'; | |
| var expectedParsedFields = { | |
| title: true, | |
| inquiries: { | |
| title: true, | |
| creator: { | |
| name: true, | |
| mbox: true | |
| }, | |
| ideaSet: { | |
| ideas: { | |
| $pageSize: 30 | |
| } | |
| }, | |
| $pageSize: 10 | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| it('should parse even more complex field parameters', function() { | |
| var fields = 'title,description,ideas:[title,creator:[name,givenName],discussion:[comments*2]]*50'; | |
| var expectedParsedFields = { | |
| title: true, | |
| description: true, | |
| ideas: { | |
| title: true, | |
| creator: { | |
| name: true, | |
| givenName: true | |
| }, | |
| discussion: { | |
| comments: { | |
| $pageSize: 2 | |
| } | |
| }, | |
| $pageSize: 50 | |
| } | |
| }; | |
| parsedFields = Parser.parseFields(fields); | |
| expect(parsedFields).toEqual(expectedParsedFields); | |
| }); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment