├── .nvmrc ├── src ├── styles │ ├── headings.css │ ├── _variables.scss │ ├── ngx-select.scss │ └── styles.scss ├── app │ ├── home │ │ ├── home.component.css │ │ ├── home.component.html │ │ ├── index.ts │ │ ├── home.component.ts │ │ ├── home-routing.module.ts │ │ ├── home.e2e.ts │ │ └── home.module.ts │ ├── canvas-graph │ │ ├── canvas-graph.component.css │ │ ├── canvas-graph.component.html │ │ ├── canvas-graph.module.ts │ │ └── canvas-graph.component.spec.ts │ ├── global-alert │ │ ├── global-alert.component.css │ │ ├── global-alert.component.html │ │ └── global-alert.component.ts │ ├── inventory │ │ ├── modal │ │ │ ├── confirm-modal.component.scss │ │ │ ├── details-modal.component.scss │ │ │ ├── details-modal.component.html │ │ │ ├── confirm-modal.component.html │ │ │ ├── details-modal.component.ts │ │ │ └── confirm-modal.component.ts │ │ ├── footer │ │ │ ├── footer.component.scss │ │ │ ├── footer.component.html │ │ │ └── footer.component.ts │ │ ├── dropdown-group │ │ │ ├── dropdown-group.component.scss │ │ │ └── dropdown-group.component.html │ │ ├── header │ │ │ ├── header.component.scss │ │ │ ├── header.component.html │ │ │ └── header.component.ts │ │ └── inventory.module.ts │ ├── workflow-center │ │ ├── workflow-center.component.scss │ │ ├── active-workflow │ │ │ ├── active-workflow.component.scss │ │ │ └── active-workflow.component.spec.ts │ │ ├── history-workflow │ │ │ ├── history-workflow.component.scss │ │ │ └── history-workflow.component.spec.ts │ │ ├── workflow-viewer │ │ │ ├── workflow-viewer.component.scss │ │ │ ├── workflow-viewer.component.html │ │ │ ├── workflow-viewer.component.spec.ts │ │ │ └── workflow-viewer.component.ts │ │ ├── workflow-center.component.ts │ │ ├── run-workflow │ │ │ ├── run-workflow.component.spec.ts │ │ │ ├── run-workflow.component.scss │ │ │ └── run-workflow.component.html │ │ ├── workflow-center.component.spec.ts │ │ ├── workflow-editor │ │ │ ├── workflow-editor.component.spec.ts │ │ │ ├── workflow-editor.component.scss │ │ │ └── workflow-editor.component.html │ │ ├── workflow-center.component.html │ │ ├── workflow-center-routing.module.ts │ │ └── workflow-center.module.ts │ ├── management-center │ │ ├── management-center.component.css │ │ ├── skus │ │ │ ├── sku.component.scss │ │ │ └── sku.component.spec.ts │ │ ├── files │ │ │ ├── files.component.scss │ │ │ ├── files.component.spec.ts │ │ │ └── files.component.html │ │ ├── catalogs │ │ │ ├── catalogs.component.scss │ │ │ ├── catalogs.component.spec.ts │ │ │ ├── catalogs.component.ts │ │ │ └── catalogs.component.html │ │ ├── profiles │ │ │ ├── profiles.component.scss │ │ │ ├── profiles.component.spec.ts │ │ │ ├── profiles.component.html │ │ │ └── profiles.component.ts │ │ ├── templates │ │ │ ├── templates.component.scss │ │ │ ├── templates.component.spec.ts │ │ │ ├── templates.component.html │ │ │ └── templates.component.ts │ │ ├── workflows │ │ │ ├── workflows.component.scss │ │ │ └── workflows.component.spec.ts │ │ ├── obms │ │ │ ├── obm.component.scss │ │ │ └── obm.component.spec.ts │ │ ├── nodes │ │ │ ├── nodes.component.scss │ │ │ └── nodes.component.spec.ts │ │ ├── pollers │ │ │ ├── pollers.component.scss │ │ │ └── pollers.component.spec.ts │ │ ├── configs │ │ │ ├── config.component.scss │ │ │ ├── config.component.spec.ts │ │ │ └── config.component.html │ │ ├── management-center.component.ts │ │ ├── services │ │ │ ├── file.service.ts │ │ │ ├── profile.service.ts │ │ │ ├── template.service.ts │ │ │ ├── config.service.ts │ │ │ ├── ibm.service.ts │ │ │ └── management-center-service.module.ts │ │ ├── management-center.component.spec.ts │ │ ├── shared │ │ │ └── inventory.scss │ │ ├── management-center-routing.module.ts │ │ ├── management-center.component.html │ │ └── management-center.module.ts │ ├── solution-center │ │ ├── os-install │ │ │ ├── os-install.component.spec.ts │ │ │ └── os-install.component.scss │ │ ├── solution-center.component.scss │ │ ├── solution-center.component.ts │ │ ├── solution-center.component.html │ │ ├── solution-center.component.spec.ts │ │ ├── solution-center-routing.module.ts │ │ └── solution-center.module.ts │ ├── app.component.css │ ├── index.ts │ ├── header │ │ ├── index.ts │ │ ├── header.component.scss │ │ ├── header.component.spec.ts │ │ ├── header.e2e.ts │ │ ├── header.component.ts │ │ └── header.component.html │ ├── utils │ │ ├── json-editor.ts │ │ ├── globals-util.ts │ │ └── inventory-operator.ts │ ├── models │ │ ├── tag.ts │ │ ├── config.ts │ │ ├── ibm.ts │ │ ├── task.ts │ │ ├── catalog.ts │ │ ├── graph.ts │ │ ├── profile.ts │ │ ├── template.ts │ │ ├── file.ts │ │ ├── graphtask.ts │ │ ├── sku.ts │ │ ├── timeSerial.ts │ │ ├── index.ts │ │ ├── poller.ts │ │ ├── workflow.ts │ │ ├── inventory.ts │ │ ├── setting.ts │ │ └── node.ts │ ├── app.component.html │ ├── no-content │ │ ├── index.ts │ │ ├── no-content.module.ts │ │ └── no-content.component.ts │ ├── services │ │ ├── core │ │ │ ├── index.ts │ │ │ ├── global-alert.service.ts │ │ │ ├── core.module.ts │ │ │ └── error-handler.service.ts │ │ ├── rackhd │ │ │ ├── task.service.ts │ │ │ ├── graph.service.ts │ │ │ ├── obm.service.ts │ │ │ ├── rackhd.module.ts │ │ │ ├── node.service.ts │ │ │ ├── pollers.service.ts │ │ │ ├── tag.service.ts │ │ │ ├── catalogs.service.ts │ │ │ ├── sku.service.ts │ │ │ └── workflow.service.ts │ │ └── sharedServices.module.ts │ ├── app.e2e.ts │ ├── app.component.spec.ts │ ├── app-routing.module.ts │ ├── settings │ │ ├── setting.module.ts │ │ ├── setting-form.component.scss │ │ └── setting.service.ts │ ├── app.routes.ts │ ├── environment.ts │ ├── app.component.ts │ └── app.module.ts ├── assets │ ├── logo.png │ ├── icon │ │ └── favicon.ico │ └── rackhd-icon.png ├── environments │ ├── model.ts │ ├── environment.e2e.prod.ts │ ├── environment.prod.ts │ └── environment.ts ├── testing │ ├── index.ts │ └── router-stub.ts ├── main.browser.aot.ts ├── config │ └── consts.ts ├── main.browser.ts ├── index.html └── polyfills.browser.ts ├── config ├── resource-override.js ├── empty.js ├── nginx.conf ├── helpers.js ├── protractor.conf.js ├── head-config.common.js ├── spec-bundle.js ├── github-deploy │ └── index.js ├── webpack.github-deploy.js ├── html-elements-plugin │ └── index.js ├── karma.conf.js └── build-utils.js ├── .gitattributes ├── netlify.toml ├── karma.conf.js ├── protractor.conf.js ├── firebase.json ├── .editorconfig ├── typedoc.json ├── .dockerignore ├── webpack.config.js ├── tsconfig.webpack.json ├── tsconfig.json ├── .gitignore ├── Dockerfile ├── .angular-cli.json ├── tslint.json ├── docs └── deployment.md └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /src/styles/headings.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/resource-override.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/home/home.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | yarn.lock -diff 2 | -------------------------------------------------------------------------------- /src/app/canvas-graph/canvas-graph.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/global-alert/global-alert.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/inventory/modal/confirm-modal.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/inventory/modal/details-modal.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-center.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/management-center/management-center.component.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/solution-center/os-install/os-install.component.spec.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .main-container{ 2 | height:auto; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 | Inventory 2 | -------------------------------------------------------------------------------- /src/app/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * App 3 | */ 4 | export * from './app.module'; 5 | -------------------------------------------------------------------------------- /src/app/header/index.ts: -------------------------------------------------------------------------------- 1 | export { HeaderComponent } from './header.component'; 2 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "npm run build:prod" 3 | publish = "dist" 4 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackHD/on-web-ui/master/src/assets/logo.png -------------------------------------------------------------------------------- /src/app/management-center/skus/sku.component.scss: -------------------------------------------------------------------------------- 1 | .app-sku{ 2 | @import "../shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackHD/on-web-ui/master/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /src/assets/rackhd-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackHD/on-web-ui/master/src/assets/rackhd-icon.png -------------------------------------------------------------------------------- /src/app/management-center/files/files.component.scss: -------------------------------------------------------------------------------- 1 | .app-file { 2 | @import "../shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/management-center/catalogs/catalogs.component.scss: -------------------------------------------------------------------------------- 1 | .app-catalogs { 2 | @import "../shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/management-center/profiles/profiles.component.scss: -------------------------------------------------------------------------------- 1 | .profile-app { 2 | @import "../shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/management-center/templates/templates.component.scss: -------------------------------------------------------------------------------- 1 | .template-app{ 2 | @import "../shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/solution-center/solution-center.component.scss: -------------------------------------------------------------------------------- 1 | .offset-header { 2 | height: 100vh; 3 | margin-top: 60px; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/utils/json-editor.ts: -------------------------------------------------------------------------------- 1 | let JSONEditor = require('../../../node_modules/jsoneditor/src/js/JSONEditor.js'); 2 | export {JSONEditor}; 3 | -------------------------------------------------------------------------------- /src/app/home/index.ts: -------------------------------------------------------------------------------- 1 | // for unit test 2 | export { HomeModule } from './home.module'; 3 | export { HomeComponent } from './home.component'; 4 | -------------------------------------------------------------------------------- /src/app/workflow-center/active-workflow/active-workflow.component.scss: -------------------------------------------------------------------------------- 1 | .app-active-workflows { 2 | @import "../../management-center/shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/workflow-center/history-workflow/history-workflow.component.scss: -------------------------------------------------------------------------------- 1 | .app-history-workflows { 2 | @import "../../management-center/shared/inventory"; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/management-center/workflows/workflows.component.scss: -------------------------------------------------------------------------------- 1 | .app-graph { 2 | @import "../shared/inventory"; 3 | .input-size { 4 | width: 320px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * Look in ./config for karma.conf.js 7 | */ 8 | module.exports = require('./config/karma.conf.js'); 9 | -------------------------------------------------------------------------------- /src/app/header/header.component.scss: -------------------------------------------------------------------------------- 1 | header { 2 | background: linear-gradient(to right, #000000 20%, #00447C 80%); 3 | position: fixed; 4 | width: 100vw; 5 | z-index: 9999; 6 | } 7 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * look in ./config for protractor.conf.js 7 | */ 8 | exports.config = require('./config/protractor.conf.js').config; 9 | -------------------------------------------------------------------------------- /src/app/canvas-graph/canvas-graph.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/models/tag.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Tag. 3 | */ 4 | export class Tag { 5 | } 6 | 7 | export const TAG_URL = { 8 | getAllUrl: '/tags', 9 | getByIdentifierUrl: '/tags/', 10 | } 11 | -------------------------------------------------------------------------------- /src/app/management-center/obms/obm.component.scss: -------------------------------------------------------------------------------- 1 | .app-obm{ 2 | @import "../shared/inventory"; 3 | .align-width { 4 | width: 80%; 5 | } 6 | .node-dropdown { 7 | padding-left: 12px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/management-center/nodes/nodes.component.scss: -------------------------------------------------------------------------------- 1 | .app-nodes { 2 | @import "../shared/inventory"; 3 | .grid-header { 4 | margin-bottom: 6px; 5 | } 6 | .aligned-width { 7 | width: 30%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/inventory/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | display: flex; 3 | justify-content: flex-end; 4 | align-items: center; 5 | .select { 6 | margin-left: 5px; 7 | margin-right: 36px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/environments/model.ts: -------------------------------------------------------------------------------- 1 | import { NgModuleRef } from '@angular/core'; 2 | 3 | export interface Environment { 4 | production: boolean; 5 | ENV_PROVIDERS: any; 6 | showDevModule: boolean; 7 | decorateModuleRef(modRef: NgModuleRef): NgModuleRef; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/management-center/pollers/pollers.component.scss: -------------------------------------------------------------------------------- 1 | .app-pollers{ 2 | @import "../shared/inventory"; 3 | } 4 | 5 | .warning{ 6 | color: red; 7 | } 8 | 9 | #pollerUpdateForm { 10 | height: 250px; 11 | } 12 | 13 | a { 14 | cursor: pointer; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit,OnDestroy } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'home', 5 | styleUrls: ['./home.component.css'], 6 | templateUrl: './home.component.html' 7 | }) 8 | export class HomeComponent { 9 | } 10 | -------------------------------------------------------------------------------- /src/app/models/config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of RackHD configures. 3 | */ 4 | export class Config { 5 | key: string; 6 | value: string; 7 | } 8 | 9 | export const CONFIG_URL = { 10 | getAllUrl: '/config', 11 | patchUrl: '/config', 12 | }; 13 | -------------------------------------------------------------------------------- /src/app/no-content/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * route with ** doesn't support lazy loading components, so this module must be 3 | * exported for app.moule importing. Then app.moule can just use NoContentComponent 4 | * in 404 route. 5 | */ 6 | export { NoContentModule } from './no-content.module'; 7 | -------------------------------------------------------------------------------- /src/app/no-content/no-content.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { NoContentComponent } from './no-content.component'; 4 | 5 | 6 | @NgModule({ 7 | declarations: [ 8 | NoContentComponent 9 | ], 10 | }) 11 | export class NoContentModule {} 12 | -------------------------------------------------------------------------------- /src/app/models/ibm.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Node's OBM. 3 | */ 4 | export class IBM { 5 | id: string; 6 | node: string; 7 | service: string; 8 | config: {}; 9 | } 10 | 11 | export const IBM_URL = { 12 | ibms: '/ibms', 13 | ibmsById: '/ibms/', 14 | } 15 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "destination": "/index.html" 8 | } 9 | ], 10 | "ignore": [ 11 | "firebase.json", 12 | "**/.*", 13 | "**/node_modules/**" 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /src/app/inventory/modal/details-modal.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/models/task.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Node's task. 3 | */ 4 | export class Task { 5 | lable: string; 6 | instanceId: string; 7 | options?: {}; 8 | runJob: string; 9 | state: string; 10 | taskStartTime: string; 11 | terminalOnStates: Array; 12 | waitingOn: {}; 13 | } 14 | -------------------------------------------------------------------------------- /config/empty.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | hmrModule: function(ngmodule) { 3 | return ngmodule; 4 | }, 5 | NgProbeToken: {}, 6 | HmrState: function() {}, 7 | _createConditionalRootRenderer: function(rootRenderer, extraTokens, coreTokens) { 8 | return rootRenderer; 9 | }, 10 | __platform_browser_private__: {} 11 | }; 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # @AngularClass 2 | # http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | insert_final_newline = false 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /src/app/models/catalog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Node's catalog. 3 | */ 4 | export class Catalog { 5 | id: string; 6 | node: string; 7 | source: string; 8 | data: any; // {} or [] 9 | } 10 | 11 | export const CATALOG_URL = { 12 | getAllUrl: '/catalogs', 13 | getByIdentifierUrl: '/catalogs/', 14 | } 15 | -------------------------------------------------------------------------------- /src/app/services/core/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * export the core module for importing it in root module. 3 | * then all service can be injected. 4 | */ 5 | export { AppCoreModule } from './core.module'; 6 | 7 | /** 8 | * export services' class in support of importing in app wide. 9 | */ 10 | export { IconService } from './icon.service'; 11 | 12 | -------------------------------------------------------------------------------- /src/app/management-center/configs/config.component.scss: -------------------------------------------------------------------------------- 1 | .config-app{ 2 | @import "../shared/inventory"; 3 | .modal-input { 4 | margin-top: 8px; 5 | margin-bottom: 8px; 6 | width: 100%; 7 | input { 8 | width: 60%; 9 | } 10 | } 11 | .label-size { 12 | display: inline-block; 13 | width: 20%; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/testing/index.ts: -------------------------------------------------------------------------------- 1 | // export for convenience. 2 | export { 3 | ActivatedRoute, 4 | Router, 5 | RouterLink, 6 | RouterOutlet, 7 | NavigationExtras 8 | } from '@angular/router'; 9 | 10 | export { 11 | RouterLinkStubDirective, 12 | RouterStub, 13 | ActivatedRouteStub, 14 | RouterOutletStubComponent 15 | } from './router-stub'; 16 | -------------------------------------------------------------------------------- /config/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | location / { 6 | root /usr/share/nginx/html; 7 | try_files $uri $uri/ /index.html; 8 | } 9 | 10 | error_page 500 502 503 504 /50x.html; 11 | location = /50x.html { 12 | root /usr/share/nginx/html; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HeaderComponent } from './header.component'; 4 | 5 | describe('', () => { 6 | it('should have one unit test case', async(() => { 7 | expect('unit test').toBe('unit test'); 8 | })); 9 | }); 10 | -------------------------------------------------------------------------------- /src/app/models/graph.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Workflow Metadata. 3 | */ 4 | export class Graph { 5 | friendlyName: string; 6 | injectableName: string; 7 | tasks: any; 8 | options: any; 9 | } 10 | 11 | export const GRAPH_URL = { 12 | getAllUrl: '/workflows/graphs', 13 | getByIdentifierUrl: '/workflows/graphs/', 14 | } 15 | -------------------------------------------------------------------------------- /src/app/models/profile.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of RackHD profiles. 3 | */ 4 | export class Profile { 5 | id: string; 6 | name: string; 7 | hash: string; 8 | scope: string; 9 | } 10 | 11 | export const PROFILE_URL = { 12 | getAllUrl: '/profiles/metadata', 13 | getByIdentifierUrl: '/profiles/library/', 14 | getMetadataUrl: '/profiles/metadata/' 15 | } 16 | -------------------------------------------------------------------------------- /src/app/models/template.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of RackHD templates. 3 | */ 4 | export class Template { 5 | id: string; 6 | name: string; 7 | hash: string; 8 | scope: string; 9 | } 10 | 11 | export const TEMPLATE_URL = { 12 | getAllUrl: '/templates/metadata', 13 | getByIdentifierUrl: '/templates/library/', 14 | getMetadataUrl: '/templates/metadata/', 15 | } 16 | -------------------------------------------------------------------------------- /src/app/app.e2e.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | import 'tslib'; 3 | 4 | describe('App', () => { 5 | 6 | beforeEach(async () => { 7 | await browser.get('/'); 8 | }); 9 | 10 | it('should have a title', async () => { 11 | let subject = await browser.getTitle(); 12 | let result = 'RackHD Web UI 2.0'; 13 | expect(subject).toEqual(result); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/models/file.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of RackHD profiles. 3 | */ 4 | export class File { 5 | uuid: string; 6 | version: string; 7 | sha256: string; 8 | md5: string; 9 | filename: string; 10 | basename: string; 11 | } 12 | 13 | export const FILE_URL = { 14 | getAllUrl: '/files', 15 | getByIdentifierUrl: '/files/', 16 | getMetadataUrl: '/files/' 17 | } 18 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-viewer/workflow-viewer.component.scss: -------------------------------------------------------------------------------- 1 | .workflow-viewer{ 2 | width: 100%; 3 | height: 100%; 4 | .header-text { 5 | padding-top: 0; 6 | margin-top: 0; 7 | font-weight: 600; 8 | } 9 | .search-form { 10 | padding-top: -15px 11 | } 12 | .canvas { 13 | float: top; 14 | margin-left: 0px; 15 | margin-right: 5px; 16 | height: 85%; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/no-content/no-content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'no-content', 5 | template: ` 6 |
7 |

404: page not found

8 |
9 | ` 10 | }) 11 | export class NoContentComponent implements OnInit { 12 | public ngOnInit() { 13 | console.log('hello `404` component'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/solution-center/solution-center.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-solution-center', 5 | templateUrl: './solution-center.component.html', 6 | styleUrls: ['./solution-center.component.scss'] 7 | }) 8 | export class SolutionCenterComponent implements OnInit { 9 | 10 | constructor() { 11 | } 12 | 13 | ngOnInit() { 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-center.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-workflow-center', 5 | templateUrl: './workflow-center.component.html', 6 | styleUrls: ['./workflow-center.component.scss'] 7 | }) 8 | export class WorkflowCenterComponent implements OnInit { 9 | constructor() { 10 | } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/management-center/management-center.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-management-center', 5 | templateUrl: './management-center.component.html', 6 | styleUrls: ['./management-center.component.css'] 7 | }) 8 | export class ManagementCenterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/models/graphtask.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Graph Tasks 3 | */ 4 | export class GraphTask { 5 | friendlyName: string; 6 | injectableName: string; 7 | options: any; 8 | implementsTask: string; 9 | optionsSchema?: string; 10 | properties?: any; 11 | } 12 | 13 | export const GRAPHTASK_URL = { 14 | getAllUrl: '/workflows/tasks', 15 | getByIdentifierUrl: '/workflows/tasks/', 16 | } 17 | -------------------------------------------------------------------------------- /src/app/models/sku.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Node's SKU. 3 | */ 4 | export class SKU { 5 | id: string; 6 | name: string; 7 | discoveryGraphName: string; 8 | discoveryGraphOptions: {}; 9 | rules: any; 10 | skuConfig: any; 11 | } 12 | 13 | export const SKU_URL = { 14 | getAllUrl: '/skus', 15 | getByIdentifierUrl: '/skus/', 16 | uploadUrl: '/skus/pack', 17 | uploadSuffix: '/pack', 18 | } 19 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "out": "doc", 4 | "theme": "default", 5 | "ignoreCompilerErrors": "true", 6 | "experimentalDecorators": "true", 7 | "emitDecoratorMetadata": "true", 8 | "target": "ES5", 9 | "moduleResolution": "node", 10 | "preserveConstEnums": "true", 11 | "stripInternal": "true", 12 | "suppressExcessPropertyErrors": "true", 13 | "suppressImplicitAnyIndexErrors": "true", 14 | "module": "commonjs" 15 | } 16 | -------------------------------------------------------------------------------- /src/app/home/home-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { HomeComponent } from './home.component'; 4 | 5 | const homeRoutes: Routes = [ 6 | { path: '', component: HomeComponent} 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [ 11 | RouterModule.forChild(homeRoutes) 12 | ], 13 | exports: [ 14 | RouterModule 15 | ] 16 | }) 17 | export class HomeRoutingModule { } 18 | -------------------------------------------------------------------------------- /src/app/management-center/services/file.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | import {FILE_URL } from 'app/models'; 4 | 5 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 6 | 7 | @Injectable() 8 | export class FileService extends RackhdHttpService { 9 | 10 | constructor(public http: HttpClient) { 11 | super(http, FILE_URL); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/management-center/services/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | import {PROFILE_URL } from 'app/models'; 4 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 5 | 6 | @Injectable() 7 | export class ProfileService extends RackhdHttpService { 8 | 9 | constructor(public http: HttpClient) { 10 | super(http, PROFILE_URL); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/canvas-graph/canvas-graph.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { CanvasGraphComponent } from './canvas-graph.component'; 4 | import { NodeExtensionService } from './node-extension.service'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | CanvasGraphComponent, 9 | ], 10 | exports: [ 11 | CanvasGraphComponent, 12 | ], 13 | providers: [ 14 | NodeExtensionService, 15 | ] 16 | }) 17 | export class CanvasGraphModule {} 18 | -------------------------------------------------------------------------------- /src/app/management-center/services/template.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | import {TEMPLATE_URL } from 'app/models'; 4 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 5 | 6 | @Injectable() 7 | export class TemplateService extends RackhdHttpService { 8 | 9 | constructor(public http: HttpClient) { 10 | super(http, TEMPLATE_URL); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/models/timeSerial.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of time serial data. 3 | */ 4 | import * as _ from 'lodash'; 5 | 6 | export class TimeSerialData{ 7 | constructor(d:any){ 8 | _.assign(this,d); 9 | } 10 | time: Date ; 11 | value: number; 12 | } 13 | 14 | export class MetricData{ 15 | constructor(d:any){ 16 | _.assign(this,d); 17 | } 18 | name: string; // name of the metrics 19 | dataArray: TimeSerialData[]; 20 | } 21 | -------------------------------------------------------------------------------- /src/app/home/home.e2e.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | import 'tslib'; 3 | 4 | describe('Home', () => { 5 | 6 | beforeEach(async () => { 7 | /** 8 | * Change hash depending on router LocationStrategy. 9 | */ 10 | await browser.get('/'); 11 | }); 12 | 13 | it('should have a title', async () => { 14 | let subject = await browser.getTitle(); 15 | let result = 'RackHD Web UI 2.0'; 16 | expect(subject).toEqual(result); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/inventory/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/app/solution-center/solution-center.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | OS Install 7 | 8 | 9 |
10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/app/services/core/global-alert.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs/Subject'; 3 | 4 | @Injectable() 5 | export class GlobalAlertService { 6 | alertQueue: Subject; 7 | constructor(){ 8 | this.alertQueue = new Subject(); 9 | } 10 | 11 | 12 | putAlertMsg(msg: string, type: string = 'modal'): void{ 13 | this.alertQueue.next({msg: msg, type: type}); 14 | } 15 | 16 | getAlertQueue(): Subject{ 17 | return this.alertQueue; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .vscode 4 | coverage 5 | 6 | # OS generated files # 7 | .DS_Store 8 | ehthumbs.db 9 | Icon? 10 | Thumbs.db 11 | 12 | # Node Files # 13 | node_modules 14 | npm-debug.log 15 | npm-debug.log.* 16 | 17 | # Typing # 18 | src/typings/tsd 19 | typings 20 | tsd_typings 21 | 22 | # Dist # 23 | dist 24 | .awcache 25 | .webpack.json 26 | compiled 27 | dll 28 | 29 | # IDE # 30 | .idea 31 | *.swp 32 | 33 | 34 | # Angular # 35 | *.ngfactory.ts 36 | *.css.shim.ts 37 | *.ngsummary.json 38 | *.shim.ngstyle.ts 39 | 40 | -------------------------------------------------------------------------------- /src/app/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './setting'; 2 | export * from './timeSerial'; 3 | export * from './catalog'; 4 | export * from './node'; 5 | export * from './obm'; 6 | export * from './ibm'; 7 | export * from './poller'; 8 | export * from './task'; 9 | export * from './workflow'; 10 | export * from './profile'; 11 | export * from './config'; 12 | export * from './file'; 13 | export * from './template'; 14 | export * from './inventory'; 15 | export * from './graph'; 16 | export * from './graphtask'; 17 | export * from './sku'; 18 | export * from './tag'; 19 | -------------------------------------------------------------------------------- /src/app/utils/globals-util.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | 3 | export class RackhdLocalStorage { 4 | constructor () {} 5 | 6 | static isSecured(): boolean { 7 | return window.localStorage.getItem('rackhd.connSecured') === 'true' ? true : false; 8 | } 9 | 10 | static getBaseUrl(): string { 11 | return (RackhdLocalStorage.isSecured() ? 'https://' : 'http://') + 12 | window.localStorage.getItem('rackhd.northboundApi'); 13 | } 14 | 15 | static getToken(): string { 16 | return window.localStorage.getItem('rackhd.authToken'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * Look in ./config folder for webpack.dev.js 7 | */ 8 | switch (process.env.NODE_ENV) { 9 | case 'prod': 10 | case 'production': 11 | module.exports = require('./config/webpack.prod')({env: 'production'}); 12 | break; 13 | case 'test': 14 | case 'testing': 15 | module.exports = require('./config/webpack.test')({env: 'test'}); 16 | break; 17 | case 'dev': 18 | case 'development': 19 | default: 20 | module.exports = require('./config/webpack.dev')({env: 'development'}); 21 | } 22 | -------------------------------------------------------------------------------- /src/app/management-center/services/config.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | import {CONFIG_URL } from 'app/models'; 4 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 5 | import { Observable } from 'rxjs/Observable'; 6 | 7 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 8 | 9 | @Injectable() 10 | export class ConfigService extends RackhdHttpService { 11 | constructor(public http: HttpClient) { 12 | super(http, CONFIG_URL); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/services/rackhd/task.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { GRAPHTASK_URL } from 'app/models'; 3 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 4 | import { Observable } from 'rxjs/Observable'; 5 | 6 | import { 7 | HttpErrorResponse, 8 | HttpResponse, 9 | HttpClient, 10 | HttpHeaders, 11 | HttpParams, 12 | } from '@angular/common/http'; 13 | 14 | @Injectable() 15 | export class GraphTaskService extends RackhdHttpService { 16 | constructor( 17 | public http: HttpClient 18 | ){ 19 | super(http, GRAPHTASK_URL); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/inventory/modal/confirm-modal.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /src/app/models/poller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Node's poller. 3 | */ 4 | export class Poller { 5 | id: string; 6 | type: string; 7 | pollInterval: number; 8 | node: string; 9 | config: {}; 10 | lastStarted: string; // or Date 11 | lastFinished: string; 12 | paused: boolean; 13 | failureCount: number; 14 | latestData: string; 15 | } 16 | 17 | export const POLLER_URL = { 18 | getAllUrl: '/pollers', 19 | getByIdentifierUrl: '/pollers/', 20 | data: '/data/current' 21 | } 22 | 23 | export const POLLER_INTERVAL = [30000, 60000, 120000, 300000, 600000]; 24 | -------------------------------------------------------------------------------- /src/app/management-center/services/ibm.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | import { IBM, IBM_URL } from 'app/models'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import 'rxjs/add/operator/delay'; 6 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 7 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 8 | 9 | @Injectable() 10 | export class IbmService extends RackhdHttpService { 11 | constructor(public http: HttpClient) { 12 | super(http, IBM_URL); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('CatalogsComponent', () => { 6 | let component: AppComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AppComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AppComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/header/header.e2e.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | import 'tslib'; 3 | 4 | describe('Header', () => { 5 | beforeAll(async () => { 6 | await browser.get('/'); 7 | }); 8 | 9 | it('should have logo', () => { 10 | let subject = element(by.css('.logo-placeholder')).isPresent(); 11 | let result = true; 12 | expect(subject).toEqual(result); 13 | }); 14 | 15 | afterAll(async () => { 16 | await browser.wait(function() { 17 | return browser.getCurrentUrl().then(function(url) { 18 | return /managementCenter\/nodes$/.test(url); 19 | }); 20 | }, 10000); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /src/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 3 | 4 | import { CommonModule } from '@angular/common'; 5 | import { ClarityModule } from '@clr/angular'; 6 | 7 | import { HomeComponent } from './home.component'; 8 | import { HomeRoutingModule } from './home-routing.module'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | ClarityModule.forChild(), 13 | CommonModule, 14 | FormsModule, 15 | ReactiveFormsModule, 16 | HomeRoutingModule 17 | ], 18 | declarations: [ 19 | HomeComponent, 20 | ], 21 | providers: [] 22 | }) 23 | export class HomeModule {} 24 | -------------------------------------------------------------------------------- /src/app/management-center/obms/obm.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ObmComponent } from './obm.component'; 4 | 5 | describe('ObmComponent', () => { 6 | let component: ObmComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ObmComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ObmComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/management-center/skus/sku.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SkuComponent } from './sku.component'; 4 | 5 | describe('SkuComponent', () => { 6 | let component: SkuComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SkuComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SkuComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/models/workflow.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of Node's workflow. 3 | */ 4 | import { Task } from './task'; 5 | 6 | export class Workflow { 7 | node: string; 8 | status: string; 9 | context: {}; 10 | definition: string; 11 | domain: string; 12 | id: string; 13 | injectableName: string; 14 | instanceId: string; 15 | logContext: {}; 16 | name: string; 17 | serviceGraph: string; 18 | tasks: Array; 19 | } 20 | 21 | export const WORKFLOW_URL = { 22 | getAllUrl: "/workflows", 23 | getByIdentifierUrl: "/workflows/", 24 | } 25 | 26 | export const HISTORY_WORKFLOW_STATUS = ["succeeded", "failed", "cancelled"]; 27 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { 3 | RouterModule, 4 | PreloadAllModules 5 | } from '@angular/router'; 6 | 7 | // import routing table 8 | import { ROUTES } from './app.routes'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | RouterModule.forRoot(ROUTES, { 13 | //If we don't want to use hash mode, we need to change RackHD 14 | // useHash: Boolean(history.pushState) === true, 15 | useHash: true, 16 | preloadingStrategy: PreloadAllModules 17 | // enableTracing: true 18 | }) 19 | ], 20 | exports: [ 21 | RouterModule 22 | ] 23 | }) 24 | 25 | export class AppRoutingModule { } 26 | -------------------------------------------------------------------------------- /src/app/management-center/nodes/nodes.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NodesComponent } from './nodes.component'; 4 | 5 | describe('NodesComponent', () => { 6 | let component: NodesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NodesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NodesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/management-center/files/files.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FilesComponent } from './files.component'; 4 | 5 | describe('FilesComponent', () => { 6 | let component: FilesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FilesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FilesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-viewer/workflow-viewer.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Workflow Viewer 4 |

5 | 6 |
7 | 11 | 12 |
13 | 14 |
15 | 18 | 19 |
20 |
21 | -------------------------------------------------------------------------------- /src/app/management-center/configs/config.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConfigComponent } from './config.component'; 4 | 5 | describe('ConfigComponent', () => { 6 | let component: ConfigComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ConfigComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ConfigComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/management-center/pollers/pollers.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PollersComponent } from './pollers.component'; 4 | 5 | describe('PollersComponent', () => { 6 | let component: PollersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PollersComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PollersComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/management-center/catalogs/catalogs.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CatalogsComponent } from './catalogs.component'; 4 | 5 | describe('CatalogsComponent', () => { 6 | let component: CatalogsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CatalogsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CatalogsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/management-center/profiles/profiles.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProfilesComponent } from './profiles.component'; 4 | 5 | describe('ProfilesComponent', () => { 6 | let component: ProfilesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ProfilesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ProfilesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/canvas-graph/canvas-graph.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CanvasGraphComponent } from './canvas-graph.component'; 4 | 5 | describe('CanvasGraphComponent', () => { 6 | let component: CanvasGraphComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CanvasGraphComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CanvasGraphComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/management-center/templates/templates.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TemplatesComponent } from './templates.component'; 4 | 5 | describe('TemplatesComponent', () => { 6 | let component: TemplatesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TemplatesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TemplatesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/management-center/workflows/workflows.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WorkflowsComponent } from './workflows.component'; 4 | 5 | describe('WorkflowsComponent', () => { 6 | let component: WorkflowsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WorkflowsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WorkflowsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/models/inventory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of common inventory contants. 3 | */ 4 | import * as _ from 'lodash'; 5 | 6 | export const PAGE_SIZE_OPTIONS = [15, 20, 50, 100]; 7 | 8 | export class ModalTypes { 9 | detailActions: string []; 10 | alertActions: string []; 11 | formActions: string []; 12 | otherActions: string []; 13 | constructor( 14 | detailList = ["Detail", "Raw", "Meta"], 15 | alertList = ["Delete", "Cancel"], 16 | formList = ["Update", "Create", "Upload"], 17 | otherList = [] 18 | ) { 19 | this.detailActions = detailList; 20 | this.alertActions = alertList; 21 | this.formActions = formList; 22 | this.otherActions = otherList; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/environments/environment.e2e.prod.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { enableProdMode, NgModuleRef } from '@angular/core'; 3 | import { disableDebugTools } from '@angular/platform-browser'; 4 | import { Environment } from './model'; 5 | 6 | enableProdMode(); 7 | 8 | export const environment: Environment = { 9 | production: true, 10 | showDevModule: true, 11 | 12 | /** Angular debug tools in the dev console 13 | * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 14 | * @param modRef 15 | * @return {any} 16 | */ 17 | decorateModuleRef(modRef: NgModuleRef) { 18 | disableDebugTools(); 19 | return modRef; 20 | }, 21 | ENV_PROVIDERS: [] 22 | }; 23 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { enableProdMode, NgModuleRef } from '@angular/core'; 3 | import { disableDebugTools } from '@angular/platform-browser'; 4 | import { Environment } from './model'; 5 | 6 | enableProdMode(); 7 | 8 | export const environment: Environment = { 9 | production: true, 10 | showDevModule: false, 11 | 12 | /** Angular debug tools in the dev console 13 | * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 14 | * @param modRef 15 | * @return {any} 16 | */ 17 | decorateModuleRef(modRef: NgModuleRef) { 18 | disableDebugTools(); 19 | return modRef; 20 | }, 21 | ENV_PROVIDERS: [] 22 | }; 23 | -------------------------------------------------------------------------------- /src/app/workflow-center/run-workflow/run-workflow.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RunWorkflowComponent } from './run-workflow.component'; 4 | 5 | describe('RunWorkflowComponent', () => { 6 | let component: RunWorkflowComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ RunWorkflowComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RunWorkflowComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/inventory/dropdown-group/dropdown-group.component.scss: -------------------------------------------------------------------------------- 1 | .form-input { 2 | padding-top: 15px; 3 | } 4 | .input-width { 5 | width: 100%; 6 | } 7 | .button-align { 8 | float: right; 9 | margin-right: 2px; 10 | } 11 | 12 | div.row div { 13 | display: flex; 14 | label { 15 | white-space:nowrap 16 | } 17 | } 18 | 19 | label.required:after { 20 | color: #c92100; 21 | content: "\00a0\00a0*"; 22 | } 23 | 24 | // For default bootstrap/clarity form-group 25 | .row { 26 | div.form-group { 27 | display: table; 28 | label { 29 | display: table-cell; 30 | } 31 | clr-icon { 32 | display: table-cell; 33 | align: left; 34 | } 35 | input { 36 | display: table-cell; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/solution-center/solution-center.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SolutionCenterComponent } from './solution-center.component'; 4 | 5 | describe('SolutionCenterComponent', () => { 6 | let component: SolutionCenterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SolutionCenterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SolutionCenterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-center.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WorkflowCenterComponent } from './workflow-center.component'; 4 | 5 | describe('WorkflowCenterComponent', () => { 6 | let component: WorkflowCenterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WorkflowCenterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WorkflowCenterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { Subject } from 'rxjs/Subject'; 5 | import { EnvironmentService } from '../services/environment.service'; 6 | 7 | @Component({ 8 | selector: 'app-header', 9 | templateUrl: './header.component.html', 10 | styleUrls: ['./header.component.scss'] 11 | }) 12 | 13 | export class HeaderComponent implements OnInit { 14 | showSettingModal = false; 15 | openSetting = false; 16 | 17 | ngOnInit() { 18 | } 19 | 20 | constructor() {} 21 | 22 | popSettings(){ 23 | this.openSetting = true; 24 | } 25 | 26 | onSettingSave(){ 27 | this.openSetting = false; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/settings/setting.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import {HttpClientModule} from '@angular/common/http'; 5 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 6 | import { ClarityModule } from '@clr/angular'; 7 | import { SettingComponent } from './setting-form.component'; 8 | import { SettingService } from './setting.service'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | ClarityModule.forChild(), 14 | FormsModule, 15 | ReactiveFormsModule, 16 | HttpClientModule, 17 | ], 18 | declarations: [SettingComponent], 19 | exports: [SettingComponent], 20 | providers: [SettingService] 21 | }) 22 | export class SettingModule { } 23 | -------------------------------------------------------------------------------- /src/app/workflow-center/active-workflow/active-workflow.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ActiveWorkflowComponent } from './active-workflow.component'; 4 | 5 | describe('ActiveWorkflowComponent', () => { 6 | let component: ActiveWorkflowComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ActiveWorkflowComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ActiveWorkflowComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-editor/workflow-editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WorkflowEditorComponent } from './workflow-editor.component'; 4 | 5 | describe('WorkflowEditorComponent', () => { 6 | let component: WorkflowEditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WorkflowEditorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WorkflowEditorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-viewer/workflow-viewer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WorkflowViewerComponent } from './workflow-viewer.component'; 4 | 5 | describe('WorkflowViewerComponent', () => { 6 | let component: WorkflowViewerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WorkflowViewerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WorkflowViewerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /config/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: tipe.io 3 | */ 4 | const path = require('path'); 5 | 6 | const EVENT = process.env.npm_lifecycle_event || ''; 7 | 8 | /** 9 | * Helper functions. 10 | */ 11 | var ROOT = path.resolve(__dirname, '..'); 12 | 13 | function hasProcessFlag(flag) { 14 | return process.argv.join('').indexOf(flag) > -1; 15 | } 16 | 17 | function hasNpmFlag(flag) { 18 | return EVENT.includes(flag); 19 | } 20 | 21 | function isWebpackDevServer() { 22 | return process.argv[1] && !! (/webpack-dev-server/.exec(process.argv[1])); 23 | } 24 | 25 | 26 | var root = path.join.bind(path, ROOT); 27 | 28 | exports.hasProcessFlag = hasProcessFlag; 29 | exports.hasNpmFlag = hasNpmFlag; 30 | exports.isWebpackDevServer = isWebpackDevServer; 31 | exports.root = root; 32 | -------------------------------------------------------------------------------- /src/app/management-center/management-center.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ManagementCenterComponent } from './management-center.component'; 4 | 5 | describe('ManagementCenterComponent', () => { 6 | let component: ManagementCenterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ManagementCenterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ManagementCenterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/workflow-center/history-workflow/history-workflow.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HistoryWorkflowComponent } from './history-workflow.component'; 4 | 5 | describe('HistoryWorkflowComponent', () => { 6 | let component: HistoryWorkflowComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HistoryWorkflowComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HistoryWorkflowComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/services/rackhd/graph.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | import { GRAPH_URL } from 'app/models'; 4 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 5 | import { Observable } from 'rxjs/Observable'; 6 | 7 | @Injectable() 8 | export class GraphService extends RackhdHttpService { 9 | 10 | constructor(public http: HttpClient) { 11 | super(http, GRAPH_URL); 12 | } 13 | 14 | public createGraph(payload: any): Observable { 15 | return this.put(payload, 'text'); 16 | } 17 | 18 | public getInitGraph(): any { 19 | return { 20 | "friendlyName": "", 21 | "injectableName": "", 22 | "tasks": [] 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/app/inventory/header/header.component.scss: -------------------------------------------------------------------------------- 1 | .header-line { 2 | margin-bottom: -25px; 3 | margin-right: 0; 4 | z-index: 9; 5 | .create { 6 | float: left; 7 | } 8 | .delete { 9 | float: left; 10 | } 11 | .refresh { 12 | float: left; 13 | } 14 | .cancel { 15 | float: left; 16 | } 17 | .search{ 18 | float: right; 19 | width: 20%; 20 | margin-top: 16px; 21 | display: table; 22 | label { 23 | display: table-cell; 24 | } 25 | input { 26 | display: table-cell; 27 | width: 100%; 28 | } 29 | clr-icon { 30 | display: table-cell; 31 | } 32 | } 33 | } 34 | 35 | .header-text { 36 | padding-top: 0; 37 | margin-top: 0; 38 | margin-bottom: 12px; 39 | .font-style { 40 | font-weight: 600; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/management-center/shared/inventory.scss: -------------------------------------------------------------------------------- 1 | //Common scss for mangement center inventories; 2 | 3 | .grid-item:hover { 4 | color: blue; 5 | cursor: pointer; 6 | } 7 | 8 | .inventory { 9 | margin-top: 6px; 10 | margin-bottom: 4em; 11 | z-index: 0; 12 | .selected { 13 | margin-top: 1em; 14 | } 15 | } 16 | 17 | .status-header { 18 | margin-top: -25px; 19 | margin-bottom: 15px; 20 | margin-left: 5px; 21 | .selected { 22 | background-color: #e0e0e0; 23 | } 24 | .status-button:hover { 25 | background-color: #e0e0e0; 26 | cursor: pointer; 27 | } 28 | .status-button { 29 | text-align: center; 30 | margin: 0 auto; 31 | } 32 | .font-bold { 33 | font-weight: 500; 34 | font-size: large; 35 | } 36 | } 37 | 38 | .alertMessage { 39 | color: red; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/app/services/rackhd/obm.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | 4 | import { Observable } from 'rxjs/Observable'; 5 | import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 6 | import { catchError, retry } from 'rxjs/operators'; 7 | import { OBM, OBM_URL } from 'app/models'; 8 | 9 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 10 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 11 | 12 | @Injectable() 13 | export class ObmService extends RackhdHttpService { 14 | 15 | constructor(public http: HttpClient) { 16 | super(http, OBM_URL); 17 | } 18 | 19 | public creatObm(jsonData: object): Observable { 20 | return this.put(jsonData); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/services/sharedServices.module.ts: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // This file is to share injectable instance across the app 3 | // otherwise, if diff components add the same service into their own providers pool 4 | // there will be multiple instance of this service instead of singleton 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | import { NgModule, ModuleWithProviders } from '@angular/core'; 8 | 9 | import { AppCoreModule } from './core/core.module'; 10 | import { ActivityService } from './activity.service'; 11 | 12 | import { RackhdCommonServicesModule } from './rackhd/rackhd.module'; 13 | 14 | 15 | @NgModule({ 16 | exports: [ 17 | AppCoreModule, 18 | RackhdCommonServicesModule 19 | ], 20 | providers: [] 21 | }) 22 | export class SharedServicesModule { } 23 | -------------------------------------------------------------------------------- /src/app/inventory/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component, ViewEncapsulation } from '@angular/core'; 2 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 3 | import { CommonModule } from '@angular/common'; 4 | import { ClarityModule } from '@clr/angular'; 5 | 6 | import * as _ from 'lodash'; 7 | 8 | import { PAGE_SIZE_OPTIONS } from 'app/models'; 9 | 10 | @Component({ 11 | selector: 'inventory-footer', 12 | templateUrl: './footer.component.html', 13 | styleUrls: ['./footer.component.scss'], 14 | encapsulation: ViewEncapsulation.None 15 | }) 16 | 17 | export class InventoryFooterComponent { 18 | 19 | selectedPageSize = "15"; 20 | 21 | get pageSizes() { 22 | return PAGE_SIZE_OPTIONS 23 | }; 24 | 25 | get dgPageSize() { 26 | return parseInt(this.selectedPageSize); 27 | } 28 | 29 | constructor(){} 30 | } 31 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-editor/workflow-editor.component.scss: -------------------------------------------------------------------------------- 1 | .workflow-editor { 2 | .float-right { 3 | float: right; 4 | } 5 | .workflow-json-operation { 6 | display: inline-flex; 7 | padding-bottom: 60px; 8 | width: 100%; 9 | 10 | p { 11 | float: right; 12 | } 13 | 14 | .workflow-editor { 15 | flex: 3; 16 | } 17 | .editor { 18 | margin-left: 15px; 19 | flex: 1; 20 | text-align: left; 21 | height: 100vh; 22 | border: none; 23 | #jsoneditor { 24 | height: 100vh; 25 | } 26 | .filter-line { 27 | margin-left: 0px; 28 | display: -webkit-inline-box; 29 | .editor-dropdown { 30 | margin-top: 18px; 31 | width: 85%; 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | .red-title { 39 | color: #ff0000; //Red 40 | } 41 | -------------------------------------------------------------------------------- /src/styles/ngx-select.scss: -------------------------------------------------------------------------------- 1 | .ngx-select.dropdown { 2 | outline: none; 3 | } 4 | 5 | .ngx-select__toggle { 6 | height: 24px; 7 | margin-top: .25rem; 8 | margin-bottom: .25rem; 9 | width: 100%; 10 | border: none; 11 | border-radius: 0; 12 | box-shadow: none; 13 | background: none; 14 | display: inline-block; 15 | min-width: 2.5rem; 16 | border-bottom: 1px solid #9a9a9a; 17 | padding: 0 .25rem; 18 | text-transform: none; 19 | } 20 | .ngx-select__toggle.btn.form-control { 21 | &:active { 22 | box-shadow: none; 23 | } 24 | } 25 | 26 | span.ngx-select__selected-single { 27 | height: 14px; 28 | } 29 | 30 | .ngx-select { 31 | .btn { 32 | margin-top: auto; 33 | margin-bottom: auto; 34 | } 35 | } 36 | 37 | .ngx-select__choices.dropdown-menu.ng-star-inserted.show { 38 | width: auto; 39 | } 40 | -------------------------------------------------------------------------------- /src/app/solution-center/solution-center-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { SolutionCenterComponent } from './solution-center.component'; 5 | import { ConfigComponent } from '../management-center/config/config.component'; 6 | import { OsInstallComponent } from 'app/solution-center/os-install/os-install.component'; 7 | 8 | const SolutionCenterRoutes: Routes = [ 9 | { 10 | path: '', 11 | component: SolutionCenterComponent, 12 | children: [ 13 | { path: '', redirectTo: 'osInstall' }, 14 | { path: 'osInstall', component: OsInstallComponent }, 15 | ] 16 | } 17 | ]; 18 | 19 | @NgModule({ 20 | imports: [ 21 | RouterModule.forChild(SolutionCenterRoutes) 22 | ], 23 | exports: [ 24 | RouterModule 25 | ] 26 | }) 27 | export class SolutionCenterRoutingModule { 28 | } 29 | -------------------------------------------------------------------------------- /src/app/services/rackhd/rackhd.module.ts: -------------------------------------------------------------------------------- 1 | 2 | import { NgModule, ModuleWithProviders } from '@angular/core'; 3 | import { CatalogsService } from './catalogs.service'; 4 | import { NodeService } from './node.service'; 5 | import { ObmService } from './obm.service'; 6 | import { PollersService } from './pollers.service'; 7 | import { SkusService } from './sku.service'; 8 | import { WorkflowService } from './workflow.service'; 9 | import { GraphTaskService } from './task.service'; 10 | import { GraphService } from './graph.service'; 11 | import { TagService } from './tag.service'; 12 | 13 | @NgModule({ 14 | exports: [], 15 | providers: [ 16 | NodeService, 17 | CatalogsService, 18 | PollersService, 19 | ObmService, 20 | SkusService, 21 | WorkflowService, 22 | GraphTaskService, 23 | GraphService, 24 | TagService, 25 | ] 26 | }) 27 | export class RackhdCommonServicesModule {} 28 | -------------------------------------------------------------------------------- /src/app/solution-center/solution-center.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { ClarityModule } from '@clr/angular'; 5 | 6 | import { SolutionCenterComponent } from './solution-center.component'; 7 | import { SolutionCenterRoutingModule } from './solution-center-routing.module'; 8 | import { OsInstallComponent } from 'app/solution-center/os-install/os-install.component'; 9 | import { InventoryModule } from 'app/inventory/inventory.module'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | ClarityModule.forChild(), 14 | CommonModule, 15 | FormsModule, 16 | ReactiveFormsModule, 17 | InventoryModule, 18 | SolutionCenterRoutingModule 19 | ], 20 | declarations: [SolutionCenterComponent, OsInstallComponent] 21 | }) 22 | 23 | export class SolutionCenterModule { } 24 | -------------------------------------------------------------------------------- /src/app/management-center/services/management-center-service.module.ts: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // This file is to service used in management center 3 | // 4 | ////////////////////////////////////////////////////////////////////// 5 | import { NgModule, ModuleWithProviders } from '@angular/core'; 6 | 7 | // import { NodeService } from './node.service'; 8 | import { ProfileService } from './profile.service'; 9 | import { TemplateService } from './template.service'; 10 | import { FileService } from './file.service'; 11 | import { ConfigService } from './config.service'; 12 | import { IbmService } from './ibm.service'; 13 | 14 | 15 | @NgModule({ 16 | exports: [], 17 | providers: [ 18 | // NodeService, 19 | ProfileService, 20 | TemplateService, 21 | FileService, 22 | ConfigService, 23 | IbmService, 24 | ] 25 | }) 26 | export class ManagementCenterServicesModule {} 27 | -------------------------------------------------------------------------------- /src/app/services/rackhd/node.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | 4 | import { Observable } from 'rxjs/Observable'; 5 | import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 6 | import { catchError, retry } from 'rxjs/operators'; 7 | import 'rxjs/add/operator/delay'; 8 | import 'rxjs/add/observable/of'; 9 | 10 | import { Node, NODE_TYPES, NODE_URL } from 'app/models'; 11 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 12 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 13 | 14 | import { SKU_URL } from 'app/models/sku'; 15 | 16 | @Injectable() 17 | export class NodeService extends RackhdHttpService { 18 | 19 | constructor(public http: HttpClient) { 20 | super(http, NODE_URL); 21 | } 22 | 23 | public getNodeTypes(): Observable { 24 | return Observable.of(NODE_TYPES).delay(5); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/services/rackhd/pollers.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | 4 | import { Observable } from 'rxjs/Observable'; 5 | import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 6 | import { catchError, retry } from 'rxjs/operators'; 7 | import { Poller, POLLER_URL } from 'app/models'; 8 | 9 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 10 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 11 | 12 | @Injectable() 13 | export class PollersService extends RackhdHttpService { 14 | 15 | constructor(public http: HttpClient) { 16 | super(http, POLLER_URL); 17 | } 18 | 19 | public createPoller(payload: object): Observable { 20 | return this.post(payload); 21 | } 22 | 23 | public getLatestData(id: string): Observable { 24 | return this.getByIdentifier(id, 'json', POLLER_URL.data); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/services/rackhd/tag.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | 4 | import { Observable } from 'rxjs/Observable'; 5 | import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 6 | import { catchError, retry } from 'rxjs/operators'; 7 | import { TAG_URL } from 'app/models'; 8 | 9 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 10 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 11 | import { NodeService } from './node.service'; 12 | 13 | @Injectable() 14 | export class TagService extends RackhdHttpService { 15 | 16 | constructor( 17 | public http: HttpClient, 18 | public nodeService: NodeService 19 | ) { 20 | super(http, TAG_URL); 21 | } 22 | 23 | public getTagByNodeId(nodeId: string): Observable { 24 | let param = TAG_URL.getAllUrl; 25 | return this.nodeService.getByIdentifier(nodeId, 'json', param); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { NoContentComponent } from './no-content/no-content.component'; 4 | 5 | /** 6 | * The order or route is important. 7 | * Never put a Guard with canActivateChild to path '', then all the loadChildren 8 | * route after '' will be denied. Cause angular will regard all routes as '' children 9 | */ 10 | export const ROUTES: Routes = [ 11 | {path: '', redirectTo: '/managementCenter/nodes', pathMatch: 'full'}, 12 | { 13 | path: 'managementCenter', 14 | loadChildren: 'app/management-center/management-center.module#ManagementCenterModule', 15 | }, 16 | { 17 | path: 'workflowCenter', 18 | loadChildren: 'app/workflow-center/workflow-center.module#WorkflowCenterModule', 19 | }, 20 | { 21 | path: 'solutionCenter', 22 | loadChildren: 'app/solution-center/solution-center.module#SolutionCenterModule', 23 | }, 24 | // 404 page, page with ** can not be lazily loaded. 25 | {path: '**', component: NoContentComponent}, 26 | ]; 27 | -------------------------------------------------------------------------------- /src/main.browser.aot.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular bootstrapping 3 | */ 4 | import { platformBrowser } from '@angular/platform-browser'; 5 | import { decorateModuleRef } from './app/environment'; 6 | /** 7 | * App Module 8 | * our top level module that holds all of our components. 9 | */ 10 | import { AppModuleNgFactory } from '../compiled/src/app/app.module.ngfactory'; 11 | /** 12 | * Bootstrap our Angular app with a top level NgModule. 13 | */ 14 | export function main(): Promise { 15 | return platformBrowser() 16 | .bootstrapModuleFactory(AppModuleNgFactory) 17 | .then(decorateModuleRef) 18 | .catch((err) => console.error(err)); 19 | } 20 | 21 | switch (document.readyState) { 22 | case 'loading': 23 | document.addEventListener('DOMContentLoaded', _domReadyHandler, false); 24 | break; 25 | case 'interactive': 26 | case 'complete': 27 | default: 28 | main(); 29 | } 30 | 31 | function _domReadyHandler() { 32 | document.removeEventListener('DOMContentLoaded', _domReadyHandler, false); 33 | main(); 34 | } 35 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | 3 | import { ApplicationRef, NgModuleRef } from '@angular/core'; 4 | import { enableDebugTools } from '@angular/platform-browser'; 5 | import { Environment } from './model'; 6 | 7 | Error.stackTraceLimit = Infinity; 8 | require('zone.js/dist/long-stack-trace-zone'); 9 | 10 | export const environment: Environment = { 11 | production: false, 12 | 13 | showDevModule: true, 14 | 15 | /** Angular debug tools in the dev console 16 | * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 17 | * @param modRef 18 | * @return {any} 19 | */ 20 | decorateModuleRef(modRef: NgModuleRef) { 21 | const appRef = modRef.injector.get(ApplicationRef); 22 | const cmpRef = appRef.components[0]; 23 | 24 | let _ng = (window).ng; 25 | enableDebugTools(cmpRef); 26 | (window).ng.probe = _ng.probe; 27 | (window).ng.coreTokens = _ng.coreTokens; 28 | return modRef; 29 | }, 30 | ENV_PROVIDERS: [] 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /src/config/consts.ts: -------------------------------------------------------------------------------- 1 | export const CONSTS = { 2 | taskResult: { 3 | finished: 'finished', 4 | succeeded: 'succeeded', 5 | failed: 'failed', 6 | cancelled: 'cancelled', 7 | running: 'running', 8 | timeout: 'timeout' 9 | }, 10 | waitOn: { 11 | finished: 'finished', 12 | succeeded: 'succeeded', 13 | failed: 'failed' 14 | }, 15 | outputSlots: { 16 | failed: 0, 17 | succeeded: 1, 18 | finished: 2 19 | }, 20 | // referenced from clarity alerts 21 | // color: node header and font color 22 | // bgcolor: background color 23 | colors: { 24 | error: {color: '#e06851', bgColor: '#c92100'}, 25 | failed: {color: '#dfbda3', bgColor: '#c25400'}, 26 | succeeded: {color: '#caf0a5', bgColor: '#62a420'}, 27 | finished: {color: '#8fcbe9', bgColor: '#007cbb'}, 28 | pending: {color: '#e0e0e0', bgColor: ''}, 29 | running: {color: '#0000FF', bgColor: '#00FFFF'}, 30 | cancelled: {color: '#DC143C', bgColor: '#9932CC'}, 31 | timeout: {color: '#ADFF2F', bgColor: '#DAA520'} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/app/services/rackhd/catalogs.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | 4 | import { Observable } from 'rxjs/Observable'; 5 | import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 6 | import { catchError, retry } from 'rxjs/operators'; 7 | import { Catalog, CATALOG_URL, NODE_URL } from 'app/models'; 8 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 9 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 10 | import { NodeService } from './node.service'; 11 | 12 | @Injectable() 13 | export class CatalogsService extends RackhdHttpService { 14 | 15 | constructor( 16 | public http: HttpClient, 17 | private nodeService: NodeService 18 | ) { 19 | super(http, CATALOG_URL); 20 | } 21 | 22 | public getSource(nodeId: string, source: string): Observable { 23 | let param = CATALOG_URL.getByIdentifierUrl + source; 24 | return this.nodeService.getByIdentifier(nodeId, 'json', param); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/services/rackhd/sku.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse, HttpClient } from '@angular/common/http'; 3 | 4 | import { Observable } from 'rxjs/Observable'; 5 | import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 6 | import { catchError, retry } from 'rxjs/operators'; 7 | import { SKU, SKU_URL} from 'app/models/sku'; 8 | 9 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 10 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 11 | 12 | @Injectable() 13 | export class SkusService extends RackhdHttpService { 14 | 15 | constructor(public http: HttpClient) { 16 | super(http, SKU_URL); 17 | } 18 | 19 | public createSku(jsonData: any): Observable { 20 | return this.post(jsonData); 21 | } 22 | 23 | public uploadByPost(file, identifier?:string): Observable { 24 | return this.upload(file, identifier, 'post'); 25 | } 26 | 27 | public updateSku(jsonData: any): Observable { 28 | return this.put(jsonData); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.webpack.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "sourceMap": true, 10 | "noEmitHelpers": true, 11 | "importHelpers": true, 12 | "strictNullChecks": false, 13 | "lib": [ 14 | "es2015", 15 | "dom" 16 | ], 17 | "baseUrl": "./src", 18 | "paths": { 19 | "@angular/*": ["../node_modules/@angular/*"] 20 | }, 21 | "typeRoots": [ 22 | "node_modules/@types" 23 | ], 24 | "types": [ 25 | "hammerjs", 26 | "node" 27 | ] 28 | }, 29 | "exclude": [ 30 | "node_modules", 31 | "dist", 32 | "src/**/*.spec.ts", 33 | "src/**/*.e2e.ts", 34 | "src/**/testing/*" 35 | ], 36 | "angularCompilerOptions": { 37 | "skipMetadataEmit": true 38 | }, 39 | "compileOnSave": false, 40 | "buildOnSave": false, 41 | "atom": { 42 | "rewriteTsconfig": false 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/models/setting.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This defines the data model of web user. 3 | */ 4 | import * as _ from 'lodash'; 5 | 6 | export class RackhdSetting { 7 | constructor(payload:any){ 8 | _.assign(this,payload); 9 | } 10 | northboundApi: string; 11 | websocketUrl: string; 12 | authEnabled: boolean; 13 | connSecured: boolean; 14 | authToken?: string; 15 | } 16 | 17 | export const RACKHD_CONFIG = new RackhdSetting({ 18 | northboundApi: '127.0.0.1:9090/api/2.0', 19 | websocketUrl: '127.0.0.1:9100', 20 | authEnabled: false, 21 | connSecured: false, 22 | authToken: '' 23 | }); 24 | 25 | export const API_PATTERN = '^(\\d{1,3}\\.){3}\\d{1,3}\\:\\d{1,5}\\/api\\/(2\\.0|current)$'; 26 | export const ADDR_PATTERN = '^(\\d{1,3}\\.){3}\\d{1,3}\\:\\d{1,5}$'; 27 | export const DNS_PATTERN = '((\\d{1,3}\\.){3}\\d{1,3},)*\\s*((\\d{1,3}\\.){3}\\d{1,3})$'; 28 | export const IP_PATTERN = '^(\\d{1,3}\\.){3}\\d{1,3}$'; 29 | export const REPO_PATTERN = '^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?(((\\d{1,3}\\.){3}\\d{1,3})|localhost)\\:\\d{1,5}?(\/.*)?'; 30 | -------------------------------------------------------------------------------- /src/main.browser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular bootstrapping 3 | */ 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { environment } from 'environments/environment'; 6 | 7 | /** 8 | * App Module 9 | * our top level module that holds all of our components 10 | */ 11 | import { AppModule } from './app'; 12 | 13 | /** 14 | * Bootstrap our Angular app with a top level NgModule 15 | */ 16 | export function main(): Promise { 17 | return platformBrowserDynamic() 18 | .bootstrapModule(AppModule) 19 | .then(environment.decorateModuleRef) 20 | .catch((err) => console.error(err)); 21 | } 22 | 23 | /** 24 | * Needed for hmr 25 | * in prod this is replace for document ready 26 | */ 27 | switch (document.readyState) { 28 | case 'loading': 29 | document.addEventListener('DOMContentLoaded', _domReadyHandler, false); 30 | break; 31 | case 'interactive': 32 | case 'complete': 33 | default: 34 | main(); 35 | } 36 | 37 | function _domReadyHandler() { 38 | document.removeEventListener('DOMContentLoaded', _domReadyHandler, false); 39 | main(); 40 | } 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "sourceMap": true, 10 | "noEmit": true, 11 | "noEmitHelpers": true, 12 | "importHelpers": true, 13 | "strictNullChecks": false, 14 | "lib": [ 15 | "dom", 16 | "es6" 17 | ], 18 | "baseUrl": "./src", 19 | "paths": { 20 | "@angular/*": ["../node_modules/@angular/*"] 21 | }, 22 | "typeRoots": [ 23 | "node_modules/@types" 24 | ], 25 | "types": [ 26 | "hammerjs", 27 | "jasmine", 28 | "node", 29 | "source-map", 30 | "uglify-js", 31 | "webpack" 32 | ] 33 | }, 34 | "exclude": [ 35 | "node_modules", 36 | "dist" 37 | ], 38 | "awesomeTypescriptLoaderOptions": { 39 | "forkChecker": true, 40 | "useWebpackText": true 41 | }, 42 | "compileOnSave": false, 43 | "buildOnSave": false, 44 | "atom": { 45 | "rewriteTsconfig": false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/inventory/modal/details-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ViewEncapsulation, 4 | Input, 5 | Output, 6 | EventEmitter 7 | } from '@angular/core'; 8 | import { FormsModule, ReactiveFormsModule, FormGroup,FormControl } from '@angular/forms'; 9 | import { CommonModule } from '@angular/common'; 10 | import { ClarityModule } from '@clr/angular'; 11 | 12 | import * as _ from 'lodash'; 13 | 14 | @Component({ 15 | selector: 'details-modal', 16 | templateUrl: './details-modal.component.html', 17 | styleUrls: ['./details-modal.component.scss'], 18 | encapsulation: ViewEncapsulation.None 19 | }) 20 | 21 | export class GridDetailsModalComponent { 22 | isPopValue: boolean = false; 23 | @Input() isJson: boolean = true; 24 | @Input() size: string = 'lg'; 25 | @Input() title: string; 26 | @Input() data: any; 27 | @Input() dataType: string = "Details"; 28 | @Input() get isPop() { 29 | return this.isPopValue; 30 | } 31 | @Output() isPopChange = new EventEmitter(); 32 | 33 | set isPop(value) { 34 | this.isPopValue = value; 35 | this.isPopChange.emit(value) 36 | } 37 | 38 | constructor(){} 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/app/global-alert/global-alert.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 17 |
18 | {{ msg.text }} 19 |
20 |
21 | 24 |
25 | -------------------------------------------------------------------------------- /src/app/settings/setting-form.component.scss: -------------------------------------------------------------------------------- 1 | .fieldset { 2 | border: 1px solid silver; 3 | margin: 0 2px; 4 | padding: .35em .625em .75em; 5 | } 6 | 7 | .legend { 8 | display: block; 9 | padding: 0; 10 | line-height: inherit; 11 | width: auto; 12 | } 13 | 14 | .disabled-label { 15 | color: #565656; 16 | opacity: .4; 17 | } 18 | 19 | .float-right-local { 20 | float: right; 21 | } 22 | 23 | .input-width { 24 | width: 100% 25 | } 26 | 27 | .text-indicator { 28 | margin-left: 2em; 29 | font-size: 0.8em; 30 | } 31 | 32 | .token-button { 33 | display: -webkit-box; 34 | display: -ms-flexbox; 35 | display: flex; 36 | -ms-flex-wrap: wrap; 37 | flex-wrap: wrap; 38 | position: relative; 39 | padding-left: 9.5rem; 40 | margin-bottom: .5rem; 41 | font-size: .54167rem; 42 | letter-spacing: normal; 43 | line-height: 1rem; 44 | float: right; 45 | } 46 | 47 | .check-box { 48 | margin-bottom: 1em; 49 | } 50 | 51 | .add-bottom-margin { 52 | margin-bottom: 25px; 53 | } 54 | 55 | .reminder { 56 | font-style:oblique; 57 | color: red; 58 | } 59 | .error { 60 | margin-left: 2px; 61 | margin-top: -15px; 62 | font-size: 0.8em; 63 | width: 100%; 64 | } 65 | -------------------------------------------------------------------------------- /src/app/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 | 8 | 13 |
14 | 17 |
18 |
19 | 20 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= htmlWebpackPlugin.options.title %> 8 | 9 | 10 | 11 | 12 | 13 | <% if (webpackConfig.htmlElements.headTags) { %> 14 | 15 | <%= webpackConfig.htmlElements.headTags %> 16 | <% } %> 17 | 18 | <%= htmlWebpackPlugin.files.webpackManifest %> 19 | 20 | <% if (htmlWebpackPlugin.options.metadata.isDevServer && htmlWebpackPlugin.options.metadata.HMR !== true) { %> 21 | 22 | 23 | <% } %> 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Loading... 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # @AngularClass 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Users Environment Variables 25 | .lock-wscript 26 | 27 | # OS generated files # 28 | .DS_Store 29 | ehthumbs.db 30 | Icon? 31 | Thumbs.db 32 | 33 | # Node Files # 34 | /node_modules/ 35 | /bower_components/ 36 | npm-debug.log 37 | /npm-debug.log.* 38 | 39 | # Coverage # 40 | /coverage/ 41 | 42 | # Typing # 43 | /src/typings/tsd/ 44 | /typings/ 45 | /tsd_typings/ 46 | 47 | # Dist # 48 | /dist 49 | /public/__build__/ 50 | /src/*/__build__/ 51 | /__build__/** 52 | /public/dist/ 53 | /src/*/dist/ 54 | /dist/** 55 | /.awcache 56 | .webpack.json 57 | /compiled/ 58 | dll/ 59 | 60 | # Doc # 61 | /doc/ 62 | /documentation/ 63 | 64 | # IDE # 65 | .idea/ 66 | *.swp 67 | 68 | 69 | # Angular # 70 | *.ngfactory.ts 71 | *.css.shim.ts 72 | *.ngsummary.json 73 | *.shim.ngstyle.ts 74 | -------------------------------------------------------------------------------- /src/app/inventory/inventory.module.ts: -------------------------------------------------------------------------------- 1 | import { ClarityModule } from '@clr/angular'; 2 | import { NgModule, ModuleWithProviders } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { CommonModule } from '@angular/common'; 5 | 6 | import { InventoryFooterComponent } from './footer/footer.component'; 7 | import { InventoryHeaderComponent } from './header/header.component'; 8 | import { GridDetailsModalComponent } from './modal/details-modal.component'; 9 | import { GridConfirmModalComponent } from './modal/confirm-modal.component'; 10 | import { DropdownGroupComponent } from './dropdown-group/dropdown-group.component'; 11 | 12 | 13 | @NgModule({ 14 | imports: [ 15 | ClarityModule.forChild(), 16 | FormsModule, 17 | ReactiveFormsModule, 18 | CommonModule, 19 | ClarityModule, 20 | ], 21 | exports: [ 22 | InventoryFooterComponent, 23 | InventoryHeaderComponent, 24 | GridDetailsModalComponent, 25 | GridConfirmModalComponent, 26 | DropdownGroupComponent, 27 | ], 28 | declarations: [ 29 | InventoryFooterComponent, 30 | InventoryHeaderComponent, 31 | GridDetailsModalComponent, 32 | GridConfirmModalComponent, 33 | DropdownGroupComponent, 34 | ] 35 | }) 36 | export class InventoryModule {} 37 | -------------------------------------------------------------------------------- /src/app/models/node.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | /* 3 | This defines the data model of Node. 4 | */ 5 | export class Node { 6 | constructor(data: any) { 7 | _.assign(this, data); 8 | } 9 | autoDiscover: boolean; 10 | catalogs: string; 11 | id: string; 12 | identifiers: Array; 13 | name: string; 14 | obms: string[]; 15 | tags: string; 16 | pollers: string; 17 | relations: string[]; 18 | sku?: string; 19 | type: string; 20 | workflows: string; 21 | ibms: string[]; 22 | 23 | discoveredTime: string; 24 | manufacturer?: string; 25 | model: string; 26 | } 27 | 28 | export class NodeType { 29 | identifier: string; 30 | displayName: string; 31 | } 32 | 33 | export class NodeStatus { 34 | identifier: string; 35 | displayName: string; 36 | } 37 | 38 | export const NODE_TYPES = [ 39 | 'compute', 40 | 'enclosure', 41 | 'switch', 42 | 'pdu', 43 | 'mgmt', 44 | 'others', 45 | ]; 46 | 47 | export const NODE_URL = { 48 | getAllUrl: '/nodes', 49 | getByIdentifierUrl: '/nodes/', 50 | } 51 | 52 | export const NODE_TYPE_MAP = { 53 | compute: 'Compute', 54 | enclosure: 'Enclosure', 55 | switch: 'Switch', 56 | pdu: 'PDU', 57 | mgmt: 'MGMT', 58 | others: 'Others', 59 | } 60 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-center.component.html: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /src/styles/styles.scss: -------------------------------------------------------------------------------- 1 | /* this file will be extracted to main dist folder and is imported in index.html */ 2 | /* This file is for setting global styles */ 3 | @import 'variables'; 4 | /** 5 | * third party styles from node_modules global css 6 | */ 7 | @import './ngx-select.scss'; 8 | @import '../../node_modules/@clr/icons/clr-icons.min.css'; 9 | @import '../../node_modules/litegraph.js/css/litegraph.css'; 10 | @import '../../node_modules/jsoneditor/src/css/jsoneditor.css'; 11 | @import '../../node_modules/@clr/ui/clr-ui.min.css'; 12 | 13 | .content-container { 14 | overflow: auto; 15 | } 16 | 17 | .offset-header { 18 | height: -webkit-fill-available; 19 | margin-top: 60px; 20 | } 21 | 22 | .clear { 23 | clear: both; 24 | } 25 | 26 | .jsoneditor-menu { 27 | display: none; 28 | } 29 | 30 | div.jsoneditor-outer.has-status-bar { 31 | margin: 0; 32 | padding: 0; 33 | } 34 | 35 | div.jsoneditor-outer { 36 | position: static; 37 | width: 100%; 38 | height: 100%; 39 | margin: -35px 0 0 0; 40 | padding: 35px 0 0 0; 41 | -moz-box-sizing: border-box; 42 | -webkit-box-sizing: border-box; 43 | box-sizing: border-box; 44 | } 45 | 46 | .jsoneditor-statusbar { 47 | line-height: 30px; 48 | height: 30px; 49 | text-align: left; 50 | } 51 | 52 | //.row{ 53 | // margin: 0; 54 | //} 55 | -------------------------------------------------------------------------------- /src/app/inventory/header/header.component.html: -------------------------------------------------------------------------------- 1 |

2 |
{{name}} Grid
3 |

4 |
5 |
6 | 11 | 14 | 17 | 20 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /src/app/services/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NgModule, 3 | Optional, 4 | SkipSelf 5 | } from '@angular/core'; 6 | 7 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 8 | 9 | import { AppRoutingModule } from './app-route/app-routing.module'; 10 | import { IconService } from './icon.service'; 11 | import {ErrorHandlerService} from "./error-handler.service"; 12 | import {GlobalAlertService} from "./global-alert.service"; 13 | 14 | 15 | 16 | 17 | /** 18 | * App wide providers, inject all global services. 19 | * This providers also can be regard as a global service list for retriving. 20 | */ 21 | const APP_PROVIDERS = [ 22 | IconService, 23 | GlobalAlertService, 24 | ErrorHandlerService, 25 | 26 | ]; 27 | 28 | /** 29 | * The core module that provides global services for the whole app. 30 | * It only can be import only once by app.module(root module). 31 | * usually this moule will only have "providers" 32 | */ 33 | @NgModule({ 34 | /** 35 | * Expose our Services and Providers into Angular's dependency injection. 36 | */ 37 | providers:[ 38 | ...APP_PROVIDERS 39 | ] 40 | }) 41 | 42 | export class AppCoreModule { 43 | // prevent to import this module twice. 44 | constructor (@Optional() @SkipSelf() parentModule: AppCoreModule) { 45 | if (parentModule) { 46 | throw new Error( 47 | 'CoreModule is already loaded. Import it in the AppModule only'); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-center-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { WorkflowCenterComponent } from './workflow-center.component'; 5 | import { ActiveWorkflowComponent } from './active-workflow/active-workflow.component'; 6 | import { HistoryWorkflowComponent } from './history-workflow/history-workflow.component'; 7 | import { WorkflowViewerComponent } from './workflow-viewer/workflow-viewer.component'; 8 | import { RunWorkflowComponent } from './run-workflow/run-workflow.component'; 9 | import { WorkflowEditorComponent } from './workflow-editor/workflow-editor.component'; 10 | 11 | const WorkflowCenterRoutes: Routes = [ 12 | { 13 | path: '', 14 | component: WorkflowCenterComponent, 15 | children: [ 16 | {path: '', redirectTo: 'activeWorkflow'}, 17 | {path: 'activeWorkflow', component: ActiveWorkflowComponent}, 18 | {path: 'historyWorkflow', component: HistoryWorkflowComponent}, 19 | {path: 'workflowViewer', component: WorkflowViewerComponent}, 20 | {path: 'runWorkflow', component: RunWorkflowComponent}, 21 | {path: 'workflowEditor', component: WorkflowEditorComponent}, 22 | ] 23 | } 24 | ]; 25 | 26 | @NgModule({ 27 | imports: [ 28 | RouterModule.forChild(WorkflowCenterRoutes) 29 | ], 30 | exports: [ 31 | RouterModule 32 | ] 33 | }) 34 | export class WorkflowCenterRoutingModule { 35 | } 36 | -------------------------------------------------------------------------------- /src/app/inventory/modal/confirm-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ViewEncapsulation, 4 | Input, 5 | Output, 6 | EventEmitter 7 | } from '@angular/core'; 8 | import { FormsModule, ReactiveFormsModule, FormGroup,FormControl } from '@angular/forms'; 9 | import { CommonModule } from '@angular/common'; 10 | import { ClarityModule } from '@clr/angular'; 11 | 12 | import * as _ from 'lodash'; 13 | 14 | @Component({ 15 | selector: 'confirm-modal', 16 | templateUrl: './confirm-modal.component.html', 17 | styleUrls: ['./confirm-modal.component.scss'], 18 | encapsulation: ViewEncapsulation.None 19 | }) 20 | 21 | export class GridConfirmModalComponent { 22 | isPopValue: boolean = false; 23 | @Input() size: string = 'lg'; // Modal size 24 | @Input() title: string; // Modal key title 25 | @Input() data: any; // Data to be shown 26 | @Input() displayAttr: string = "id"; // Attribute of data to be shown 27 | @Input() action: string = "delete"; // Modal action 28 | @Input() get isPop() { // Modal popup flag input 29 | return this.isPopValue; 30 | } 31 | @Output() isPopChange = new EventEmitter(); // Modal popup flag output 32 | @Output() confirm = new EventEmitter(); // Actions output 33 | 34 | set isPop(value) { 35 | this.isPopValue = value; 36 | this.isPopChange.emit(value) 37 | } 38 | 39 | constructor(){} 40 | 41 | onReject(){ 42 | this.confirm.emit("reject"); 43 | } 44 | 45 | onAccept(){ 46 | this.confirm.emit("accept"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/app/environment.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular 2 3 | */ 4 | import { 5 | enableDebugTools, 6 | disableDebugTools 7 | } from '@angular/platform-browser'; 8 | import { 9 | ApplicationRef, 10 | enableProdMode 11 | } from '@angular/core'; 12 | /** 13 | * Environment Providers 14 | */ 15 | let PROVIDERS: any[] = [ 16 | /** 17 | * Common env directives 18 | */ 19 | ]; 20 | 21 | /** 22 | * Angular debug tools in the dev console 23 | * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 24 | */ 25 | let _decorateModuleRef = (value: T): T => { return value; }; 26 | 27 | if ('production' === ENV) { 28 | enableProdMode(); 29 | 30 | /** 31 | * Production 32 | */ 33 | _decorateModuleRef = (modRef: any) => { 34 | disableDebugTools(); 35 | 36 | return modRef; 37 | }; 38 | 39 | PROVIDERS = [ 40 | ...PROVIDERS, 41 | /** 42 | * Custom providers in production. 43 | */ 44 | ]; 45 | 46 | } else { 47 | 48 | _decorateModuleRef = (modRef: any) => { 49 | const appRef = modRef.injector.get(ApplicationRef); 50 | const cmpRef = appRef.components[0]; 51 | 52 | enableDebugTools(cmpRef); 53 | return modRef; 54 | }; 55 | 56 | /** 57 | * Development 58 | */ 59 | PROVIDERS = [ 60 | ...PROVIDERS, 61 | /** 62 | * Custom providers in development. 63 | */ 64 | ]; 65 | 66 | } 67 | 68 | export const decorateModuleRef = _decorateModuleRef; 69 | 70 | export const ENV_PROVIDERS = [ 71 | ...PROVIDERS 72 | ]; 73 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Usage (given build times depend on machine): 2 | # 3 | # Build SMALL image (no cache; ~20MB, time for build=rebuild = ~360s): 4 | # docker build --squash="true" -t angular-starter . 5 | # 6 | # Build FAST (rebuild) image (cache; >280MB, build time ~360s, rebuild time ~80s): 7 | # docker build -t angular-starter . 8 | # 9 | # Clean (remove intermidiet images): 10 | # docker rmi -f $(docker images -f "dangling=true" -q) 11 | # 12 | # Run image (on localhost:8080): 13 | # docker run --name angular-starter -p 8080:80 angular-starter & 14 | # 15 | # Run image as virtual host (read more: https://github.com/jwilder/nginx-proxy): 16 | # docker run -e VIRTUAL_HOST=angular-starter.your-domain.com --name angular-starter angular-starter & 17 | 18 | FROM nginx:1.13.0-alpine 19 | 20 | # install console and node 21 | RUN apk add --no-cache bash=4.3.46-r5 &&\ 22 | apk add --no-cache openssl &&\ 23 | apk add --no-cache nodejs 24 | 25 | # install npm ( in separate dir due to docker cache) 26 | ADD package.json /tmp/npm_inst/package.json 27 | RUN cd /tmp/npm_inst &&\ 28 | npm install &&\ 29 | mkdir -p /tmp/app &&\ 30 | mv /tmp/npm_inst/node_modules /tmp/app/ 31 | 32 | # build and publish application 33 | ADD . /tmp/app 34 | RUN cd /tmp/app &&\ 35 | npm run build:aot &&\ 36 | mv ./dist/* /usr/share/nginx/html/ 37 | 38 | # clean 39 | RUN rm -Rf /tmp/npm_inst &&\ 40 | rm -Rf /tmp/app &&\ 41 | rm -Rf /root/.npm &&\ 42 | apk del nodejs 43 | 44 | ADD ./config/nginx.conf /etc/nginx/conf.d/default.conf 45 | # this is for virtual host purposes 46 | EXPOSE 80 47 | -------------------------------------------------------------------------------- /.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "flower-project" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css", 23 | "../node_modules/@clr/ui/clr-ui.min.css", 24 | "../node_modules/@clr/icons/clr-icons.min.css" 25 | ], 26 | "scripts": [], 27 | "environmentSource": "environments/environment.ts", 28 | "environments": { 29 | "dev": "environments/environment.ts", 30 | "prod": "environments/environment.prod.ts" 31 | } 32 | } 33 | ], 34 | "e2e": { 35 | "protractor": { 36 | "config": "./protractor.conf.js" 37 | } 38 | }, 39 | "lint": [ 40 | { 41 | "project": "src/tsconfig.app.json", 42 | "exclude": "**/node_modules/**" 43 | }, 44 | { 45 | "project": "src/tsconfig.spec.json", 46 | "exclude": "**/node_modules/**" 47 | }, 48 | { 49 | "project": "e2e/tsconfig.e2e.json", 50 | "exclude": "**/node_modules/**" 51 | } 52 | ], 53 | "test": { 54 | "karma": { 55 | "config": "./karma.conf.js" 56 | } 57 | }, 58 | "defaults": { 59 | "styleExt": "css", 60 | "component": {} 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /config/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | require('ts-node/register'); 6 | var helpers = require('./helpers'); 7 | 8 | exports.config = { 9 | /** 10 | * default host url:port of app 11 | * this is related with e2e cmd in package.json 12 | */ 13 | baseUrl: 'http://localhost:3000/', 14 | 15 | /** 16 | * Use `npm run e2e` 17 | */ 18 | specs: [ 19 | helpers.root('src/**/**.e2e.ts'), 20 | helpers.root('src/**/*.e2e.ts') 21 | ], 22 | exclude: [], 23 | 24 | framework: 'jasmine2', 25 | 26 | allScriptsTimeout: 110000, 27 | 28 | jasmineNodeOpts: { 29 | showTiming: true, 30 | showColors: true, 31 | isVerbose: false, 32 | includeStackTrace: false, 33 | defaultTimeoutInterval: 400000 34 | }, 35 | directConnect: true, 36 | 37 | capabilities: { 38 | 'browserName': 'chrome', 39 | 'chromeOptions': { 40 | 'args': ["--headless", "--disable-gpu", "--window-size=1280x800", "--no-sandbox"] 41 | } 42 | }, 43 | 44 | onPrepare: function() { 45 | browser.ignoreSynchronization = true; 46 | browser.get('/'); 47 | return browser.wait(function() { 48 | return browser.getCurrentUrl().then(function(url) { 49 | return /managementCenter\/nodes$/.test(url); 50 | }); 51 | }, 10000); 52 | }, 53 | 54 | /** 55 | * Angular 2 configuration 56 | * 57 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching 58 | * `rootEl` 59 | */ 60 | useAllAngular2AppRoots: true, 61 | 62 | SELENIUM_PROMISE_MANAGER: false, 63 | }; 64 | -------------------------------------------------------------------------------- /src/app/services/rackhd/workflow.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HISTORY_WORKFLOW_STATUS, WORKFLOW_URL } from 'app/models'; 3 | import { RackhdHttpService } from 'app/utils/rackhd-http'; 4 | import { NodeService } from 'app/services/rackhd/node.service'; 5 | import { RackhdLocalStorage as RackHD } from 'app/utils/globals-util'; 6 | import { Observable } from 'rxjs/Observable'; 7 | 8 | 9 | import { 10 | HttpErrorResponse, 11 | HttpResponse, 12 | HttpClient, 13 | HttpHeaders, 14 | HttpParams, 15 | } from '@angular/common/http'; 16 | 17 | @Injectable() 18 | export class WorkflowService extends RackhdHttpService { 19 | 20 | constructor( 21 | public http: HttpClient, 22 | private nodeService: NodeService 23 | ) { 24 | super(http, WORKFLOW_URL); 25 | } 26 | 27 | public getAllActive(): Observable { 28 | return this.getAll({ active: true }); 29 | } 30 | 31 | public getAllHistory(): Observable { 32 | return this.getAll({ active: false }); 33 | } 34 | 35 | public getWorkflowStatusTypes(): Observable { 36 | return Observable.of(HISTORY_WORKFLOW_STATUS).delay(5); 37 | } 38 | 39 | public runWorkflow(nodeId: string, workflowName: string, payload: object): Observable{ 40 | let param = WORKFLOW_URL.getAllUrl + "?name=" + workflowName; 41 | return this.nodeService.postByIdentifier(nodeId, payload, param); 42 | } 43 | 44 | public cancelActiveWorkflow(nodeId: string): Observable { 45 | let param = "/workflows/action"; 46 | let payload = { "command": "cancel" }; 47 | return this.nodeService.putByIdentifier(nodeId, payload, param); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/app/workflow-center/run-workflow/run-workflow.component.scss: -------------------------------------------------------------------------------- 1 | .run-workflow { 2 | width: 100%; 3 | height: -webkit-fill-available; 4 | input[type=text]:not([readonly]) { 5 | background-size: 0 100%; 6 | transition: background-size .2s ease; 7 | } 8 | .header-text { 9 | font-weight: 600; 10 | } 11 | p { 12 | margin-top: 0.5rem; 13 | margin-bottom: 0.5rem; 14 | } 15 | position: relative; 16 | .header-text { 17 | padding-top: 0; 18 | margin-top: 0; 19 | margin-bottom: 25px; 20 | } 21 | .run-workflow-row { 22 | span { 23 | padding: 0; 24 | } 25 | } 26 | .operations { 27 | padding: 0; 28 | } 29 | .clearfix:after { 30 | clear:both; 31 | content:"."; 32 | display:block; 33 | height:0; 34 | line-height:0; 35 | visibility:hidden; 36 | } 37 | .bottom-button { 38 | padding: 0; 39 | margin-top: 10px; 40 | button { 41 | margin-right: 0; 42 | float: right; 43 | } 44 | } 45 | .filter-by{ 46 | position: relative; 47 | border: 1px solid #aaaaaa; 48 | margin-top: 25px; 49 | padding: 20px; 50 | height: 5.5rem; 51 | button{ 52 | float: right; 53 | } 54 | } 55 | .filter-title{ 56 | position: absolute; 57 | padding: 0 10px; 58 | top: -10px; 59 | left: 10px; 60 | height: 20px; 61 | line-height: 20px; 62 | background: #fafafa; 63 | } 64 | .filter-content { 65 | padding-right: 100px; 66 | } 67 | .json-editor { 68 | padding-left: 0; 69 | padding-right: 0; 70 | .ace-jsoneditor.ace_editor{ 71 | margin: 1px; 72 | } 73 | } 74 | .reminder { 75 | color: red; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /config/head-config.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for head elements added during the creation of index.html. 3 | * 4 | * All href attributes are added the publicPath (if exists) by default. 5 | * You can explicitly hint to prefix a publicPath by setting a boolean value to a key that has 6 | * the same name as the attribute you want to operate on, but prefix with = 7 | * 8 | * Example: 9 | * { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 10 | * Will prefix the publicPath to content. 11 | * 12 | * { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png', '=href': false }, 13 | * Will not prefix the publicPath on href (href attributes are added by default 14 | * 15 | */ 16 | module.exports = { 17 | link: [ 18 | /** 19 | * tags for 'apple-touch-icon' (AKA Web Clips). 20 | */ 21 | 22 | /** 23 | * tags for android web app icons 24 | */ 25 | 26 | /** 27 | * tags for favicons 28 | */ 29 | { rel: 'icon', type: 'image/png', sizes: '32x32', href: 'assets/icon/favicon.ico' }, 30 | { rel: 'icon', type: 'image/png', sizes: '96x96', href: 'assets/icon/favicon.ico' }, 31 | { rel: 'icon', type: 'image/png', sizes: '16x16', href: 'assets/icon/favicon.ico' }, 32 | 33 | /** 34 | * tags for a Web App Manifest 35 | */ 36 | { rel: 'manifest', href: 'assets/manifest.json' } 37 | ], 38 | meta: [ 39 | { name: 'msapplication-TileColor', content: '#00bcd4' }, 40 | { name: 'msapplication-TileImage', content: 'assets/icon/ms-icon-144x144.png', '=content': true }, 41 | { name: 'theme-color', content: '#00bcd4' } 42 | ] 43 | }; 44 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular 2 decorators and services 3 | */ 4 | import { 5 | Component, 6 | OnInit, 7 | ViewEncapsulation 8 | } from '@angular/core'; 9 | // This Component is the top level component so import ClarityIcons here 10 | // instead of import "../node_modules/clarity-icons/clarity-icons.min.js" 11 | // in root module 12 | import { ClarityIcons } from '@clr/icons'; 13 | import '@clr/icons/shapes/all-shapes'; 14 | 15 | /** 16 | * App Component 17 | * Top Level Component 18 | */ 19 | @Component({ 20 | selector: 'app', 21 | encapsulation: ViewEncapsulation.None, 22 | styleUrls: [ 23 | './app.component.css' 24 | ], 25 | templateUrl: './app.component.html' 26 | }) 27 | 28 | export class AppComponent implements OnInit { 29 | public angularclassLogo = 'assets/img/angularclass-avatar.png'; 30 | public name = 'Angular 2 Webpack Starter'; 31 | public url = 'https://twitter.com/AngularClass'; 32 | 33 | constructor( ) { 34 | // call ClarityIcons for once to include ClarityIcons js when packing. 35 | // in the future we will need to add customized icons 36 | // a service will be created for such needs and ClarityIcons will be called 37 | // in the service. Here will just be left a service calling. 38 | ClarityIcons.get(); 39 | } 40 | 41 | public ngOnInit() { 42 | } 43 | 44 | } 45 | 46 | /** 47 | * Please review the https://github.com/AngularClass/angular2-examples/ repo for 48 | * more angular app examples that you may copy/paste 49 | * (The examples may not be updated as quickly. Please open an issue on github for us to update it) 50 | * For help or questions please contact us at @AngularClass on twitter 51 | * or our chat on Slack at https://AngularClass.com/slack-join 52 | */ 53 | -------------------------------------------------------------------------------- /src/app/inventory/dropdown-group/dropdown-group.component.html: -------------------------------------------------------------------------------- 1 |
2 |
5 | 7 | 9 | 10 | 15 | 16 | 17 | 25 | 26 |
27 |
28 |
29 | 32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended" 4 | ], 5 | "rulesDirectory": [ 6 | "node_modules/codelyzer" 7 | ], 8 | "rules": { 9 | // Custom 10 | "trailing-comma": [false, {"multiline": "always", "singleline": "never"}], 11 | "interface-name": [false, "always-prefix"], 12 | // Angular 2 13 | "component-class-suffix": true, 14 | // "component-selector": [true, "element", "my", "kebab-case"], 15 | "directive-class-suffix": true, 16 | // "directive-selector": [true, "attribute", "my", "camelCase"], 17 | "import-destructuring-spacing": true, 18 | "invoke-injectable": true, 19 | "no-access-missing-member": true, 20 | "no-attribute-parameter-decorator": true, 21 | "no-forward-ref": true, 22 | "no-input-rename": true, 23 | "no-output-rename": true, 24 | "only-arrow-functions": false, 25 | "pipe-naming": [true, "camelCase", "my"], 26 | "templates-use-public": true, 27 | "use-host-property-decorator": true, 28 | "use-input-property-decorator": true, 29 | "use-life-cycle-interface": true, 30 | "use-output-property-decorator": true, 31 | "use-pipe-transform-interface": true, 32 | // General 33 | "no-console": [true, 34 | "time", 35 | "timeEnd", 36 | "trace" 37 | ], 38 | "max-line-length": [ 39 | true, 40 | 100 41 | ], 42 | "no-string-literal": false, 43 | "no-use-before-declare": true, 44 | "object-literal-sort-keys": false, 45 | "ordered-imports": false, 46 | "quotemark": [ 47 | true, 48 | "single", 49 | "avoid-escape" 50 | ], 51 | "variable-name": [ 52 | true, 53 | "allow-leading-underscore", 54 | "allow-pascal-case", 55 | "ban-keywords", 56 | "check-format" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-center.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { ClarityModule } from '@clr/angular'; 5 | import { NodeExtensionService } from '../canvas-graph/node-extension.service'; 6 | 7 | import { ManagementCenterServicesModule } from '../management-center/services/management-center-service.module'; 8 | 9 | import { ActiveWorkflowComponent } from './active-workflow/active-workflow.component'; 10 | import { HistoryWorkflowComponent } from './history-workflow/history-workflow.component'; 11 | import { WorkflowViewerComponent } from './workflow-viewer/workflow-viewer.component'; 12 | import { RunWorkflowComponent } from './run-workflow/run-workflow.component'; 13 | 14 | import { WorkflowCenterComponent } from './workflow-center.component'; 15 | import { WorkflowCenterRoutingModule } from './workflow-center-routing.module'; 16 | 17 | import { CanvasGraphModule } from 'app/canvas-graph/canvas-graph.module'; 18 | import { InventoryModule } from 'app/inventory/inventory.module'; 19 | import { WorkflowEditorComponent } from './workflow-editor/workflow-editor.component'; 20 | 21 | @NgModule({ 22 | imports: [ 23 | ClarityModule.forChild(), 24 | CommonModule, 25 | FormsModule, 26 | ReactiveFormsModule, 27 | WorkflowCenterRoutingModule, 28 | ManagementCenterServicesModule, 29 | CanvasGraphModule, 30 | InventoryModule, 31 | ], 32 | declarations: [ 33 | WorkflowCenterComponent, 34 | ActiveWorkflowComponent, 35 | HistoryWorkflowComponent, 36 | WorkflowViewerComponent, 37 | RunWorkflowComponent, 38 | WorkflowEditorComponent, 39 | ], 40 | providers: [NodeExtensionService ] 41 | }) 42 | 43 | export class WorkflowCenterModule {} 44 | -------------------------------------------------------------------------------- /src/app/management-center/management-center-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { ManagementCenterComponent } from './management-center.component'; 5 | 6 | import { NodesComponent } from './nodes/nodes.component'; 7 | import { ProfilesComponent } from './profiles/profiles.component'; 8 | import { PollersComponent } from './pollers/pollers.component'; 9 | import { WorkflowsComponent } from './workflows/workflows.component'; 10 | import { CatalogsComponent } from './catalogs/catalogs.component'; 11 | import { ObmComponent } from './obms/obm.component'; 12 | import { SkuComponent } from './skus/sku.component'; 13 | import { FilesComponent } from './files/files.component'; 14 | import { TemplatesComponent } from './templates/templates.component'; 15 | import { ConfigComponent } from './configs/config.component'; 16 | 17 | const ManagementCenterRoutes: Routes = [ 18 | { 19 | path: '', 20 | component: ManagementCenterComponent, 21 | children: [ 22 | {path: '', redirectTo: 'nodes'}, 23 | {path: 'nodes', component: NodesComponent}, 24 | {path: 'profiles', component: ProfilesComponent}, 25 | {path: 'pollers', component: PollersComponent}, 26 | {path: 'workflows', component: WorkflowsComponent}, 27 | {path: 'catalogs', component: CatalogsComponent}, 28 | {path: 'obms', component: ObmComponent}, 29 | {path: 'skus', component: SkuComponent}, 30 | {path: 'files', component: FilesComponent}, 31 | {path: 'templates', component: TemplatesComponent}, 32 | {path: 'configs', component: ConfigComponent} 33 | ] 34 | } 35 | ]; 36 | 37 | @NgModule({ 38 | imports: [ 39 | RouterModule.forChild(ManagementCenterRoutes) 40 | ], 41 | exports: [ 42 | RouterModule 43 | ] 44 | }) 45 | export class ManagementCenterRoutingModule { 46 | } 47 | -------------------------------------------------------------------------------- /src/app/services/core/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import {HttpErrorResponse} from "@angular/common/http"; 2 | import {ErrorObservable} from "rxjs/observable/ErrorObservable"; 3 | import {catchError} from "rxjs/operators"; 4 | import {GlobalAlertService} from "./global-alert.service"; 5 | import {Injectable} from "@angular/core"; 6 | 7 | @Injectable() 8 | export class ErrorHandlerService { 9 | 10 | constructor( 11 | public globalAlertService: GlobalAlertService 12 | ){} 13 | 14 | handleError(error : HttpErrorResponse){ 15 | if(error instanceof ErrorEvent) { 16 | // return a common client side error 17 | console.error('An error occurred:', error.message); 18 | return new ErrorObservable("common error" + error.message); 19 | }else{ 20 | // if the backend return the unsuccessful code 21 | console.error( `Backend returned code ${error.status}, ` + 22 | `body was: ${error.status}`); 23 | if(+error.status === 0){ 24 | this.globalAlertService.putAlertMsg( 25 | "Can't access RackHD services, please confirm if configurations are correct.", 26 | 'bar' 27 | ); 28 | } else { 29 | this.globalAlertService.putAlertMsg(error.message); 30 | } 31 | } 32 | 33 | return new ErrorObservable("backend error"); 34 | } 35 | } 36 | 37 | export function ErrorHanlder(){ 38 | return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { 39 | let originalMethod = descriptor.value; // save a reference to the original method 40 | const newMethod = { 41 | value: function(...args: any[]) { 42 | var result = originalMethod.apply(this, args) 43 | .pipe( 44 | catchError(error => this.errorHandlerService.handleError(error)) 45 | ); 46 | return result; 47 | } 48 | }; 49 | return newMethod; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/app/global-alert/global-alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import * as _ from 'lodash'; 3 | import {GlobalAlertService} from "../services/core/global-alert.service"; 4 | 5 | @Component({ 6 | selector: 'app-global-alert', 7 | templateUrl: './global-alert.component.html', 8 | styleUrls: ['./global-alert.component.css'] 9 | }) 10 | export class GlobalAlertComponent implements OnInit { 11 | modalErrorMsges = []; 12 | barErrorMsges = [] 13 | msgId = 0; 14 | showErrors = false; 15 | constructor( 16 | public globalAlertService: GlobalAlertService 17 | ){} 18 | 19 | ngOnInit(){ 20 | this.globalAlertService.getAlertQueue().subscribe( 21 | alertObj => { 22 | let msg = alertObj.msg; 23 | if(alertObj.type === 'modal'){ 24 | if(_.findIndex(this.modalErrorMsges, (cMsg)=> cMsg.text === msg) === -1){ 25 | let errorContent; 26 | try { 27 | errorContent = JSON.parse(msg); 28 | }catch (e){ 29 | errorContent = msg; 30 | } 31 | this.modalErrorMsges.push({id: this.msgId, text: errorContent}); 32 | this.modalErrorMsges = [].concat(this.modalErrorMsges); 33 | this.showErrors = true; 34 | this.msgId += 1; 35 | } 36 | } else if(alertObj.type === 'bar') { 37 | if(_.findIndex(this.barErrorMsges, (cMsg)=> cMsg.text === msg) === -1){ 38 | this.barErrorMsges.push({id: this.msgId, text: msg}); 39 | this.barErrorMsges = [].concat(this.barErrorMsges); 40 | this.msgId += 1; 41 | } 42 | } 43 | } 44 | ) 45 | } 46 | 47 | closeModalAlert(){ 48 | _.remove(this.modalErrorMsges); 49 | this.showErrors = false; 50 | } 51 | 52 | closeBarAlert(msgId: string){ 53 | _.remove(this.barErrorMsges, (msg) => msg.id === msgId); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/testing/router-stub.ts: -------------------------------------------------------------------------------- 1 | import { Component, Directive, Injectable, Input } from '@angular/core'; 2 | import { NavigationExtras } from '@angular/router'; 3 | 4 | @Directive({ 5 | selector: '[routerLink]', 6 | host: { 7 | '(click)': 'onClick()' 8 | } 9 | }) 10 | export class RouterLinkStubDirective { 11 | @Input('routerLink') linkParams: any; 12 | navigatedTo: any = null; 13 | 14 | onClick() { 15 | this.navigatedTo = this.linkParams; 16 | } 17 | } 18 | 19 | @Component({selector: 'router-outlet', template: ''}) 20 | export class RouterOutletStubComponent { } 21 | 22 | @Injectable() 23 | export class RouterStub { 24 | navigate(commands: any[], extras?: NavigationExtras) { } 25 | } 26 | 27 | 28 | // Only implements params and part of snapshot.paramMap 29 | import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 30 | import { convertToParamMap, ParamMap } from '@angular/router'; 31 | 32 | 33 | /* 34 | * Stub Test Class for ActivatedRoute 35 | * 36 | * Usage: 37 | * 1. define var: 38 | * let x : ActivatedRouteStub; 39 | * 2. useValue in providers: 40 | * { provide: ActivatedRoute, useValue: x } 41 | * 3. Before component created. by TestBed.createComponent(), 42 | * initialize the var x: 43 | * x = new ActivatedRouteStub(); 44 | * x.testParams = { id: '123' }; 45 | * 46 | */ 47 | 48 | @Injectable() 49 | export class ActivatedRouteStub { 50 | // ActivatedRoute.params is Observable 51 | private subject = new BehaviorSubject(this.testParams); 52 | params = this.subject.asObservable(); 53 | 54 | // Test parameters 55 | private _testParams: {}; 56 | get testParams() { return this._testParams; } 57 | set testParams(params: {}) { 58 | this._testParams = params; 59 | this.subject.next(params); 60 | } 61 | 62 | // ActivatedRoute.snapshot.params 63 | get snapshot() { 64 | return { params: this.testParams }; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/app/inventory/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; 2 | import { Subject } from 'rxjs/Subject'; 3 | import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; 4 | import { StringOperator } from '../../utils/inventory-operator'; 5 | 6 | @Component({ 7 | selector: 'inventory-header', 8 | templateUrl: './header.component.html', 9 | styleUrls: ['./header.component.scss'], 10 | encapsulation: ViewEncapsulation.None 11 | }) 12 | 13 | export class InventoryHeaderComponent implements OnInit { 14 | @Input() allItems: any; 15 | @Input() name: string; 16 | @Input() isSearchRequired: boolean = true; 17 | @Input() isRefreshRequired: boolean = true; 18 | @Input() isDeleteRequired: boolean = true; 19 | @Input() isCreateRequired: boolean = true; 20 | @Input() isCancelRequired: boolean = false; 21 | 22 | @Output() filter = new EventEmitter(); 23 | @Output() action = new EventEmitter(); 24 | 25 | searchValue:string = ''; 26 | filteredItems: any[]; 27 | searchTerms = new Subject(); 28 | 29 | constructor() { 30 | } 31 | 32 | ngOnInit() { 33 | let searchTrigger = this.searchTerms.pipe( 34 | debounceTime(300), 35 | distinctUntilChanged(), 36 | switchMap((term: string) => { 37 | this.search(term); 38 | return 'whatever'; 39 | }) 40 | ); 41 | searchTrigger.subscribe(); 42 | } 43 | 44 | search(term: string) { 45 | this.filteredItems = StringOperator.search(term, this.allItems); 46 | this.filter.emit(this.filteredItems); 47 | } 48 | 49 | onSearch(term) { 50 | this.searchTerms.next(term); 51 | } 52 | 53 | onClear() { 54 | this.searchTerms.next(''); 55 | } 56 | 57 | onCreate() { 58 | this.action.emit("Create"); 59 | } 60 | 61 | onRefresh() { 62 | this.action.emit("Refresh"); 63 | } 64 | 65 | onBatchDelete() { 66 | this.action.emit("Delete"); 67 | } 68 | 69 | onBatchCancel() { 70 | this.action.emit("Cancel") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/app/management-center/management-center.component.html: -------------------------------------------------------------------------------- 1 | 49 | -------------------------------------------------------------------------------- /src/app/management-center/management-center.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { ClarityModule } from '@clr/angular'; 5 | // the dependent files of ManagementCenter 6 | import { ManagementCenterComponent } from './management-center.component'; 7 | import { ManagementCenterRoutingModule } from './management-center-routing.module'; 8 | // child component 9 | import { NodesComponent } from './nodes/nodes.component'; 10 | import { ProfilesComponent } from './profiles/profiles.component'; 11 | import { PollersComponent } from './pollers/pollers.component'; 12 | import { WorkflowsComponent } from './workflows/workflows.component'; 13 | import { CatalogsComponent } from './catalogs/catalogs.component'; 14 | import { ObmComponent } from './obms/obm.component'; 15 | import { SkuComponent } from './skus/sku.component'; 16 | import { FilesComponent } from './files/files.component'; 17 | import { TemplatesComponent } from './templates/templates.component'; 18 | import { ConfigComponent } from './configs/config.component'; 19 | import { ManagementCenterServicesModule } from './services/management-center-service.module'; 20 | import { RackhdCommonServicesModule } from 'app/services/rackhd-common/rackhd-common.module'; 21 | import { InventoryModule } from 'app/inventory/inventory.module'; 22 | import { NgxSelectModule } from 'ngx-select-ex'; 23 | 24 | // the dependent services of ManagementCenter 25 | 26 | @NgModule({ 27 | imports: [ 28 | ClarityModule.forChild(), 29 | CommonModule, 30 | FormsModule, 31 | ReactiveFormsModule, 32 | ManagementCenterRoutingModule, 33 | ManagementCenterServicesModule, 34 | InventoryModule, 35 | NgxSelectModule 36 | ], 37 | declarations: [ 38 | ManagementCenterComponent, 39 | NodesComponent, 40 | ProfilesComponent, 41 | PollersComponent, 42 | WorkflowsComponent, 43 | CatalogsComponent, 44 | ObmComponent, 45 | SkuComponent, 46 | FilesComponent, 47 | TemplatesComponent, 48 | ConfigComponent, 49 | ] 50 | }) 51 | 52 | export class ManagementCenterModule { 53 | } 54 | -------------------------------------------------------------------------------- /config/spec-bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * When testing with webpack and ES6, we have to do some extra 7 | * things to get testing to work right. Because we are gonna write tests 8 | * in ES6 too, we have to compile those as well. That's handled in 9 | * karma.conf.js with the karma-webpack plugin. This is the entry 10 | * file for webpack test. Just like webpack will create a bundle.js 11 | * file for our client, when we run test, it will compile and bundle them 12 | * all here! Crazy huh. So we need to do some setup 13 | */ 14 | Error.stackTraceLimit = Infinity; 15 | 16 | require('core-js/es6'); 17 | require('core-js/es7/reflect'); 18 | 19 | require('zone.js/dist/zone'); 20 | require('zone.js/dist/long-stack-trace-zone'); 21 | require('zone.js/dist/proxy'); // since zone.js 0.6.15 22 | require('zone.js/dist/sync-test'); 23 | require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14 24 | require('zone.js/dist/async-test'); 25 | require('zone.js/dist/fake-async-test'); 26 | 27 | /** 28 | * RxJS 29 | */ 30 | require('rxjs/Rx'); 31 | 32 | var testing = require('@angular/core/testing'); 33 | var browser = require('@angular/platform-browser-dynamic/testing'); 34 | 35 | testing.TestBed.initTestEnvironment( 36 | browser.BrowserDynamicTestingModule, 37 | browser.platformBrowserDynamicTesting() 38 | ); 39 | 40 | /** 41 | * Ok, this is kinda crazy. We can use the context method on 42 | * require that webpack created in order to tell webpack 43 | * what files we actually want to require or import. 44 | * Below, context will be a function/object with file names as keys. 45 | * Using that regex we are saying look in ../src then find 46 | * any file that ends with spec.ts and get its path. By passing in true 47 | * we say do this recursively 48 | */ 49 | var testContext = require.context('../src', true, /\.spec\.ts/); 50 | 51 | /** 52 | * Get all the files, for each file, call the context function 53 | * that will require the file and load it up here. Context will 54 | * loop and require those spec files here 55 | */ 56 | function requireAll(requireContext) { 57 | return requireContext.keys().map(requireContext); 58 | } 59 | 60 | /** 61 | * Requires and returns all modules that match 62 | */ 63 | var modules = requireAll(testContext); 64 | -------------------------------------------------------------------------------- /src/app/management-center/catalogs/catalogs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Catalog } from 'app/models'; 3 | import { CatalogsService } from 'app/services/rackhd/catalogs.service'; 4 | import { Subject } from 'rxjs/Subject'; 5 | import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; 6 | 7 | import * as _ from 'lodash'; 8 | import { AlphabeticalComparator, ObjectFilterByKey, StringOperator } from 'app/utils/inventory-operator'; 9 | 10 | @Component({ 11 | selector: 'app-catalogs', 12 | templateUrl: './catalogs.component.html', 13 | styleUrls: ['./catalogs.component.scss'] 14 | }) 15 | export class CatalogsComponent implements OnInit { 16 | allCatalogs: Catalog[]; 17 | catalogsStore: Catalog[]; 18 | 19 | selectedCatalog: Catalog; 20 | specCatalog: Catalog; 21 | isShowDetail: boolean; 22 | isShowData: boolean; 23 | 24 | // data grid helper 25 | dgDataLoading = false; 26 | dgPlaceholder = 'No catalog found!' 27 | 28 | constructor(public catalogsService: CatalogsService) { 29 | this.specCatalog = new Catalog(); 30 | } 31 | 32 | public idComparator = new AlphabeticalComparator('id'); 33 | public nodeComparator = new AlphabeticalComparator('node'); 34 | public sourceComparator = new AlphabeticalComparator('source'); 35 | public createTimeComparator = new AlphabeticalComparator('createdAt'); 36 | public updateTimeComparator = new AlphabeticalComparator('updatedAt'); 37 | 38 | public idFilter = new ObjectFilterByKey('id'); 39 | public nodeFilter = new ObjectFilterByKey('node'); 40 | public sourceFilter = new ObjectFilterByKey('source'); 41 | 42 | ngOnInit() { 43 | this.getAllCatalogs(); 44 | } 45 | 46 | getAllCatalogs(): void { 47 | this.catalogsService.getAll() 48 | .subscribe( data => { 49 | this.allCatalogs = data; 50 | this.catalogsStore = data; 51 | this.dgDataLoading = false; 52 | }); 53 | } 54 | 55 | refresh() { 56 | this.dgDataLoading = true; 57 | this.getAllCatalogs(); 58 | } 59 | 60 | goToDetail(catalog: Catalog) { 61 | this.selectedCatalog = catalog; 62 | this.isShowDetail = true; 63 | } 64 | 65 | onAction(action){ 66 | switch(action) { 67 | case 'Refresh': 68 | this.refresh(); 69 | break; 70 | }; 71 | } 72 | 73 | onFilter(filtered){ 74 | this.catalogsStore = filtered; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/app/management-center/catalogs/catalogs.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 |
9 | 10 | {{ dgPlaceholder }} 11 | 12 | ID 13 | 14 | 15 | 16 | 17 | Node 18 | 19 | 20 | 21 | 22 | Source 23 | 24 | 25 | 26 | 27 | CreatedAt 28 | 29 | 30 | UpdatedAt 31 | 32 | 33 | 34 | {{catalog.id}} 35 | {{catalog.node.split('/')[4]}} 36 | {{catalog.source}} 37 | {{catalog.createdAt}} 38 | {{catalog.updatedAt}} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 |
52 | 53 |
54 | -------------------------------------------------------------------------------- /src/app/management-center/configs/config.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 |
9 | 10 | {{ dgPlaceholder }} 11 | Key 12 | 13 | 14 | 15 | Value 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {{config.key}} 25 | {{config.value | json}} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 52 | 56 | 57 |
58 | 59 |
60 | -------------------------------------------------------------------------------- /src/app/management-center/templates/templates.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 |
9 | 10 | {{ dgPlaceholder }} 11 | ID 12 | 13 | 14 | 15 | Name 16 | 17 | 18 | 19 | Scope 20 | 21 | 22 | 23 | Hash 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{template.id}} 32 | {{template.name}} 33 | {{template.scope}} 34 | {{template.hash}} 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 | 46 | 47 | 48 | 51 | 52 | 56 | 59 | 60 | 61 |
62 | -------------------------------------------------------------------------------- /src/app/management-center/profiles/profiles.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 |
9 | 10 | {{ dgPlaceholder }} 11 | ID 12 | 13 | 14 | 15 | Name 16 | 17 | 18 | 19 | Scope 20 | 21 | 22 | 23 | Hash 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{profile.id}} 32 | {{profile.name}} 33 | {{profile.scope}} 34 | {{profile.hash}} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 45 | 46 | 47 | 51 | 52 | 56 | 59 | 60 |
61 | 62 |
63 | -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-editor/workflow-editor.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 | 10 |

11 | Right-click to add a new task at canvas. 12 |

13 | 14 | 15 | 16 |
17 | 18 |
19 |
20 | 23 |
24 | 27 | 28 |
29 |
30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 62 |
63 | -------------------------------------------------------------------------------- /src/app/workflow-center/run-workflow/run-workflow.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Run Workflow 4 |

5 | 6 |
7 | The fields with * below are required! 8 |
9 |

10 | 13 | 14 |

15 | 16 |
17 | 18 |
19 | 23 | 24 |
25 |
26 | 27 |
28 | 29 |
30 | 31 |

32 | 33 | 36 | 37 | 38 |

39 |
40 |

41 |
42 |
43 |
44 | 48 |
49 | 50 | 51 | 52 | 55 | 59 | 63 | 64 |
65 | -------------------------------------------------------------------------------- /src/app/settings/setting.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpHeaders, HttpClient } from '@angular/common/http'; 3 | import { RACKHD_CONFIG } from 'app/models/index'; 4 | import { Observable } from 'rxjs/Observable'; 5 | import { timeout } from 'rxjs/operators/timeout'; 6 | 7 | import * as _ from 'lodash'; 8 | 9 | @Injectable() 10 | export class SettingService { 11 | 12 | constructor(private http: HttpClient) {} 13 | 14 | get northboundApi():string { return this.getConfigValue('northboundApi'); } 15 | set northboundApi(value:string) {this.setConfigValue('northboundApi', value); } 16 | 17 | get websocketUrl():string { return this.getConfigValue('websocketUrl'); } 18 | set websocketUrl(value:string) { this.setConfigValue('websocketUrl', value); } 19 | 20 | get authEnabled():boolean { return this.getConfigValue('authEnabled'); } 21 | set authEnabled(value:boolean) { this.setConfigValue('authEnabled', !!value);} 22 | 23 | get connSecured():boolean { return this.getConfigValue('connSecured'); } 24 | set connSecured(value:boolean) { this.setConfigValue('connSecured', !!value); } 25 | 26 | get authToken():string { return this.getConfigValue('authToken'); } 27 | set authToken(value:string) { this.setConfigValue('authToken', value); } 28 | 29 | getConfigValue(key: string): any { 30 | let globalKey = 'rackhd.' + key; 31 | let value = window.localStorage.getItem(globalKey); 32 | if (!value //window.localStorage only stores string 33 | || value === "undefined" 34 | || value === "null") { return RACKHD_CONFIG[key]; } 35 | if (value === "false") return false; 36 | return value; 37 | } 38 | 39 | setConfigValue(key: string, value: any): any { 40 | let globalKey = 'rackhd.' + key; 41 | window.localStorage.setItem(globalKey, value); 42 | return value; 43 | } 44 | 45 | loadDefaultConfig(){ 46 | return RACKHD_CONFIG; 47 | } 48 | 49 | loadInitialConfig(){ 50 | _.forEach(_.keys(RACKHD_CONFIG), key => { 51 | let _key = 'rackhd.' + key; 52 | if (!window.localStorage.getItem(_key)){ 53 | this[key] = RACKHD_CONFIG[key]; 54 | } 55 | }) 56 | } 57 | 58 | clearAllConfig(){ 59 | _.forEach(_.keys(RACKHD_CONFIG), function(key){ 60 | key = 'rackhd.' + key; 61 | if (window.localStorage.getItem(key)){ 62 | window.localStorage.removeItem(key); 63 | } 64 | }); 65 | } 66 | 67 | generateToken(user: string, password: string): Observable { 68 | let url = this.northboundApi.split("/")[0]; 69 | let body = { 70 | username: user, 71 | password: password, 72 | role: "Administrator" 73 | } 74 | url = (this.connSecured ? "https" : "http") + "://" + url + "/login"; 75 | return this.http.post(url, JSON.stringify(body), 76 | {headers: new HttpHeaders({"Content-Type": "application/json"})} 77 | ).pipe( 78 | timeout(500) 79 | ); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/app/solution-center/os-install/os-install.component.scss: -------------------------------------------------------------------------------- 1 | .content-container { 2 | 3 | margin-left: 0px; 4 | margin-right: 0px; 5 | width: 100%; 6 | height: 100%; 7 | input[type=text]:not([readonly]) { 8 | background-size: 0 100%; 9 | transition: background-size .2s ease; 10 | } 11 | p { 12 | margin-top: 0.5rem; 13 | margin-bottom: 0.5rem; 14 | } 15 | .title { 16 | margin-top: -22px; 17 | margin-bottom: 20px; 18 | h4 { 19 | font-weight: 600; 20 | display: block; 21 | } 22 | } 23 | position: relative; 24 | .form-group { 25 | width: 100%; 26 | .select { 27 | width: 100%; 28 | } 29 | .root-password { 30 | margin: 0%; 31 | } 32 | 33 | .os-install-repo { 34 | top: 0px; 35 | } 36 | } 37 | 38 | .offset-bottom { 39 | margin-bottom: 60px 40 | } 41 | 42 | .form-padding { 43 | padding-left: 1rem; 44 | } 45 | 46 | .form-padding-right { 47 | padding-right: 0px; 48 | } 49 | 50 | .fieldset { 51 | border: 1px solid silver; 52 | margin: 0; 53 | padding: 0px 20px 10px 20px; 54 | } 55 | 56 | .os-install-note { 57 | color: red; 58 | margin-top: 5px; 59 | margin-bottom: 15px; 60 | } 61 | 62 | .legend { 63 | display: block; 64 | padding: 0; 65 | line-height: inherit; 66 | width: auto; 67 | } 68 | 69 | .disabled-label { 70 | color: #565656; 71 | opacity: .4; 72 | } 73 | 74 | .float-right-local { 75 | float: right; 76 | } 77 | 78 | .my-form-block { 79 | .form-group { 80 | padding-left: 128px; 81 | } 82 | margin-bottom: 0px; 83 | padding-bottom: 5px; 84 | } 85 | 86 | .bottom-gap { 87 | margin-bottom: 10px; 88 | .col-md-3 { 89 | padding-left: 0; 90 | } 91 | } 92 | 93 | .networkOptions { 94 | .row { 95 | margin-top: 15px; 96 | } 97 | input { 98 | float: right; 99 | margin-bottom: -8px; 100 | } 101 | margin-bottom: 20px; 102 | } 103 | 104 | .json-editor { 105 | margin-left: 5px; 106 | height: 60%; 107 | width: auto; 108 | margin-top: 15px; 109 | } 110 | 111 | .bottom-button { 112 | padding: 0; 113 | margin-top: 5px; 114 | margin-bottom: 5px; 115 | button { 116 | float: right; 117 | margin-right: 0; 118 | } 119 | } 120 | 121 | .filter-by{ 122 | position: relative; 123 | border: 1px solid #aaaaaa; 124 | margin-top: 20px; 125 | margin-right: 0px; 126 | padding: 10px 20px; 127 | height: 5rem; 128 | } 129 | 130 | .filter-title{ 131 | position: absolute; 132 | padding: 0 10px; 133 | top: -10px; 134 | left: 10px; 135 | height: 20px; 136 | line-height: 20px; 137 | background: #fafafa; 138 | } 139 | 140 | .input-width { 141 | width: 100%; 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /config/github-deploy/index.js: -------------------------------------------------------------------------------- 1 | const execSync = require('child_process').execSync; 2 | /** 3 | * Used to merge webpack configs. 4 | */ 5 | const webpackMerge = require('webpack-merge'); // used to merge webpack configs 6 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 7 | const helpers = require('../helpers'); 8 | 9 | const REPO_NAME_RE = /Push {2}URL: ((git@github\.com:)|(https:\/\/github\.com\/)).+\/(.+)\.git/; 10 | 11 | function getWebpackConfigModule(options) { 12 | if (options.githubDev) { 13 | return require('../webpack.dev.js'); 14 | } else if (options.githubProd) { 15 | return require('../webpack.prod.js'); 16 | } else { 17 | throw new Error('Invalid compile option.'); 18 | } 19 | } 20 | 21 | function getRepoName(remoteName) { 22 | remoteName = remoteName || 'origin'; 23 | 24 | var stdout = execSync('git remote show ' + remoteName), 25 | match = REPO_NAME_RE.exec(stdout); 26 | 27 | if (!match) { 28 | throw new Error('Could not find a repository on remote ' + remoteName); 29 | } else { 30 | return match[4]; 31 | } 32 | } 33 | 34 | function stripTrailing(str, char) { 35 | 36 | if (str[0] === char) { 37 | str = str.substr(1); 38 | } 39 | 40 | if (str.substr(-1) === char) { 41 | str = str.substr(0, str.length - 1); 42 | } 43 | 44 | return str; 45 | } 46 | 47 | /** 48 | * Given a string remove trailing slashes and adds 1 slash at the end of the string. 49 | * 50 | * Example: 51 | * safeUrl('/value/') 52 | * // 'value/' 53 | * 54 | * @param url 55 | * @returns {string} 56 | */ 57 | function safeUrl(url) { 58 | const stripped = stripTrailing(url || '', '/'); 59 | return stripped ? stripped + '/' : ''; 60 | } 61 | 62 | function replaceHtmlWebpackPlugin(plugins, ghRepoName) { 63 | for (var i=0; i= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | // Internet Explorer 9 support 22 | import 'ie-shim'; 23 | 24 | // IE9, IE10 and IE11 requires all of the following polyfills. 25 | import 'core-js/es6/symbol'; 26 | import 'core-js/es6/object'; 27 | import 'core-js/es6/function'; 28 | import 'core-js/es6/parse-int'; 29 | import 'core-js/es6/parse-float'; 30 | import 'core-js/es6/number'; 31 | import 'core-js/es6/math'; 32 | import 'core-js/es6/string'; 33 | import 'core-js/es6/date'; 34 | import 'core-js/es6/array'; 35 | import 'core-js/es6/regexp'; 36 | import 'core-js/es6/map'; 37 | import 'core-js/es6/weak-map'; 38 | import 'core-js/es6/set'; 39 | import 'core-js/es7/array'; 40 | 41 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 42 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 43 | 44 | // Evergreen browsers require these. 45 | import 'core-js/es6/reflect'; 46 | import 'core-js/es7/reflect'; 47 | 48 | /** 49 | * Required to support Web Animations `@angular/animation`. 50 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 51 | */ 52 | 53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; 59 | // import 'zone.js/dist/long-stack-trace-zone' // async stack traces with zone.js included for dev 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | 65 | /** 66 | * Date, currency, decimal and percent pipes. 67 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 68 | */ 69 | // import 'intl'; // Run `npm install --save intl`. 70 | /** 71 | * Need to import at least one locale-data with intl. 72 | */ 73 | // import 'intl/locale-data/jsonp/en'; 74 | /** 75 | * Fix: Firefox clarity icon throw error 76 | */ 77 | import '@webcomponents/custom-elements/custom-elements.min.js'; 78 | 79 | if ('production' === ENV) { 80 | // Production 81 | 82 | } else { 83 | 84 | // Development 85 | Error.stackTraceLimit = Infinity; 86 | 87 | /* tslint:disable no-var-requires */ 88 | require('zone.js/dist/long-stack-trace-zone'); 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/app/utils/inventory-operator.ts: -------------------------------------------------------------------------------- 1 | import { Comparator, StringFilter } from "@clr/angular"; 2 | import * as _ from 'lodash'; 3 | 4 | export class AlphabeticalComparator implements Comparator { 5 | sortBy: string; 6 | constructor(sortBy: string) { 7 | this.sortBy = sortBy; 8 | } 9 | compare(a: T, b: T) { 10 | let sortedArray = _.sortBy([a, b], [o => 11 | (JSON.stringify(o[this.sortBy]))]); 12 | return _.findIndex(sortedArray, a) - _.findIndex(sortedArray, b); 13 | } 14 | } 15 | 16 | export class DateComparator implements Comparator { 17 | sortBy: string; 18 | 19 | constructor(sortBy: string) { 20 | this.sortBy = sortBy; 21 | } 22 | 23 | parseTime(time){ 24 | if(typeof time === "number") { 25 | return time; 26 | } 27 | return Date.parse(time); 28 | } 29 | 30 | compare(a: Node, b: Node) { 31 | return this.parseTime(a[this.sortBy]) - this.parseTime(b[this.sortBy]); 32 | } 33 | } 34 | 35 | export class ObjectFilterByKey implements StringFilter { 36 | private _field: string; 37 | 38 | constructor(field: string) { 39 | this._field = field; 40 | } 41 | 42 | accepts(obj: T, searchKey: string): boolean { 43 | let stringValue : string; 44 | let originValue: any = obj && _.get(obj, this._field); 45 | if (typeof originValue === 'undefined') { 46 | return false; 47 | } 48 | stringValue = (typeof originValue === "object") ? JSON.stringify(originValue) : originValue.toString(); 49 | return stringValue.toLowerCase().indexOf(searchKey) >= 0; 50 | } 51 | } 52 | 53 | export class StringOperator { 54 | static contain(src: string, term: string): boolean { 55 | if (!src) { 56 | return false; 57 | } 58 | if (!term) { 59 | return true; 60 | } 61 | return src.toLowerCase().includes(term.toLowerCase()); 62 | } 63 | 64 | static search(term: string, tableData: Array, skipDomain: string[] = []): Array { 65 | let searchDomain: string[] = _.without(_.keys(tableData[0]), ...skipDomain); 66 | return _.filter(tableData, data => { 67 | let flag = false; 68 | _.forEach(searchDomain, item => { 69 | let originValue: any = data && _.get(data, item); 70 | if (typeof originValue === 'undefined') { 71 | return true; 72 | } 73 | let stringValue = (typeof data[item] === "object") ? JSON.stringify(originValue) : originValue.toString(); 74 | if(this.contain(stringValue, term)){ 75 | flag = true; 76 | return false; 77 | } 78 | }) 79 | return flag; 80 | }); 81 | } 82 | } 83 | 84 | export function createFilters (obj: any, filterKeys: string[], model: T): void { 85 | _.map(filterKeys, key => { 86 | let _key = key + "Filter"; 87 | obj[_key] = new ObjectFilterByKey(key); 88 | }) 89 | } 90 | 91 | export function createComparator (obj: any, comparatorKeys: string[], model: T): void { 92 | _.map(comparatorKeys, key => { 93 | let _key = key + "Comparator"; 94 | obj[_key] = new AlphabeticalComparator(key); 95 | }) 96 | } 97 | 98 | export function isJsonTextValid(input): boolean { 99 | let valid = true; 100 | try { 101 | if (!_.isEmpty(input)) { 102 | JSON.parse(input); 103 | } 104 | } catch (e) { 105 | valid = false; 106 | } 107 | return valid; 108 | } -------------------------------------------------------------------------------- /src/app/workflow-center/workflow-viewer/workflow-viewer.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | Component, 4 | EventEmitter, 5 | OnInit, 6 | ViewChild, 7 | } from '@angular/core'; 8 | 9 | import { Router, ActivatedRoute, ParamMap } from '@angular/router'; 10 | import { WorkflowService } from 'app/services/rackhd/workflow.service'; 11 | import { Workflow, Graph } from 'app/models'; 12 | import { GraphService } from 'app/services/rackhd/graph.service'; 13 | 14 | import * as _ from 'lodash'; 15 | 16 | @Component({ 17 | selector: 'app-workflow-viewer', 18 | templateUrl: './workflow-viewer.component.html', 19 | styleUrls: ['./workflow-viewer.component.scss'] 20 | }) 21 | 22 | export class WorkflowViewerComponent implements OnInit, AfterViewInit { 23 | @ViewChild("viewCanvas") viewCanvas: any; 24 | onWorkflowInput = new EventEmitter(); 25 | graphId: string; 26 | isDefinition: boolean = false; // true: graph definition; false: graph object 27 | 28 | selectedWorkflow: any; 29 | allWorkflows: any[] = []; 30 | 31 | fieldList: string[] = []; 32 | labelList: string[] = []; 33 | offsetList: number[] = []; 34 | columnList : number[] = []; 35 | widthList : number[] = []; 36 | 37 | service: any; 38 | 39 | constructor( 40 | private route: ActivatedRoute, 41 | private router: Router, 42 | private workflowService: WorkflowService, 43 | private graphService: GraphService, 44 | ){} 45 | 46 | ngOnInit() { 47 | this.route.queryParams 48 | .subscribe(params => { 49 | this.isDefinition = this.isDefinition || !!params.graphName; 50 | this.graphId = params && (params.graphId || params.graphName); 51 | }); 52 | this.service = this.isDefinition ? this.graphService : this.workflowService; 53 | this.fieldList = this.isDefinition ? ["injectableName", "friendlyName"] : ["instanceId", "name", "node"]; 54 | this.labelList = this.isDefinition ? ["InjectableName", "FriendlyName"] : ["GraphId", "Name", "Node"]; 55 | this.offsetList = this.isDefinition ? [0, 0] : [0, 0, 0]; 56 | this.columnList = this.isDefinition ? [5, 5] : [4, 4, 3]; 57 | this.widthList = this.isDefinition ? [47, 47] : [42, 47, 28]; 58 | } 59 | 60 | ngAfterViewInit() { 61 | if(this.graphId) { 62 | this.service.getByIdentifier(this.graphId) 63 | .subscribe(workflowData => { 64 | this.selectedWorkflow = (workflowData instanceof Array) ? workflowData[0] : workflowData; 65 | this.allWorkflows = [this.selectedWorkflow]; 66 | this.onWorkflowInput.emit(this.selectedWorkflow); 67 | }); 68 | } else { 69 | this.getAllWorkflows(); 70 | } 71 | } 72 | 73 | getAllWorkflows() { 74 | this.service.getAll() 75 | .subscribe(workflows=> { 76 | this.allWorkflows = workflows; 77 | }); 78 | } 79 | 80 | updateCanvas(url: string): void { 81 | this.onWorkflowInput.emit(this.selectedWorkflow); 82 | if (url) { 83 | // This router doesn't trigger page reload in Angular5; 84 | // This is only to change the navigator history 85 | this.router.navigateByUrl(url); 86 | }; 87 | } 88 | 89 | onSelected(workflow: Workflow) { 90 | this.selectedWorkflow = workflow; 91 | this.graphId = this.selectedWorkflow.instanceId || this.selectedWorkflow.injectableName; 92 | let url = `/workflowCenter/workflowViewer?${this.isDefinition ? 'graphName': 'graphId'}=${this.graphId}`; 93 | this.updateCanvas(url); 94 | } 95 | 96 | onRefresh(item: string) { 97 | this.selectedWorkflow = {}; 98 | this.updateCanvas('/workflowCenter/workflowViewer'); 99 | this.getAllWorkflows(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { FormsModule } from '@angular/forms'; 3 | /** 4 | * IMPORTANT: just import HttpClient only once in app.module 5 | * Otherwise the interceptor and mock api calling may not work properly. 6 | */ 7 | import { HttpClientModule } from '@angular/common/http'; 8 | import {ComponentRef, Injector, NgModule} from '@angular/core'; 9 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 10 | /** 11 | * Third Party Modules 12 | */ 13 | import { ClarityModule } from '@clr/angular'; 14 | import { CookieModule } from 'ngx-cookie'; 15 | /** 16 | * Customized Service modules, they are used to inject/provide services. 17 | */ 18 | import { AppRoutingModule } from './app-routing.module'; 19 | /** 20 | * Customized Component/directives/pipe modules, they are used to provide view unit 21 | */ 22 | import { NoContentModule } from './no-content/index'; 23 | import { HeaderComponent } from './header/index'; 24 | /* 25 | * Platform and Environment providers 26 | */ 27 | import { environment } from 'environments/environment'; 28 | // App is our top level component 29 | import { AppComponent } from './app.component'; 30 | // Services Modules 31 | import { SharedServicesModule } from './services/sharedServices.module' 32 | /* 33 | * node_modules js here 34 | */ 35 | import '../../node_modules/@webcomponents/custom-elements/custom-elements.min.js'; 36 | import '../../node_modules/prismjs/prism.js'; 37 | import '../../node_modules/prismjs/components/prism-typescript.min.js'; 38 | 39 | import 'imports-loader?this=>window!../../node_modules/litegraph.js/build/litegraph.js'; 40 | 41 | /* 42 | * Global css here, scss will be processed by webpack. 43 | * Third party css should be imported into 'style.scss'file under the 'styles' doc. 44 | * Newly created css file should be created under the 'styles' doc. 45 | */ 46 | import '../styles/styles.scss'; 47 | import '../styles/headings.css'; 48 | 49 | // import '../../node_modules/litegraph.js/css/litegraph.css'; 50 | // import '../../node_modules/jsoneditor/src/css/jsoneditor.css'; 51 | // import serives, objs use only in this module. 52 | import { IconService, } from './services/core/index'; 53 | 54 | import { SettingModule } from './settings/setting.module'; 55 | import { GlobalAlertComponent } from './global-alert/global-alert.component'; 56 | 57 | /** 58 | * `AppModule` is the main entry point into Angular2's bootstraping process 59 | */ 60 | 61 | @NgModule({ 62 | bootstrap: [AppComponent], 63 | declarations: [ 64 | AppComponent, 65 | HeaderComponent, 66 | GlobalAlertComponent 67 | ], 68 | /** 69 | * Import Angular's modules. 70 | */ 71 | imports: [ 72 | BrowserModule, 73 | ClarityModule.forRoot(), 74 | CookieModule.forRoot(), 75 | AppRoutingModule, 76 | BrowserAnimationsModule, 77 | FormsModule, 78 | HttpClientModule, 79 | SharedServicesModule, 80 | // Feature module 81 | NoContentModule, 82 | SettingModule, 83 | /** 84 | * This section will import the `DevModuleModule` only in certain build types. 85 | * When the module is not imported it will get tree shaked. 86 | * This is a simple example, a big app should probably implement some logic 87 | */ 88 | ...environment.showDevModule ? [] : [], 89 | ], 90 | /** 91 | * Expose our Services and Providers into Angular's dependency injection. 92 | */ 93 | providers: [ 94 | ...environment.ENV_PROVIDERS 95 | ], 96 | }) 97 | 98 | export class AppModule { 99 | constructor(public iconService: IconService, public injector: Injector) { 100 | // must be called once to init IconService 101 | window["appInjector"] = this.injector; 102 | iconService.load(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /docs/deployment.md: -------------------------------------------------------------------------------- 1 | # Docker Deployment 2 | 3 | * [Install docker](deployment.md#dockers) 4 | * [Build image](deployment.md#build-image) 5 | * [Run image](deployment.md#run-image) 6 | * [Login into docker](deployment.md#login-into-docker-container) 7 | 8 | 9 | ## Docker 10 | 11 | To run project you only need host machine with **operating system** with installed **git** (to clone this repo) 12 | and [docker](https://www.docker.com/) and thats all - any other software is not needed 13 | (other software like node.js etc. will be automatically downloaded and installed inside docker container during build step based on dockerfile). 14 | 15 | ### Install docker 16 | 17 | #### MacOS: 18 | 19 | `brew cask install docker` 20 | 21 | And run docker by Mac bottom menu> launchpad > docker (on first run docker will ask you about password) 22 | 23 | #### Ubuntu: 24 | 25 | ``` 26 | sudo apt-get update 27 | sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 28 | sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main' 29 | sudo apt-get update 30 | apt-cache policy docker-engine 31 | sudo apt-get install -y docker-engine 32 | sudo systemctl status docker # test: shoud be ‘active’ 33 | ``` 34 | And add your user to docker group (to avoid `sudo` before using `docker` command in future): 35 | ``` 36 | sudo usermod -aG docker $(whoami) 37 | ``` 38 | and logout and login again. 39 | 40 | ### Build image 41 | 42 | Because *node.js* is big memory consumer you need 1-2GB RAM or virtual memory to build docker image 43 | (it was successfully tested on machine with 512MB RAM + 2GB virtual memory - building process take 7min) 44 | 45 | Go to main project folder. To build big (~280MB) image which has cached data and is able to **FAST** rebuild 46 | (this is good for testing or staging environment) type: 47 | 48 | `docker build -t angular-starter .` 49 | 50 | To build **SMALL** (~20MB) image without cache (so each rebuild will take the same amount of time as first build) 51 | (this is good for production environment) type: 52 | 53 | `docker build --squash="true" -t angular-starter .` 54 | 55 | The **angular-starter** name used in above commands is only example image name. 56 | To remove intermediate images created by docker on build process, type: 57 | 58 | `docker rmi -f $(docker images -f "dangling=true" -q)` 59 | 60 | ### Run image 61 | 62 | To run created docker image on [localhost:8080](localhost:8080) type (parameter `-p 8080:80` is host:container port mapping) 63 | 64 | `docker run --name angular-starter -p 8080:80 angular-starter &` 65 | 66 | And that's all, you can open browser and go to [localhost:8080](localhost:8080). 67 | 68 | ### Run image on sub-domain 69 | 70 | If you want to run image as virtual-host on sub-domain you must setup [proxy](https://github.com/jwilder/nginx-proxy) 71 | . You should install proxy and set sub-domain in this way: 72 | 73 | ``` 74 | docker pull jwilder/nginx-proxy:alpine 75 | docker run -d -p 80:80 --name nginx-proxy -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy:alpine 76 | ``` 77 | 78 | And in your `/etc/hosts` file (linux) add line: `127.0.0.1 angular-starter.your-domain.com` or in yor hosting add 79 | folowing DNS record (wildchar `*` is handy because when you add new sub-domain in future, you don't need to touch/add any DNS record) 80 | 81 | ``` 82 | Type: CNAME 83 | Hostname: *.your-domain.com 84 | Direct to: your-domain.com 85 | TTL(sec): 43200 86 | ``` 87 | 88 | And now you are ready to run image on subdomain by: 89 | 90 | ``` 91 | docker run -e VIRTUAL_HOST=angular-starter.your-domain.com --name angular-starter angular-starter & 92 | ``` 93 | 94 | ### Login into docker container 95 | 96 | `docker exec -t -i angular-starter /bin/bash` 97 | ___ 98 | 99 | 100 | -------------------------------------------------------------------------------- /config/html-elements-plugin/index.js: -------------------------------------------------------------------------------- 1 | function HtmlElementsPlugin(locations) { 2 | this.locations = locations; 3 | } 4 | 5 | HtmlElementsPlugin.prototype.apply = function(compiler) { 6 | var self = this; 7 | compiler.plugin('compilation', function(compilation) { 8 | compilation.options.htmlElements = compilation.options.htmlElements || {}; 9 | 10 | compilation.plugin('html-webpack-plugin-before-html-generation', function(htmlPluginData, callback) { 11 | const locations = self.locations; 12 | 13 | if (locations) { 14 | const publicPath = htmlPluginData.assets.publicPath; 15 | 16 | Object.getOwnPropertyNames(locations).forEach(function(loc) { 17 | compilation.options.htmlElements[loc] = getHtmlElementString(locations[loc], publicPath); 18 | }); 19 | } 20 | 21 | 22 | callback(null, htmlPluginData); 23 | }); 24 | }); 25 | 26 | }; 27 | 28 | const RE_ENDS_WITH_BS = /\/$/; 29 | 30 | /** 31 | * Create an HTML tag with attributes from a map. 32 | * 33 | * Example: 34 | * createTag('link', { rel: "manifest", href: "/assets/manifest.json" }) 35 | * // 36 | * @param tagName The name of the tag 37 | * @param attrMap A Map of attribute names (keys) and their values. 38 | * @param publicPath a path to add to eh start of static asset url 39 | * @returns {string} 40 | */ 41 | function createTag(tagName, attrMap, publicPath) { 42 | publicPath = publicPath || ''; 43 | 44 | /** 45 | * Add trailing slash if we have a publicPath and it doesn't have one. 46 | */ 47 | if (publicPath && !RE_ENDS_WITH_BS.test(publicPath)) { 48 | publicPath += '/'; 49 | } 50 | 51 | const attributes = Object.getOwnPropertyNames(attrMap) 52 | .filter(function(name) { return name[0] !== '='; } ) 53 | .map(function(name) { 54 | var value = attrMap[name]; 55 | 56 | if (publicPath) { 57 | /** 58 | * Check if we have explicit instruction, use it if so (e.g: =herf: false) 59 | * if no instruction, use public path if it's href attribute. 60 | */ 61 | const usePublicPath = attrMap.hasOwnProperty('=' + name) ? !!attrMap['=' + name] : name === 'href'; 62 | 63 | if (usePublicPath) { 64 | /** 65 | * Remove a starting trailing slash if the value has one so we wont have // 66 | */ 67 | value = publicPath + (value[0] === '/' ? value.substr(1) : value); 68 | } 69 | } 70 | 71 | return `${name}="${value}"`; 72 | }); 73 | 74 | const closingTag = tagName === 'script' ? '' : ''; 75 | 76 | return `<${tagName} ${attributes.join(' ')}>${closingTag}`; 77 | } 78 | 79 | /** 80 | * Returns a string representing all html elements defined in a data source. 81 | * 82 | * Example: 83 | * 84 | * const ds = { 85 | * link: [ 86 | * { rel: "apple-touch-icon", sizes: "57x57", href: "/assets/icon/apple-icon-57x57.png" } 87 | * ], 88 | * meta: [ 89 | * { name: "msapplication-TileColor", content: "#00bcd4" } 90 | * ] 91 | * } 92 | * 93 | * getHeadTags(ds); 94 | * // "" 95 | * "" 96 | * 97 | * @returns {string} 98 | */ 99 | function getHtmlElementString(dataSource, publicPath) { 100 | return Object.getOwnPropertyNames(dataSource) 101 | .map(function(name) { 102 | if (Array.isArray(dataSource[name])) { 103 | return dataSource[name].map(function(attrs) { return createTag(name, attrs, publicPath); } ); 104 | } else { 105 | return [ createTag(name, dataSource[name], publicPath) ]; 106 | } 107 | }) 108 | .reduce(function(arr, curr) { 109 | return arr.concat(curr); 110 | }, []) 111 | .join('\n\t'); 112 | } 113 | module.exports = HtmlElementsPlugin; 114 | -------------------------------------------------------------------------------- /src/app/management-center/files/files.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 |
9 | 10 | {{ dgPlaceholder }} 11 | 12 | ID 13 | 14 | 15 | 16 | 17 | FileName 18 | 19 | 20 | 21 | 22 | BaseName 23 | 24 | 25 | 26 | 27 | Version 28 | 29 | 30 | 31 | 32 | md5 33 | 34 | 35 | sha256 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {{file.uuid}} 46 | {{file.filename}} 47 | {{file.basename}} 48 | {{file.version}} 49 | {{file.md5}} 50 | {{file.sha256}} 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | Selected Files: 60 | 61 | {{file? file.filename : null}}{{isLast ? '' : ', '}} 62 | 63 |
64 |
65 | 66 | 69 | 70 | 71 | 73 | 74 | 75 | 78 | 79 | 83 | 86 | 87 |
88 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | module.exports = function (config) { 6 | var testWebpackConfig = require('./webpack.test.js')({ env: 'test' }); 7 | 8 | var configuration = { 9 | 10 | /** 11 | * Base path that will be used to resolve all patterns (e.g. files, exclude). 12 | */ 13 | basePath: '', 14 | 15 | /** 16 | * Frameworks to use 17 | * 18 | * available frameworks: https://npmjs.org/browse/keyword/karma-adapter 19 | */ 20 | frameworks: ['jasmine'], 21 | 22 | /** 23 | * List of files to exclude. 24 | */ 25 | exclude: [], 26 | 27 | client: { 28 | captureConsole: false 29 | }, 30 | 31 | /** 32 | * List of files / patterns to load in the browser 33 | * 34 | * we are building the test environment in ./spec-bundle.js 35 | */ 36 | files: [ 37 | { pattern: './config/spec-bundle.js', watched: false }, 38 | { pattern: './src/assets/**/*', watched: false, included: false, served: true, nocache: false }, 39 | './node_modules/@clr/icons/clr-icons.min.css', 40 | './node_modules/@clr/ui/dec-clarity-ui.min.css' 41 | ], 42 | 43 | /** 44 | * By default all assets are served at http://localhost:[PORT]/base/ 45 | */ 46 | proxies: { 47 | "/assets/": "/base/src/assets/" 48 | }, 49 | 50 | /** 51 | * Preprocess matching files before serving them to the browser 52 | * available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 53 | */ 54 | preprocessors: { './config/spec-bundle.js': ['coverage', 'webpack', 'sourcemap'] }, 55 | 56 | /** 57 | * Webpack Config at ./webpack.test.js 58 | */ 59 | webpack: testWebpackConfig, 60 | 61 | coverageReporter: { 62 | type: 'in-memory' 63 | }, 64 | 65 | remapCoverageReporter: { 66 | 'text-summary': null, 67 | json: './coverage/coverage.json', 68 | html: './coverage/html' 69 | }, 70 | 71 | /** 72 | * Webpack please don't spam the console when running in karma! 73 | */ 74 | webpackMiddleware: { 75 | /** 76 | * webpack-dev-middleware configuration 77 | * i.e. 78 | */ 79 | noInfo: true, 80 | /** 81 | * and use stats to turn off verbose output 82 | */ 83 | stats: { 84 | /** 85 | * options i.e. 86 | */ 87 | chunks: false 88 | } 89 | }, 90 | 91 | /** 92 | * Test results reporter to use 93 | * 94 | * possible values: 'dots', 'progress' 95 | * available reporters: https://npmjs.org/browse/keyword/karma-reporter 96 | */ 97 | reporters: ['mocha', 'coverage', 'remap-coverage'], 98 | 99 | /** 100 | * Web server port. 101 | */ 102 | port: 9876, 103 | 104 | /** 105 | * enable / disable colors in the output (reporters and logs) 106 | */ 107 | colors: true, 108 | 109 | /** 110 | * Level of logging 111 | * possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 112 | */ 113 | logLevel: config.LOG_WARN, 114 | 115 | /** 116 | * enable / disable watching file and executing tests whenever any file changes 117 | */ 118 | autoWatch: false, 119 | 120 | /** 121 | * start these browsers 122 | * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 123 | */ 124 | browsers: [ 125 | 'Chrome', 126 | 'ChromeHeadlessNoSandbox' 127 | ], 128 | 129 | customLaunchers: { 130 | ChromeHeadlessNoSandbox: { 131 | base: 'ChromeHeadless', 132 | flags: ['--no-sandbox'] 133 | } 134 | }, 135 | 136 | /** 137 | * Continuous Integration mode 138 | * if true, Karma captures browsers, runs the tests and exits 139 | */ 140 | singleRun: true 141 | }; 142 | 143 | if (process.env.CI || process.env.TRAVIS ) { 144 | configuration.browsers = [ 145 | 'ChromeHeadlessNoSandbox' 146 | ]; 147 | } 148 | 149 | config.set(configuration); 150 | }; 151 | -------------------------------------------------------------------------------- /src/app/management-center/profiles/profiles.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; 2 | import { Comparator, StringFilter } from "@clr/angular"; 3 | import { Subject } from 'rxjs/Subject'; 4 | import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; 5 | import { Observable } from 'rxjs/Observable'; 6 | import { AlphabeticalComparator, StringOperator, ObjectFilterByKey } from 'app/utils/inventory-operator'; 7 | import * as _ from 'lodash'; 8 | 9 | import { ProfileService } from 'app/management-center/services/profile.service'; 10 | import { Profile, ModalTypes } from 'app/models'; 11 | 12 | @Component({ 13 | selector: 'app-profiles', 14 | templateUrl: './profiles.component.html', 15 | styleUrls: ['./profiles.component.scss'], 16 | encapsulation: ViewEncapsulation.None 17 | }) 18 | export class ProfilesComponent implements OnInit { 19 | profilesStore: Profile[] = []; 20 | allProfiles: Profile[] = []; 21 | selectedProfile: Profile; 22 | 23 | files: FileList; 24 | 25 | action: string; 26 | isShowModal: boolean; 27 | rawData: string; 28 | 29 | modalTypes: ModalTypes; 30 | 31 | // data grid helper 32 | dgDataLoading = false; 33 | dgPlaceholder = 'No profile found!' 34 | 35 | public scopeComparator = new AlphabeticalComparator('scope'); 36 | public nameComparator = new AlphabeticalComparator('name'); 37 | public scopeFilter = new ObjectFilterByKey('scope'); 38 | public nameFilter = new ObjectFilterByKey('name'); 39 | public idFilter = new ObjectFilterByKey('id'); 40 | 41 | constructor(private profileService: ProfileService) { } 42 | 43 | ngOnInit() { 44 | this.getAll(); 45 | this.modalTypes = new ModalTypes(); 46 | } 47 | 48 | getAll(): void { 49 | this.profileService.getAll() 50 | .subscribe(data => { 51 | this.profilesStore = data; 52 | this.allProfiles = data; 53 | this.dgDataLoading = false; 54 | }); 55 | } 56 | 57 | getMetaData(identifier: string) { 58 | this.profileService.getMetaByIdentifier(identifier) 59 | .subscribe(data => { 60 | this.rawData = data; 61 | this.isShowModal = true; 62 | }) 63 | } 64 | 65 | getRawData(identifier: string) { 66 | this.profileService.getByIdentifier(identifier, 'text') 67 | .subscribe(data => { 68 | this.rawData = data; 69 | this.isShowModal = true; 70 | }) 71 | } 72 | 73 | onFilter(filtered){ 74 | this.profilesStore = filtered; 75 | } 76 | 77 | refresh() { 78 | this.dgDataLoading = true; 79 | this.getAll(); 80 | } 81 | 82 | create(){ 83 | this.action = "Upload"; 84 | this.isShowModal = true; 85 | } 86 | 87 | onAction(action){ 88 | switch(action) { 89 | case 'Refresh': 90 | this.refresh(); 91 | break; 92 | case 'Create': 93 | this.create(); 94 | break; 95 | }; 96 | } 97 | 98 | onUpdate(profile: Profile){ 99 | this.selectedProfile = profile; 100 | this.action = "Update"; 101 | this.isShowModal = true; 102 | } 103 | 104 | onGetDetails(profile: Profile) { 105 | this.selectedProfile = profile; 106 | this.action = "Meta"; 107 | this.getMetaData(profile.name); 108 | }; 109 | 110 | onGetRawData(profile: Profile) { 111 | this.selectedProfile = profile; 112 | this.action = "Raw" 113 | this.getRawData(profile.name); 114 | }; 115 | 116 | onChange(event){ 117 | this.files = event.target.files; 118 | } 119 | 120 | onCreateSubmit(){ 121 | //existingFilename is used to store filename when updating file 122 | let existingFilename = this.selectedProfile && this.selectedProfile.name; 123 | let file = this.files[0]; 124 | //TODO: Add more details on progress 125 | //TODO: And use sync mode instead of async mode 126 | //TODO: Add support on multiple files upload support 127 | this.isShowModal = false; 128 | this.profileService.upload(file, existingFilename || file.name) 129 | .subscribe(() => { 130 | this.selectedProfile = null; 131 | this.refresh(); 132 | }) 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/app/management-center/templates/templates.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; 2 | import { Comparator, StringFilter } from "@clr/angular"; 3 | import { Subject } from 'rxjs/Subject'; 4 | import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; 5 | import { Observable } from 'rxjs/Observable'; 6 | import { AlphabeticalComparator, StringOperator, ObjectFilterByKey } from '../../utils/inventory-operator'; 7 | import * as _ from 'lodash'; 8 | 9 | import { TemplateService } from '../services/template.service'; 10 | import { Template, ModalTypes } from '../../models'; 11 | 12 | @Component({ 13 | selector: 'app-templates', 14 | templateUrl: './templates.component.html', 15 | styleUrls: ['./templates.component.scss'], 16 | encapsulation: ViewEncapsulation.None 17 | }) 18 | export class TemplatesComponent implements OnInit { 19 | templatesStore: Template[] = []; 20 | allTemplates: Template[] = []; 21 | selectedTemplate: Template; 22 | 23 | files: FileList; 24 | 25 | action: string; 26 | isShowModal: boolean; 27 | rawData: string; 28 | 29 | dgDataLoading = false; 30 | dgPlaceholder = 'No template found!'; 31 | 32 | modalTypes: ModalTypes; 33 | 34 | public scopeComparator = new AlphabeticalComparator