Skip to content

Instantly share code, notes, and snippets.

@TGOlson
Created October 28, 2014 22:24
Show Gist options
  • Select an option

  • Save TGOlson/2a3df929270564b3b44e to your computer and use it in GitHub Desktop.

Select an option

Save TGOlson/2a3df929270564b3b44e to your computer and use it in GitHub Desktop.
Field Parser
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') {
}
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