diff --git a/DEVELOPER.md b/DEVELOPER.md index 5fc2d358..d009e422 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -16,33 +16,70 @@ Now you should an api key, api secret, and an organization associated to your Us ##### Steps to get impac loaded: -1. Terminal commands to load dependancies +Temporary instructions to get Impac! Angular running for development: + +1. Create an account on http://impac-mnoe-uat.maestrano.io. +2. Fork impac-express (https://github.com/maestrano/impac-express) and clone it to your local machine +3. In the impac-express project, modify config/settings/development.yml as follow: + + ```ruby + impac: + protocol: https + host: api-impac-uat.maestrano.io + mno: + protocol: https + host: api-hub-uat.maestrano.io ``` - npm install - bower install +4. In the impac-express project, create config/application.yml as follow: + + ```ruby + # Tenant Credentials + tenant_id: c6b44271-7334-0134-8df7-35f23abf243c + tenant_key: 4Vcpt_2GnCfhlyBYbN_bUQ + + # Emailing platform + SPARKPOST_API_KEY: changeme + + # Rails secret key + SECRET_KEY_BASE: e48e21d80b2943c1872f69fb3cf4f8a8c24bc9fa3abdc14549df2dc181d46bfb5698271fb4bc1511e45ba0633ef02b494985b67f6e418182014f95a5e600f808 + + # Google Tag Manager + google_tag_container: GTM-TH3MLB ``` - -2. Configuring with credentials & launching workspace - - Add your api_key & api_secret to the settings factory in the `workspace/index.js` file +5. Build and start the impac-express project: + + ```bash + bundle install + foreman start + ``` +6. Fork impac-angular (https://github.com/maestrano/impac-angular) and clone it to your local machine. _Note this method for running impac-angular is only valid from versions >= 1.4.5_. +7. In the impac-angular project, modify workspace/app/services/dev-settings.svc.js as follow: ```javascript - module.factory('settings', function () { - return { - // Credentials and endpoints - mno_url: 'https://uat.maestrano.io', - impac_url: 'https://api-impac-uat.maestrano.io', - api_key: 'YOUR_KEY', - api_secret: 'YOUR_SECRET', + var DEFAULTS = { + // API Endpoints + mnoeUrl: { + host: 'http://localhost:7000', + base: '/mnoe/jpi/v1' + }, + impacUrl: { + host: 'https://api-impac-uat.maestrano.io', + base: '/api' + } ... - } - }); ``` +8. build and start the impac-angular project -3. There a few ways to prepare & launch the Impac Developer Workspace.. - 1. Run `gulp` or `gulp serve`, this will inject dependencies, serve impac-angular, and **live reload** on change. - 2. Run `gulp serve:noreload`, if rebuilding the library on every change is using too much cpu - **you will need to run `gulp workspace` and refresh the page after every change** - 2. Run `gulp workspace` to build and inject dependencies, the open the `workspace/index.html` file in a browser - **you will need to run `gulp workspace` and refresh the page after every change** + ```bash + npm install + bower install + gulp + ``` +9. A new tab with the Developer Workspace will open in your browser, log-in with the email/password you've created at step 1. +10. You should be able to start coding in the project impac-angular: just saving any .coffee or .html file will automatically reload your Developer Workspace, applying your code. +11. To populate the Widgets and KPIs with data, integrate an App! I recommend Xero. +Note: Coming in a patch version soon you will be able to skip steps 2. to 5. ##### Workspace Architecture diff --git a/bower.json b/bower.json index 681e60ee..4b1acabd 100755 --- a/bower.json +++ b/bower.json @@ -38,7 +38,10 @@ }, "devDependencies": { "angular-mocks": "~1.4.0", - "angular-scenario": "~1.4.0" + "angular-scenario": "~1.4.0", + "AngularDevise": "angular-devise#^1.3.0", + "angular-ui-router": "^0.3.1", + "angular-cookies": "^1.5.8" }, "license": "Copyright 2015 Maestrano Pty Ltd", "repository": { @@ -46,6 +49,7 @@ "url": "git://github.com/maestrano/impac-angular.git" }, "resolutions": { - "angular": "~1.4.0" + "angular": "1.4.14", + "angular-cookies": "^1.5.8" } } diff --git a/gulp/server.js b/gulp/server.js index cf44320e..e4f018f2 100644 --- a/gulp/server.js +++ b/gulp/server.js @@ -11,6 +11,8 @@ var $ = require('gulp-load-plugins')({ pattern: ['browser-*'] }); +var proxyMiddleware = require('http-proxy-middleware'); + function browserSyncInit(baseDir, browser) { browser = browser === undefined ? 'default' : browser; diff --git a/gulp/workspace.js b/gulp/workspace.js index a12c2274..9d83d594 100644 --- a/gulp/workspace.js +++ b/gulp/workspace.js @@ -2,12 +2,19 @@ var gulp = require('gulp'); var wiredep = require('wiredep').stream; +var $ = require('gulp-load-plugins')(); /** * Builds and injects dependencies into workspace/index.html */ gulp.task('workspace', ['build'], function () { + var injectFiles = gulp.src(['workspace/app/**/*.js', 'workspace/app/**/*.css'], {read: false}); + var injectOptions = { relative: true }; + return gulp.src('workspace/index.html') - .pipe(wiredep({})) + .pipe(wiredep({ + devDependencies: true + })) + .pipe($.inject(injectFiles, injectOptions)) .pipe(gulp.dest('workspace')); }); diff --git a/package.json b/package.json index f1a30828..74ead509 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "gulp-sourcemaps": "^1.6.0", "gulp-uglify": "^0.3.1", "gulp-util": "^3.0.6", + "http-proxy-middleware": "^0.17.2", "jasmine-core": "^2.4.1", "karma": "~0.12.22", "karma-chrome-launcher": "^0.1.4", diff --git a/src/services/routes/routes.svc.coffee b/src/services/routes/routes.svc.coffee index bf0e85db..040b0dd1 100644 --- a/src/services/routes/routes.svc.coffee +++ b/src/services/routes/routes.svc.coffee @@ -1,4 +1,5 @@ # provider for configuring http endpoints. +# TODO: REFACTOR THIS FOR MNOE AS DEFAULT + REVISE BASE PATHS DEFAULTS. angular .module('impac.services.routes', []) .provider('ImpacRoutes', () -> diff --git a/workspace/app/app.js b/workspace/app/app.js new file mode 100644 index 00000000..dde52ce2 --- /dev/null +++ b/workspace/app/app.js @@ -0,0 +1,133 @@ +// IMPAC! WORKSPACE +//-------------------------------------------------------- +// PLEASE DO NOT COMMIT CHANGES TO THIS FILE. +// You can use `git add -p ./` to selectively add files to your git index. +// ------------------------------------------------------- +var module = angular.module('impacWorkspace', [ + 'maestrano.impac', + 'toastr', + 'Devise', + 'ui.router', + 'ngCookies' +]); + +// -- +// The Developer Toolkit Workspace States. +// ------------------------------------------------------- +module.config(function ($stateProvider, $urlRouterProvider) { + + $urlRouterProvider.otherwise('/login'); + + $stateProvider + .state('workspace', { + abstract: true, + templateUrl: 'app/views/workspace/workspace.html', + controller: 'WorkspaceController', + controllerAs: 'main' + }) + .state('workspace.login', { + url: '/login', + templateUrl: 'app/views/workspace/login/login.html', + controller: 'LoginController', + controllerAs: 'vm' + }) + .state('workspace.impac', { + url: '/impac', + templateUrl: 'app/views/workspace/impac/impac.html', + controller: 'ImpacController', + controllerAs: 'vm' + }) + .state('workspace.settings', { + url: '/settings', + templateUrl: 'app/views/workspace/settings/settings.html', + controller: 'SettingsController', + controllerAs: 'vm' + }); + +}); + +// -- +// Configure Angular Devise paths for mno-enterprise. +// ------------------------------------------------------- +module.config(function (AuthProvider, DevSettingsProvider) { + var mnoeHostUrl = DevSettingsProvider.$get().defaults().mnoeUrl.host; + // Customize login + AuthProvider.loginMethod('POST'); + AuthProvider.loginPath(mnoeHostUrl + '/mnoe/auth/users/sign_in.json'); + + // Customize logout + AuthProvider.logoutMethod('DELETE'); + AuthProvider.logoutPath(mnoeHostUrl + '/mnoe/auth/users/sign_out.json'); + + // Customize register + AuthProvider.registerMethod('POST'); + AuthProvider.registerPath(mnoeHostUrl + '/mnoe/auth/users'); +}); + +// -- +// Configure Angular $http to apply XSRF Token headers to CORS requests. +// ------------------------------------------------------- +module.constant('CSRF', { + "headerTokenKey": 'X-XSRF-TOKEN', + "cookieTokenKey": 'XSRF-TOKEN' +}); +module.config(function($httpProvider) { + // Allow "credentialed" requests that are aware of HTTP cookies and HTTP + // Authentication information. + $httpProvider.defaults.withCredentials = true; +}); +module.run(function($http, DevSession) { + DevSession.create(); +}); + +// -- +// Impac! Angular Provider Service Configurations. +// ------------------------------------------------------- +module.run(function (ImpacLinking, ImpacAssets, ImpacRoutes, ImpacTheming, ImpacDeveloper, DevUser, DevSettings) { + + var defaults = DevSettings.defaults(); + + // Configure ImpacRoutes + // ------------------------------------------------------- + ImpacRoutes.configureRoutes(DevSettings.buildRoutesConfig(defaults.mnoeUrl, defaults.impacUrl, defaults.multipleWatchableMode)); + + + // Configure ImpacTheming - aesthetic and feature customisations across the app + // ------------------------------------------------------- + ImpacTheming.configure({ + dhbKpisConfig: { + enableKpis: true + }, + alertsConfig: { + enableAlerts: true + }, + dhbConfig: { + showDhbHeading: true, + dhbHeadingText: 'Your business at a glance, in real-time' + }, + dhbSelectorConfig: { + selectorType: 'dropdown', + pdfModeEnabled: true + }, + dhbSettings: { + syncApps: { + show: function() { return false; } + } + } + }); + + // Configure ImpacAssets - app asset file configurations + // ------------------------------------------------------- + ImpacAssets.configure({ + defaultImagesPath: '/dist/images', + }) + + // Configure ImpacDeveloper - developer toolkit specifc customisations + // ------------------------------------------------------- + ImpacDeveloper.configure(DevSettings.buildDeveloperConfig(defaults.widgetsTemplates)); + + // Configure ImpacLinking - Links core callbacks required for impac-angular lib to run. + // ------------------------------------------------------- + ImpacLinking.linkData(DevSettings.buildLinkingConfig(defaults.orgUid, defaults.mnoeUrl)); + +}); diff --git a/workspace/app/services/dev-session.svc.js b/workspace/app/services/dev-session.svc.js new file mode 100644 index 00000000..fd12c40f --- /dev/null +++ b/workspace/app/services/dev-session.svc.js @@ -0,0 +1,29 @@ +// ------------------------------------------------------- +// Impac! Angular DevSession Service +// -------- +// Providing CSRF Support for Impac! Workspace. +// ------------------------------------------------------- +angular.module('impacWorkspace').service('DevSession', function ($log, $http, $cookies, CSRF, DevSettings) { + var _self = this; + + // Create a session and xsrf token - ping mnoe api so we get a valid XSRF cookie. + this.create = function () { + return $http.get(DevSettings.defaults().mnoeUrl.host + '/mnoe/auth/users/sign_in.json') + .success(function() { + _self.setCsrfHttpHeader(); + }); + }; + + // Angular Devise methods (e.g `Auth.currentUser()`) generate new sessions, this keeps the + // default http headers up to date, ensuring the session is maintained. + this.update = function () { + this.setCsrfHttpHeader(); + }; + + // Set stored XSRF cookie as $http headers common. + this.setCsrfHttpHeader = function () { + $http.defaults.headers.common[CSRF.headerTokenKey] = $cookies.get(CSRF.cookieTokenKey); + }; + + return this; +}); diff --git a/workspace/app/services/dev-settings.svc.js b/workspace/app/services/dev-settings.svc.js new file mode 100644 index 00000000..a775bc86 --- /dev/null +++ b/workspace/app/services/dev-settings.svc.js @@ -0,0 +1,120 @@ +// ------------------------------------------------------- +// Impac Workspace Settings +// -------- +// Providing Impac! Angular defaults and configuration abilities. +// ------------------------------------------------------- +angular.module('impacWorkspace').service('DevSettings', function ($q, ImpacRoutes, ImpacLinking, ImpacDeveloper, DevUser) { + var _self = this; + + var DEFAULTS = { + // API Endpoints + mnoeUrl: { + host: 'http://localhost:7000', + base: '/mnoe/jpi/v1' + }, + impacUrl: { + host: 'https://api-impac-uat.maestrano.io', + base: '/api' + }, + // Selected Organization - first organisations if unspecified + orgUid: '', + // ----------------------------------------------- + // Kpis configurations + // ----------------------------------------------- + // Change this to `true` to force kpi's to allow multiple watchables, instead of + // using the mno_hub kpis#discover action maps kpis to have single watchables per kpi. + multipleWatchableMode: false, + // Stub widget templates - add new widgets! + //------------------------------------------------ + // Widget templates are stored on Maestrano API, stub your + // new widget templates here, and contact us for permenant additions or + // changes to existing widget templates. + widgetsTemplates: [ + // Example widget template, please see documentation "How-To: Create a Widget" for available + // options, and importantly, note how the path & metadata.template attributes work. + // -- + // { + // path: 'accounts/assets_summary', + // name: 'Assets / Liabilities summary', + // metadata: { template: 'accounts/assets_liability_summary' }, + // desc: 'Compare Assets and Liabilities accounts', + // icon: 'pie-chart', + // width: 3 + // }, + ] + }; + + this._defaults = angular.copy(DEFAULTS); + + this.defaults = function () { + return angular.copy(_self._defaults); + }; + + this.resetDefaults = function () { + return _self._defaults = angular.copy(DEFAULTS); + } + + // NOTE: this will be refactored a lot when mnoe routes are added as default + // in impac-angular. + // TODO: add routes settings to #/settings, 'basic' & 'advance' tabs could be good. + this.buildRoutesConfig = function (mnoeUrl, impacUrl, multipleWatchableMode) { + var routesConfig = { + mnoHub: mnoeUrl.host + mnoeUrl.base, + impacPrefix: "/impac", + impacApi: impacUrl.host + impacUrl.base, + dashboards: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/dashboards" + }, + widgets: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/widgets", + create: mnoeUrl.host + mnoeUrl.base + "/impac/dashboards/:dashboard_id/widgets" + }, + kpis: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/kpis", + create: mnoeUrl.host + mnoeUrl.base + "/impac/dashboards/:dashboard_id/kpis", + update: mnoeUrl.host + mnoeUrl.base + "/impac/kpis/:id", + del: mnoeUrl.host + mnoeUrl.base + "/impac/kpis/:id" + }, + alerts: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/alerts", + create: mnoeUrl.host + mnoeUrl.base + "/impac/kpis/:kpi_id/alerts", + del: mnoeUrl.host + mnoeUrl.base + "/impac/alerts/:id" + } + } + // Removes custom index path, defaulting kpi discovery to impac api, where KPIs with + // multiple watchables are returned. + if (multipleWatchableMode) { delete routesConfig.kpis.index; } + return angular.copy(routesConfig) + }; + + this.buildLinkingConfig = function (orgUid, mnoeUrl) { + return { + organizations: function () { + return DevUser.getOrganizations(orgUid, mnoeUrl); + }, + user: function () { + return DevUser.getUser(mnoeUrl); + }, + // TODO: add pusher key to advance settings on #/settings. + pusher_key: 'e98dfd8e4a359a7faf48' // Maestrano pusher account key. + } + } + + this.buildDeveloperConfig = function (widgetsTemplates) { + return { + status: true, + widgetsTemplates: widgetsTemplates + } + }; + + this.updateProviders = function (data) { + ImpacRoutes.configureRoutes(_self.buildRoutesConfig(data.mnoeUrl, data.impacUrl, data.multipleWatchableMode)); + ImpacLinking.linkData(_self.buildLinkingConfig(data.orgUid, data.mnoeUrl)); + ImpacDeveloper.configure(_self.buildDeveloperConfig(data.widgetsTemplates)); + + // Save settings data to this service's copy of defaults. + _self._defaults = data; + }; + + return this; +}); diff --git a/workspace/app/services/dev-user.svc.js b/workspace/app/services/dev-user.svc.js new file mode 100644 index 00000000..148da8de --- /dev/null +++ b/workspace/app/services/dev-user.svc.js @@ -0,0 +1,81 @@ +// ------------------------------------------------------- +// Impac! Angular DevUser Service +// -------- +// Providing Auth & User data for the Impac! Workspace. +// ------------------------------------------------------- +angular.module('impacWorkspace').service('DevUser', function ($log, $http, $q, Auth, toastr) { + var _self = this; + + // Angular Devise methods exposed via this service + // ------ + this.login = Auth.login; + this.logout = Auth.logout; + this.register = Auth.register; + this.isAuthenticated = Auth.isAuthenticated; + this.currentUser = Auth.currentUser; + + // MnoExpress User & Organization retrieval + // ------ + this.getUserData = function(mnoeUrl) { + var errMsg = 'Unable to retrieve User data'; + var url = mnoeUrl.host + mnoeUrl.base + '/current_user'; + return $http.get(url).then( + function (response) { + var user = (response.data && response.data.current_user); + if (!user.logged_in) { + _self.fail(errMsg); + return $q.reject(errMsg); + } + return user || {}; + }, function (error) { + _self.fail(errMsg, error); + return $q.reject(errMsg); + } + ); + }; + + this.getCurrentOrganization = function(orgUid, mnoeUrl) { + var deferred = $q.defer(); + _self.getOrganizations(orgUid, mnoeUrl).then(function (response) { + var currentOrg = _.find(response.organizations, function (org) { + return org.id == response.currentOrgId; + }); + deferred.notify(currentOrg); + }); + return deferred.promise; + }; + + this.getOrganizations = function(orgUid, mnoeUrl) { + return _self.getUserData(mnoeUrl).then( + function (user) { + var orgs = (user.organizations || []); + var orga = orgs.find(function(orga) { return orga.uid == orgUid }); + var orgId = (orga && orga.id) || orgs[0].id || null; + + return { organizations: orgs, currentOrgId: orgId }; + }, function (err) { + return $q.reject(err); + } + ); + }; + + this.getUser = function(mnoeUrl) { + return _self.getUserData(mnoeUrl).then( + function (user) { + return user; + }, function (err) { + return $q.reject(err); + } + ); + }; + + // Logging and Toastr notifications + // ------ + this.fail = function (msg, error) { + if (error == null) { error = ''; } + $log.error('Develop Workspace Error: ' + msg, error); + toastr.error(msg, 'Error'); + }; + + return this; +}); diff --git a/workspace/app/views/workspace/impac/impac.controller.js b/workspace/app/views/workspace/impac/impac.controller.js new file mode 100644 index 00000000..05357901 --- /dev/null +++ b/workspace/app/views/workspace/impac/impac.controller.js @@ -0,0 +1,11 @@ +angular.module('impacWorkspace').controller('ImpacController', function ($state, DevUser) { + vm = this; + + vm.showImpac = DevUser.isAuthenticated; + + DevUser.currentUser().then(null, function () { + $state.go('workspace.login'); + }); + + return vm; +}); diff --git a/workspace/app/views/workspace/impac/impac.html b/workspace/app/views/workspace/impac/impac.html new file mode 100644 index 00000000..342e5680 --- /dev/null +++ b/workspace/app/views/workspace/impac/impac.html @@ -0,0 +1,3 @@ +