diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..6e812a1 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,51 @@ +# Set up globals +globals: + angular: false + +extends: openstack + +# Most environment options are not explicitly enabled or disabled, only +# included here for completeness' sake. They are commented out, because the +# global updates.py script would otherwise override them during a global +# requirements synchronization. +# +# Individual projects should choose which platforms they deploy to. + +env: + # browser global variables. + browser: true + + # Adds all of the Jasmine testing global variables for version 1.3 and 2.0. + jasmine: true + + +# Below we adjust rules specific to horizon's usage of openstack's linting +# rules, and its own plugin inclusions. +rules: + ############################################################################# + # Disabled Rules from eslint-config-openstack + ############################################################################# + valid-jsdoc: [1, { + requireParamDescription: false + }] + no-undefined: 1 + brace-style: 1 + no-extra-parens: 1 + callback-return: 1 + block-scoped-var: 1 + quote-props: 0 + space-in-parens: 1 + no-use-before-define: 1 + no-unneeded-ternary: 1 + + ############################################################################# + # Angular Plugin Customization + ############################################################################# + + angular/controller-as-vm: + - 1 + - "ctrl" + + # Remove after migrating to angular 1.4 or later. + angular/no-cookiestore: + - 1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 963b050..15521ec 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ dist ChangeLog AUTHORS craton_ui.egg-info/* +node_modules/* .cache .testrepository .tox diff --git a/README.rst b/README.rst index d8a4589..e049153 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,14 @@ Craton UI for Horizon Dashboard =============================== +Test +____ + +* Python tests: `./run_test.sh` +* JS tests: + 1. `npm install` (first do `./run_tests.sh` to create virtual environment) + 2. `npm run lint` for eslint + 3. `npm run test` for JS unit tests Install ------- diff --git a/craton_dashboard/api/craton.py b/craton_dashboard/api/craton.py index a806b71..d68704e 100644 --- a/craton_dashboard/api/craton.py +++ b/craton_dashboard/api/craton.py @@ -14,13 +14,28 @@ from cratonclient.v1 import client as craton_client +from cratonclient import session as craton_session + +from horizon.utils.memoized import memoized # noqa +from horizon.utils.memoized import memoized_with_request # noqa + from openstack_dashboard.api import base -from six.moves.urllib import request -def cratonclient(): - url = base.url_for(request, 'craton') - c = craton_client.Client(session=request.session, url=url) +def get_auth_params_from_request(request): + return( + request.user.username, + request.user.token.id, + request.user.tenant_id, + base.url_for(request, 'craton') + ) + + +@memoized_with_request(get_auth_params_from_request) +def cratonclient(request_auth_params): + username, token, project_id, url = request_auth_params + session = craton_session.Session(username=username, token=token) + c = craton_client.Client(session=session, url=url) return c @@ -52,6 +67,7 @@ def region_delete(request, **kwargs): pass +@memoized def region_list(request, **kwargs): return cratonclient(request).regions.list(**kwargs) diff --git a/craton_dashboard/api/rest/craton.py b/craton_dashboard/api/rest/craton.py index ba77a9b..9d89a21 100644 --- a/craton_dashboard/api/rest/craton.py +++ b/craton_dashboard/api/rest/craton.py @@ -30,7 +30,7 @@ class Regions(generic.View): @rest_utils.ajax() def get(self, request, **kwargs): """Get all Regions.""" - regions = craton.region_list(request) + regions = craton.region_list(request, **kwargs) return {'items': regions} @rest_utils.ajax() diff --git a/craton_dashboard/static/app/core/openstack-service-api/craton.service.js b/craton_dashboard/static/app/core/openstack-service-api/craton.service.js index d0535e5..6cb3d6d 100644 --- a/craton_dashboard/static/app/core/openstack-service-api/craton.service.js +++ b/craton_dashboard/static/app/core/openstack-service-api/craton.service.js @@ -18,25 +18,41 @@ 'use strict'; /** - * @ngdoc overview - * @name - * @description + * @ngdoc service + * @name cratonAPI + * @param {Object} apiService + * @param {Object} toastService + * @description provides direct pass through craton with NO abstraction + * @returns {Object} the service */ angular .module('horizon.app.core.openstack-service-api') - .factory('horizon.app.core.openstack-service-api.craton', CratonAPI); + .factory('horizon.app.core.openstack-service-api.craton', cratonAPI); - CratonAPI.$inject = [ + cratonAPI.$inject = [ 'horizon.framework.util.http.service', 'horizon.framework.widgets.toast.service' ]; - function CratonAPI(apiService, toastService) { + function cratonAPI(apiService, toastService) { var service = { - + getRegions: getRegions }; + /** + * @name getRegions + * @description Gets a list of regions + * + * @returns {Object} an object with 'items' + */ + function getRegions() { + return apiService.get('api/craton/regions') + .error(function error() { + toastService.add('error', gettext("Unable to get the Craton regions listing")); + }); + } + return service; } diff --git a/craton_dashboard/static/app/core/openstack-service-api/craton.service.spec.js b/craton_dashboard/static/app/core/openstack-service-api/craton.service.spec.js new file mode 100644 index 0000000..9316ff6 --- /dev/null +++ b/craton_dashboard/static/app/core/openstack-service-api/craton.service.spec.js @@ -0,0 +1,62 @@ +/** + * Copyright 2016 Intel Corporation + * + * 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. + */ + +(function() { + + 'use strict'; + + describe('Craton API', function() { + var testCall, service; + var apiService = {}; + var toastService = {}; + + beforeEach( + module('horizon.mock.openstack-service-api', + function($provide, initServices) { + testCall = initServices($provide, apiService, toastService); + }) + ); + + beforeEach(module('horizon.app.core.openstack-service-api')); + + beforeEach(inject(['horizon.app.core.openstack-service-api.craton', function(cratonAPI) { + service = cratonAPI; + }])); + + it('defines the service', function() { + expect(service).toBeDefined(); + }); + + var tests = [ + { + func: "getRegions", + method: "get", + path: "api/craton/regions", + error: "Unable to get the Craton regions listing" + } + ]; + + // Iterate through the defined tests and apply as Jasmine specs. + angular.forEach(tests, function(params) { + it('defines the ' + params.func + ' call properly', function test() { + var callParams = [apiService, service, toastService, params]; + testCall.apply(this, callParams); + }); + }); + + }); + +})(); diff --git a/craton_dashboard/static/dashboard/project/fleet_management/fleet_management.module.spec.js b/craton_dashboard/static/dashboard/project/fleet_management/fleet_management.module.spec.js new file mode 100644 index 0000000..bc6c983 --- /dev/null +++ b/craton_dashboard/static/dashboard/project/fleet_management/fleet_management.module.spec.js @@ -0,0 +1,22 @@ +/** + * Copyright 2016 Intel Corporation + * + * 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. + */ + +(function() { + 'use strict'; + + //TODO: ADD Tests!! + +})(); diff --git a/craton_dashboard/static/dashboard/project/fleet_management/fleet_managment.module.js b/craton_dashboard/static/dashboard/project/fleet_management/fleet_managment.module.js index 15495d6..23151c7 100644 --- a/craton_dashboard/static/dashboard/project/fleet_management/fleet_managment.module.js +++ b/craton_dashboard/static/dashboard/project/fleet_management/fleet_managment.module.js @@ -38,32 +38,32 @@ var basePath = $windowProvider.$get().STATIC_URL + 'dashboard/project/fleet_management/'; $provide.constant('horizon.dashboard.project.fleet_management.basePath', basePath); - var regions = '/project/regions', - taskflows = '/project/fleet/taskflows', - alerts = '/project/fleet/alerts', - auditor = '/project/fleet/auditor', - reporting = '/project/fleet/reporting', - inventory = '/project/fleet/inventory'; + var regions = '/project/regions'; + var taskflows = '/project/fleet/taskflows'; + var alerts = '/project/fleet/alerts'; + var auditor = '/project/fleet/auditor'; + var reporting = '/project/fleet/reporting'; + var inventory = '/project/fleet/inventory'; $routeProvider .when(regions, { templateUrl: basePath + 'regions/index.html' - }). - when(taskflows, { + }) + .when(taskflows, { templateUrl: basePath + 'taskflows/index.html' - }). - when(alerts, { + }) + .when(alerts, { templateUrl: basePath + 'alerts/index.html' - }). - when(auditor, { + }) + .when(auditor, { templateUrl: basePath + 'auditor/index.html' - }). - when(reporting, { + }) + .when(reporting, { templateUrl: basePath + 'reporting/index.html' - }). - when(inventory, { + }) + .when(inventory, { templateUrl: basePath + 'inventory/index.html' - }); + }); } })(); diff --git a/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.js b/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.js index e69de29..d996a1b 100644 --- a/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.js +++ b/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.js @@ -0,0 +1,15 @@ +/** + * Copyright 2016 Intel Corporation + * + * 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. + */ diff --git a/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.spec.js b/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.spec.js new file mode 100644 index 0000000..aebe1fc --- /dev/null +++ b/craton_dashboard/static/dashboard/project/fleet_management/inventory/inventory.controller.spec.js @@ -0,0 +1,22 @@ +/** + * Copyright 2016 Intel Corporation + * + * 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. + */ + +(function() { + 'use strict'; + + //TODO: ADD TESTS!! + +})(); diff --git a/craton_dashboard/static/dashboard/project/fleet_management/taskflows/taskflows.controller.spec.js b/craton_dashboard/static/dashboard/project/fleet_management/taskflows/taskflows.controller.spec.js new file mode 100644 index 0000000..cf430f9 --- /dev/null +++ b/craton_dashboard/static/dashboard/project/fleet_management/taskflows/taskflows.controller.spec.js @@ -0,0 +1,22 @@ +/** + * Copyright 2016 Intel Corporation + * + * 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. + */ + +(function() { + 'use strict'; + + // TODO: ADD TESTS!! + +})(); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..8357208 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,190 @@ +/** + * + * 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'; + +var fs = require('fs'); +var path = require('path'); + +module.exports = function(config) { + var xstaticPath; + var horizonPath; + var openstackDashboardPath; + var horizonRoot; + var basePaths = [ + './.venv', + './.tox/py27' + ]; + + for (var i = 0; i < basePaths.length; i++) { + var basePath = path.resolve(basePaths[i]); + + if (fs.existsSync(basePath)) { + xstaticPath = basePath + '/lib/python2.7/site-packages/xstatic/pkg/'; + horizonRoot = basePath + '/src/horizon/'; + horizonPath = basePath + '/src/horizon/horizon/'; + openstackDashboardPath = basePath + '/src/horizon/openstack_dashboard/'; + break; + } + } + + if (!xstaticPath) { + console.error('xStatic libraries not found, please set up venv'); + process.exit(1); + } + + config.set({ + preprocessors: { + // Used to collect templates for preprocessing. + // NOTE: the templates must also be listed in the files section below. + './**/*.html': ['ng-html2js'], + // Used to indicate files requiring coverage reports. + './craton_dashboard/static/**/!(*.spec).js': ['coverage'] + }, + + // Sets up module to process templates. + ngHtml2JsPreprocessor: { + moduleName: 'templates', + cacheIdFromPath: function(filepath) { + // This function takes the raw provided path from the file searches + // below (in the files: pattern list), applies the filter from the + // preprocessor above (basically, finds the html files), then uses + // this function to translate the relative file path into a path + // that matches what would actually be called in production. + // + // e.g. + // dashboards/project/static/dashboard/project/workflow/launch-instance/configuration/load-edit.html + // becomes: + // /static/dashboard/project/workflow/launch-instance/configuration/load-edit.html + // We can't just use stripPrefix because there are a couple of + // prefixes that need to be altered (and may be more). + return filepath.replace(/^dashboards\/[^\/]+/, '') + .replace(/^static\/app/, '/static/app'); + }, + }, + + // This establishes the base for most referenced paths as being relative + // to this file, i.e. ./craton_dashboard. + basePath: '.', + + // Contains both source and test files. + files: [ + + // from jasmine.html + xstaticPath + 'jquery/data/jquery.js', + xstaticPath + 'angular/data/angular.js', + xstaticPath + 'angular/data/angular-route.js', + xstaticPath + 'angular/data/angular-mocks.js', + xstaticPath + 'angular/data/angular-cookies.js', + xstaticPath + 'angular_bootstrap/data/angular-bootstrap.js', + xstaticPath + 'angular_gettext/data/angular-gettext.js', + xstaticPath + 'angular_fileupload/data/ng-file-upload-all.js', + xstaticPath + 'angular/data/angular-sanitize.js', + xstaticPath + 'd3/data/d3.js', + xstaticPath + 'rickshaw/data/rickshaw.js', + xstaticPath + 'angular_smart_table/data/smart-table.js', + xstaticPath + 'angular_lrdragndrop/data/lrdragndrop.js', + xstaticPath + 'spin/data/spin.js', + xstaticPath + 'spin/data/spin.jquery.js', + xstaticPath + 'tv4/data/tv4.js', + xstaticPath + 'objectpath/data/ObjectPath.js', + xstaticPath + 'angular_schema_form/data/schema-form.js', + + /** + * Include framework source code from horizon and openstack_dashboard + * that we need. + * Otherwise, karma will not be able to find them when testing. + * These files should be mocked in the foreseeable future. + * These are located within the project's ./.venv/horizon directory. + */ + + // Getting horizon's test-shim.js for gettext modules and others + horizonRoot + 'test-shim.js', + + horizonPath + 'static/horizon/js/horizon.js', + horizonPath + 'static/framework/**/*.module.js', + horizonPath + 'static/framework/**/!(*.spec|*.mock).js', + openstackDashboardPath + 'static/app/**/*.module.js', + openstackDashboardPath + 'static/app/**/!(*.spec|*.mock).js', + openstackDashboardPath + 'static/app/**/*.mock.js', + openstackDashboardPath + 'dashboards/**/static/**/**/*.module.js', + + /** + * First, list all the files that defines application's angular modules. + * Those files have extension of `.module.js`. The order among them is + * not significant. + */ + './craton_dashboard/static/app/core/**/*.module.js', + + /** + * Followed by other JavaScript files that defines angular providers + * on the modules defined in files listed above. And they are not mock + * files or spec files defined below. The order among them is not + * significant. + */ + './craton_dashboard/static/app/core/**/!(*.spec|*.mock).js', + + /** + * Then, list files for mocks with `mock.js` extension. The order + * among them should not be significant. + */ + './craton_dashboard/static/app/core/**/*.mock.js', + + /** + * Finally, list files for spec with `spec.js` extension. The order + * among them should not be significant. + */ + './craton_dashboard/static/app/core/**/*.spec.js', + + /** + * Angular external templates + */ + './craton_dashboard/static/app/core/**/*.html' + ], + + autoWatch: true, + + frameworks: ['jasmine'], + + browsers: ['Chrome'], + + browserNoActivityTimeout: 60000, + + reporters: ['progress', 'coverage', 'threshold'], + + plugins: [ + 'karma-chrome-launcher', + 'karma-jasmine', + 'karma-ng-html2js-preprocessor', + 'karma-coverage', + 'karma-threshold-reporter' + ], + + // Places coverage report in HTML format in the subdirectory below. + coverageReporter: { + type: 'html', + dir: '../cover/craton_dashboard' + }, + + // Coverage threshold values. + thresholdReporter: { + statements: 1, // target 100 + branches: 1, // target 100 + functions: 1, // target 100 + lines: 1 // target 100 + } + }); +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..dff18ec --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "version": "0.0.0", + "private": true, + "name": "craton-dashboard", + "description": "OpenStack Craton Dashboard - Angular", + "repository": "none", + "license": "Apache 2.0", + "devDependencies": { + "eslint": "1.10.3", + "eslint-config-openstack": "1.2.4", + "eslint-plugin-angular": "1.0.1", + "jasmine-core": "2.4.1", + "karma": "1.1.2", + "karma-chrome-launcher": "1.0.1", + "karma-cli": "1.0.1", + "karma-coverage": "1.1.1", + "karma-jasmine": "1.0.2", + "karma-ng-html2js-preprocessor": "1.0.0", + "karma-threshold-reporter": "0.1.15" + }, + "scripts": { + "postinstall": "if [ ! -d .venv ]; then tox -epy27 --notest; fi", + "test": "karma start ./karma.conf.js --single-run", + "lint": "eslint --no-color craton_dashboard/static", + "lintq": "eslint --quiet craton_dashboard/static" + }, + "dependencies": { + "angular": "1.5.8" + } +} \ No newline at end of file