diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index be4578bbb4d..996087b5439 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -17,7 +17,7 @@ angular.module('zeppelinWebApp') .controller('ParagraphCtrl', function($scope,$rootScope, $route, $window, $element, $routeParams, $location, - $timeout, $compile, websocketMsgSrv) { + $timeout, $compile, websocketMsgSrv, dataValidatorSrv) { $scope.paragraph = null; $scope.editor = null; @@ -1338,6 +1338,12 @@ angular.module('zeppelinWebApp') }); } + var msg = dataValidatorSrv.validateChartData(data); + //Only console print error found + if(msg.error){ + console.log(msg.msg); + } + // clear aggregation name, if possible var namesWithoutAggr = {}; var colName; @@ -1473,6 +1479,12 @@ angular.module('zeppelinWebApp') }; } + var msg = dataValidatorSrv.validateScatterData(data); + //TODO- warning need to error model or services will needed + if(msg.error){ + console.log(msg.msg); + } + for (var i = 0; i < data.rows.length; i++) { row = data.rows[i]; if (xAxis) { diff --git a/zeppelin-web/src/components/data-validator/chart-data-validator-factory.js b/zeppelin-web/src/components/data-validator/chart-data-validator-factory.js new file mode 100644 index 00000000000..ffc2f7c9b53 --- /dev/null +++ b/zeppelin-web/src/components/data-validator/chart-data-validator-factory.js @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp').factory('chartdataValidator', function( + $rootScope, DataValidator, dataModelSchemas) { + + var simpleChartSchema = dataModelSchemas.D3ChartDataSchema; + var chartdataValidator = new DataValidator(simpleChartSchema); + + //overriding the check data + chartdataValidator.checkData = function() { + basicCheck(); + }; + + function rowCheck(dataRows) { + if(dataRows instanceof Object) { + for(var key in dataRows) { + schemaChecker(key, 0); + if(dataRows.hasOwnProperty(key)) { + var obj = dataRows[key]; + rowValueCheck(obj); + } + } + } else { + markValidationError('dataRows is not a Object | '); + } + } + + function rowValueCheck(record) { + if(record instanceof Object) { + for(var key in record) { + var recordValues = record[key]; + var countKey = 1; + for(var recordKey in recordValues) { + + if(recordValues.hasOwnProperty(recordKey)) { + var values = recordValues[recordKey]; + schemaChecker(values, countKey); + countKey++; + } + } + } + } else { + markValidationError('record is not a Object | '); + } + } + + function basicCheck() { + var data = chartdataValidator.data; + if(data.schema && data.rows) { + rowCheck(data.rows); + return true; + } else { + markValidationError('data rows does not exisiting | '); + } + } + + function schemaChecker(record, index) { + if(isNaN(record) !== (simpleChartSchema.type[index] === 'string')) { + markValidationError('data record ' + record + + ' is not matching for schema | '); + } + //record is passed + } + + function markValidationError(msg) { + chartdataValidator.setMsg(msg); + chartdataValidator.setError(); + } + + return chartdataValidator; +}); \ No newline at end of file diff --git a/zeppelin-web/src/components/data-validator/data-validator-factory.js b/zeppelin-web/src/components/data-validator/data-validator-factory.js new file mode 100644 index 00000000000..6024f6913de --- /dev/null +++ b/zeppelin-web/src/components/data-validator/data-validator-factory.js @@ -0,0 +1,104 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp').factory('DataValidator', function($rootScope) { + + var msg = ''; + var errorStatus = false; + var DataValidator = function(schema) { + this.schema = schema; + this.error = getErrorStatus; + this.setError = setError; + this.checkData = checkData; + this.getMsg = getMsg; + this.setMsg = setMsg; + this.clearMsg = clearMsg; + this.data = null; + }; + + function checkData() { + if(basicCheck(this.data, this.schema)) { // jshint ignore:line + msg += 'data is exisiting | '; + } else { + msg += 'data does not exisiting | '; + } + } + + function getMsg() { + return msg; + } + + function setMsg(newMsg) { + msg += newMsg; + } + + function clearMsg() { + msg = ''; + } + + function getErrorStatus() { + return errorStatus; + } + + function setError() { + errorStatus = true; + } + + function basicCheck(data, schema) { + if(data.code && data.rows) { + rowCheck(data.rows, 3, schema); + return true; + } else { + msg += 'data rows does not exisiting | '; + setError(); + return false; + } + } + + function rowCheck(rowData, num, schema) { + if(rowData) { + for(var i = 0; i < rowData.length; i++) { + var row = rowData[i]; + if(dataCheckValidator(row, schema)) { + msg += 'data record does not mapping to data schema| '; + } + } + } else { + setError(); + msg += 'data row does not exisiting | '; + } + } + + function dataCheckValidator(record, schema) { + if(record) { + for(var i = 0; i < schema.type.length; i++) { + if(isNaN(record[i]) !== (schema.type[i] === 'string')) { + errorStatus = true; + msg += 'data record ' + (record[i]) + + ' is not matching for schema | '; + return true; + } + } //end validation on data record + errorStatus = false; + return false; + } else { + msg += 'data record does not exisiting | '; + setError(); + return true; + } + } + + return DataValidator; +}); \ No newline at end of file diff --git a/zeppelin-web/src/components/data-validator/data-validator-schema.js b/zeppelin-web/src/components/data-validator/data-validator-schema.js new file mode 100644 index 00000000000..3fb3e82fd54 --- /dev/null +++ b/zeppelin-web/src/components/data-validator/data-validator-schema.js @@ -0,0 +1,27 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp') + .constant('dataModelSchemas', { + 'D3ChartDataSchema': { + type: ['number', 'number', 'number'], + }, + 'PieChartSchema': { + type: ['number', 'number'], + }, + 'ScatterDataSchema': { + type: ['number', 'number'], + } + }); \ No newline at end of file diff --git a/zeppelin-web/src/components/data-validator/data-validator-service.js b/zeppelin-web/src/components/data-validator/data-validator-service.js new file mode 100644 index 00000000000..1d777abed3c --- /dev/null +++ b/zeppelin-web/src/components/data-validator/data-validator-service.js @@ -0,0 +1,46 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp').service('dataValidatorSrv', function( + $rootScope, scatterdataValidator, chartdataValidator, + dataModelSchemas) { + + this.validateChartData = function(data) { + var chartValidator = chartdataValidator; + doBasicCheck(chartValidator, data); + return buildMsg(chartValidator); + }; + + this.validateScatterData = function(data) { + var scatterChartValidator = scatterdataValidator; + doBasicCheck(scatterChartValidator, data); + return buildMsg(scatterChartValidator); + }; + + function buildMsg(validator) { + var msg = { + 'error': validator.error(), + 'msg': validator.getMsg() + }; + return msg; + } + + function doBasicCheck(validator, data) { + validator.clearMsg(); + validator.data = data; + validator.checkData(); + } + +}); \ No newline at end of file diff --git a/zeppelin-web/src/components/data-validator/scatter-data-validator-factory.js b/zeppelin-web/src/components/data-validator/scatter-data-validator-factory.js new file mode 100644 index 00000000000..416dd3627da --- /dev/null +++ b/zeppelin-web/src/components/data-validator/scatter-data-validator-factory.js @@ -0,0 +1,22 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp').factory('scatterdataValidator', function( + $rootScope, DataValidator, dataModelSchemas) { + + var simpleChartSchema = dataModelSchemas.ScatterDataSchema; + var scatterdataValidator = new DataValidator(simpleChartSchema); + return scatterdataValidator; +}); \ No newline at end of file diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html index 9e41b30060c..bc23e6a2444 100644 --- a/zeppelin-web/src/index.html +++ b/zeppelin-web/src/index.html @@ -136,6 +136,11 @@ + + + + + diff --git a/zeppelin-web/test/karma.conf.js b/zeppelin-web/test/karma.conf.js index d76f72cb661..bc5d9d4613e 100644 --- a/zeppelin-web/test/karma.conf.js +++ b/zeppelin-web/test/karma.conf.js @@ -58,6 +58,7 @@ module.exports = function(config) { // endbower 'src/app/app.js', 'src/app/app.controller.js', + 'src/components/data-validator/*.js', 'src/app/**/*.js', 'test/spec/**/*.js' ], diff --git a/zeppelin-web/test/spec/components/data-validator/chart-data-validator-test.js b/zeppelin-web/test/spec/components/data-validator/chart-data-validator-test.js new file mode 100644 index 00000000000..fa3f080ecba --- /dev/null +++ b/zeppelin-web/test/spec/components/data-validator/chart-data-validator-test.js @@ -0,0 +1,67 @@ +'use strict'; + +describe('DataValidatorSrv ', function() { + beforeEach(module('zeppelinWebApp')); + + var chartDataValidatorSrv; + beforeEach(inject(function(dataValidatorSrv) { + chartDataValidatorSrv = dataValidatorSrv; + })); + + describe('validateChartData function', function() { + + var mockDataOne = { + 'schema': Object, + 'rows': Object + }; + it('testing with empty data', function() { + expect(chartDataValidatorSrv.validateChartData({}).error).toBe( + true); + expect(chartDataValidatorSrv.validateChartData({}).msg).toBe( + 'data rows does not exisiting | '); + }); + it('testing with data contain empty rows objects', function() { + expect(chartDataValidatorSrv.validateChartData({}).error).toBe( + true); + expect(chartDataValidatorSrv.validateChartData({}).msg).toBe( + 'data rows does not exisiting | '); + }); + //mock correct data set model + var mockRecord = { + 'value(sum)': { + 'count': 1, + 'value': '4' + } + }; + var mockRow = { + '19': mockRecord + }; + mockDataOne.rows = mockRow; + it('testing with validated data', function() { + expect(chartDataValidatorSrv.validateChartData(mockDataOne).error) + .toBe(false); + expect(chartDataValidatorSrv.validateChartData(mockDataOne).msg) + .toBe(''); + }); + //mock incorrect data set model + var incorrectMockRecord = { + 'value(sum)': { + 'count': 1, + 'value': 'wrong' + } + }; + var incorrectMockRow = { + '23': incorrectMockRecord + }; + var mockDataTwo = { + 'schema': Object, + 'rows': incorrectMockRow + }; + it('testing with invalidated data', function() { + expect(chartDataValidatorSrv.validateChartData(mockDataTwo).error) + .toBe(true); + expect(chartDataValidatorSrv.validateChartData(mockDataTwo).msg) + .toBe('data record wrong is not matching for schema | '); + }); + }); +}); \ No newline at end of file diff --git a/zeppelin-web/test/spec/components/data-validator/data-validator-service.js b/zeppelin-web/test/spec/components/data-validator/data-validator-service.js new file mode 100644 index 00000000000..aa528cd777e --- /dev/null +++ b/zeppelin-web/test/spec/components/data-validator/data-validator-service.js @@ -0,0 +1,34 @@ +'use strict'; + +describe('Service : DataValidatorSrv', function() { + beforeEach(module('zeppelinWebApp')); + + var chartDataValidatorSrv; + beforeEach(inject(function(dataValidatorSrv) { + chartDataValidatorSrv = dataValidatorSrv; + })); + + describe('Testing API', function() { + + it('to be define', function() { + expect(chartDataValidatorSrv).toBeDefined(); + }); + + it('Services to be define', function() { + expect(chartDataValidatorSrv.validateScatterData).toBeDefined(); + expect(chartDataValidatorSrv.validateChartData).toBeDefined(); + }); + //mock data for testing + var mockData = { + 'schema': Object, + 'rows': Object + }; + it('testing return message format', function() { + expect(chartDataValidatorSrv.validateChartData(mockData).msg) + .toBeDefined(); + expect(chartDataValidatorSrv.validateChartData(mockData).error) + .toBeDefined(); + }); + + }); +}); \ No newline at end of file diff --git a/zeppelin-web/test/spec/components/data-validator/scatter-data-validator-test.js b/zeppelin-web/test/spec/components/data-validator/scatter-data-validator-test.js new file mode 100644 index 00000000000..c8bf2d9acb3 --- /dev/null +++ b/zeppelin-web/test/spec/components/data-validator/scatter-data-validator-test.js @@ -0,0 +1,63 @@ +'use strict'; + +describe('DataValidatorSrv ', function() { + beforeEach(module('zeppelinWebApp')); + + var validateScatterDataSrv; + beforeEach(inject(function(dataValidatorSrv) { + validateScatterDataSrv = dataValidatorSrv; + })); + + describe('validateScatterData function', function() { + + var mockDataOne = { + 'code': 'SUCCESS', + 'rows': Object + }; + it('testing with empty data', function() { + expect(validateScatterDataSrv.validateScatterData({}).error).toBe( + true); + expect(validateScatterDataSrv.validateScatterData({}).msg).toBe( + 'data rows does not exisiting | data does not exisiting | ' + ); + }); + it('testing with data contain empty rows objects', function() { + //mockDataOne.rows = [[]]; + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .error).toBe( + true); + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .msg).toContain( + 'data is exisiting | '); + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .msg).toContain( + 'data record does not exisiting | '); + }); + + it('testing with valid data ', function() { + mockDataOne.rows = [ + [12, 34] + ]; + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .error).toBe( + false); + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .msg).toBe( + 'data is exisiting | '); + }); + + it('testing with string data model', function() { + mockDataOne.rows = [ + ['test', '34'] + ]; + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .error).toBe( + true); + expect(validateScatterDataSrv.validateScatterData(mockDataOne) + .msg).toBe( + 'data record test is not matching for schema | data record does not mapping to data schema| data is exisiting | ' + ); + }); + + }); +}); \ No newline at end of file