TEZ-3023. Tez UI 2: Abstract adapter and route (sree)
Project: http://git-wip-us.apache.org/repos/asf/tez/repo Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/a0dd70c1 Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/a0dd70c1 Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/a0dd70c1 Branch: refs/heads/TEZ-2980 Commit: a0dd70c191228d6ddfb37fe17e4dd3f59a43fd2d Parents: 1292bf4 Author: Sreenath Somarajapuram <[email protected]> Authored: Mon Jan 4 22:28:44 2016 +0530 Committer: Sreenath Somarajapuram <[email protected]> Committed: Thu Feb 25 03:31:59 2016 +0530 ---------------------------------------------------------------------- TEZ-2980-CHANGES.txt | 1 + .../src/main/webapp/app/adapters/abstract.js | 58 ++++++ .../main/webapp/app/errors/unlinked-promise.js | 34 ++++ tez-ui2/src/main/webapp/app/routes/abstract.js | 68 +++++++ .../webapp/tests/unit/adapters/abstract-test.js | 82 ++++++++ .../webapp/tests/unit/routes/abstract-test.js | 186 ++++++++++++++++++- 6 files changed, 428 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tez/blob/a0dd70c1/TEZ-2980-CHANGES.txt ---------------------------------------------------------------------- diff --git a/TEZ-2980-CHANGES.txt b/TEZ-2980-CHANGES.txt index 77a8d29..0eb31bf 100644 --- a/TEZ-2980-CHANGES.txt +++ b/TEZ-2980-CHANGES.txt @@ -8,3 +8,4 @@ ALL CHANGES: TEZ-3020. Tez UI 2: Add entity blueprint TEZ-2985. Tez UI 2: Create loader and entity classes TEZ-3021. Tez UI 2: Add env service & initializer + TEZ-3023. Tez UI 2: Abstract adapter and route http://git-wip-us.apache.org/repos/asf/tez/blob/a0dd70c1/tez-ui2/src/main/webapp/app/adapters/abstract.js ---------------------------------------------------------------------- diff --git a/tez-ui2/src/main/webapp/app/adapters/abstract.js b/tez-ui2/src/main/webapp/app/adapters/abstract.js new file mode 100644 index 0000000..b412a46 --- /dev/null +++ b/tez-ui2/src/main/webapp/app/adapters/abstract.js @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import Ember from 'ember'; + +import LoaderAdapter from './loader'; + +export default LoaderAdapter.extend({ + serverName: null, //Must be set by inheriting classes + + host: Ember.computed("serverName", function () { + var serverName = this.get("serverName"); + return this.get(`hosts.${serverName}`); + }), + namespace: Ember.computed("serverName", function () { + var serverName = this.get("serverName"), + env = this.get("env"); + return env.getAppConfig(`namespaces.webService.${serverName}`); + }), + pathTypeHash: Ember.computed("serverName", function () { + var serverName = this.get("serverName"), + env = this.get("env"); + + return env.getAppConfig(`paths.${serverName}`); + }), + + ajaxOptions: function(url, method, options) { + options = options || {}; + options.crossDomain = true; + options.xhrFields = { + withCredentials: true + }; + options.targetServer = this.get('serverName'); + return this._super(url, method, options); + }, + + pathForType: function(type) { + var serverName = this.get("serverName"), + path = this.get("pathTypeHash")[type]; + Ember.assert(`Path not found for type:${type} to server:${serverName}`, path); + return path; + }, +}); http://git-wip-us.apache.org/repos/asf/tez/blob/a0dd70c1/tez-ui2/src/main/webapp/app/errors/unlinked-promise.js ---------------------------------------------------------------------- diff --git a/tez-ui2/src/main/webapp/app/errors/unlinked-promise.js b/tez-ui2/src/main/webapp/app/errors/unlinked-promise.js new file mode 100644 index 0000000..770b095 --- /dev/null +++ b/tez-ui2/src/main/webapp/app/errors/unlinked-promise.js @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import Ember from 'ember'; + +let UnlinkedPromise = function (errors, message = 'Promise chain was unlinked.') { + Ember.Error.call(this, message); + + this.errors = errors || [ + { + title: 'Unlinked promise chain.', + detail: message + } + ]; +}; + +UnlinkedPromise.prototype = Object.create(Ember.Error.prototype); + +export default UnlinkedPromise; http://git-wip-us.apache.org/repos/asf/tez/blob/a0dd70c1/tez-ui2/src/main/webapp/app/routes/abstract.js ---------------------------------------------------------------------- diff --git a/tez-ui2/src/main/webapp/app/routes/abstract.js b/tez-ui2/src/main/webapp/app/routes/abstract.js index a4d2bb5..d0f3c3d 100644 --- a/tez-ui2/src/main/webapp/app/routes/abstract.js +++ b/tez-ui2/src/main/webapp/app/routes/abstract.js @@ -17,10 +17,18 @@ */ import Ember from 'ember'; +import LoaderService from '../services/loader'; +import UnlinkedPromise from '../errors/unlinked-promise'; export default Ember.Route.extend({ title: null, // Must be set by inheriting class + isLoading: false, + currentPromiseId: null, + loadedValue: null, + + queryParams: null, + setDocTitle: function () { Ember.$(document).attr('title', this.get('title')); }, @@ -28,5 +36,65 @@ export default Ember.Route.extend({ setupController: function (controller, model) { this._super(controller, model); this.setDocTitle(); + }, + + beforeModel: function (transition) { + this.set('queryParams', transition.queryParams); + return this._super(transition); + }, + + checkAndCall: function (id, functionName, value) { + if(id === this.get("currentPromiseId")) { + return this[functionName](value); + } + else { + throw new UnlinkedPromise(); + } + }, + + loadData: Ember.observer("queryParams", function () { + var promiseId = Math.random(); + + this.set('currentPromiseId', promiseId); + + return Ember.RSVP.resolve(). + then(this.checkAndCall.bind(this, promiseId, "setLoading")). + then(this.checkAndCall.bind(this, promiseId, "beforeLoad")). + then(this.checkAndCall.bind(this, promiseId, "load")). + then(this.checkAndCall.bind(this, promiseId, "afterLoad")). + then(this.checkAndCall.bind(this, promiseId, "setValue")); + }), + + setLoading: function () { + this.set('isLoading', true); + }, + beforeLoad: function (value) { + return value; + }, + load: function (value) { + return value; + }, + afterLoad: function (value) { + return value; + }, + setValue: function (value) { + this.set('loadedValue', value); + this.set('isLoading', false); + }, + + _setControllerModel: Ember.observer("_controller", "loadedValue", function () { + var controller = this.get("controller"); + if(controller) { + controller.set("model", this.get("loadedValue")); + } + }), + + setLoader: function (nameSpace) { + this.set("loader", LoaderService.create({ + nameSpace: nameSpace, + store: this.get("store"), + container: this.get("container") + })); } + }); http://git-wip-us.apache.org/repos/asf/tez/blob/a0dd70c1/tez-ui2/src/main/webapp/tests/unit/adapters/abstract-test.js ---------------------------------------------------------------------- diff --git a/tez-ui2/src/main/webapp/tests/unit/adapters/abstract-test.js b/tez-ui2/src/main/webapp/tests/unit/adapters/abstract-test.js new file mode 100644 index 0000000..bc00679 --- /dev/null +++ b/tez-ui2/src/main/webapp/tests/unit/adapters/abstract-test.js @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import { moduleFor, test } from 'ember-qunit'; + +moduleFor('adapter:abstract', 'Unit | Adapter | abstract', { + // Specify the other units that are required for this test. + // needs: ['serializer:foo'] +}); + +test('Basic creation test', function(assert) { + let adapter = this.subject(); + + assert.ok(adapter); + assert.equal(adapter.serverName, null); + + assert.ok(adapter.host); + assert.ok(adapter.namespace); + assert.ok(adapter.pathTypeHash); + + assert.ok(adapter.ajaxOptions); + assert.ok(adapter.pathForType); +}); + +test('ajaxOptions test', function(assert) { + let adapter = this.subject(), + testUrl = "foo.bar", + testMethod = "tm", + testOptions = { + a: 1 + }, + testServer = "ts", + + result; + + // Without options + adapter.serverName = testServer; + result = adapter.ajaxOptions(testUrl, testMethod); + assert.ok(result); + assert.ok(result.crossDomain); + assert.ok(result.xhrFields.withCredentials); + assert.equal(result.targetServer, testServer); + + // Without options + adapter.serverName = testServer; + result = adapter.ajaxOptions(testUrl, testMethod, testOptions); + assert.ok(result); + assert.ok(result.crossDomain); + assert.ok(result.xhrFields.withCredentials); + assert.equal(result.targetServer, testServer); + assert.equal(result.a, testOptions.a); +}); + +test('pathForType test', function(assert) { + let adapter = this.subject(), + testHash = { + typ: "type" + }; + + assert.expect(2); + + adapter.pathTypeHash = testHash; + assert.equal(adapter.pathForType("typ"), testHash.typ); + assert.throws(function () { + adapter.pathForType("noType"); + }); +}); http://git-wip-us.apache.org/repos/asf/tez/blob/a0dd70c1/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js ---------------------------------------------------------------------- diff --git a/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js b/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js index e11183e..9513b91 100644 --- a/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js +++ b/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js @@ -16,6 +16,10 @@ * limitations under the License. */ +import Ember from 'ember'; + +import UnlinkedPromise from '../../../errors/unlinked-promise'; + import { moduleFor, test } from 'ember-qunit'; moduleFor('route:abstract', 'Unit | Route | abstract', { @@ -23,7 +27,187 @@ moduleFor('route:abstract', 'Unit | Route | abstract', { // needs: ['controller:foo'] }); -test('Basic test for existence', function(assert) { +test('Basic creation test', function(assert) { let route = this.subject(); + assert.ok(route); + assert.ok(route.setDocTitle); + assert.ok(route.setupController); + assert.ok(route.beforeModel); + + assert.ok(route.checkAndCall); + + assert.ok(route.setLoading); + assert.ok(route.loadData); + assert.ok(route.beforeLoad); + assert.ok(route.load); + assert.ok(route.afterLoad); + assert.ok(route.setValue); + + assert.ok(route._setControllerModel); + assert.ok(route.setLoader); +}); + +test('beforeModel test', function(assert) { + let route = this.subject(), + testQueryParams = {}; + + route.loadData = Ember.K; + assert.notOk(route.queryParams.queryParams); + route.beforeModel({queryParams: testQueryParams}); + assert.equal(route.queryParams, testQueryParams); +}); + +test('checkAndCall test', function(assert) { + let route = this.subject(), + testValue = {}; + + assert.expect(2); + + route.testFunction = function (value) { + assert.equal(value, testValue, "Call with current id"); + }; + route.currentPromiseId = 1; + + route.checkAndCall(1, "testFunction", testValue); + assert.throws(function () { + route.checkAndCall(2, "testFunction", testValue); + }); +}); + +test('loadData test - Hook sequence check', function(assert) { + let route = this.subject(); + + // Bind poilyfill + Function.prototype.bind = function (context, val1, val2) { + var that = this; + return function (val) { + return that.call(context, val1, val2, val); + }; + }; + + assert.expect(4 + 1); + + route.setLoading = function () { + return 1; + }; + route.beforeLoad = function (value) { + assert.equal(value, 1, "beforeLoad"); + return ++value; + }; + route.load = function (value) { + assert.equal(value, 2, "load"); + return ++value; + }; + route.afterLoad = function (value) { + assert.equal(value, 3, "afterLoad"); + return ++value; + }; + route.setValue = function (value) { + assert.equal(value, 4, "setValue"); + return ++value; + }; + + route.loadData().then(function (value) { + assert.equal(value, 5, "Value returned by loadData"); + }); + +}); + +test('loadData test - ID change check with exception throw', function(assert) { + let route = this.subject(); + + // Bind poilyfill + Function.prototype.bind = function (context, val1, val2) { + var that = this; + return function (val) { + return that.call(context, val1, val2, val); + }; + }; + + assert.expect(2 + 1); + + route.setLoading = function () { + return 1; + }; + route.beforeLoad = function (value) { + assert.equal(value, 1, "beforeLoad"); + return ++value; + }; + route.load = function (value) { + assert.equal(value, 2, "load"); + + route.currentPromiseId = 0; + + return ++value; + }; + route.afterLoad = function (value) { + assert.equal(value, 3, "afterLoad"); + return ++value; + }; + route.setValue = function (value) { + assert.equal(value, 4, "setValue"); + return ++value; + }; + + route.loadData().then(function () { + assert.notOk("Shouldn't be called"); + }).catch(function (e) { + assert.ok(e instanceof UnlinkedPromise, "Exception thrown"); + }); +}); + +test('setLoading test', function(assert) { + let route = this.subject(); + + assert.equal(route.get("isLoading"), false); + route.setLoading(); + assert.equal(route.get("isLoading"), true); +}); + +test('beforeLoad load afterLoad test', function(assert) { + let route = this.subject(), + testVal = {}; + + assert.equal(route.beforeLoad(testVal), testVal); + assert.equal(route.load(testVal), testVal); + assert.equal(route.afterLoad(testVal), testVal); +}); + +test('setValue test', function(assert) { + let route = this.subject(), + testVal = {}; + + route.setLoading(); + assert.equal(route.get("loadedValue"), null); + assert.equal(route.get("isLoading"), true); + assert.equal(route.setValue(testVal), testVal); + assert.equal(route.get("loadedValue"), testVal); + assert.equal(route.get("isLoading"), false); +}); + +test('_setControllerModel test', function(assert) { + let route = this.subject(), + testValue = {}, + testController = Ember.Object.create(); + + route.set("loadedValue", testValue); + route.set("controller", testController); + + assert.notOk(testController.model); + route._setControllerModel(); + assert.equal(testController.model, testValue, "With controller"); +}); + +test('setLoader test', function(assert) { + let route = this.subject(), + testNamespace = "tn", + oldLoader = route.get("loader"); + + route.setLoader(testNamespace); + + assert.notEqual(route.get("loader"), oldLoader); + assert.equal(route.get("loader.nameSpace"), testNamespace); + assert.equal(route.get("loader.store"), route.get("store")); + assert.equal(route.get("loader.container"), route.get("container")); });
