From 4ef92e6abe4dd1ec891ba7c71f65cc98d8d91fe5 Mon Sep 17 00:00:00 2001 From: Andrey Smyntyna Date: Thu, 18 Jan 2018 13:24:17 +0200 Subject: [PATCH] Added searchExpressions and Group joins support --- .gitignore | 2 ++ lib/search.js | 84 +++++++++++++++++++++++++++++++++++++-------- spec/search.spec.js | 65 +++++++++++++++++++++++++++++++++-- spec/upsert.spec.js | 6 ++-- 4 files changed, 137 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 4720e8f..ba06096 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ log/*.log tags tmp npm-debug.log + +.idea \ No newline at end of file diff --git a/lib/search.js b/lib/search.js index b11c731..0292043 100644 --- a/lib/search.js +++ b/lib/search.js @@ -11,7 +11,7 @@ this.Searcher = (function() { /** @constructor */ - function Searcher(recordType, batchSize, lowerBound, searchFilters, searchColumns) { + function Searcher(recordType, batchSize, lowerBound, searchFilters, searchColumns, advancedOptions) { this.SEARCH_FILTER_NAME_KEY = 'name'; this.SEARCH_FILTER_OPERATOR_KEY = 'operator'; this.SEARCH_FILTER_VALUE_KEY = 'value'; @@ -37,6 +37,10 @@ this.Searcher = (function() { this.searchFilters = []; this.searchColumns = []; this.results = []; + this.advancedOptions = advancedOptions || { + useFilterExpressions: false, + groupJoins: false + }; intBatchSize = parseInt(this.originalBatchSize); if((intBatchSize % 1000) != 0) { @@ -44,12 +48,20 @@ this.Searcher = (function() { } this.batchSize = intBatchSize; - this.createSearchFilters(); - this.generateLowerBoundFilter(); + this.createFilters(); this.createSearchColumns(); this.generateSortColumn(); } + Searcher.prototype.createFilters = function () { + if (this.advancedOptions.useFilterExpressions) { + this.searchFilters = this.rawSearchFilters; + } else { + this.createSearchFilters(); + this.generateLowerBoundFilter(); + } + } + /** * Iterates over `rawSearchFilters` calling generating an nlobjSearchFilter * from each @@ -155,10 +167,10 @@ this.Searcher = (function() { * @return {nlobjSearchFilter} The generated search filter */ Searcher.prototype.getSearchFilterObject = function(searchFilterData) { - name = searchFilterData[this.SEARCH_FILTER_NAME_KEY]; - operator = searchFilterData[this.SEARCH_FILTER_OPERATOR_KEY]; - value = searchFilterData[this.SEARCH_FILTER_VALUE_KEY]; - join = searchFilterData[this.SEARCH_FILTER_JOIN_KEY] || null; + var name = searchFilterData[this.SEARCH_FILTER_NAME_KEY]; + var operator = searchFilterData[this.SEARCH_FILTER_OPERATOR_KEY]; + var value = searchFilterData[this.SEARCH_FILTER_VALUE_KEY]; + var join = searchFilterData[this.SEARCH_FILTER_JOIN_KEY] || null; filter = NetsuiteToolkit.searchFilter(name, join, operator, value); return filter; @@ -174,8 +186,8 @@ this.Searcher = (function() { */ Searcher.prototype.createSearchColumns = function() { for(index in this.rawSearchColumns) { - searchColumnData = this.rawSearchColumns[index]; - searchColumnObject = this.getSearchColumnObject(searchColumnData); + var searchColumnData = this.rawSearchColumns[index]; + var searchColumnObject = this.getSearchColumnObject(searchColumnData); this.searchColumns.push(searchColumnObject); } } @@ -203,9 +215,9 @@ this.Searcher = (function() { * @return {nlobjSearchColumn} The generated search column */ Searcher.prototype.getSearchColumnObject = function(searchColumnData) { - name = searchColumnData[this.SEARCH_COLUMN_NAME_KEY]; - join = searchColumnData[this.SEARCH_COLUMN_JOIN_KEY]; - column = NetsuiteToolkit.searchColumn(name, join); + var name = searchColumnData[this.SEARCH_COLUMN_NAME_KEY]; + var join = searchColumnData[this.SEARCH_COLUMN_JOIN_KEY]; + var column = NetsuiteToolkit.searchColumn(name, join); return column; } @@ -328,9 +340,49 @@ this.Searcher = (function() { * @return {null} */ Searcher.prototype.appendResults = function(resultsBlock) { - this.results = this.results.concat(resultsBlock); + if (this.advancedOptions.groupJoins) { + this.appendResultsGroupJoins(resultsBlock); + } else { + this.results = this.results.concat(resultsBlock); + } } + /** + * Concatenates the given result set onto the result list and group join fields into nested object + * + * @method + * @param {Array} resultsBlock The array containing a set of results from Netsuite + * @memberof Searcher + * @return {null} + */ + Searcher.prototype.appendResultsGroupJoins = function(resultsBlock) { + var newResultsBlock = []; + for ( var i = 0; resultsBlock != null && i < resultsBlock.length; i++ ) + { + var newResult = {"id":resultsBlock[i].getId(), "recordtype":resultsBlock[i].getRecordType(), "columns":{}}; + var result = resultsBlock[i]; + var columns = result.getAllColumns(); + var columnLen = columns.length; + + for (var x = 0; x < columnLen; x++) { + var column = columns[x]; + var name = column.getName(); + var join = column.getJoin(); + var value = result.getValue(column); + var joinTitle = join; + + if (join) { + if(!(joinTitle in newResult.columns)) newResult.columns[joinTitle] = {}; + newResult.columns[joinTitle][name] = value; + } else { + newResult.columns[name] = value; + } + } + newResultsBlock.push(newResult); + } + this.results = this.results.concat(newResultsBlock); + } + /** * Generates a list of params given in the HTTP request * @@ -345,6 +397,8 @@ this.Searcher = (function() { params['lower_bound'] = this.originalLowerBound; params['search_filters'] = this.rawSearchFilters; params['search_columns'] = this.rawSearchColumns; + params['advanced_options'] = this.advancedOptions; + return params; } @@ -367,7 +421,7 @@ this.Searcher = (function() { /** * The script function that Netsuite will call to execute the Search process - * + * * @method * @param {object} request The object representing the HTTP request body * @memberof global @@ -375,7 +429,7 @@ this.Searcher = (function() { */ var searchPostHandler = function(request) { searcher = new Searcher(request['record_type'], request['batch_size'], request['lower_bound'], - request['search_filters'], request['search_columns']); + request['search_filters'], request['search_columns'], request['advanced_options']); searcher.executeSearch(); return searcher.reply(); } diff --git a/spec/search.spec.js b/spec/search.spec.js index d3ba8ad..96ec1d8 100644 --- a/spec/search.spec.js +++ b/spec/search.spec.js @@ -20,6 +20,11 @@ describe("Searcher", function() { 'operator': 'anyof' } ]; + var searchExpression = [ + ['displayname', 'is', 'VENDOR-STYLE-SIZE' ], + 'and', + ['upccode', 'anyof', ['123456789012', '098765432109']] + ]; var searchColumns = [ { 'name': 'custitem22', @@ -42,6 +47,7 @@ describe("Searcher", function() { describe('#init(recordType, batchSize, lowerBound, searchFilters, searchColumns', function() { beforeEach(function() { + spyOn(Searcher.prototype, 'createFilters').andCallThrough(); spyOn(Searcher.prototype, 'createSearchFilters'); spyOn(Searcher.prototype, 'generateLowerBoundFilter'); spyOn(Searcher.prototype, 'createSearchColumns'); @@ -170,6 +176,38 @@ describe("Searcher", function() { }); + describe('#init(recordType, batchSize, lowerBound, searchFilters, searchColumns, advancedOptions', function() { + + beforeEach(function() { + spyOn(Searcher.prototype, 'createFilters').andCallThrough(); + spyOn(Searcher.prototype, 'createSearchFilters'); + spyOn(Searcher.prototype, 'generateLowerBoundFilter'); + spyOn(Searcher.prototype, 'createSearchColumns'); + spyOn(Searcher.prototype, 'generateSortColumn'); + + var advancedOptions = { + useFilterExpressions: true, + groupJoins: true + }; + this.newSearcher = new Searcher(recordType, batchSize, lowerBound, + searchExpression, searchColumns, advancedOptions); + }); + + it("should set the searchFilters to an provided Expression", function() { + expect(this.newSearcher.searchFilters).toEqual(searchExpression); + }); + + it("should NOT call createSearchFilters", function() { + expect(this.newSearcher.createSearchFilters).not.toHaveBeenCalled(); + }); + + it("should NOT call generateLowerBoundFilter", function() { + expect(this.newSearcher.generateLowerBoundFilter).not.toHaveBeenCalled(); + }); + + }); + + describe('#createSearchFilters', function() { beforeEach(function() { @@ -291,7 +329,7 @@ describe("Searcher", function() { spyOn(searcher, 'getSearchFilterObject').andReturn(netsuiteSearchFilterObject); searcher.generateLowerBoundFilter(); }); - + it("should call getSearchFilterObject with the current lowerBound", function() { expect(searcher.getSearchFilterObject).toHaveBeenCalledWith(this.searchFilterData); }); @@ -518,6 +556,29 @@ describe("Searcher", function() { }); + describe('#searchIteration (Group join)', function() { + it("should call appendResultsGroupJoins", function() { + this.resultsBlock = [{}]; + spyOn(searcher, 'appendResultsGroupJoins'); + searcher.advancedOptions = { + groupJoins: true + }; + searcher.appendResults(this.resultsBlock); + expect(searcher.appendResultsGroupJoins).toHaveBeenCalledWith(this.resultsBlock); + }); + + it("should NOT call appendResultsGroupJoins", function() { + this.resultsBlock = [{}]; + spyOn(searcher, 'appendResultsGroupJoins'); + searcher.advancedOptions = { + groupJoins: false + }; + searcher.appendResults(this.resultsBlock); + expect(searcher.appendResultsGroupJoins).not.toHaveBeenCalled(); + }); + }); + + describe('#isExecutionDone(resultsBlock)', function() { it("should be true if resultsBlock is undefined", function() { @@ -622,7 +683,7 @@ describe("Searcher", function() { this.resultsBlock = ([{}, {}, {}, {}, this.resultRow]); this.recordId = searcher.extractLowerBound(this.resultsBlock); }); - + it("should call getId on the resultRow", function() { expect(this.resultRow.getId).toHaveBeenCalled(); }); diff --git a/spec/upsert.spec.js b/spec/upsert.spec.js index 3a7ac46..82f36ec 100644 --- a/spec/upsert.spec.js +++ b/spec/upsert.spec.js @@ -33,7 +33,7 @@ describe('Upserter', function() { }); it('should initialize the reply_list as an empty Array', function() { - expect(upserter.reply_list).toEqual([]); + expect(upserter.reply_list).toEqual([]); }); it('should initialize the exception to null', function() { @@ -115,7 +115,7 @@ describe('Upserter', function() { }); it('should call formatReply on NetsuiteToolkit', function() { - expect(NetsuiteToolkit.formatReply).toHaveBeenCalledWith(upserter.params, upserter.replyList); + expect(NetsuiteToolkit.formatReply).toHaveBeenCalledWith(upserter.params, upserter.reply_list); }); it('should return the output of formatreply', function() { @@ -296,7 +296,7 @@ describe('UpsertRequest', function() { spyOn(this.fake_processor, 'execute'); upsert_request.executeSublistProcessor(sublist_one); }); - + it('should call the constructor of NetsuiteToolkit.SublistProcessor', function() { expect(NetsuiteToolkit.SublistProcessor).toHaveBeenCalledWith(upsert_request.record, sublist_one);