Repository: catalogicsoftware/ngx-dynamic-dashboard-framework Branch: master Commit: 9dfa15c5c4b7 Files: 267 Total size: 604.7 KB Directory structure: gitextract_fareezle/ ├── .gitignore ├── LICENSE ├── README.md ├── angular.json ├── build.sh ├── e2e/ │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── ngx-dynamic-dashboard-framework.iml ├── package.json ├── protractor.conf.js ├── semantic.json ├── src/ │ ├── app/ │ │ ├── about/ │ │ │ ├── about-component.ts │ │ │ ├── about.module.ts │ │ │ ├── model.ts │ │ │ ├── service.ts │ │ │ ├── styles.css │ │ │ └── view.html │ │ ├── add-gadget/ │ │ │ ├── add-gadget-component.ts │ │ │ ├── add-gadget.module.ts │ │ │ ├── gadget-factory.ts │ │ │ ├── gadgetLibraryResponse.ts │ │ │ ├── service.ts │ │ │ ├── styles.css │ │ │ └── view.html │ │ ├── api-token/ │ │ │ └── api-token.service.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── board/ │ │ │ ├── board.component.ts │ │ │ ├── board.module.ts │ │ │ └── view.html │ │ ├── configuration/ │ │ │ ├── configuration-component.ts │ │ │ ├── configuration.module.ts │ │ │ ├── styles.css │ │ │ ├── tab-artificial-intelligence/ │ │ │ │ ├── ai-configuration-tab.component.ts │ │ │ │ ├── styles.css │ │ │ │ └── view.html │ │ │ ├── tab-boards/ │ │ │ │ ├── boards-configuration-tab.component.ts │ │ │ │ ├── styles.css │ │ │ │ └── view.html │ │ │ ├── tab-endpoint/ │ │ │ │ ├── endpoint-configuration-tab.component.ts │ │ │ │ ├── endpoint.help.ts │ │ │ │ ├── endpoint.model.ts │ │ │ │ ├── endpoint.service.ts │ │ │ │ ├── endpointDetail.component.ts │ │ │ │ ├── endpointDetail.html │ │ │ │ ├── styles.css │ │ │ │ └── view.html │ │ │ ├── tab-options/ │ │ │ │ ├── options-configuration-tab.component.ts │ │ │ │ ├── service.ts │ │ │ │ ├── styles.css │ │ │ │ └── view.html │ │ │ ├── tabs.model.ts │ │ │ └── view.html │ │ ├── datalist/ │ │ │ ├── action-model.ts │ │ │ ├── data-list.component.ts │ │ │ ├── data-list.module.ts │ │ │ ├── styles.css │ │ │ └── view.html │ │ ├── detail/ │ │ │ ├── detail.component.ts │ │ │ ├── detail.model.ts │ │ │ ├── detail.module.ts │ │ │ ├── detail.resolver.ts │ │ │ ├── filter.pipe.ts │ │ │ ├── service.ts │ │ │ ├── styles.css │ │ │ └── view.html │ │ ├── dynamic-form/ │ │ │ ├── dynamic-form-module.ts │ │ │ ├── dynamic-form-property.component.html │ │ │ ├── dynamic-form-property.component.ts │ │ │ ├── dynamic-form.component.html │ │ │ ├── dynamic-form.component.ts │ │ │ ├── property-base.ts │ │ │ ├── property-checkbox.ts │ │ │ ├── property-control.service.ts │ │ │ ├── property-dropdown.ts │ │ │ ├── property-dynamicdropdown.ts │ │ │ ├── property-hidden.ts │ │ │ ├── property-number.ts │ │ │ ├── property-textbox.ts │ │ │ └── styles-props.css │ │ ├── error/ │ │ │ ├── error-handler.component.ts │ │ │ ├── error-handler.ts │ │ │ ├── error-model.ts │ │ │ ├── error.module.ts │ │ │ ├── styles-error.css │ │ │ └── view.html │ │ ├── facet/ │ │ │ ├── capitalize-first-character-pipe.ts │ │ │ ├── facet-component.ts │ │ │ ├── facet-model.ts │ │ │ ├── facet-tag-processor.ts │ │ │ ├── facet.module.ts │ │ │ ├── filter-list-component.ts │ │ │ ├── filter-tag-component.ts │ │ │ └── styles.css │ │ ├── gadgets/ │ │ │ ├── _common/ │ │ │ │ ├── base-chart-models/ │ │ │ │ │ ├── bar.model.ts │ │ │ │ │ └── series.model.ts │ │ │ │ ├── gadget-base.ts │ │ │ │ ├── gadget-config-model.ts │ │ │ │ ├── gadget-header-component.ts │ │ │ │ ├── gadget-header.html │ │ │ │ ├── gadget-operation-control-component.ts │ │ │ │ ├── gadget-property.service.ts │ │ │ │ ├── gadget-shared.module.ts │ │ │ │ ├── help-modal-component.ts │ │ │ │ ├── help-modal.html │ │ │ │ ├── igadget.ts │ │ │ │ ├── styles-gadget.css │ │ │ │ ├── vis-drill-down-component.ts │ │ │ │ └── vis-drill-down.html │ │ │ ├── barchart/ │ │ │ │ ├── barchart-gadget.component.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── bubble/ │ │ │ │ ├── bubble-gadget.component.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── cpu/ │ │ │ │ ├── cpu-gadget.component.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── cpum/ │ │ │ │ ├── cpu.model.ts │ │ │ │ ├── cpum-gadget.component.ts │ │ │ │ └── view.html │ │ │ ├── disk/ │ │ │ │ ├── disk-gadget.component.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── donut/ │ │ │ │ ├── donut-gadget.component.ts │ │ │ │ ├── drill-down-component.ts │ │ │ │ ├── drill-down-style.css │ │ │ │ ├── drill-down.html │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── edge-service-list/ │ │ │ │ ├── edge-service-list-gadget.component.ts │ │ │ │ ├── service-list.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── gadget.module.ts │ │ │ ├── job-analysis/ │ │ │ │ ├── ja.css │ │ │ │ ├── job-analysis-gadget.component.ts │ │ │ │ ├── model.json │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── memory/ │ │ │ │ ├── memory-gadget.component.ts │ │ │ │ └── view.html │ │ │ ├── news/ │ │ │ │ ├── news-gadget.component.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── piechart/ │ │ │ │ ├── piechart-gadget.component.ts │ │ │ │ ├── service.ts │ │ │ │ └── view.html │ │ │ ├── port-connection/ │ │ │ │ ├── port-connection-gadget.component.ts │ │ │ │ ├── port-connection.css │ │ │ │ ├── result-view.component.ts │ │ │ │ ├── result-view.html │ │ │ │ ├── service.model.ts │ │ │ │ ├── service.ts │ │ │ │ ├── solution-view.component.ts │ │ │ │ ├── solution-view.html │ │ │ │ └── view.html │ │ │ ├── property-list/ │ │ │ │ ├── property-list-gadget.component.ts │ │ │ │ └── view.html │ │ │ ├── service-list/ │ │ │ │ ├── service-list-gadget.component.ts │ │ │ │ ├── service-list.ts │ │ │ │ └── view.html │ │ │ ├── statistic/ │ │ │ │ ├── service.ts │ │ │ │ ├── statistic-gadget.component.ts │ │ │ │ └── view.html │ │ │ ├── storage-object-list/ │ │ │ │ ├── service.ts │ │ │ │ ├── storage-object-list.component.ts │ │ │ │ ├── style.css │ │ │ │ └── view.html │ │ │ ├── todo/ │ │ │ │ ├── service.ts │ │ │ │ ├── todo-gadget.component.ts │ │ │ │ └── view.html │ │ │ ├── trend/ │ │ │ │ ├── service.ts │ │ │ │ ├── trend-gadget.component.ts │ │ │ │ └── view.html │ │ │ └── trend-line/ │ │ │ ├── service.ts │ │ │ ├── trend-line-gadget.component.ts │ │ │ └── view.html │ │ ├── grid/ │ │ │ ├── cell.component.ts │ │ │ ├── grid.component.ts │ │ │ ├── grid.html │ │ │ ├── grid.module.ts │ │ │ ├── grid.service.ts │ │ │ └── styles-grid.css │ │ ├── layout/ │ │ │ ├── layout-component.ts │ │ │ ├── layout.module.ts │ │ │ ├── model.ts │ │ │ ├── styles.css │ │ │ └── view.html │ │ ├── menu/ │ │ │ ├── IEvent.ts │ │ │ ├── menu-service.ts │ │ │ ├── menu.component.ts │ │ │ ├── menu.module.ts │ │ │ ├── styles.css │ │ │ └── view.html │ │ ├── notification/ │ │ │ ├── notification-component.css │ │ │ ├── notification-component.html │ │ │ ├── notification-component.spec.ts │ │ │ ├── notification-component.ts │ │ │ ├── notification-service.spec.ts │ │ │ ├── notification-service.ts │ │ │ ├── notification.model.ts │ │ │ ├── notification.module.ts │ │ │ ├── notificationDetail.component.ts │ │ │ └── notificationDetail.html │ │ ├── routing.module.ts │ │ ├── services/ │ │ │ ├── configuration-sample-boards-prod.model.ts │ │ │ ├── configuration-sample-boards.model.ts │ │ │ ├── configuration-sample-default-board.ts │ │ │ ├── configuration.service.ts │ │ │ ├── runtime.service.ts │ │ │ └── websocket-service.ts │ │ ├── toast/ │ │ │ ├── message.ts │ │ │ ├── reverse.pipe.spec.ts │ │ │ ├── reverse.pipe.ts │ │ │ ├── toast.component.css │ │ │ ├── toast.component.html │ │ │ ├── toast.component.spec.ts │ │ │ ├── toast.component.ts │ │ │ ├── toast.module.ts │ │ │ ├── toast.service.spec.ts │ │ │ └── toast.service.ts │ │ └── typeahead-input/ │ │ ├── styles.css │ │ ├── typeahead-input.component.ts │ │ ├── typeahead-input.module.ts │ │ └── view.html │ ├── assets/ │ │ ├── .gitkeep │ │ └── api/ │ │ ├── chart-mock-bar-model.json │ │ ├── chart-mock-bubble-model.json │ │ ├── chart-mock-pie-model.json │ │ ├── connection-model.json │ │ ├── cpu-model.json │ │ ├── disk-help-model.json │ │ ├── disk-model.json │ │ ├── donut-model.json │ │ ├── gadget-library-model-prod.json │ │ ├── gadget-library-model.json │ │ ├── http-codes.json │ │ ├── news-model.json │ │ ├── port-model.json │ │ ├── stat-database-model.json │ │ ├── stat-job-model.json │ │ ├── stat-undefined-model.json │ │ ├── stat-vm-model.json │ │ ├── stat-volume-model.json │ │ ├── storage-model.json │ │ ├── todo-model.json │ │ ├── trend-model.json │ │ ├── trendline-help-model.json │ │ └── version-model.json │ ├── environments/ │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### JetBrains template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff: .idea .idea/**/workspace.xml .idea/**/tasks.xml .idea/dictionaries # webpack stuff dist # node_modules node_modules # semantic semantic # Sensitive or high-churn files: .idea/**/dataSources/ .idea/**/dataSources.ids .idea/**/dataSources.xml .idea/**/dataSources.local.xml .idea/**/sqlDataSources.xml .idea/**/dynamic.xml .idea/**/uiDesigner.xml # Gradle: .idea/**/gradle.xml .idea/**/libraries # Mongo Explorer plugin: .idea/**/mongoSettings.xml ## File-based project format: *.iws ## Plugin-specific files: # IntelliJ /out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties documentation/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) [year] [fullname] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # NGX Dynamic Dashboard Framework ## Natural Language Processing (NLP) integration ![Image of Main Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/nlp.gif) ## Sample Board 1 ![Image of Main Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/images/sb1.png) ## Add Board and Gadget ![Image of Add Gadget To Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/add.gif) ## Layout ![Image Layout](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/layout.gif) ## Example JSON document The following JSON document describes a single board along with its layout, gadgets and their properites. ```json { "board": [ { "title": "Board Sample 1", "structure": "3-6-3", "id": 9, "boardInstanceId": 1, "rows": [ { "columns": [ { "styleClass": "three wide", "gadgets": [ { "componentType": "NewsGadgetComponent", "name": "News", "description": "What's new", "icon": "images/news.png", "instanceId": 1500253814523, "tags": [ { "facet": "Informational", "name": "news" }, { "facet": "List", "name": "news" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "value": "news", "key": "endpoint", "label": "News URL", "required": false, "order": 3, "controlType": "dynamicdropdown" }, { "value": "News", "key": "title", "label": "Title", "required": false, "order": 1, "controlType": "textbox" }, { "value": 2, "key": "instanceId", "required": false, "order": -1, "controlType": "hidden" } ] } ] } } ] }, { "styleClass": "six wide", "gadgets": [ { "componentType": "CPUGadgetComponent", "name": "CPU Chart", "description": "Monitors CPU utilization for application.", "icon": "images/cpu.png", "instanceId": 1499912922910, "tags": [ { "facet": "Performance", "name": "real-time" }, { "facet": "Chart", "name": "bar" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "value": "CPU Utilization", "key": "title", "label": "Title", "required": false, "order": 1, "controlType": "textbox" }, { "value": "Carlosappliance - Process Monitor", "key": "endpoint", "label": "API Endpoints", "required": false, "order": 3, "controlType": "dynamicdropdown" }, { "value": 999, "key": "instanceId", "required": false, "order": -1, "controlType": "hidden" } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "value": true, "key": "chart_properties", "label": "Show chart details", "required": false, "order": 3, "controlType": "checkbox" } ] } ] } }, { "componentType": "TrendGadgetComponent", "name": "Trend", "description": "General trends.", "icon": "images/trend.png", "instanceId": 1499912901569, "tags": [ { "facet": "Performance", "name": "trend" }, { "facet": "Chart", "name": "area" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "value": "Devappliance", "key": "endpoint", "label": "API Endpoints", "required": false, "order": 2, "controlType": "dynamicdropdown" }, { "value": "Trend", "key": "title", "label": "Title", "required": false, "order": 1, "controlType": "textbox" }, { "value": 2, "key": "instanceId", "required": false, "order": -1, "controlType": "hidden" } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "value": true, "key": "chart_properties", "label": "Show chart details", "required": false, "order": 3, "controlType": "checkbox" } ] } ] } } ] }, { "styleClass": "three wide", "gadgets": [] } ] } ] } ] } ``` ## Alert/Notification ![Image Notification](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/notification.gif) ## Sample Realtime Web Socket Based Gadget ![Image of Add Gadget To Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/websocket-realtime.gif) ## Sample Board 2 ![Image of Main Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/images/sb2.png) ## Drag and Drop ![Image of Add Board To Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/drag-drop.gif) ## Facet Filter ![Image of Filter Board To Screen](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/filter.gif) > Note: This project is under heavy construction and is not intended for general production use yet. As such, we are not accepting bugs at the moment and documentation is quite lacking. This is an angular (ngx) based dashboard framework that is inspired by JIRA's dashboard implementation and https://github.com/angular-dashboard-framework/angular-dashboard-framework The primary projects leveraged: * Angular - https://angularjs.org/ * ngx-charts (angular based d3 charts) - https://github.com/swimlane/ngx-charts * Semantic-UI - https://semantic-ui.com/ * ng2-dnd drag and drop - https://github.com/akserg/ng2-dnd * AI Natural Language Processing - The board includes two options for AI, Wit.Ai and IBM Watson. * Wit.ai - Natural Language Processing site has been integrated via JSONP * IBM Watson - IBM Watson does not support JSONP so the code relies on a backend implementation of the IBM Watson SDK. I offer sample backend code based on Spring Boot within the comments of the Runtime Service Features: * Leverages Angular's dynamic data driven forms approach for gadget property pages and properties - https://angular.io/guide/dynamic-form * Dynamic component strategy for creating gadget instances during runtime - https://angular.io/guide/dynamic-component-loader * Faceted gadget search approach leveraging tags * Support multiple board creation * Drag and Drop support * Multiple Data Source/Endpoint management * Web Socket support * Completely customizable and configurable # Getting Started Developing a Gadget The code includes a very simple Todo gadget that can be used as an example for getting started developing your own gadget. The following steps uses that Todo Gadget as a reference. You focus on defining the gadget and the rest of the framework will deal with making it available to the Add Gadget Modal, drag and drop, instance creation, tracking, persistence and cleanup, etc. ## Define the Gadget Component, Service and View * Todo Component [todo-gadget.component.ts](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/gadgets/todo/todo-gadget.component.ts) * Todo View [view.html](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/gadgets/todo/view.html) * Todo Service [service.ts](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/gadgets/todo/service.ts) * Sample mock service data [todo-model.json](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/assets/api/todo-model.json) ## Define the gadget's model The model is used to dynamically create and render the gadget and its property page forms. This model is an entry into a model array used for all gadgets. You will simply add an entry to the model's array. See the Todo entry. Add an entry for the gadget in the library model array [gadget-library-model.json](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/assets/api/gadget-library-model.json) ## Add the gadget entry to the gadget factory class Add an entry for your gadget in the factory gadget class [gadget-factory.ts](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/add-gadget/gadget-factory.ts) ## Gadget Icon Define an image/icon for your gadget [todo.png](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/assets/images/todo.png) ## Gadget Module References * Import the gadget's component into the board module [board.module.ts](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/board/board.module.ts) * Import the gadget's service into the grid module [grid.module.ts](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/grid/grid.module.ts) * Import the gadget's component and service into the gadget module [gadget.module.ts](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework/blob/master/src/app/gadgets/gadget.module.ts) ![Todo Gadget](https://github.com/catalogicsoftware/Angular-2-Dashboard-Framework/blob/master/src/assets/documentation/gifs/TodoGadget.gif) # NgADF This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.x. ## Setup Clone this repository then run `npm install` ## Development server Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. ## Build Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--aot --prod` flag for ahead of time compilation and production mode. The title of that issue suggests an issue with AOT but in my testing `--prod` seems to be the problem. ## Running unit tests Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). ## Running end-to-end tests Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). Before running the tests make sure you are serving the app via `ng serve`. ## Source Code Documentation This project uses the compodoc project : https://github.com/compodoc/compodoc Run `npm install -g @compodoc/compodoc` to install compodoc globally Run `compodoc -p tsconfig.json -n 'NGX Dynamic Dashboard Framework'` to generate the documentation. It will be placed in the documentation folder Run `compodoc -s` to serve up the documentation site at http://localhost:8080 ## Spring Boot Backend Project There is an accompanying java based backend project that serves up some of the endpoints used by the board. [https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework-microservice](https://github.com/catalogicsoftware/ngx-dynamic-dashboard-framework-microservice) It is a maven based project so you will need to do the following: * Install and configure Maven. * Copy the dist directory produced from this project's build into the `static` folder of the microservice project. * Build the microservice project using `/mvn install` from within the project's root directory. * Launch the microservice over the default port: `http://localhost:8080` using `java -jar /target/ngxdd-x.y.z.jar` ## Further help To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). ================================================ FILE: angular.json ================================================ { "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "ng-adf": { "root": "", "sourceRoot": "src", "projectType": "application", "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", "tsConfig": "src/tsconfig.app.json", "polyfills": "src/polyfills.ts", "assets": [ "src/assets", "src/favicon.ico" ], "styles": [ "src/styles.css", "node_modules/semantic-ui/dist/semantic.min.css", "node_modules/ng2-dnd/bundles/style.css", "node_modules/material-design-icons/iconfont/material-icons.css" ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", "node_modules/semantic-ui/dist/semantic.min.js", "node_modules/d3-shape/dist/d3-shape.min.js" ] }, "configurations": { "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ] } } }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "ng-adf:build" }, "configurations": { "production": { "browserTarget": "ng-adf:build:production" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "ng-adf:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "karmaConfig": "./karma.conf.js", "polyfills": "src/polyfills.ts", "tsConfig": "src/tsconfig.spec.json", "scripts": [ "node_modules/jquery/dist/jquery.min.js", "node_modules/semantic-ui/dist/semantic.min.js", "node_modules/d3-shape/build/d3-shape.min.js" ], "styles": [ "src/styles.css", "node_modules/semantic-ui/dist/semantic.min.css", "node_modules/ng2-dnd/bundles/style.css" ], "assets": [ "src/assets", "src/favicon.ico" ] } }, "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { "tsConfig": [ "src/tsconfig.app.json", "src/tsconfig.spec.json" ], "exclude": [] } } } }, "ng-adf-e2e": { "root": "", "sourceRoot": "e2e", "projectType": "application", "architect": { "e2e": { "builder": "@angular-devkit/build-angular:protractor", "options": { "protractorConfig": "./protractor.conf.js", "devServerTarget": "ng-adf:serve" } }, "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { "tsConfig": [ "e2e/tsconfig.e2e.json" ], "exclude": [] } } } } }, "defaultProject": "ng-adf", "cli": { "warnings": { "typescriptMismatch": false } }, "schematics": { "@schematics/angular:component": { "prefix": "app", "styleext": "css" }, "@schematics/angular:directive": { "prefix": "app" } } } ================================================ FILE: build.sh ================================================ #!/bin/bash #ng config -g cli.warnings.versionMismatch false npm install ng build --prod --aot ================================================ FILE: e2e/app.e2e-spec.ts ================================================ import { NgADFPage } from './app.po'; describe('ng-adf App', () => { let page: NgADFPage; beforeEach(() => { page = new NgADFPage(); }); it('should display message saying app works', () => { page.navigateTo(); expect(page.getParagraphText()).toEqual('app works!'); }); }); ================================================ FILE: e2e/app.po.ts ================================================ import { browser, element, by } from 'protractor'; export class NgADFPage { navigateTo() { return browser.get('/'); } getParagraphText() { return element(by.css('app-root h1')).getText(); } } ================================================ FILE: e2e/tsconfig.e2e.json ================================================ { "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", "target": "es5", "types":[ "jasmine", "node" ] } } ================================================ FILE: karma.conf.js ================================================ // Karma configuration file, see link for more information // https://karma-runner.github.io/0.13/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma') ], client:{ clearContext: false // leave Jasmine Spec Runner output visible in browser }, files: [ ], preprocessors: { }, mime: { 'text/x-typescript': ['ts','tsx'] }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], fixWebpackSourcePaths: true }, reporters: config.angularCli && config.angularCli.codeCoverage ? ['progress', 'coverage-istanbul'] : ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false }); }; ================================================ FILE: ngx-dynamic-dashboard-framework.iml ================================================ ================================================ FILE: package.json ================================================ { "name": "ng-dadf", "version": "0.0.0", "license": "MIT", "ngPackage": { "lib": { "entryFile": "public_api.ts" } }, "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { "@angular/animations": "7.2.3", "@angular/cdk": "^7.3.1", "@angular/common": "7.2.3", "@angular/compiler": "^7.2.3", "@angular/core": "^7.2.3", "@angular/forms": "7.2.3", "@angular/http": "7.2.3", "@angular/material": "^7.3.1", "@angular/platform-browser": "7.2.3", "@angular/platform-browser-dynamic": "7.2.3", "@angular/router": "7.2.3", "@swimlane/ngx-charts": "10.0.0", "core-js": "^2.5.7", "d3": "^5.7.0", "d3-shape": "^1.2.2", "jquery": "^3.3.1", "material-design-icons": "^3.0.1", "ng2-cookies": "1.0.12", "ng2-dnd": "^5.0.2", "npm": "^6.4.1", "reflect-metadata": "0.1.12", "rxjs": "^6.4.0", "semantic-ui": "^2.4.2", "socket.io-client": "^2.1.1", "sockjs-client": "^1.3.0", "stompjs": "^2.3.3", "tslib": "^1.9.0", "zone.js": "^0.8.29" }, "devDependencies": { "@angular-devkit/build-angular": "^0.13.0", "@angular/cli": "^7.3.0", "@angular/compiler-cli": "^7.2.3", "@types/jasmine": "2.8.9", "@types/node": "~10.12.2", "codelyzer": "~4.5.0", "jasmine-core": "~3.3.0", "jasmine-spec-reporter": "~4.2.1", "karma": "^3.1.1", "karma-chrome-launcher": "~2.2.0", "karma-cli": "~1.0.1", "karma-coverage-istanbul-reporter": "^2.0.4", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^1.4.0", "protractor": "^5.4.1", "ts-node": "~7.0.1", "tslint": "^5.11.0", "typescript": "^3.2.4" } } ================================================ FILE: protractor.conf.js ================================================ // Protractor configuration file, see link for more information // https://github.com/angular/protractor/blob/master/lib/config.ts const { SpecReporter } = require('jasmine-spec-reporter'); exports.config = { allScriptsTimeout: 11000, specs: [ './e2e/**/*.e2e-spec.ts' ], capabilities: { 'browserName': 'chrome' }, directConnect: true, baseUrl: 'http://localhost:4200/', framework: 'jasmine', jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 30000, print: function() {} }, beforeLaunch: function() { require('ts-node').register({ project: 'e2e/tsconfig.e2e.json' }); }, onPrepare() { jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); } }; ================================================ FILE: semantic.json ================================================ { "base": "semantic/", "paths": { "source": { "config": "src/theme.config", "definitions": "src/definitions/", "site": "src/site/", "themes": "src/themes/" }, "output": { "packaged": "dist/", "uncompressed": "dist/components/", "compressed": "dist/components/", "themes": "dist/themes/" }, "clean": "dist/" }, "permission": false, "autoInstall": true, "rtl": false, "version": "2.4.2" } ================================================ FILE: src/app/about/about-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { AfterViewInit, Component, Output, EventEmitter, Input } from '@angular/core'; import {environment} from '../../environments/environment'; import {AboutService} from "./service"; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-about-modal', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class AboutComponent implements AfterViewInit { modalHeader = 'About'; apiVersion: string; messageModal: any; env: any; constructor(private _aboutService: AboutService) { this.env = environment; } ngAfterViewInit() { this.getVersion(); } getVersion() { this._aboutService.getAPIVersion().subscribe(data => { this.apiVersion = data['version']; }); } } ================================================ FILE: src/app/about/about.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {AboutComponent} from './about-component'; import {AboutService} from "./service"; @NgModule({ imports: [ CommonModule ], declarations: [AboutComponent], exports: [AboutComponent], providers: [AboutService] }) export class AboutModule { } ================================================ FILE: src/app/about/model.ts ================================================ /** * Created by jayhamilton on 1/31/17. */ export const boardLayouts = [ { id: 0, boardInstanceId: 0, title: 'one-narrow', checked: false, structure: '8', rows: [{ columns: [ { styleClass: 'eight wide', }] }] }, { id: 1, boardInstanceId: 1, title: 'single', checked: false, structure: '14', rows: [{ columns: [ { styleClass: 'fourteen wide', }] }] }, { id: 2, boardId: 2, title: 'narrow-right', checked: false, structure: '10-6', rows: [{ columns: [ { styleClass: 'ten wide', }, { styleClass: 'six wide', }] }] }, { id: 3, boardInstanceId: 3, title: 'wide-center', checked: false, structure: '4-8-4', rows: [{ columns: [ { styleClass: 'four wide', }, { styleClass: 'eight wide', }, { styleClass: 'four wide', }] }] }, { id: 4, boardInstanceId: 4, title: 'narrow-left', checked: false, structure: '4-12', rows: [{ columns: [ { styleClass: 'four wide', }, { styleClass: 'twelve wide', }] }] }, { id: 5, boardInstanceId: 5, title: 'two-even', checked: true, structure: '6-6', rows: [{ columns: [ { styleClass: 'six wide', }, { styleClass: 'six wide', }] }] }, { id: 6, boardInstanceId: 6, title: 'three-even', checked: false, structure: '5-5-5', rows: [{ columns: [ { styleClass: 'five wide', }, { styleClass: 'five wide', }, { styleClass: 'five wide', } ] }] }, { id: 7, boardInstanceId: 7, title: 'wide-top', checked: false, structure: '16/8-8', rows: [ { columns: [ { styleClass: 'sixteen wide' } ] }, { columns: [ { styleClass: 'eight wide' }, { styleClass: 'eight wide' } ] } ] }, { id: 8, boardInstanceId: 8, title: 'ngadmin', checked: false, structure: '4-4-4-4/8-4-4', rows: [ { columns: [ { styleClass: 'four wide' }, { styleClass: 'four wide' }, { styleClass: 'four wide' }, { styleClass: 'four wide' } ] }, { columns: [ { styleClass: 'eight wide' }, { styleClass: 'four wide' }, { styleClass: 'four wide' } ] } ] }, { id: 9, boardInstanceId: 9, title: 'google-layout', checked: false, structure: '3-6-3', rows: [ { columns: [ { styleClass: 'three wide' }, { styleClass: 'six wide' }, { styleClass: 'three wide' } ] } ] } ]; ================================================ FILE: src/app/about/service.ts ================================================ import {Injectable} from "@angular/core"; import {environment} from "../../environments/environment"; import {HttpClient} from "@angular/common/http"; import {RuntimeService} from "../services/runtime.service"; import {catchError} from "rxjs/operators"; @Injectable() export class AboutService { env: any; constructor(private _http: HttpClient) { this.env = environment; } getAPIVersion() { let url: string; if (this.env.production == true) { url = '/version'; } else { url = '/assets/api/version-model.json'; } return this._http.get(url) .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/about/styles.css ================================================ ul{ text-align: center; } h2, h3, h4{ color:white !important; font-weight: normal !important; } ================================================ FILE: src/app/about/view.html ================================================

{{modalHeader}}




{{env.productName}}

Version {{env.productVersion}}



API Version

{{apiVersion}}

================================================ FILE: src/app/add-gadget/add-gadget-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { ViewChild, ElementRef, AfterViewInit, Component, Output, EventEmitter } from '@angular/core'; import { style, trigger, animate, transition } from '@angular/animations'; import {AddGadgetService} from './service'; import {Facet} from '../facet/facet-model'; import {FacetTagProcessor} from '../facet/facet-tag-processor'; declare var jQuery: any; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-add-gadget-modal', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'], animations: [ trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(750, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(750, style({opacity: 0})) ]) ]) ] }) export class AddGadgetComponent implements AfterViewInit { @Output() addGadgetEvent: EventEmitter = new EventEmitter(); gadgetObjectList: any[] = []; gadgetObjectTitleList: string[] = []; placeHolderText = 'Begin typing gadget name'; layoutColumnOneWidth = 'six'; layoutColumnTwoWidth = 'ten'; listHeader= 'Gadgets'; facetTags: Array; color = 'white'; typeAheadIsInMenu = false; modalicon: string; modalheader: string; modalmessage: string; @ViewChild('messagemodal_tag') messagemodalRef: ElementRef; messageModal: any; constructor(private _addGadgetService: AddGadgetService) { this.getObjectList(); } actionHandler(actionItem, actionName) { this.addGadgetEvent.emit(actionItem); this.hideMessageModal(); } showMessageModal(icon: string, header: string, message: string) { this.modalicon = icon; this.modalheader = header; this.modalmessage = message; this.messageModal.modal('show'); } showComponentLibraryModal(header: string) { this.modalheader = header; this.messageModal.modal('show'); } hideMessageModal() { this.modalicon = ''; this.modalheader = ''; this.modalmessage = ''; this.messageModal.modal('hide'); } ngAfterViewInit() { this.messageModal = jQuery(this.messagemodalRef.nativeElement); } getObjectList() { this._addGadgetService.getGadgetLibrary().subscribe(data => { this.gadgetObjectList.length = 0; const me = this; data.library.forEach(function (item) { me.gadgetObjectList.push(item); me.gadgetObjectTitleList.push(item.name); }); const facetTagProcess = new FacetTagProcessor(this.gadgetObjectList); this.facetTags = facetTagProcess.getFacetTags(); }); } } ================================================ FILE: src/app/add-gadget/add-gadget.module.ts ================================================ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {AddGadgetComponent} from './add-gadget-component'; import {AddGadgetService} from './service'; import {HttpClientModule} from '@angular/common/http'; import {DataListModule} from '../datalist/data-list.module'; import {MatButtonModule} from '@angular/material'; @NgModule({ imports: [ CommonModule, DataListModule, HttpClientModule, MatButtonModule, ], declarations: [ AddGadgetComponent ], providers: [ AddGadgetService ], exports: [ AddGadgetComponent ] }) export class AddGadgetModule { } ================================================ FILE: src/app/add-gadget/gadget-factory.ts ================================================ import {CPUGadgetComponent} from '../gadgets/cpu/cpu-gadget.component'; import {MemoryGadgetComponent} from '../gadgets/memory/memory-gadget.component'; import {PropertyListGadgetComponent} from '../gadgets/property-list/property-list-gadget.component'; import {DiskGadgetComponent} from '../gadgets/disk/disk-gadget.component'; import {ServiceListGadgetComponent} from '../gadgets/service-list/service-list-gadget.component'; import {StatisticGadgetComponent} from '../gadgets/statistic/statistic-gadget.component'; import {TrendGadgetComponent} from '../gadgets/trend/trend-gadget.component'; import {NewsGadgetComponent} from '../gadgets/news/news-gadget.component'; import {JobAnalysisGadgetComponent} from '../gadgets/job-analysis/job-analysis-gadget.component'; import {TrendLineGadgetComponent} from '../gadgets/trend-line/trend-line-gadget.component'; import {EdgeServiceListGadgetComponent} from '../gadgets/edge-service-list/edge-service-list-gadget.component'; import {CPUMGadgetComponent} from '../gadgets/cpum/cpum-gadget.component'; import {PortConnectionGadgetComponent} from '../gadgets/port-connection/port-connection-gadget.component'; import {StorageObjectListComponent} from '../gadgets/storage-object-list/storage-object-list.component'; import {DonutGadgetComponent} from '../gadgets/donut/donut-gadget.component'; import {TodoGadgetComponent} from '../gadgets/todo/todo-gadget.component'; import {BubbleGadgetComponent} from "../gadgets/bubble/bubble-gadget.component"; import {BarChartGadgetComponent} from "../gadgets/barchart/barchart-gadget.component"; import {PieChartGadgetComponent} from "../gadgets/piechart/piechart-gadget.component"; /** * Created by jayhamilton on 6/30/17. */ export class GadgetFactory { /** * todo - return new instances instead of the same instance. This requires the creation of new configuration options. * @param gadgetType * @returns {any} */ static getComponentType(gadgetType): any { switch (gadgetType) { case 'DonutGadgetComponent': return DonutGadgetComponent; case 'CPUGadgetComponent': return CPUGadgetComponent; case 'MemoryGadgetComponent': return MemoryGadgetComponent; case 'PropertyListGadgetComponent': return PropertyListGadgetComponent; case 'DiskGadgetComponent': return DiskGadgetComponent; case 'ServiceListGadgetComponent': return ServiceListGadgetComponent; case 'StatisticGadgetComponent': return StatisticGadgetComponent; case 'TrendGadgetComponent': return TrendGadgetComponent; case 'NewsGadgetComponent': return NewsGadgetComponent; case'JobAnalysisGadgetComponent': return JobAnalysisGadgetComponent; case'TrendLineGadgetComponent': return TrendLineGadgetComponent; case'EdgeServiceListGadgetComponent': return EdgeServiceListGadgetComponent; case 'CPUMGadgetComponent': return CPUMGadgetComponent; case 'PortConnectionGadgetComponent': return PortConnectionGadgetComponent; case 'StorageObjectListComponent': return StorageObjectListComponent; case 'TodoGadgetComponent': // todo gadget return TodoGadgetComponent; case 'BubbleGadgetComponent': // todo gadget return BubbleGadgetComponent; case 'BarChartGadgetComponent': // todo gadget return BarChartGadgetComponent; case 'PieChartGadgetComponent': // todo gadget return PieChartGadgetComponent; default: return null;// todo add default gadget that would be displayed. Useful for troubleshooting new gadget dev } } } ================================================ FILE: src/app/add-gadget/gadgetLibraryResponse.ts ================================================ interface GadgetLibraryResponse { library: any[]; } ================================================ FILE: src/app/add-gadget/service.ts ================================================ /** * Created by jayhamilton on 2/7/17. */ import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import {environment} from '../../environments/environment' @Injectable() export class AddGadgetService { env: any; constructor(private _http: HttpClient) { this.env = environment; } getGadgetLibrary() { let gadgetLibraryJson = ''; if (this.env.production == true) { gadgetLibraryJson = 'gadget-library-model-prod.json'; } else { gadgetLibraryJson = 'gadget-library-model.json'; } return this._http.get('/assets/api/' + gadgetLibraryJson); } } ================================================ FILE: src/app/add-gadget/styles.css ================================================ .modal{ background-color:#f7f7f7; } ================================================ FILE: src/app/add-gadget/view.html ================================================ ================================================ FILE: src/app/api-token/api-token.service.ts ================================================ import {Injectable} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; @Injectable() export class APITokenService { private apiToken: string; private credentials = {url: '', user: '', password: ''}; constructor(private _http: HttpClient) { } public getAPITokenForCredentials(_credentials: any) { let headers = new HttpHeaders(); this.credentials = _credentials; headers = headers.append('Content-Type', 'application/json'); headers = headers.append('Authorization', 'Basic ' + B64encode(this.credentials.user + ':' + this.credentials.password)); return this._http.post(this.credentials.url, '', {headers: headers}); } public setAPIToken(apiToken: string) { this.apiToken = apiToken; } public getAPIToken() { return this.apiToken; } } function B64encode(data: string) { var c1, c2, c3; var Base64 = { _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', encode: function (e) { var t = ''; var n, r, i, s, o, u, a; var f = 0; e = Base64._utf8_encode(e); while (f < e.length) { n = e.charCodeAt(f++); r = e.charCodeAt(f++); i = e.charCodeAt(f++); s = n >> 2; o = (n & 3) << 4 | r >> 4; u = (r & 15) << 2 | i >> 6; a = i & 63; if (isNaN(r)) { u = a = 64 } else if (isNaN(i)) { a = 64 } t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a) } return t }, decode: function (e) { var t = ''; var n, r, i; var s, o, u, a; var f = 0; e = e.replace(/[^A-Za-z0-9\+\/\=]/g, ''); while (f < e.length) { s = this._keyStr.indexOf(e.charAt(f++)); o = this._keyStr.indexOf(e.charAt(f++)); u = this._keyStr.indexOf(e.charAt(f++)); a = this._keyStr.indexOf(e.charAt(f++)); n = s << 2 | o >> 4; r = (o & 15) << 4 | u >> 2; i = (u & 3) << 6 | a; t = t + String.fromCharCode(n); if (u != 64) { t = t + String.fromCharCode(r); } if (a != 64) { t = t + String.fromCharCode(i); } } t = Base64._utf8_decode(t); return t; }, _utf8_encode: function (e) { e = e.replace(/\r\n/g, '\n'); var t = ''; for (var n = 0; n < e.length; n++) { var r = e.charCodeAt(n); if (r < 128) { t += String.fromCharCode(r); } else if (r > 127 && r < 2048) { t += String.fromCharCode(r >> 6 | 192); t += String.fromCharCode(r & 63 | 128); } else { t += String.fromCharCode(r >> 12 | 224); t += String.fromCharCode(r >> 6 & 63 | 128); t += String.fromCharCode(r & 63 | 128); } } return t; }, _utf8_decode: function (e) { var t = ''; var n = 0; var r = c1 = c2 = 0; while (n < e.length) { r = e.charCodeAt(n); if (r < 128) { t += String.fromCharCode(r); n++ } else if (r > 191 && r < 224) { c2 = e.charCodeAt(n + 1); t += String.fromCharCode((r & 31) << 6 | c2 & 63); n += 2 } else { c2 = e.charCodeAt(n + 1); c3 = e.charCodeAt(n + 2); t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63); n += 3 } } return t; } } return Base64.encode(data); } ================================================ FILE: src/app/app.component.css ================================================ ================================================ FILE: src/app/app.component.html ================================================ ================================================ FILE: src/app/app.component.spec.ts ================================================ import { TestBed, async } from '@angular/core/testing'; import 'rxjs/Rx'; import { AppComponent } from './app.component'; import {RoutingModule} from './routing.module'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ AppComponent ], imports: [ RoutingModule ] }).compileComponents(); })); it('should create the app', async(() => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app).toBeTruthy(); })); /* it(`should have as title 'app works!'`, async(() => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app.title).toEqual('app works!'); })); it('should render title in a h1 tag', async(() => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h1').textContent).toContain('app works!'); })); */ }); ================================================ FILE: src/app/app.component.ts ================================================ import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app works!'; } ================================================ FILE: src/app/app.module.ts ================================================ import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {AppComponent} from './app.component'; import {RoutingModule} from './routing.module'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {HttpClientJsonpModule, HttpClientModule} from '@angular/common/http'; import {DetailModule} from './detail/detail.module'; import {MenuModule} from './menu/menu.module'; import {BoardModule} from './board/board.module'; @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, RoutingModule, FormsModule, HttpClientModule, BoardModule, MenuModule, DetailModule, HttpClientJsonpModule ], declarations: [ AppComponent, ], bootstrap: [AppComponent] }) export class AppModule { } ================================================ FILE: src/app/board/board.component.ts ================================================ import { Component} from '@angular/core'; /**a * Board component * */ @Component({ moduleId: module.id, templateUrl: './view.html' }) export class BoardComponent { } ================================================ FILE: src/app/board/board.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {GridModule} from '../grid/grid.module'; import {BoardComponent} from './board.component'; import {CPUMGadgetComponent} from '../gadgets/cpum/cpum-gadget.component'; import {EdgeServiceListGadgetComponent} from '../gadgets/edge-service-list/edge-service-list-gadget.component'; import {TrendLineGadgetComponent} from '../gadgets/trend-line/trend-line-gadget.component'; import {JobAnalysisGadgetComponent} from '../gadgets/job-analysis/job-analysis-gadget.component'; import {NewsGadgetComponent} from '../gadgets/news/news-gadget.component'; import {TrendGadgetComponent} from '../gadgets/trend/trend-gadget.component'; import {StatisticGadgetComponent} from '../gadgets/statistic/statistic-gadget.component'; import {DiskGadgetComponent} from '../gadgets/disk/disk-gadget.component'; import {PropertyListGadgetComponent} from '../gadgets/property-list/property-list-gadget.component'; import {ServiceListGadgetComponent} from '../gadgets/service-list/service-list-gadget.component'; import {CPUGadgetComponent} from '../gadgets/cpu/cpu-gadget.component'; import {MemoryGadgetComponent} from '../gadgets/memory/memory-gadget.component'; import {PortConnectionGadgetComponent} from '../gadgets/port-connection/port-connection-gadget.component'; import {StorageObjectListComponent} from '../gadgets/storage-object-list/storage-object-list.component'; import {DonutGadgetComponent} from '../gadgets/donut/donut-gadget.component'; import {TodoGadgetComponent} from '../gadgets/todo/todo-gadget.component'; // todo gadget import {BubbleGadgetComponent} from "../gadgets/bubble/bubble-gadget.component"; import {BarChartGadgetComponent} from "../gadgets/barchart/barchart-gadget.component"; import {PieChartGadgetComponent} from "../gadgets/piechart/piechart-gadget.component"; @NgModule({ imports: [ CommonModule, GridModule.withComponents([ MemoryGadgetComponent, CPUGadgetComponent, ServiceListGadgetComponent, PropertyListGadgetComponent, DiskGadgetComponent, StatisticGadgetComponent, TrendGadgetComponent, NewsGadgetComponent, JobAnalysisGadgetComponent, TrendLineGadgetComponent, EdgeServiceListGadgetComponent, CPUMGadgetComponent, PortConnectionGadgetComponent, StorageObjectListComponent, DonutGadgetComponent, TodoGadgetComponent // todo gadget , BubbleGadgetComponent, BarChartGadgetComponent, PieChartGadgetComponent ]), ], providers: [], declarations: [ BoardComponent ] }) export class BoardModule { } ================================================ FILE: src/app/board/view.html ================================================ ================================================ FILE: src/app/configuration/configuration-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { ViewChild, ElementRef, AfterViewInit, Component, Output, EventEmitter, Input } from '@angular/core'; import { style, state, trigger, animate, transition } from '@angular/animations'; import {tabsModel} from './tabs.model'; import {environment} from '../../environments/environment' declare var jQuery: any; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-configuration-modal', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'], animations: [ trigger('contentSwitch', [ state('inactive', style({ opacity: 0 })), state('active', style({ opacity: 1 })), transition('inactive => active', animate('750ms ease-in')), transition('active => inactive', animate('750ms ease-out')) ]) ] }) export class ConfigurationComponent implements AfterViewInit { @Output() dashboardCreateEvent: EventEmitter = new EventEmitter(); @Output() dashboardEditEvent: EventEmitter = new EventEmitter(); @Output() dashboardDeleteEvent: EventEmitter = new EventEmitter(); @Input() dashboardList: any []; newDashboardItem = ''; modalicon: string; modalheader: string; modalconfig: string; env:any; @ViewChild('boardconfigmodal_tag') boardconfigmodalaRef: ElementRef; configModal: any; currentTab: string; tabsModel: any[]; constructor() { Object.assign(this, {tabsModel}); this.setCurrentTab(0); this.env = environment; } showConfigurationModal(header: string) { this.modalheader = header; this.configModal.modal('show'); } hideMessageModal() { this.modalicon = ''; this.modalheader = ''; this.modalconfig = ''; this.configModal.modal('hide'); } createBoard(name: string) { if (name !== '') { this.dashboardCreateEvent.emit(name); this.newDashboardItem = ''; } console.log("Creating new board event from configuration component: " + name); } editBoard(name: string) { this.dashboardEditEvent.emit(name); } deleteBoard(name: string) { this.dashboardDeleteEvent.emit(name); } ngAfterViewInit() { this.configModal = jQuery(this.boardconfigmodalaRef.nativeElement); this.configModal.modal('hide'); } setCurrentTab(tab_index) { this.currentTab = this.tabsModel[tab_index].displayName; } } ================================================ FILE: src/app/configuration/configuration.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {EndpointConfigurationTabComponent} from './tab-endpoint/endpoint-configuration-tab.component'; import {EndPointDetailComponent} from './tab-endpoint/endpointDetail.component'; import {EndPointService} from './tab-endpoint/endpoint.service'; import {BoardsConfigurationTabComponent} from './tab-boards/boards-configuration-tab.component'; import {DndModule} from 'ng2-dnd'; import { MatButtonModule, MatCheckboxModule, MatIconModule, MatInputModule, MatOptionModule, MatSelectModule, MatSlideToggleModule, MatChipsModule } from '@angular/material'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {AIConfigurationTabComponent} from './tab-artificial-intelligence/ai-configuration-tab.component'; import {OptionsConfigurationTabComponent} from "./tab-options/options-configuration-tab.component"; import {ConfigurationComponent} from "./configuration-component"; import {OptionsService} from "./tab-options/service"; @NgModule({ imports: [ CommonModule, DndModule.forRoot(), MatButtonModule, MatIconModule, MatCheckboxModule, MatInputModule, MatSelectModule, MatOptionModule, FormsModule, ReactiveFormsModule, MatSlideToggleModule, MatChipsModule ], declarations: [ BoardsConfigurationTabComponent, EndpointConfigurationTabComponent, EndPointDetailComponent, AIConfigurationTabComponent, OptionsConfigurationTabComponent, ConfigurationComponent ], providers: [ EndPointService, OptionsService ], exports: [ BoardsConfigurationTabComponent, EndpointConfigurationTabComponent, EndPointDetailComponent, OptionsConfigurationTabComponent, ConfigurationComponent ] }) export class ConfigurationModule { } ================================================ FILE: src/app/configuration/styles.css ================================================ .ui.tabular.menu .active.item { border-top: 3px solid #3f51b5 !important; } .ui.attached.segment { margin: 0; width: 100%; } ================================================ FILE: src/app/configuration/tab-artificial-intelligence/ai-configuration-tab.component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { Component } from '@angular/core'; import {environment} from '../../../environments/environment'; @Component({ selector: 'app-ai-config-tab', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class AIConfigurationTabComponent { token; ibmwatsonuid; ibmwatsonpwd; ibmwatsoncid; ai_engines = [ {value: 'watson', viewValue: 'Watson'}, {value: 'witai', viewValue: 'WitAI'}, {value: ' ', viewValue: ' '} ]; selectedAIEngineValue: string; env: any; constructor() { /** * todo - get this information from a backend store * **/ this.env = environment; if (this.env.experimental) { this.token = localStorage.getItem('Wit.aiToken'); this.ibmwatsonuid = localStorage.getItem('ibmwatsonuid'); this.ibmwatsonpwd = localStorage.getItem('ibmwatsonpwd'); this.ibmwatsoncid = localStorage.getItem('ibmwatsoncid'); this.selectedAIEngineValue = localStorage.getItem('ai_engine'); } } selectChange(selectControl) { console.log(selectControl.value); this.selectedAIEngineValue = selectControl.value; localStorage.setItem('ai_engine', selectControl.value); } save() { /** * todo - get this information from a backend store * **/ if (!environment.production) { localStorage.setItem('Wit.aiToken', this.token); localStorage.setItem('ibmwatsonuid', this.ibmwatsonuid); localStorage.setItem('ibmwatsonpwd', this.ibmwatsonpwd); localStorage.setItem('ibmwatsoncid', this.ibmwatsoncid); } } } ================================================ FILE: src/app/configuration/tab-artificial-intelligence/styles.css ================================================ .ui.menu{ margin-bottom: .1em !important; } .ui.secondary.pointing.menu .active.item { color: #3f51b5 !important; font-weight: 500 !important; border-color: #3f51b5 !important; border-width: medium !important; font-family: 'Roboto', sans-serif !important; font-size: 1.4em; } .ui.secondary.pointing.menu .item { color: rgba(0, 0, 0, 0.3) !important; font-weight: 500 !important; font-family: 'Roboto', sans-serif !important; font-size: 1.4em; } .ui.secondary.pointing.menu { border-bottom: none !important; } .ui.tabular.menu .active.item { border-top-width: 3px !important; border-top-color: #3f51b5 !important; } input { padding: 5px; font-weight: 300 !important; font-family: 'Roboto', sans-serif !important; font-size: 1.1em; outline: none !important; } #content { background: #3f51b5; width: 120px; margin-left: auto; margin-right: auto; position: relative; top: 50%; transform: translateY(-50%); } label { font-family: 'Roboto', sans-serif; font-size: 1.3em !important; color: #4a4d50 !important; } label.endpoint { font-size: 1em !important; line-height: 1.5em !important; } td.detail { background-color: white !important; } .c-col { background-color: #ddebf5 !important; border: none !important; width: 5% !important; color: #788ca0 !important; } .ui.table thead th td { color: #788ca0 !important; } .ui.table { color: rgba(97, 97, 97, 0.68) !important } .inLine { display: inline-block; padding-left: 4px; padding-right: 4px; } .ui.tabular.menu + .attached:not(.top).segment, .ui.tabular.menu + .attached:not(.top).segment + .attached:not(.top).segment { border-bottom: none !important; } .example-full-width { width: 100%; } .grid-row { display: flex; align-items: center; justify-content: space-around; } ================================================ FILE: src/app/configuration/tab-artificial-intelligence/view.html ================================================
{{ ai_engine.viewValue }}

Wit.ai Access Token

Save

IBM Watson User

IBM Watson Password

IBM Watson Classifier ID

Save
This feature is not ready yet!
================================================ FILE: src/app/configuration/tab-boards/boards-configuration-tab.component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { Component, Output, EventEmitter, Input } from '@angular/core'; import {ConfigurationService} from '../../services/configuration.service'; import {environment} from '../../../environments/environment' /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-boards-config-tab', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class BoardsConfigurationTabComponent { @Output() dashboardCreateEvent: EventEmitter = new EventEmitter(); @Output() dashboardEditEvent: EventEmitter = new EventEmitter(); @Output() dashboardDeleteEvent: EventEmitter = new EventEmitter(); @Input() dashboardList: any []; newDashboardItem = ''; env: any; constructor(private _configurationService: ConfigurationService) { this.env = environment; } createBoard(name: string) { if (name !== '') { this.dashboardCreateEvent.emit(name); this.newDashboardItem = ''; } } editBoard(name: string) { this.dashboardEditEvent.emit(name); } deleteBoard(name: string) { this.dashboardDeleteEvent.emit(name); } } ================================================ FILE: src/app/configuration/tab-boards/styles.css ================================================ input { padding: 5px; font-weight: 300 !important; font-family: 'Roboto', sans-serif !important; font-size: 1.1em; outline: none !important; } label { font-family: 'Roboto', sans-serif; font-size: 1.3em !important; color: #4a4d50 !important; } label.endpoint { font-size: 1em !important; line-height: 1.5em !important; } td.detail { background-color: white !important; } .index-column { background-color: #ddebf5 !important; border: none !important; width: 5% !important; color: #788ca0 !important; } .ui.table thead th td { color: #788ca0 !important; } .ui.table { color: rgba(97, 97, 97, 0.68) !important } .inLine { display: inline-block; padding-left: 4px; padding-right: 4px; } .example-full-width { width: 100%; } .grid-row { display: flex; align-items: center; justify-content: space-around; } ================================================ FILE: src/app/configuration/tab-boards/view.html ================================================
# name
{{i + 1}} {{board}} clear
================================================ FILE: src/app/configuration/tab-endpoint/endpoint-configuration-tab.component.ts ================================================ /** * Created by jayhamilton on 5/31/17. */ import {Component} from '@angular/core'; import {EndPoint} from './endpoint.model'; import {EndPointService} from './endpoint.service'; import {environment} from '../../../environments/environment'; @Component({ selector: 'app-endpoint-config-tab', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class EndpointConfigurationTabComponent { env:any; endPoints: EndPoint[]; currentEndPoint: EndPoint = new EndPoint('', '', '', '', '', '', '', '', '',{'tags':[]}); constructor(private _endPointService: EndPointService) { this.env = environment; this._endPointService.getEndPoints().subscribe(data => { this.endPoints = data['endPoint']; if (this.endPoints && this.endPoints.length) { this.setSelectedEndPoint(this.endPoints[0]); } }); } setSelectedEndPoint(endPoint: EndPoint) { this.currentEndPoint = endPoint; } createEndPoint(endPoint: EndPoint) { if (!this.endPoints) { this.endPoints = []; } this.endPoints.push(endPoint); this.persistInMemoryDataToStore(); this.setSelectedEndPoint(endPoint); } updateEndPoint(endPoint: EndPoint) { this.deleteEndPoint(endPoint); this.createEndPoint(endPoint); } deleteEndPoint(endPoint: EndPoint) { // find endPoint in memory by name for now (need to use the id) and remove it this.endPoints.forEach(item => { if (item.name === endPoint.name) { const index = this.endPoints.indexOf(item); if (index > -1) { this.endPoints.splice(index, 1); } } }); this.persistInMemoryDataToStore(); if (this.endPoints && this.endPoints.length === 0) { this.currentEndPoint.name = ''; this.currentEndPoint.address = ''; this.currentEndPoint.description = ''; this.currentEndPoint.credential = ''; this.currentEndPoint.credentialType = ''; this.currentEndPoint.tokenAPI = ''; this.currentEndPoint.tokenAPIProperty = ''; this.currentEndPoint.user = ''; }else{ this.setSelectedEndPoint(this.endPoints[0]) } } persistInMemoryDataToStore() { const endpointModel = { endPoint: this.endPoints }; // persist in memory structure this._endPointService.saveEndPoint(endpointModel).subscribe(data => { /** * todo - error handling */ }); } } ================================================ FILE: src/app/configuration/tab-endpoint/endpoint.help.ts ================================================ /** * Created by jayhamilton on 5/18/17. */ ================================================ FILE: src/app/configuration/tab-endpoint/endpoint.model.ts ================================================ export interface TAG { name: string; } export class EndPoint { public id: number; public name: string; public address: string; public user: string; public credentialType: string; public credential: string; public description: string; public tokenAPI: string; public tokenAPIProperty: string; public tokenAPIHeader: string; public tags: any; constructor(name: string, address: string, user: string, credential: string, credentialType: string, description: string, tokenAPI: string, tokenAPIProperty: string, tokenAPIHeader: string, tags: any) { this.name = name; this.address = address; this.user = user; this.credential = credential; this.credentialType = credentialType; this.description = description; this.tokenAPI = tokenAPI; this.tokenAPIProperty = tokenAPIProperty; this.tokenAPIHeader = tokenAPIHeader; Object.assign(this, tags); this.id = 0; } } export const credentialScheme = ['none', 'password', 'ssh key', 'OAuth Secret']; ================================================ FILE: src/app/configuration/tab-endpoint/endpoint.service.ts ================================================ /** * Created by jayhamilton on 2/7/17. */ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {EndPoint} from './endpoint.model'; import {HttpClient} from '@angular/common/http'; @Injectable() export class EndPointService { constructor(private _http: HttpClient) { } getEndPoints() { if (localStorage.getItem('endpoint') == null) { return new Observable(observer => { const base = {endPoint: []}; const mockBarChart = new EndPoint( 'Chart Mock Bar Data Source', '/assets/api/chart-mock-bar-model.json', 'test user', 'testCredential', 'unknown', 'Predefined data source that cannot be modified or removed.', 'token API', 'token API Header', 'token API Property', {'tags':[{'name':'bar'},{'name':'chart'}]} ); base.endPoint.push(mockBarChart); const mockPieChart = new EndPoint( 'Chart Mock Pie Data Source', '/assets/api/chart-mock-pie-model.json', 'test user', 'testCredential', 'unknown', 'Predefined data source that cannot be modified or removed', 'token API', 'token API Header', 'token API Property', {'tags':[{'name':'pie'},{'name':'chart'}]} ); base.endPoint.push(mockPieChart); const memoryEndpoint = new EndPoint( 'Memory', '/metric?measure=memory', 'test user', 'testCredential', 'unknown', 'Predefined data source that cannot be modified or removed', 'token API', 'token API Header', 'token API Property', {'tags':[{'name':'memory'}]} ); base.endPoint.push(memoryEndpoint); localStorage.setItem('endpoint', JSON.stringify(base)); observer.next(base); return () => { }; }); } else { return new Observable(observer => { const data = JSON.parse(localStorage.getItem('endpoint')); observer.next(data); return () => { }; }); } } saveEndPoint(endpoint: any) { return new Observable(observer => { localStorage.setItem('endpoint', JSON.stringify(endpoint)); observer.next({}); return () => { }; }); } } ================================================ FILE: src/app/configuration/tab-endpoint/endpointDetail.component.ts ================================================ /** * Created by jayhamilton on 5/16/17. */ import {AfterViewInit, Component, EventEmitter, Input, OnChanges, Output} from '@angular/core'; import {FormGroup, FormBuilder, Validators} from '@angular/forms'; import {credentialScheme, EndPoint, TAG} from './endpoint.model'; import {COMMA, ENTER} from '@angular/cdk/keycodes'; import {MatChipInputEvent} from "@angular/material"; @Component({ selector: 'app-endpoint-detail', moduleId: module.id, templateUrl: './endpointDetail.html', styleUrls: ['./styles.css'] }) /** * * TODO- Redo this entire file and add a state machine. This code is very fragile. */ export class EndPointDetailComponent implements OnChanges, AfterViewInit { @Input() currentEndPoint: EndPoint; @Output() createEvent: EventEmitter = new EventEmitter(); @Output() updateEvent: EventEmitter = new EventEmitter(); @Output() deleteEvent: EventEmitter = new EventEmitter(); preDefinedEndPoints = ["memory", "mock"]; currentState: string; preDefined = false; endPointForm: FormGroup; credentialScheme = credentialScheme; useCredentials = false; //chip/tag list control selectable = true; removable = true; visible = true; addOnBlur = false; tagPlaceHolderText = ''; formTags = {tags: []}; readonly separatorKeysCodes: number[] = [ENTER, COMMA]; addTag(event: MatChipInputEvent): void { const input = event.input; const value = event.value; // Add tag if ((value || '').trim()) { this.formTags.tags.push({name: value.trim()}); } // Reset the input value if (input) { input.value = ''; } if (this.currentState == 'reset') { this.currentState = 'update'; } } removeTag(tag: TAG): void { const index = this.formTags.tags.indexOf(tag); if (index >= 0) { this.formTags.tags.splice(index, 1); } if (this.currentState == 'reset') { this.currentState = 'update'; } } checkPredefinition() { this.preDefined = false; for (let x = 0; x < this.preDefinedEndPoints.length; x++) { if (this.currentEndPoint.name.toLocaleLowerCase().trim().indexOf(this.preDefinedEndPoints[x].toLocaleLowerCase().trim()) >= 0) { this.disableControls(); this.preDefined = true; } } } ngAfterViewInit() { this.checkPredefinition(); } ngOnChanges() { this.enableControls(); this.resetForm(); this.populateFormWithCurrentEndpointValues(); this.checkPredefinition(); } populateFormWithCurrentEndpointValues() { this.endPointForm.setValue({ name: this.currentEndPoint.name, address: this.currentEndPoint.address, user: this.currentEndPoint.user, credentialType: this.currentEndPoint.credentialType, credential: this.currentEndPoint.credential, description: this.currentEndPoint.description, tokenAPI: this.currentEndPoint.tokenAPI, tokenAPIProperty: this.currentEndPoint.tokenAPIProperty, tokenAPIHeader: this.currentEndPoint.tokenAPIHeader }); this.formTags.tags = this.currentEndPoint.tags.slice(); } constructor(private fb: FormBuilder) { let me = this; this.createForm(); this.endPointForm.valueChanges.forEach( (value => { if (this.currentState !== 'create') { me.setFormState(); } }) ); } disableControls() { this.endPointForm.get('name').disable(); this.endPointForm.get('address').disable(); this.endPointForm.get('description').disable(); this.removable = false; this.selectable = false; this.tagPlaceHolderText = ""; } enableControls() { this.endPointForm.get('name').enable(); this.endPointForm.get('address').enable(); this.endPointForm.get('description').enable(); this.removable = true; this.selectable = true; this.tagPlaceHolderText = "Add tag..."; } setFormState() { /** * todo - implement state machine */ if (this.endPointForm.get('name').pristine) { // something other than name changed so this must be an update if (this.endPointForm.dirty) { this.currentState = 'update'; } } else { // name change so assume user wants to perform save as this.currentState = 'save as'; } // reset state when a form is clean if (this.endPointForm.pristine) { this.currentState = 'reset'; } } createForm() { this.endPointForm = this.fb.group({ name: ['', Validators.required], address: ['', Validators.required], user: '', credentialType: '', credential: '', tokenAPI: '', tokenAPIProperty: '', tokenAPIHeader: '', description: '' }); } createEndPoint() { this.enableControls(); const ep: EndPoint = new EndPoint( this.endPointForm.value.name, this.endPointForm.value.address, this.endPointForm.value.user, this.endPointForm.value.credential, this.endPointForm.value.credentialType, this.endPointForm.value.description, this.endPointForm.value.tokenAPI, this.endPointForm.value.tokenAPIProperty, this.endPointForm.value.tokenAPIHeader, this.formTags ); this.createEvent.emit(ep); this.currentState = 'reset'; } updateEndPoint() { this.currentEndPoint.name = this.endPointForm.value.name; this.currentEndPoint.address = this.endPointForm.value.address; this.currentEndPoint.user = this.endPointForm.value.user; this.currentEndPoint.credential = this.endPointForm.value.credential; this.currentEndPoint.credentialType = this.endPointForm.value.credentialType; this.currentEndPoint.description = this.endPointForm.value.description; this.currentEndPoint.tokenAPI = this.endPointForm.value.tokenAPI; this.currentEndPoint.tokenAPIProperty = this.endPointForm.value.tokenAPIProperty; this.currentEndPoint.tokenAPIHeader = this.endPointForm.value.tokenAPIHeader; this.currentEndPoint.tags = this.formTags.tags.slice(); this.updateEvent.emit(this.currentEndPoint); this.currentState = 'reset'; } newEndPoint() { this.enableControls(); this.resetForm(); this.selectable = true; this.removable = true; this.preDefined = false; this.tagPlaceHolderText = "Add tag..." /** * The create state is used to display the save icon even if the form is being edited * todo - implmenet state machine */ this.currentState = 'create'; } resetEndPoint() { this.ngOnChanges(); } deleteEndPoint() { this.deleteEvent.emit(this.currentEndPoint); this.resetForm(); } resetForm() { this.endPointForm.reset(); if (this.formTags.tags && this.formTags.tags.length) { this.formTags.tags = []; } } } ================================================ FILE: src/app/configuration/tab-endpoint/endpointDetail.html ================================================
content_copy  save as  save  update  save  save  undo  undo  add  create  clear  remove 

{{tag.name}} cancel
================================================ FILE: src/app/configuration/tab-endpoint/styles.css ================================================ td.selected { background-color: #3f51b5 !important; color: white; font-weight: bold; } .ui.table { border: none !important; color: rgba(97, 97, 97, 0.68) !important; } .ui.form .field > label { color: rgba(97, 97, 97, 0.68) !important; line-height: 3em !important; } .ui.form select{ color: rgba(97, 97, 97, 0.68) !important; height: 3em !important; } .ui.form input{ color: rgba(97, 97, 97, 0.68) !important; } .ui.table tr td{ border:none !important; } .ui.form textarea{ color: rgba(97, 97, 97, 0.68) !important; } tr { cursor: pointer; } .example-chip-list { width: 100%; } ================================================ FILE: src/app/configuration/tab-endpoint/view.html ================================================
{{endPoint.name}}
This feature is not ready yet!
================================================ FILE: src/app/configuration/tab-options/options-configuration-tab.component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { Component } from '@angular/core'; import {OptionsService} from "./service"; import {ToastService} from "../../toast/toast.service"; @Component({ selector: 'app-options-config-tab', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class OptionsConfigurationTabComponent { enableHover: boolean; displayGadgetOptionsInSideBar:boolean; constructor(private _optionsService: OptionsService, private _toastService: ToastService) { this.enableHover = this._optionsService.getBoardOptions()['enableHover']; this.displayGadgetOptionsInSideBar = this._optionsService.getBoardOptions()['displayGadgetOptionsInSideBar']; } onHooverOptionChange(value) { this._optionsService.setBoardOptions( { "enableHover": value['checked'], "displayGadgetOptionsInSideBar": this.displayGadgetOptionsInSideBar }); this._toastService.sendMessage("The board configuration has changed!",null); } onDisplayGadgetOptionsInSideBarChange(value) { this._optionsService.setBoardOptions( { "enableHover": this.enableHover, "displayGadgetOptionsInSideBar": value['checked'] }); this._toastService.sendMessage("The board configuration has changed!",null); } } ================================================ FILE: src/app/configuration/tab-options/service.ts ================================================ import {Injectable} from "@angular/core"; import {Subject,Observable} from "rxjs"; @Injectable() export class OptionsService { optionsCollectionName = 'dashboardOptions'; defaultOptions = { 'enableHover': false, 'displayGadgetOptionsInSideBar': false }; private globalOptionsChangeEventSubject: Subject = new Subject(); constructor() { } public getBoardOptions() { let databaseOptions = JSON.parse(localStorage.getItem(this.optionsCollectionName)); if (databaseOptions == null) { this.persistDefautBoardOptions(); return this.defaultOptions; } else { return databaseOptions; } } private persistDefautBoardOptions(){ localStorage.setItem(this.optionsCollectionName, JSON.stringify(this.defaultOptions)) } public setBoardOptions(options: any) { /** * Todo this will need to change to support the update to individual options. Currently there is only one * option but once there is more than one this method must change to take the input and update just that * property of the options object. */ localStorage.removeItem(this.optionsCollectionName); /** * Raise an event to listeners, primarily the gadgets, when the global options change. The listeners can use * the event to change their behavior */ this.globalOptionsChangeEventSubject.next(options); return localStorage.setItem(this.optionsCollectionName, JSON.stringify(options)); } /** * The gadget-base can use this method to subscribe to events that are created when the global options change. */ listenForGlobalOptionsChanges(): Observable { return this.globalOptionsChangeEventSubject.asObservable(); } } ================================================ FILE: src/app/configuration/tab-options/styles.css ================================================ ================================================ FILE: src/app/configuration/tab-options/view.html ================================================
The following options control various aspects of the board's and gadget's behavior. Changes take effect immediately.

Always show the gadget buttons in the title. By default buttons show/hide when you hover over the title.

(Experimental) When set the options are displayed in the side bar. Otherwise it is displayed within the gadget. ================================================ FILE: src/app/configuration/tabs.model.ts ================================================ export const tabsModel: ({ groupId: string; displayName: string } | { groupId: string; displayName: string })[] = [ { groupId: 'boards', displayName: 'Boards' }, { groupId: 'options', displayName: 'Options' }, { groupId: 'endpoint', displayName: 'Data Source Registration' }, { groupId: 'ai', displayName: 'AI Configuration' } ]; ================================================ FILE: src/app/configuration/view.html ================================================ ================================================ FILE: src/app/datalist/action-model.ts ================================================ interface ActionInterface { name: string; item: any; } ================================================ FILE: src/app/datalist/data-list.component.ts ================================================ import {Component, ContentChild, Input, TemplateRef} from '@angular/core'; import {Facet} from '../facet/facet-model'; @Component({ selector: 'app-data-list', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class DataListComponent { @Input() objectList: any[]; @Input() objectTitleList: string[]; @Input() placeHolderText: string; @Input() layoutColumnOneWidth: string; @Input() layoutColumnTwoWidth: string; @Input() listHeader: string; @Input() typeAheadIsInMenu: boolean; @Input() facetTags: Array; @ContentChild(TemplateRef) template: TemplateRef; color = 'white'; objectListCopy: any[] = []; filterListByTags(filterList: string[]) { this.copyObjectList(); this.objectList = this.objectListCopy.filter(object => { let tagFound = false; if (!filterList.length) { return true; } else { object.tags.forEach(tag => { filterList.forEach(filter => { if (tag.name.toLocaleLowerCase() === filter.toLocaleLowerCase()) { tagFound = true; } }); }); return tagFound; } }); } filterListBySearchString(searchString: string) { this.copyObjectList(); this.objectList = this.objectListCopy.filter(object => { if (searchString.localeCompare('') === 0) { return true; } else { if (object.name.toLowerCase().indexOf(searchString.toLowerCase()) !== -1) { return true; } } }); } /** * todo - find a better way to manage the list that is displayed and filtered. */ copyObjectList() { if (this.objectListCopy.length === 0) { this.objectList.forEach(item => { this.objectListCopy.push(item); }); } } } ================================================ FILE: src/app/datalist/data-list.module.ts ================================================ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {FacetModule} from '../facet/facet.module'; import {HttpClientModule} from '@angular/common/http'; import {TypeAheadInputModule} from '../typeahead-input/typeahead-input.module'; import {DataListComponent} from './data-list.component'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @NgModule({ imports: [ CommonModule, FacetModule, TypeAheadInputModule, HttpClientModule, BrowserAnimationsModule ], declarations: [ DataListComponent ], providers: [], exports: [ DataListComponent ] }) export class DataListModule { } ================================================ FILE: src/app/datalist/styles.css ================================================ .content { background-color:#f7f7f7; margin-top: 25px; margin-left: 25px; padding:5px; } hr { height: 2px !important; color: rgba(110, 110, 110, 0.1) !important; background: rgba(110, 110, 110, 0.1) !important; font-size: 0; border: 0; } .list-content{ max-height: 700px; overflow-y: scroll; margin-top: 20px; padding-right: 10px } ================================================ FILE: src/app/datalist/view.html ================================================

================================================ FILE: src/app/detail/detail.component.ts ================================================ import {Component, OnInit} from '@angular/core'; import {ActivatedRoute, NavigationEnd, Router} from "@angular/router"; import {EndPointService} from "../configuration/tab-endpoint/endpoint.service"; import {RuntimeService} from "../services/runtime.service"; import {DetailService} from "./service"; /**a * Detail component * */ @Component({ moduleId: module.id, templateUrl: './view.html', styleUrls: ['styles.css'] }) export class DetailComponent implements OnInit { chartType: string; chartSeries: string; chartMetric: string; endPointName: string; data = []; searchText: string; navRoutes: Array = []; navigationSubscription: any; objectAsArray = []; constructor(private _router: Router, private _route: ActivatedRoute, private _endPointService: EndPointService, private _detailService: DetailService ) { this.navigationSubscription = this._router.events.subscribe((e: any) => { // If it is a NavigationEnd event re-initalise the component if (e instanceof NavigationEnd) { this.getObjectsByMetric(true); } }); } ngOnInit() { this.chartType = this._route.snapshot.queryParams['chartType']; this.chartSeries = this._route.snapshot.queryParams['chartSeries']; this.chartMetric = this._route.snapshot.queryParams['chartMetric']; this.endPointName = this._route.snapshot.queryParams['endPointName']; this.getObjectsByMetric(false); } getObject(record: any) { this.clearDetailDisplay(); Object.keys(record).forEach(key => { if (key.indexOf("link") < 0) { this.objectAsArray.push({"key": key, "value": record[key]}); } }) } getObjectsByHateoasLink(detail: any) { this.clearDetailDisplay(); let href = ""; detail.links.forEach(link => { if (link.rel == 'self') { href = link.href; } }); let navArray = href.split('/'); //trying to get data for the current record so do nothing. Avoid this altogether by removing the link from the table. if (navArray[navArray.length - 1] == this.navRoutes[this.navRoutes.length - 1]) { return; } this.navRoutes.push(navArray[navArray.length - 1]); this._detailService.getDetail(href).subscribe(data => { this.data = data.slice(); }); } getObjectsByMetric(isReload: boolean) { this.clearDetailDisplay(); this._detailService.getDetailByChartSeriesSelected(this.chartType, this.chartSeries, this.chartMetric, this.endPointName).subscribe(data => { this.data = data.slice(); }); if (!isReload) { this.navRoutes.push(this.chartMetric); } else { /** * todo - this won't work for many routes. */ while (this.navRoutes.length > 1) { this.navRoutes.splice(this.navRoutes.length - 1, 1); } } } goHome() { this._router.navigate(["/main-board"]); } gotToRoute(nav: string, disabled: boolean) { if (!disabled) { this._router.navigate(['/detail'], { queryParams: { chartType: this.chartType, chartSeries: this.chartSeries, chartMetric: this.chartMetric, endPointName: this.endPointName } }); } } clearDetailDisplay() { this.objectAsArray = []; } } ================================================ FILE: src/app/detail/detail.model.ts ================================================ interface DetailModel { name: string; data: any; } ================================================ FILE: src/app/detail/detail.module.ts ================================================ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {MatCheckboxModule} from '@angular/material'; import {FormsModule} from '@angular/forms'; import {DetailComponent} from './detail.component'; import {DetailService} from "./service"; import {FilterPipe} from "./filter.pipe"; @NgModule({ imports: [ CommonModule, FormsModule, MatCheckboxModule ], declarations: [ DetailComponent, FilterPipe ], providers: [ DetailService ], exports: [ DetailComponent ] }) export class DetailModule { } ================================================ FILE: src/app/detail/detail.resolver.ts ================================================ import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router'; import {Observable} from 'rxjs'; export class DetailResolver implements Resolve { data: DetailModel; constructor(detailData: DetailModel) { this.data = detailData; } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | DetailModel { return this.data; }; } ================================================ FILE: src/app/detail/filter.pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'filter' }) export class FilterPipe implements PipeTransform { transform(items: any[], searchText: string): any[] { if(!items) return []; if(!searchText) return items; searchText = searchText.toLowerCase(); return items.filter( it => { return it.node.toLowerCase().includes(searchText) || it.master.toLowerCase().includes(searchText) || it.jobName.toLowerCase().includes(searchText) || it.status.toLowerCase().includes(searchText); }); } } ================================================ FILE: src/app/detail/service.ts ================================================ import {Injectable} from "@angular/core"; import {HttpClient, HttpParams} from "@angular/common/http"; import {RuntimeService} from "../services/runtime.service"; import {environment} from "../../environments/environment"; import {catchError} from "rxjs/operators"; @Injectable() export class DetailService { constructor(private _http: HttpClient) { this.configure(); } testURL = "http://localhost:8080"; detailURL= "/detail"; configure() { if (!environment.production) { this.detailURL = this.testURL + this.detailURL; } } getDetailByChartSeriesSelected(chartType: string, chartSeries: string, chartMetric: string, endPointName: string) { /** * Todo - review these parameters and make them more meaningful. The goal is to send to the service * a pameterized URL /endpointURL?detailParameter={value or id} */ let p = new HttpParams(); p = p.append("detailParam", chartSeries); p = p.append("detailMetric", chartMetric); return this._http.get>(this.detailURL, {params: p}) .pipe( catchError(RuntimeService.handleError) ); } getDetail(url: string) { return this._http.get>(url) .pipe( catchError(RuntimeService.handleError) ); } getRecord(url: string) { return this._http.get>(url) .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/detail/styles.css ================================================ a, a label { cursor: pointer; } .field { background: #4a95c3; color: white; } .ui .breadcrumb { font-size: 1.28571429rem; } .detail-right-header { color: #616161; font-size: 1.2rem; float: right; } .detail-left-header { color: #616161; font-size: 1.2rem; } .label{ background: transparent; } .active{ color:grey !important; font-weight: normal !important; } ================================================ FILE: src/app/detail/view.html ================================================
Type: {{chartSeries}} Count: {{data.length}} Tasks
Status Master Job Name Sub Type Job Id Task Id Node Disk Task Start Task End
{{i + 1}} {{task.master}} {{task.jobName}} {{task.subType}} {{task.jobId}} {{task.taskId}} {{task.node}} {{task.disk}} {{task.taskStart}} {{task.taskEnd}}
Detail
{{property.key}} {{property.value}}
================================================ FILE: src/app/dynamic-form/dynamic-form-module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {DynamicFormComponent} from './dynamic-form.component'; import {DynamicFormPropertyComponent} from './dynamic-form-property.component'; import {PropertyControlService} from './property-control.service'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; @NgModule({ imports: [ CommonModule, FormsModule, ReactiveFormsModule ], declarations: [ DynamicFormComponent, DynamicFormPropertyComponent ], providers: [PropertyControlService], exports: [ DynamicFormComponent, DynamicFormPropertyComponent ] }) export class DynamicFormModule { } ================================================ FILE: src/app/dynamic-form/dynamic-form-property.component.html ================================================
{{property.label}} is required
================================================ FILE: src/app/dynamic-form/dynamic-form-property.component.ts ================================================ /** * Created by jayhamilton on 2/5/17. */ import {AfterViewInit, Component, Input} from '@angular/core'; import {FormGroup} from '@angular/forms'; import {PropertyBase} from './property-base'; import {EndPointService} from '../configuration/tab-endpoint/endpoint.service'; import { style, trigger, animate, transition } from '@angular/animations'; @Component({ moduleId: module.id, selector: 'app-df-property', templateUrl: './dynamic-form-property.component.html', styleUrls: ['./styles-props.css'], animations: [ trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(750, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(750, style({opacity: 0})) ]) ]) ] }) export class DynamicFormPropertyComponent implements AfterViewInit { @Input() property: PropertyBase; @Input() form: FormGroup; @Input() gadgetTags: any[];//todo - use to control what endpoints are displayed endPoints: string[] = []; get isValid() { return this.form.controls[this.property.key].valid; } constructor(private endPointService: EndPointService) { this.updateEndPointList(); } updateEndPointList() { this.endPointService.getEndPoints().subscribe(data => { this.endPoints = data['endPoint'].slice(); }); } ngAfterViewInit() { //filter endpoints based on the gadgets tags let me = this; let eligibleEndpoints = []; this.endPoints.forEach(function (point, index, object) { let found = false; point['tags'].forEach(tag => { me.gadgetTags.forEach(_gt => { if (_gt.name.trim().toLowerCase() === tag.name.trim().toLowerCase()) { found = true; } }) }); if (found) { eligibleEndpoints.push(point); } }); this.endPoints = eligibleEndpoints.slice(); } } ================================================ FILE: src/app/dynamic-form/dynamic-form.component.html ================================================ ================================================ FILE: src/app/dynamic-form/dynamic-form.component.ts ================================================ /** * Created by jayhamilton on 2/5/17. */ import { Component, Input, OnInit, Output, EventEmitter, ChangeDetectorRef, AfterViewInit } from '@angular/core'; import { style, state, trigger, animate, transition } from '@angular/animations'; import {FormGroup} from '@angular/forms'; import {PropertyControlService} from './property-control.service'; import {ConfigurationService} from '../services/configuration.service'; import {EndPointService} from '../configuration/tab-endpoint/endpoint.service'; import {EndPoint} from '../configuration/tab-endpoint/endpoint.model'; @Component({ /* solves error: Expression has changed after it was checked exception resolution - https://www.youtube.com/watch?v=K_BRcal-JfI*/ // changeDetection: ChangeDetectionStrategy.OnPush, moduleId: module.id, selector: 'app-dynamic-form', templateUrl: './dynamic-form.component.html', styleUrls: ['./styles-props.css'], animations: [ trigger('contentSwitch', [ state('inactive', style({ opacity: 0 })), state('active', style({ opacity: 1 })), transition('inactive => active', animate('750ms ease-in')), transition('active => inactive', animate('750ms ease-out')) ]), trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(750, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(750, style({opacity: 0})) ]) ]) ], providers: [PropertyControlService] }) export class DynamicFormComponent implements OnInit, AfterViewInit { @Input() gadgetTags: any[];//todo - use to control what endpoints are displayed @Input() propertyPages: any[]; @Input() instanceId: number; @Output() updatePropertiesEvent: EventEmitter = new EventEmitter(true); currentTab = 'run'; endPoints: EndPoint[]; state = 'inactive'; lastActiveTab = {}; form: FormGroup = new FormGroup({}); payLoad = ''; showMessage = false; constructor(private pcs: PropertyControlService, private configService: ConfigurationService, private changeDetectionRef: ChangeDetectorRef) { } /* better solution that solves error: Expression has changed after it was checked exception resolution*/ ngAfterViewInit(): void { this.changeDetectionRef.detectChanges(); } ngOnInit() { this.form = this.pcs.toFormGroupFromPP(this.propertyPages); } onSubmit() { this.payLoad = JSON.stringify(this.form.value); console.debug('Saving Form!'); this.updatePropertiesEvent.emit(this.payLoad); console.debug('Sending configuration to the config service!'); this.configService.notifyGadgetOnPropertyChange(this.payLoad, this.instanceId); if (this.payLoad) { this.showMessage = true; setTimeout(function() { this.showMessage = false; }.bind(this), 2000); } } setCurrentTab(tab) { this.currentTab = tab.groupId; } get isPropertyPageValid (){ return this.form.valid; } } ================================================ FILE: src/app/dynamic-form/property-base.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ export class PropertyBase { value: T; key: string; label: string; required: boolean; order: number; controlType: string; options:any; constructor(props: { value?: T, key?: string, label?: string, required?: boolean, order?: number, controlType?: string, options?:any } = {}, ) { this.value = props.value; this.key = props.key || ''; this.label = props.label || ''; this.required = !props.required; this.order = props.order === undefined ? 1 : props.order; this.controlType = props.controlType || ''; this.options = props.options; } } ================================================ FILE: src/app/dynamic-form/property-checkbox.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {PropertyBase} from './property-base'; export class CheckboxProperty extends PropertyBase { controlType = 'checkbox'; type: string; constructor(options: {} = {}) { super(options); this.type = 'checkbox'; // options['type'] || ''; } } ================================================ FILE: src/app/dynamic-form/property-control.service.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {Injectable} from '@angular/core'; import {FormControl, FormGroup, Validators} from '@angular/forms'; @Injectable() export class PropertyControlService { constructor() { } toFormGroupFromPP(propertyPages: any[]) { const group: any = {}; propertyPages.forEach(propertyPage => { propertyPage.properties.forEach(property => { group[property.key] = property.required ? new FormControl(property.value || '', Validators.required) : new FormControl(property.value || ''); }); }); return new FormGroup(group); } } ================================================ FILE: src/app/dynamic-form/property-dropdown.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {PropertyBase} from './property-base'; export class DropdownProperty extends PropertyBase { controlType = 'dropdown'; options: {key: string, value: string}[] = []; constructor(options: {} = {}) { super(options); this.options = options['options'] || []; } } ================================================ FILE: src/app/dynamic-form/property-dynamicdropdown.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {PropertyBase} from './property-base'; export class DynamicDropdownProperty extends PropertyBase { controlType = 'dynamicdropdown'; options: {key: string, value: string}[] = []; constructor(options: {} = {}) { super(options); this.options = options['options'] || []; } } ================================================ FILE: src/app/dynamic-form/property-hidden.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {PropertyBase} from './property-base'; export class HiddenProperty extends PropertyBase { controlType = 'hidden'; type: string; constructor(options: {} = {}) { super(options); this.type = 'hidden'; } } ================================================ FILE: src/app/dynamic-form/property-number.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {PropertyBase} from './property-base'; export class NumberProperty extends PropertyBase { controlType = 'number'; type: string; constructor(options: {} = {}) { super(options); this.type = 'number'; // options['type']|| ''; } } ================================================ FILE: src/app/dynamic-form/property-textbox.ts ================================================ /** * Created by jayhamilton on 2/3/17. */ import {PropertyBase} from './property-base'; export class TextboxProperty extends PropertyBase { controlType = 'textbox'; type: string; constructor(options: {} = {}) { super(options); this.type = options['type'] || ''; } } ================================================ FILE: src/app/dynamic-form/styles-props.css ================================================ .field > label { font-size: 1.1em !important; color: #767676 !important; font-weight: 300 } .ui.tabular.menu .active.item { border-top-width: 3px !important; border-top-color: #2185D0 !important; } .ui.segment{ border:none; } select{ color: rgba(97, 97, 97, 0.68) !important; height: 3em !important; } input{ line-height: 2em; padding: 5px; border-radius: 5px; border-style: solid; border-width: thin; border-color: lightgray; width:100%; } ================================================ FILE: src/app/error/error-handler.component.ts ================================================ import { Component, Input } from '@angular/core'; import { style, trigger, animate, transition } from '@angular/animations'; import {ErrorObject} from './error-model'; @Component({ moduleId: module.id, selector: 'app-error-handler', templateUrl: './view.html', styleUrls: ['./styles-error.css'], animations: [ trigger('error', [ transition(':enter', [ style({opacity: 0}), animate('1000ms', style({opacity: 1})) ]), transition(':leave', [ style({opacity: 1}), animate('1000ms', style({opacity: 0})) ]) ]) ] }) export class ErrorHandlerComponent { @Input() errorObject: ErrorObject; @Input() errorExists: boolean; constructor() { } public closeMessage() { this.errorExists = false; } } ================================================ FILE: src/app/error/error-handler.ts ================================================ import {ErrorObject, SolutionObject} from './error-model'; /** * Created by jayhamilton on 7/5/17. */ export class ErrorHandler { static getErrorObject(errMsg: any) { return new ErrorObject( errMsg.statusText, 'Some description', ErrorHandler.getSolutionList( errMsg.status + ' ' + errMsg.statusText), errMsg.resource); } /** * todo - fix this error handling logic. Move it to its own class. * @param errMsg * @returns {SolutionObject[]} */ static getSolutionList(errMsg: string) { console.log("ERROR CONDITION: " + errMsg ) const solutionList: SolutionObject[] = []; switch (ErrorHandler.getErrorType(errMsg.toLowerCase())) { case 'ERR_CERTIFICATE': solutionList.push(new SolutionObject('Check to see if your browser has accepted the certificate', 0, 'http://link1')); break; case 'ERR_CROSS_ORIGIN_RESOURCE_SHARING': solutionList.push(new SolutionObject('Check to see if your browser has accepted the certificate', 0, 'http://link1')); break; case 'ERR_CONNECTION_REFUSED': solutionList.push(new SolutionObject( 'Check to see if the host/service you are attempting to connect to is up.', 0, 'http://link1')); break; case 'ERR_NOT_FOUND': solutionList.push(new SolutionObject( 'Resource not found.', 0, 'http://link1')); break; case 'ERR_CONNECTION_TIMEOUT': solutionList.push(new SolutionObject( 'A timeout occurred. The default timeout on a connection is 60 seconds. ' + 'Check the endpoint to see if you are able to access the ip and port using ping. ' + 'If 60 seconds is to short of a timeout go into configuration and increase it.', 0, 'http://link1')); break; default: { solutionList.push(new SolutionObject(errMsg, 0, 'http://link1')); } } return solutionList; } static getErrorType(errMsg: string): string { if (errMsg.indexOf('trust') > -1) { return 'ERR_CERTIFICATE'; } if (errMsg.indexOf('cors') > -1) { return 'ERR_CROSS_ORIGIN_RESOURCE_SHARING'; } if (errMsg.indexOf('refuse') > -1) { return 'ERR_CONNECTION_REFUSED'; } if (errMsg.indexOf('timeout') > -1) { return 'ERR_CONNECTION_TIMEOUT'; } if (errMsg.indexOf('404') > -1) { return 'ERR_NOT_FOUND'; } return errMsg; } static getWebSocketErrorReason(error: any) { let reason = 'There was probably a problem with an attempt to connect to the endpoint!'; switch (error.code) { case 1000: reason = 'Normal closure'; break; case 1001: reason = 'An endpoint is going away'; break; case 1002: reason = 'An endpoint is terminating the connection due to a protocol error.'; break; case 1003: reason = 'An endpoint is terminating the connection because it has received a type of data it cannot accept'; break; case 1004: reason = 'Reserved. The specific meaning might be defined in the future.'; break; case 1005: reason = 'No status code was actually present'; break; case 1006: reason = 'The connection was closed abnormally'; break; case 1007: reason = 'The endpoint is terminating the connection because a message was received that contained inconsistent data'; break; case 1008: reason = 'The endpoint is terminating the connection because it received a message that violates its policy'; break; case 1009: reason = 'The endpoint is terminating the connection because a data frame was received that is too large'; break; case 1010: reason = 'The client is terminating the connection because it expected the server to negotiate one or more extension, but the server didn\'t.'; break; case 1011: reason = 'The server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.'; break; case 1012: reason = 'The server is terminating the connection because it is restarting'; break; case 1013: reason = 'The server is terminating the connection due to a temporary condition'; break; case 1015: reason = 'The connection was closed due to a failure to perform a TLS handshake'; break; } return reason; } } ================================================ FILE: src/app/error/error-model.ts ================================================ /** * Created by jayhamilton on 6/22/17. */ export class ErrorObject { detail: string; summary: string; solutions: SolutionObject[]; resource: string; constructor(detail: string, summary: string, solutions: SolutionObject[], resource: string) { this.detail = detail; this.summary = summary; this.solutions = solutions; this.resource = resource; } }; export class SolutionObject { summary: string; articleId: number; link: string; constructor(summary, articleId, link) { this.summary = summary; this.articleId = articleId; this.link = link; } } ================================================ FILE: src/app/error/error.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {ErrorHandlerComponent} from './error-handler.component'; @NgModule({ imports: [ CommonModule ], declarations: [ErrorHandlerComponent], exports: [ErrorHandlerComponent] }) export class ErrorHandlerModule { } ================================================ FILE: src/app/error/styles-error.css ================================================ .error-heading{ color:grey !important; text-align: left; } ================================================ FILE: src/app/error/view.html ================================================
{{errorObject.summary}}

{{errorObject.detail}}

resource: {{errorObject.resource}}

Potential solutions

article:  {{solution.link}}
summary:  {{solution.summary}}
================================================ FILE: src/app/facet/capitalize-first-character-pipe.ts ================================================ import { Pipe, PipeTransform } from '@angular/core'; /* * Capitalize the first letter of the string * Takes a string as a value. * Usage: * value | capitalizefirst * Example: * // value.name = daniel * {{ value.name | capitalizefirst }} * fromats to: Daniel */ @Pipe({ name: 'capitalizeFirst' }) export class CapitalizeFirstPipe implements PipeTransform { transform(value: string, args: any[]): string { if (value === null) { return 'Not assigned'; } return value.charAt(0).toUpperCase() + value.slice(1); } } ================================================ FILE: src/app/facet/facet-component.ts ================================================ import {Facet} from './facet-model'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { style, state, trigger, animate, transition } from '@angular/animations'; /** * Created by jayhamilton on 7/11/17. */ @Component({ moduleId: module.id, selector: 'app-facet', template: `

{{facet.name}}

{{tag.name}}  ( {{tag.count}} )

`, styleUrls: ['./styles.css'], animations: [ trigger('accordion', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('700ms ease-in-out')), transition('out => in', animate('300ms ease-in-out')) ]), trigger('accordion2', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('300ms ease-in-out')), transition('out => in', animate('800ms ease-in-out')) ]) ] }) export class FacetComponent implements OnInit { @Output() tagSelectEvent: EventEmitter = new EventEmitter(); @Input() facet: Facet; @Input() openFacet: boolean; facetOpen: string; constructor() { } ngOnInit() { if (this.openFacet) { this.facetOpen = 'in'; }else { this.facetOpen = 'out'; } } toggleAccordion() { this.facetOpen = this.facetOpen === 'out' ? 'in' : 'out'; } tagSelect(tagName) { this.tagSelectEvent.emit(tagName); } } ================================================ FILE: src/app/facet/facet-model.ts ================================================ export class Facet { name: string; tags: Array; constructor(name: string, tags: Array) { this.name = name; this.tags = tags; } } export class Tag { name: string; count: number; constructor(name: string) { this.name = name; this.count = 1; } } ================================================ FILE: src/app/facet/facet-tag-processor.ts ================================================ import {Facet, Tag} from './facet-model'; export class FacetTagProcessor { facet_tags: Array = []; objectList: any[]; constructor(objectList: any[]) { this.objectList = objectList; } getFacetTags() { const me = this; this.objectList.forEach(function (item) { me.formatAndUpdateTagList(item.tags); }); return this.facet_tags; } formatAndUpdateTagList(items: any[]) { items.forEach(tag => { // add the first facet and tag to the facet_tag array if (!this.facet_tags.length) { this.createFacetAndAddItToTheFacetTagArray(tag); } else { let facetExists = false; this.facet_tags.forEach(facet => { if (facet.name.toLowerCase() === tag.facet.toLowerCase()) { facetExists = true; } }); if (facetExists) { this.updateFacetWithTag(tag); } else { this.createFacetAndAddItToTheFacetTagArray(tag); } } }); } createFacetAndAddItToTheFacetTagArray(tag: any) { const _tags: Array = []; const _tag: Tag = this.createTag(this.capitalize(tag.name)); _tags.push(_tag); const facet: Facet = new Facet(tag.facet, _tags); this.facet_tags.push(facet); } createTag(tag) { return new Tag(tag); } updateFacetWithTag(tag: any) { // find the facet and then add the tag or update the count this.facet_tags.forEach(facet => { if (facet.name.toLowerCase() === tag.facet.toLowerCase()) { let tagExists = false; facet.tags.forEach(_tag => { if (_tag.name.toLowerCase() === tag.name.toLowerCase()) { tagExists = true; _tag.count = _tag.count + 1; } }); if (!tagExists) { facet.tags.push(this.createTag(this.capitalize(tag.name))); } } }); } capitalize(value: string) { return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase(); } } ================================================ FILE: src/app/facet/facet.module.ts ================================================ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {FacetComponent} from './facet-component'; import {FilterListComponent} from './filter-list-component'; import {FilterTagComponent} from './filter-tag-component'; import {CapitalizeFirstPipe} from './capitalize-first-character-pipe'; import {MatCheckboxModule} from '@angular/material'; import {FormsModule} from '@angular/forms'; import {AddGadgetService} from '../add-gadget/service'; @NgModule({ imports: [ CommonModule, FormsModule, MatCheckboxModule ], declarations: [ FacetComponent, FilterListComponent, FilterTagComponent, CapitalizeFirstPipe ], providers: [ AddGadgetService ], exports: [ FacetComponent, FilterListComponent, FilterTagComponent, CapitalizeFirstPipe ] }) export class FacetModule { } ================================================ FILE: src/app/facet/filter-list-component.ts ================================================ import {Component, EventEmitter, Input, Output} from '@angular/core'; import {AddGadgetService} from '../add-gadget/service'; import {Facet, Tag} from './facet-model'; /** * Created by jayhamilton on 6/27/17. */ @Component({ moduleId: module.id, selector: 'app-filter-list', template: `
`, styleUrls: ['./styles.css'] }) export class FilterListComponent { @Output() tagSelectEvent: EventEmitter = new EventEmitter(); @Input() facet_tags: Array; tagSelect(tagName) { this.tagSelectEvent.emit(tagName); } } ================================================ FILE: src/app/facet/filter-tag-component.ts ================================================ import { Component, EventEmitter, Output} from '@angular/core'; import { style, trigger, animate, transition } from '@angular/animations'; /** * Created by jayhamilton on 6/27/17. */ @Component({ moduleId: module.id, selector: 'app-filter-tag', template: ` `, styleUrls: ['../gadgets/_common/styles-gadget.css'], animations: [ trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(750, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(750, style({opacity: 0})) ]) ]) ] }) export class FilterTagComponent { @Output() updateFilterListEvent = new EventEmitter(); filterList: Array = []; constructor() { } updateFilterList(filter) { filter = filter.toLocaleLowerCase(); const index: number = this.filterList.indexOf(filter); if (index !== -1) { this.filterList.splice(index, 1); } else { this.filterList.push(filter); } this.updateFilterListEvent.emit(this.filterList); } } ================================================ FILE: src/app/facet/styles.css ================================================ .ui.avatar.image { width: 4.4em !important; height: 4em !important } hr { height: 2px !important; color: rgba(110, 110, 110, 0.1) !important; background: rgba(110, 110, 110, 0.1) !important; font-size: 0; border: 0; } .ui.input input:focus, .ui.input.focus input { border-color: #e9e9e9 !important; } h3, h4 { color: rgba(66, 66, 66, .74) !important; font-weight: 400 !important; font-size:.85em; } .ui.basic.segment { border-radius: 5px !important; } .segment { border-radius: 5px !important; } .ui.table tr td { border-top: none !important; } .ui.table td { padding: .3em !important; font-size: .8em; } ================================================ FILE: src/app/gadgets/_common/base-chart-models/bar.model.ts ================================================ import {Series} from './series.model'; export abstract class Bar { public name: string; public series: Array; constructor(name: string, series: Array) { this.name = name; this.series = series; } } ================================================ FILE: src/app/gadgets/_common/base-chart-models/series.model.ts ================================================ export class Series { public name: string; public value: number; constructor(name: string, value: number) { this.name = name; this.value = value; } } ================================================ FILE: src/app/gadgets/_common/gadget-base.ts ================================================ import {ErrorObject} from '../../error/error-model'; import {EndPoint} from '../../configuration/tab-endpoint/endpoint.model'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from './gadget-property.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {AfterViewInit, ChangeDetectorRef, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {DynamicFormComponent} from '../../dynamic-form/dynamic-form.component'; import {OptionsService} from "../../configuration/tab-options/service"; /** * Created by jayhamilton on 6/22/17. */ export abstract class GadgetBase implements IGadget, OnDestroy, OnInit, AfterViewInit { @ViewChild(DynamicFormComponent) propertyPageForm: DynamicFormComponent; title: string; instanceId: number; config: any; gadgetTags: Array; /** * Used to determine when to show the controls that appear in the gadgets * heading area. This is set by the mouseover/mouseout events. * @type {boolean} */ showControls = false; /** * determines whether to show the gadgets property page * @type {boolean} */ inConfig = false; /** * Determines if a gadget is runnning or not * @type {boolean} */ inRun = false; /** * When a gadget is manually put into run mode this property will be used to * display a spinning icon and will be enabled between intiating an operation (run or stop) * to the operation is enabled * @type {boolean} */ actionInitiated = false; /** * Gadgets that are of type realtime have a run/stop set of controls. * Those gadgets should set this property to true. This property's visibility * will also be controlled by whether the gadget's configuration form is valid. * @type {boolean} */ showOperationControls = false; /** * This property is used to simply allow the gadget to not show any run/stop controls. * This is needed because the showOperationControls does something similar but not exactly the same. * The showOperationControls property allows the gadgetBase to determine if the run control, if the gadget * uses it, to be displayed when the gadget has a valid configuration. * * Default: true - Gadgets without a need for run/stop control should override this value. * @type {boolean} */ gadgetHasOperationControls = true; /** * Most gadgets need configuration so gadgets that don't can override this property * @type {boolean} */ showConfigurationControl = true; // internally controls dynamic form properties propertyPages: any[] = []; endpointObject: EndPoint; errorObject: ErrorObject; errorExists = false; globalOptions:any; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { this._optionsService.listenForGlobalOptionsChanges().subscribe(options=>{ /** * This is called when there is a change to the options tab within the configuration modal. * The following method needs to also be called when the gadget is initially instantiated. * When the gadget is instantiated the option values need to come from the persistent store. */ this.updateGadgetWithGlobalOptions(options); }); this.updateGadgetWithGlobalOptions(this._optionsService.getBoardOptions()) } public ngOnInit() { this.toggleConfigMode(); this.changeDetectionRef.detectChanges(); } public ngAfterViewInit() { if (this.propertyPageForm) { if (this.propertyPageForm.isPropertyPageValid) { this.toggleConfigMode(); this.changeDetectionRef.detectChanges(); this.showOperationControls = true; this.changeDetectionRef.detectChanges(); } else { this.showOperationControls = false; this.changeDetectionRef.detectChanges(); } } this.preRun(); } public initializeState() { } public toggleConfigMode() { if (!this.inConfig) { this.initializeProperties(); } this.inConfig = !this.inConfig; } public initializeProperties() { if (this.propertyPages.length === 0 && this.config.propertyPages) { this._propertyService.setPropertyPagesAndProperties(this.config.propertyPages, this.propertyPages); } } public abstract run(): void public abstract stop(): void public abstract updateProperties(updatedProperties: any): void public abstract updateData(data: any[]): void public abstract preRun(): void public handleError(error: ErrorObject) { this.inRun = false; this.actionInitiated = false; this.errorExists = true; this.errorObject = error; } public initializeRunState(forceRunState: boolean) { this.errorExists = false; this.actionInitiated = true; this.inConfig = false; if (forceRunState) { this.setInRunState(); } } public setInRunState() { this.inRun = true; this.actionInitiated = false; } public setStopState(longRunningStopAction: boolean) { /** * If the gadget indicates longRunningStopAction then the gadget has to set this value to * false once the operation is complete */ this.actionInitiated = longRunningStopAction; this.inRun = false; } public remove() { this._gadgetInstanceService.removeInstance(this.instanceId); } public showGadgetControls(enable: boolean) { this.showControls = enable; } /** * called from cell.component after the gadget is created during runtime * intanceId, config, title and endpoint are common to all gadgets. Once the gadgets are configured * we give them an opportunity to perform an action during the preRun() method. For example, * the statistic gadget uses preRun() to make a single call to the endpoint to update its display. * */ public configureGadget(instanceId: number, config: any, tags: Array) { this.instanceId = instanceId; this.config = config; this.gadgetTags = tags.slice(); this.setTitle(this.getPropFromPropertyPages('title')); this.setEndPoint(this.getPropFromPropertyPages('endpoint')); /** * Todo - remove this prerun call and refactor remaining code. Prerun was called twice and had an impact on the barchart api calls. * API calls continued after route changes which is undesirable. See ngAfterViewInit where it is also called from. */ //this.preRun(); } protected setEndPoint(endpoint: string) { this._endPointService.getEndPoints().subscribe(data => { if (data['endPoint']) { data['endPoint'].forEach(item => { if (item.name === endpoint) { this.endpointObject = item; } }); } }); } protected getEndPoint() { return this.endpointObject; } protected setTitle(title: string) { this.title = title; } protected getPropFromPropertyPages(prop: string) { for (let x = 0; x < this.config.propertyPages.length; x++) { for (let i = 0; i < this.config.propertyPages[x].properties.length; i++) { if (this.config.propertyPages[x].properties[i].key === prop) { return this.config.propertyPages[x].properties[i].value; } } } return 'Unknown'; } public ngOnDestroy() { } public updateGadgetWithGlobalOptions(options:any){ this.globalOptions = Object.assign({},options); } } ================================================ FILE: src/app/gadgets/_common/gadget-config-model.ts ================================================ import {PropertyBase} from '../../dynamic-form/property-base'; /** * Created by jayhamilton on 6/15/17. */ export class GadgetConfigModel { propertyPages: PropertyPage[] = []; constructor(config: any) { config.propertyPages.forEach((page) => { const props: PropertyBase[] = []; page.properties.forEach((prop) => { switch (prop.controlType) { case 'textbox': case 'dropdown': case 'dynamicdropdown': props.push(new PropertyBase(prop)); break; case 'checkbox': props.push(new PropertyBase(prop)); break; case 'hidden': props.push(new PropertyBase(prop)); break; default: props.push(new PropertyBase(prop)); break; } }); this.propertyPages.push(new PropertyPage(page.displayName, page.groupId, page.position, props)); }); } } class PropertyPage { displayName: string; groupId: string; position: number; properties: PropertyBase[]; constructor(displayName: string, groupId: string, position: number, properties: PropertyBase[]) { this.displayName = displayName; this.groupId = groupId; this.position = position; this.properties = properties; } } ================================================ FILE: src/app/gadgets/_common/gadget-header-component.ts ================================================ /** * Created by jayhamilton on 2/28/17. */ import {Component, Input, Output, EventEmitter} from '@angular/core'; /** * Created by jayhamilton on 2/26/17. */ @Component({ moduleId: module.id, selector: 'app-gadget-header', templateUrl: './gadget-header.html', styleUrls: ['./styles-gadget.css'] }) export class GadgetHeaderComponent { @Input() title: string; @Input() showControls: boolean; @Input() inRun: boolean; @Input() inConfig: boolean; @Input() actionInitiated: boolean; @Input() showOperationControls: boolean; @Input() showConfigurationControl: boolean; @Input() gadgetHasOperationControls: boolean; @Input() globalOptions:any; @Output() removeEvent: EventEmitter = new EventEmitter(); @Output() toggleConfigModeEvent: EventEmitter = new EventEmitter(); @Output() runEvent: EventEmitter = new EventEmitter(); @Output() stopEvent: EventEmitter = new EventEmitter(); @Output() helpEvent: EventEmitter = new EventEmitter(); remove() { this.removeEvent.emit(); } toggleConfigMode() { this.toggleConfigModeEvent.emit(); } run() { this.runEvent.emit(); } stop() { this.stopEvent.emit(); } help(){ this.helpEvent.emit(); } } ================================================ FILE: src/app/gadgets/_common/gadget-header.html ================================================
{{title}}
================================================ FILE: src/app/gadgets/_common/gadget-operation-control-component.ts ================================================ import {Component, EventEmitter, Input, Output} from '@angular/core'; /** * Created by jayhamilton on 6/29/17. */ @Component({ moduleId: module.id, selector: 'app-gadget-operation-control', template: ` `, }) export class GadgetOperationComponent { @Output() runEvent: EventEmitter = new EventEmitter(); @Output() stopEvent: EventEmitter = new EventEmitter(); @Input() inRun: boolean; @Input() actionInitiated: boolean; @Input() inConfig: boolean; @Input() showOperationControls: boolean; @Input() gadgetHasOperationControls: boolean; run() { this.runEvent.emit(); } stop() { this.stopEvent.emit(); } } ================================================ FILE: src/app/gadgets/_common/gadget-property.service.ts ================================================ import {Injectable} from '@angular/core'; import {DropdownProperty} from '../../dynamic-form/property-dropdown'; import {PropertyBase} from '../../dynamic-form/property-base'; import {TextboxProperty} from '../../dynamic-form/property-textbox'; import {HiddenProperty} from '../../dynamic-form/property-hidden'; import {CheckboxProperty} from '../../dynamic-form/property-checkbox'; import {DynamicDropdownProperty} from '../../dynamic-form/property-dynamicdropdown'; import {NumberProperty} from '../../dynamic-form/property-number'; @Injectable() export class GadgetPropertyService { constructor() { } setPropertiesAndValues(defaultProperties: any[], properties: PropertyBase[]) { let ctrl: PropertyBase; properties.length = 0; defaultProperties.forEach(function (property) { if (property.controlType === 'dropdown') { ctrl = new DropdownProperty(property); properties.push(ctrl); } else if (property.controlType === 'textbox') { ctrl = new TextboxProperty(property); properties.push(ctrl); } else if (property.controlType === 'checkbox') { ctrl = new CheckboxProperty(property); properties.push(ctrl); } else if (property.controlType === 'hidden') { ctrl = new HiddenProperty(property); properties.push(ctrl); } else if (property.controlType === 'number') { ctrl = new NumberProperty(property); properties.push(ctrl); } else if (property.controlType === 'dynamicdropdown') { ctrl = new DynamicDropdownProperty(property); properties.push(ctrl); } }); properties.sort((a, b) => a.order - b.order); } setPropertyPagesAndProperties(defaultPropertyPages: any[], propertyPages: any[]) { const me = this; // for each defaultPropertyPage object, get the properties defaultPropertyPages.forEach(function (propertyPage) { const newPropertyPage: any = {}; for (const property in propertyPage) { if (propertyPage.hasOwnProperty(property)) { if (property !== 'properties') { newPropertyPage[property] = propertyPage[property]; } else { const properties: PropertyBase[] = []; me.setPropertiesAndValues(propertyPage.properties, properties); newPropertyPage['properties'] = properties; } } } propertyPages.push(newPropertyPage); }); } } ================================================ FILE: src/app/gadgets/_common/gadget-shared.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {GadgetHeaderComponent} from './gadget-header-component'; import {GadgetOperationComponent} from './gadget-operation-control-component'; import {HelpModalComponent} from './help-modal-component'; import {VisDrillDownComponent} from './vis-drill-down-component'; import {DndModule} from 'ng2-dnd'; import {MatProgressBarModule} from '@angular/material'; @NgModule({ imports: [ CommonModule, DndModule.forRoot(), MatProgressBarModule, ], declarations: [ GadgetHeaderComponent, GadgetOperationComponent, HelpModalComponent, VisDrillDownComponent ], exports: [ GadgetHeaderComponent, GadgetOperationComponent, HelpModalComponent, VisDrillDownComponent ] }) export class GadgetSharedModule { } ================================================ FILE: src/app/gadgets/_common/help-modal-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { ViewChild, ElementRef, AfterViewInit, Component, Input } from '@angular/core'; declare var jQuery: any; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-help-modal', moduleId: module.id, templateUrl: './help-modal.html', }) export class HelpModalComponent implements AfterViewInit { @Input() topic: any; modalicon: string; modalheader: string; modalconfig: string; @ViewChild('helpmodal_tag') helpmodalaRef: ElementRef; configModal: any; constructor() { } showMessageModal(icon: string, header: string, message: string) { this.modalicon = icon; this.modalheader = header; this.modalconfig = message; this.configModal.modal('show'); } hideMessageModal() { this.modalicon = ''; this.modalheader = ''; this.modalconfig = ''; this.configModal.modal('hide'); } ngAfterViewInit() { this.configModal = jQuery(this.helpmodalaRef.nativeElement); this.configModal.modal('hide'); } showHelp() { this.showMessageModal(null, 'Help', 'Get me out of here!'); } } ================================================ FILE: src/app/gadgets/_common/help-modal.html ================================================ ================================================ FILE: src/app/gadgets/_common/igadget.ts ================================================ /** * Created by jayhamilton on 2/25/17. */ interface IGadget { run(); stop(); toggleConfigMode(); initializeProperties(); updateProperties(updatedProperties: any); updateData(data: any[]); handleError(error: any); remove(); showGadgetControls(enable: boolean); configureGadget(instanceId: number, config: any, tags: Array); updateGadgetWithGlobalOptions(options:any); } ================================================ FILE: src/app/gadgets/_common/styles-gadget.css ================================================ .gadget { margin-bottom: 15px !important; min-height: inherit !important; font-family: 'Helvetica Neue', Helvetica, 'Open Sans', Arial, 'Lucida Grande', sans-serif; color: grey; } :host /deep/ div:not(.proppages) { margin-left: auto; margin-right: auto; display: block !important } .ui.form .field > label { font-family: 'Helvetica Neue', Helvetica, 'Open Sans', Arial, 'Lucida Grande', sans-serif; font-size: 1.3em !important; font-weight: 300; color: grey; } #bt { text-align: center !important } input { padding: 5px; border: none !important; width: 75% !important; font-weight: 300 !important; font-family: 'Helvetica Neue', Helvetica, 'Open Sans', Arial, 'Lucida Grande', sans-serif; font-size: 1.3em !important; outline: none !important; } .ct-title { color: #585858 !important; font-size: 1.5em !important; font-family: 'Helvetica Neue', Helvetica, 'Open Sans', Arial, 'Lucida Grande', sans-serif; text-align: left !important; font-weight: 500 !important; } .ui.top.left.floated.label, .ui.top.attached.label { background-color: white; } hr { font-weight: 300 !important; height: 1px; border-style: solid; color: black; } .ui[class*="top attached"].label:before { content: ""; position: absolute; left: 2%; bottom: 5px; height: 1px; width: 95%; /* or 100px */ border-bottom: 0px solid #d0d3d6; } .spacer { height: 2em; } .ui.items > .item > .content > .description { color: darkgray !important; } .suggestions > li { background-color: white !important; } td { font-family: 'Helvetica Neue', Helvetica, 'Open Sans', Arial, 'Lucida Grande', sans-serif; color: grey; } th { font-family: 'Helvetica Neue', Helvetica, 'Open Sans', Arial, 'Lucida Grande', sans-serif; color: #336699; } .example-full-width { width: 100%; } h4 { font-weight: 400; } .ui.segment { border: none; } ================================================ FILE: src/app/gadgets/_common/vis-drill-down-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { ViewChild, ElementRef, AfterViewInit, Component } from '@angular/core'; import { style, state, trigger, animate, transition } from '@angular/animations'; import {Facet} from '../../facet/facet-model'; declare var jQuery: any; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-vis-drill-down-modal', moduleId: module.id, templateUrl: './vis-drill-down.html', animations: [ trigger('contentSwitch', [ state('inactive', style({ opacity: 0 })), state('active', style({ opacity: 1 })), transition('inactive => active', animate('750ms ease-in')), transition('active => inactive', animate('750ms ease-out')) ]), trigger('tabSwitch', [ state('inactive', style({ opacity: .75 })), state('active', style({ opacity: 1 })), transition('inactive => active', animate('750ms ease-in')), transition('active => inactive', animate('750ms ease-out')) ]) ] }) export class VisDrillDownComponent implements AfterViewInit { modalicon: string; modalheader: string; modalconfig: string; vms: any[]; objectList: any[] = []; objectTitleList: string[] = []; placeHolderText = 'Begin typing vm name'; layoutColumnOneWidth = 'six'; layoutColumnTwoWidth = 'ten'; facetTags: Array = []; @ViewChild('vismodal_tag') vismodalaRef: ElementRef; configModal: any; constructor() { } showMessageModal(icon: string, header: string, message: string) { this.modalicon = icon; this.modalheader = header; this.modalconfig = message; this.configModal.modal('show'); } hideMessageModal() { this.modalicon = ''; this.modalheader = ''; this.modalconfig = ''; this.configModal.modal('hide'); } ngAfterViewInit() { this.configModal = jQuery(this.vismodalaRef.nativeElement); this.configModal.modal('hide'); } showDrillDownDetail($event) { const data: string = JSON.stringify($event, null, 4); this.showMessageModal(null, 'Detail', data); } showDetail($event) { const data: string = JSON.stringify($event, null, 4); this.showMessageModal(null, 'Detail', data); } } ================================================ FILE: src/app/gadgets/_common/vis-drill-down.html ================================================ ================================================ FILE: src/app/gadgets/barchart/barchart-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, ElementRef, ViewChild} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {BarChartService} from './service'; import {Router} from '@angular/router'; import {OptionsService} from "../../configuration/tab-options/service"; import {startWith, switchMap} from "rxjs/operators"; import {interval} from "rxjs"; import {ConfigurationService} from "../../services/configuration.service"; declare var jQuery: any; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class BarChartGadgetComponent extends GadgetBase { @ViewChild('chartOptionsSideBar_tag') chartOptionsSideBarRef: ElementRef; chartOptionsSideBar: any; // chart options showXAxis: boolean; showYAxis: boolean; gradient: boolean; showLegend: boolean; showXAxisLabel: boolean; showYAxisLabel: boolean; barChartType: string; showDataLabel: boolean; yAxisLabel: string; xAxisLabel: string; view: any[]; colorScheme: any = { domain: ['#0AFF16', '#B2303B'] //todo - control color from property page }; ////////////////// data: any[] = []; subscription: any; state: string; RUN_STATE = 'run'; STOP_STATE = 'stop'; POLL_INTERVAL = 15000; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _barChartService: BarChartService, private _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService, private _configService: ConfigurationService, private _route: Router ) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun() { /** * the base class initializes the common property gadgets. Prerun gives * us a chance to initialize any of the gadgets unique properties. */ this.initializeTheRemainderOfTheProperties(); if (this.getPropFromPropertyPages('state') == this.RUN_STATE) { this.run(); } } initializeTheRemainderOfTheProperties() { this.gradient = this.getPropFromPropertyPages('gradient'); this.showXAxis = this.getPropFromPropertyPages('showXAxis'); this.showYAxis = this.getPropFromPropertyPages('showYAxis'); this.showLegend = this.getPropFromPropertyPages('showLegend'); this.showXAxisLabel = this.getPropFromPropertyPages('showXAxisLabel'); this.showYAxisLabel = this.getPropFromPropertyPages('showYAxisLabel'); this.barChartType = this.getPropFromPropertyPages('barChartType'); this.showDataLabel = this.getPropFromPropertyPages('showDataLabel'); this.yAxisLabel = this.getPropFromPropertyPages('yAxisLabel'); this.xAxisLabel = this.getPropFromPropertyPages('xAxisLabel'); } public run() { this.clearChartData(); this.initializeRunState(true); this.updateData(null); this.saveState(this.RUN_STATE); } clearChartData() { this.data = []; } public stop() { this.stopWithoutStateSave(); this.saveState(this.STOP_STATE); } /** * The state is being saved to allow the board to load with the last state. Also, when the gadget is moved * within the board we need to carry the gadget's state along. * @param state */ public saveState(state: string) { this.updateProperties('{\"state\":\"' + state + '\"}'); this.persistTheChangeInInternalState(); } /** * When the gadget is destroyed (see ngOnDestroy) there is no need to * save the state. We just want to stop any API calls. */ public stopWithoutStateSave() { if (this.subscription) { this.subscription.unsubscribe(); } const data = []; Object.assign(this, {data}); this.setStopState(false); } public updateData(someData: any[]) { this.data.length = 0; /** * poll every 15 seconds * todo - change this to a websocket */ this.subscription = interval(this.POLL_INTERVAL).pipe( startWith(0), switchMap(() => this._barChartService.getData(this.endpointObject.address))) .subscribe(data => { Object.assign(this, {data}); }, error => this.handleError(error)); } public drillDown(data) { this.stopWithoutStateSave(); this._route.navigate(['/detail'], { queryParams: { chartType:"bar", chartSeries: data.series, chartMetric: data.name, endPointName: this.endpointObject.name } }); } private setInternalProperties(updatedPropsObject: any) { this.state = updatedPropsObject.state; if (updatedPropsObject.title != undefined) { this.title = updatedPropsObject.title; this.showXAxis = updatedPropsObject.showXAxis; this.showYAxis = updatedPropsObject.showYAxis; this.gradient = updatedPropsObject.gradient; this.showLegend = updatedPropsObject.showLegend; this.showXAxisLabel = updatedPropsObject.showXAxisLabel; this.showYAxisLabel = updatedPropsObject.showYAxisLabel; this.barChartType = updatedPropsObject.barChartType; this.showDataLabel = updatedPropsObject.showDataLabel; this.xAxisLabel = updatedPropsObject.xAxisLabel; this.yAxisLabel = updatedPropsObject.yAxisLabel; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } /** * todo * This is called from the dynamic property page form or when the internal running state changes * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ public updateProperties(updatedProperties: any) { const updatedPropsObject = JSON.parse(updatedProperties); /** * update this tools property pages */ this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); /** * update the tools internal state */ this.setInternalProperties(updatedPropsObject); } public ngOnDestroy() { this.stopWithoutStateSave(); } /** * todo - need to improve how internal state is saved to persistant store */ private persistTheChangeInInternalState() { let payLoad = "{\"instanceId\":" + this.instanceId + ",\"title\":\"" + this.title + "\",\"state\":\"" + this.state + "\",\"endpoint\":\"" + this.endpointObject.name + "\",\"gradient\":" + this.gradient + ",\"showXAxis\":" + this.showXAxis + ",\"showYAxis\":" + this.showYAxis + ",\"showLegend\":" + this.showLegend + ",\"showXAxisLabel\":" + this.showXAxisLabel + ",\"showYAxisLabel\":" + this.showYAxisLabel + ",\"showDataLabel\":" + this.showDataLabel + ",\"barChartType\":\"" + this.barChartType + "\",\"yAxisLabel\":\"" + this.yAxisLabel + "\",\"xAxisLabel\":\"" + this.xAxisLabel + "\"}"; this._configService.notifyGadgetOnPropertyChange(payLoad, this.instanceId); } toggleChartProperties() { if (this.globalOptions.displayGadgetOptionsInSideBar == false) { this.toggleConfigMode(); return; } this.chartOptionsSideBar = jQuery(this.chartOptionsSideBarRef.nativeElement); this.chartOptionsSideBar.sidebar('setting', 'transition', 'overlay'); this.chartOptionsSideBar.sidebar('toggle'); } } ================================================ FILE: src/app/gadgets/barchart/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class BarChartService { constructor(private _http: HttpClient) { } getData(endpoint: string) { return this._http.get(endpoint) .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/barchart/view.html ================================================
================================================ FILE: src/app/gadgets/bubble/bubble-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {BubbleService} from './service'; import {Router} from '@angular/router'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class BubbleGadgetComponent extends GadgetBase { // chart options showXAxis = true; showYAxis = true; gradient = true; showLegend = true; showXAxisLabel = true; showYAxisLabel = true; yAxisLabel = 'Available'; xAxisLabel = 'Total'; view: any[]; data: any[] = []; colorScheme: any = { domain: ['#00ff00', '#4800ff', '#4894FF', '#AF0854'] }; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _bubbleService: BubbleService, private _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService, private _route: Router) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { // this.run(); } public run() { this.data = []; this.initializeRunState(true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(_data: any[]) { this._bubbleService.getMockData().subscribe(data => { Object.assign(this, {data}); console.log(data); }, error => this.handleError(error)); } public drillDown(data) { this._route.navigate(['/detail'], {}); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); /* this.title = updatedPropsObject.title; this.showXAxis = updatedPropsObject.chart_properties; this.showYAxis = updatedPropsObject.chart_properties; this.gradient = updatedPropsObject.chart_properties; this.showLegend = updatedPropsObject.chart_properties; this.showXAxisLabel = updatedPropsObject.chart_properties; this.showYAxisLabel = updatedPropsObject.chart_properties; */ this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } ================================================ FILE: src/app/gadgets/bubble/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class BubbleService { constructor(private _http: HttpClient) { } getMockData() { return this._http.get('/assets/api/chart-mock-bubble-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/bubble/view.html ================================================ ================================================ FILE: src/app/gadgets/cpu/cpu-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {CPUService} from './service'; import {Router} from '@angular/router'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class CPUGadgetComponent extends GadgetBase { // chart options showXAxis = true; showYAxis = true; gradient = true; showLegend = true; showXAxisLabel = true; showYAxisLabel = true; yAxisLabel = 'Available CPUs'; xAxisLabel = 'Percent Utilization'; view: any[]; cpu: any[] = []; colorScheme: any = { domain: ['#0d5481', '#0AFF16'] }; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _cpuService: CPUService, private _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService, private _route: Router) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.run(); } public run() { this.cpu = []; this.initializeRunState(true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._cpuService.getMockData().subscribe(cpu => { Object.assign(this, {cpu}); }, error => this.handleError(error)); } public drillDown(data) { this._route.navigate(['/detail'], { }); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.showXAxis = updatedPropsObject.chart_properties; this.showYAxis = updatedPropsObject.chart_properties; this.gradient = updatedPropsObject.chart_properties; this.showLegend = updatedPropsObject.chart_properties; this.showXAxisLabel = updatedPropsObject.chart_properties; this.showYAxisLabel = updatedPropsObject.chart_properties; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } ================================================ FILE: src/app/gadgets/cpu/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class CPUService { constructor(private _http: HttpClient) { } getMockData() { return this._http.get('/assets/api/cpu-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/cpu/view.html ================================================ ================================================ FILE: src/app/gadgets/cpum/cpu.model.ts ================================================ import {Series} from '../_common/base-chart-models/series.model'; import {Bar} from '../_common/base-chart-models/bar.model'; export class CPUChartMetric extends Bar { data: any; constructor(data: any, name: string, series: Array) { super(name, series); this.data = data; } } ================================================ FILE: src/app/gadgets/cpum/cpum-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {timer} from 'rxjs'; import {ObservableWebSocketService} from '../../services/websocket-service'; import {ErrorHandler} from '../../error/error-handler'; import {CPUChartMetric} from './cpu.model'; import {Series} from '../_common/base-chart-models/series.model'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class CPUMGadgetComponent extends GadgetBase implements OnDestroy, OnInit { // chart options gradient = true; legend = false; xAxis = true; yAxis = true; showGridLines = true; showXAxisLabel = true; showYAxisLabel = false; xAxisLabel = 'Available CPUs'; yAxisLabel = 'Percent Utilization'; view: any[]; chartData: any[] = []; colorScheme: any = { domain: ['#0AFF16', '#0d5481'] }; webSocket: any; waitForConnectionDelay = 2000; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, private _changeDetectionRef: ChangeDetectorRef, private _webSocketService: ObservableWebSocketService, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService, ); } public preRun(): void { } public run() { this.initializeRunState(false); this.webSocket = this._webSocketService.createObservableWebSocket(this.getEndPoint().address).subscribe(data => { const dataObject = JSON.parse(data); this.updateGraph(dataObject); }, error => { /** * todo improve this error handling * @type {{status: string; statusText: string; resource: string}} */ const errMsg = { status: error.code + '', statusText: ErrorHandler.getWebSocketErrorReason(error), resource: this.getEndPoint().address }; this.handleError(ErrorHandler.getErrorObject(errMsg)); }, () => { if (this.inRun) { /** * todo improve this error handling * @type {{status: string; statusText: string; resource: string}} */ const errMsg = { status: 'disconnected', statusText: 'Service was interrupted while the gadget was running!', resource: this.getEndPoint().address }; this.handleError(ErrorHandler.getErrorObject(errMsg)); } } ); /** * todo remove dependency on timer * @type {Observable} */ const _timer = timer(this.waitForConnectionDelay); _timer.subscribe(t => { // todo test whether we are connected of not this._webSocketService.sendMessage('start'); this.setInRunState(); }); } public stop() { this.setStopState(true); try { this._webSocketService.sendMessage('stop'); this.webSocket.unsubscribe(); this.updateGraph(null); } catch (error) { this.handleError(error); } this.actionInitiated = false; } public updateGraph(cpus: Array) { /** todo * i18n */ const chartData: any = []; let id = 0; if (cpus) { cpus.forEach(cpuData => { const value = cpuData['utilPct']; const series: Array = []; series.push(new Series('used', value)); series.push(new Series('available', 100 - value)); /** todo * determine how to get access to the data from the chart for drill down purposes * @type {CPUChartMetric} */ const cpuChartMetric = new CPUChartMetric(cpuData, 'CPU ' + id++, series); chartData.push(cpuChartMetric); }); } Object.assign(this, {chartData}); } public updateData(data: any[]) { } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.gradient = updatedPropsObject.gradient; this.legend = updatedPropsObject.legend; this.xAxis = updatedPropsObject.xAxis; this.yAxis = updatedPropsObject.yAxis; this.showGridLines = updatedPropsObject.showGridLines; this.showXAxisLabel = updatedPropsObject.showXAxisLabel; this.showYAxisLabel = updatedPropsObject.showYAxisLabel; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } public ngOnDestroy() { this.stop(); } } ================================================ FILE: src/app/gadgets/cpum/view.html ================================================ ================================================ FILE: src/app/gadgets/disk/disk-gadget.component.ts ================================================ import { ChangeDetectorRef, Component } from '@angular/core'; import { style, trigger, animate, transition, state } from '@angular/animations'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {GadgetBase} from '../_common/gadget-base'; import {DiskService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'], animations: [ trigger('accordion', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('700ms ease-in-out')), transition('out => in', animate('300ms ease-in-out')) ]), trigger('accordion2', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('300ms ease-in-out')), transition('out => in', animate('800ms ease-in-out')) ]) ] }) export class DiskGadgetComponent extends GadgetBase { topic: any; showOperationControls = false; data: any; threshold: string; badColorScheme = { domain: ['#a10910', '#DDDDDD'] }; goodColorScheme = { domain: ['#00c700', '#DDDDDD'] }; used; avail; detailMenuOpen: string; colorScheme = this.goodColorScheme; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _diskService: DiskService, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); this.run(); this.setTopic(); } public preRun(): void { this.threshold = this.getPropFromPropertyPages('threshold'); this.detailMenuOpen = 'out'; } public run() { this.data = []; this.initializeRunState(true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._diskService.getMockData().subscribe(_data => { this.data = _data; const thresholdVal = Number(this.threshold); if (this.data[0].value < thresholdVal) { this.colorScheme = this.goodColorScheme; } else { this.colorScheme = this.badColorScheme; } this.used = this.data[0].value; this.avail = this.data[1].value; }, error => this.handleError(error)); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.threshold = updatedPropsObject.threshold; this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.run(); } setTopic() { this._diskService.getHelpTopic().subscribe(data => { this.topic = data; }); } toggleAcordion(): void { this.detailMenuOpen = this.detailMenuOpen === 'out' ? 'in' : 'out'; } } ================================================ FILE: src/app/gadgets/disk/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {TrendLineService} from '../trend-line/service'; import {timer, Observable} from 'rxjs'; import {catchError} from "rxjs/operators"; import {HttpClient} from '@angular/common/http'; @Injectable() export class DiskService { constructor(private _http: HttpClient) { } get() { return this._http.get('/assets/api/disk-model.json') .pipe( catchError(RuntimeService.handleError) ); } getHelpTopic() { return this._http.get('/assets/api/disk-help-model.json') .pipe( catchError(RuntimeService.handleError) ); } getMockData() { return new Observable(observer => { timer(500, 5000).subscribe(t => { const used = TrendLineService.getRandomArbitrary(0, 100); const available = 100 - used; const data = [ { 'name': 'used', 'value': used }, { 'name': 'available', 'value': available } ]; observer.next(data); }); }); } } ================================================ FILE: src/app/gadgets/disk/view.html ================================================

Device ID Type
safiow4lkf8adfkadfadf ISCSI
Allocated Available
123123 TB 323232342 TB
Read Write Latency
100 ms 10 ms 10 ms

================================================ FILE: src/app/gadgets/donut/donut-gadget.component.ts ================================================ import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { style, trigger, animate, transition, state } from '@angular/animations'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {GadgetBase} from '../_common/gadget-base'; import {DonutService} from './service'; import {APITokenService} from '../../api-token/api-token.service'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'], animations: [ trigger('accordion', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('700ms ease-in-out')), transition('out => in', animate('300ms ease-in-out')) ]), trigger('accordion2', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('300ms ease-in-out')), transition('out => in', animate('800ms ease-in-out')) ]) ] }) export class DonutGadgetComponent extends GadgetBase implements OnDestroy { topic: any; data = {}; colorScheme = { domain: ['#0cd057', '#3f8eff', '#9a0101'] }; detailMenuOpen: string; donutServiceSubscription: any; // gadget properties autoCompliance: boolean; complianceFrequency: number; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _donutService: DonutService, protected _apiTokenService: APITokenService, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.setTopic(); this.setProperties(); this.run(); } public run() { this.initializeRunState(true); this.updateData(null); } public stop() { if (this.donutServiceSubscription) { this.donutServiceSubscription.unsubscribe(); } this.setStopState(false); } public updateData(data: any[]) { this.donutServiceSubscription = this._donutService.poll().subscribe(donutData => { const me = this; this._donutService.get().subscribe(_data => { me.data = _data; }, error => this.handleError(error)); }); } public updateProperties(updatedProperties: any) { const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.autoCompliance = updatedPropsObject.auto; this.complianceFrequency = updatedPropsObject.frequency; this.showOperationControls = true; this.setEndPoint(updatedPropsObject.endpoint); } setTopic() { this._donutService.getHelpTopic().subscribe(data => { this.topic = data; }); } public setProperties() { this.title = this.getPropFromPropertyPages('title'); this.detailMenuOpen = 'out'; } toggleAccordion(): void { this.detailMenuOpen = this.detailMenuOpen === 'out' ? 'in' : 'out'; } public ngOnDestroy() { this.stop(); } } ================================================ FILE: src/app/gadgets/donut/drill-down-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { ViewChild, ElementRef, AfterViewInit, Component } from '@angular/core'; import { style, state, trigger, animate, transition } from '@angular/animations'; import {Facet, Tag} from '../../facet/facet-model'; import {DonutService} from './service'; declare var jQuery: any; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-drill-down-modal', moduleId: module.id, templateUrl: './drill-down.html', styleUrls: ['drill-down-style.css'], animations: [ trigger('contentSwitch', [ state('inactive', style({ opacity: 0 })), state('active', style({ opacity: 1 })), transition('inactive => active', animate('750ms ease-in')), transition('active => inactive', animate('750ms ease-out')) ]), trigger('tabSwitch', [ state('inactive', style({ opacity: .75 })), state('active', style({ opacity: 1 })), transition('inactive => active', animate('750ms ease-in')), transition('active => inactive', animate('750ms ease-out')) ]) ] }) export class DrillDownComponent implements AfterViewInit { modalicon: string; modalheader: string; modalconfig: string; chartSelectedName: string; chartSelectedValue: number; objects: any[]; objecNameList: string[]; placeHolderText = 'Begin typing a name'; layoutColumnOneWidth = 'six'; layoutColumnTwoWidth = 'ten'; facetTags: Array = []; listHeader = 'Header Name'; dropZone1Count = 0; dropZone2Count = 0; dropZone3Count = 0; hideRightColumn = false; leftColumnWidth = 'eleven'; @ViewChild('drillmodal_tag') modalaRef: ElementRef; configModal: any; constructor(private _donutService: DonutService) { } showMessageModal(icon: string, header: string, message: string) { this.modalicon = icon; this.modalheader = header; this.modalconfig = message; this.configModal.modal('show'); } hideMessageModal() { this.modalicon = ''; this.modalheader = ''; this.modalconfig = ''; this.configModal.modal('hide'); } ngAfterViewInit() { this.configModal = jQuery(this.modalaRef.nativeElement); this.configModal.modal('hide'); } showDrillDownDetail($event) { if (typeof $event === 'string') { this.chartSelectedName = $event.toLocaleLowerCase(); } else { this.chartSelectedName = $event['name'].toString().toLocaleLowerCase(); this.chartSelectedValue = $event['value']; } if (this.chartSelectedName === 'non-compliant') { this.hideRightColumn = false; this.leftColumnWidth = 'eleven'; } else { this.hideRightColumn = true; this.leftColumnWidth = 'sixteen'; } this._donutService.get().subscribe(_data => { _data.forEach(object => { if (object['name'] === this.chartSelectedName) { this.processObjects(object['detail']); this.setFacets(); this.showMessageModal(null, 'Detail', null); } }); }); } showDetail($event) { const data: string = JSON.stringify($event, null, 4); this.showMessageModal(null, 'Detail', data); } processObjects(objectsToProcess: any) { this.objects = []; this.objecNameList = []; Object.assign(this.objects, objectsToProcess); this.objects.forEach(name => { this.objecNameList.push(name['name']); }); } updateDropZone1(object: any) { if (object.dragData === 'all') { this.dropZone1Count += this.objects.length; } else { this.dropZone1Count++; } } updateDropZone2(object: any) { if (object.dragData === 'all') { this.dropZone2Count += this.objects.length; } else { this.dropZone2Count++; } } updateDropZone3(object: any) { if (object.dragData === 'all') { this.dropZone3Count += this.objects.length; } else { this.dropZone3Count++; } } setFacets() { this.facetTags.length = 0; const t1 = new Tag('Tag1'); const t2 = new Tag('Tag2'); const a1 = new Array(); a1.push(t1); a1.push(t2); const f1 = new Facet('Category A', a1); const t3 = new Tag('Tag3'); const t4 = new Tag('Tag4'); const a2 = new Array(); a2.push(t3); a2.push(t4); const f2 = new Facet('Category B', a2); this.facetTags.push(f1); this.facetTags.push(f2); } } ================================================ FILE: src/app/gadgets/donut/drill-down-style.css ================================================ h3 { font-weight: 400; } .drill-down-blue { color: #2c83d0 } ================================================ FILE: src/app/gadgets/donut/drill-down.html ================================================ ================================================ FILE: src/app/gadgets/donut/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {timer, Observable} from 'rxjs'; import {catchError} from "rxjs/operators"; import {HttpClient} from '@angular/common/http'; @Injectable() export class DonutService { constructor(private _http: HttpClient) { } get() { return this._http.get('/assets/api/donut-model.json') .pipe( catchError(RuntimeService.handleError) ); } getHelpTopic() { return this._http.get('/assets/api/disk-help-model.json') .pipe( catchError(RuntimeService.handleError) ); } poll() { return new Observable(observer => { timer(500, 10000).subscribe(t => { observer.next(); }); }); } complianceJob(event: string) { /** * post a request to the backend to start a compliance operation. The get API above will reflect the * changing results of the compliance job. The compliance job will have an event action that dictates * state transitions stop, start, pause, abort, status. */ } } ================================================ FILE: src/app/gadgets/donut/view.html ================================================

Detail Content

================================================ FILE: src/app/gadgets/edge-service-list/edge-service-list-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core'; import { style, trigger, animate, transition, state } from '@angular/animations'; import {RuntimeService} from '../../services/runtime.service'; import {serviceList} from './service-list'; import {GadgetInstanceService} from '../../grid/grid.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {EdgeService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; declare var jQuery: any; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'], animations: [ trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(3000, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(3000, style({opacity: 0})) ]) ]), trigger('accordion', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('700ms ease-in-out')), transition('out => in', animate('300ms ease-in-out')) ]), trigger('accordion2', [ state('in', style({ height: '*' })), state('out', style({ opacity: '0', height: '0px' })), transition('in => out', animate('300ms ease-in-out')), transition('out => in', animate('800ms ease-in-out')) ])] }) export class EdgeServiceListGadgetComponent extends GadgetBase implements OnDestroy { // chart options showXAxis = true; showYAxis = true; gradient = true; showLegend = false; showXAxisLabel = true; showYAxisLabel = true; yAxisLabel = 'Tasks'; xAxisLabel = 'Proxies'; view: any[] = [700, 200]; single: any []; colorScheme: any = { domain: ['#0d5481', '#0AFF16', '#da871e', '#D449E1'] }; remoteService: any; detailMenuOpen: string; // todo just realy on json edgeServiceList: { active: boolean, host: string, port: number, metaData: any, uri: string, serviceId: string, status: string, runningTaskCount: number }[] = []; selectedUri: string; constructor(protected _procMonRuntimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, private _changeDetectionRef: ChangeDetectorRef, private _edgeService: EdgeService, protected _optionsService: OptionsService) { super(_procMonRuntimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); Object.assign(this, {serviceList}); const single = []; Object.assign(this, {single}); } public preRun(): void { this.detailMenuOpen = 'out'; } public run() { this.initializeRunState(false); const me = this; this.remoteService = this._edgeService.getMicroServices(this.getEndPoint().address).subscribe(results => { const edgeServiceList = []; this.setInRunState(); if (results instanceof Array) { results.forEach(function (item) { me._edgeService.getTaskCount(item.uri).subscribe(data => { const i = { active: false, host: '', port: 0, metaData: {}, uri: '', serviceId: '', status: 'UP', runningTaskCount: 0 }; i['active'] = true; i['host'] = item.host; i['port'] = item.port; i['metaData'] = item.metadata; i['uri'] = item.uri; i['serviceId'] = item.serviceId; i['runningTaskCount'] = data; edgeServiceList.push(i); edgeServiceList.sort(function (a, b) { if (a['port'] < b['port']) { return -1; } else if (a['port'] > b['port']) { return 1; } else { return 0; } }); Object.assign(me.edgeServiceList, edgeServiceList); me.updateGraph(); }); }); } } , error => this.handleError(error) , () => console .debug( 'Connecting to the service' )); } public checkPoxySelection() { this._edgeService.getSelectedProxy().subscribe(result => { this.selectedUri = result['_body']; }); } public seedProxiesWithWork() { this._edgeService.seedProxiesWithWork().subscribe(data => { console.log('job running'); }); } public runProxyJob(uri: string) { this._edgeService.runJob(uri).subscribe(data => { console.log('running job on proxy: ' + uri); }); } public stop() { this.setStopState(true); if (this.remoteService) { this.remoteService.unsubscribe(); } this.edgeServiceList.length = 0; this.actionInitiated = false; } public updateData(data: any[]) { data.forEach(function (item) { serviceList.forEach(function (service) { if (item.toString().includes(service.pseudoName)) { service.active = true; service.processId = item.toString().split(':')[0]; } }); }); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } updateGraph() { const single = []; this.edgeServiceList.forEach(item => { single.push({ 'name': item.port + ' ' + item.host, 'value': item.runningTaskCount }); }); single.sort(function (a, b) { if (a['name'] < b['name']) { return -1; } else if (a['name'] > b['name']) { return 1; } else { return 0; } }); Object.assign(this, {single}); } public ngOnDestroy() { this.stop(); } toggleAcordion(): void { this.detailMenuOpen = this.detailMenuOpen === 'out' ? 'in' : 'out'; } } ================================================ FILE: src/app/gadgets/edge-service-list/service-list.ts ================================================ /** * Created by jayhamilton on 1/28/17. */ export const serviceList: { active: boolean, applicationName: string, description: string, icon: string, pseudoName: string, processId: string}[] = [ { active: false, applicationName: 'Virgo', description: 'Main ECX Application container that manages all of the ECX functions.', icon: 'images/donut.png', pseudoName: 'ECX', processId: '' }, { active: false, applicationName: 'Eureka', description: 'This is a microservice discovery service. Used for such items as VADP Proxy.', icon: 'images/donut.png', pseudoName: 'Eureka', processId: '' }, { active: false, applicationName: 'PostGresql', description: 'This is a database server used for job management and security.', icon: 'images/donut.png', pseudoName: 'SQL DB', processId: '' }, { active: false, applicationName: 'Mongo', description: 'This is a database server used for ECX operation management.', icon: 'images/donut.png', pseudoName: 'Catalog DB', processId: '' }, { active: false, applicationName: 'Mongo', description: 'This is a database server used for ECX configuration.', icon: 'images/donut.png', pseudoName: 'Configuration DB', processId: '' }, ]; ================================================ FILE: src/app/gadgets/edge-service-list/service.ts ================================================ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {timer, Observable} from 'rxjs'; import {catchError} from "rxjs/operators"; import {HttpClient} from '@angular/common/http'; @Injectable() export class EdgeService { constructor(private _http: HttpClient) { } getSelectedProxy() { return this._http.get('http://localhost:9090/select') .pipe( catchError(RuntimeService.handleError) ); } // http://localhost:9090/service-instances/vadp' getMicroServices(url: string) { return new Observable(observer => { timer(500, 5000).subscribe(t => { this._http.get(url).subscribe(data => { observer.next(data); }), catchError(RuntimeService.handleError); }); }); } getTaskCount(uri: string) { console.log('Getting task count for: ' + uri); return this._http.get(uri + '/task') .pipe( catchError(RuntimeService.handleError) ); } seedProxiesWithWork() { return this._http.post('http://localhost:9090/run', null, null) .pipe( catchError(RuntimeService.handleError) ); } runJob(uri: string) { return this._http.post(uri + '/run', null, null) .pipe( catchError(RuntimeService.handleError) ); } getGraphInfo() { return this._http.get('/assets/api/disk-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/edge-service-list/view.html ================================================
Status Name Host Port Select Tasks Meta-Data Run Job On Proxy
{{serviceItem.serviceId}} {{serviceItem.host}} {{serviceItem.port}}
{{serviceItem.runningTaskCount}} {{serviceItem.metaData.affinity}} {{serviceItem.metaData.proximity}}

================================================ FILE: src/app/gadgets/gadget.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {CPUGadgetComponent} from './cpu/cpu-gadget.component'; import {CPUMGadgetComponent} from './cpum/cpum-gadget.component'; import {DiskGadgetComponent} from './disk/disk-gadget.component'; import {MemoryGadgetComponent} from './memory/memory-gadget.component'; import {EdgeServiceListGadgetComponent} from './edge-service-list/edge-service-list-gadget.component'; import {StatisticGadgetComponent} from './statistic/statistic-gadget.component'; import {TrendGadgetComponent} from './trend/trend-gadget.component'; import {TrendLineGadgetComponent} from './trend-line/trend-line-gadget.component'; import {NewsGadgetComponent} from './news/news-gadget.component'; import {TodoGadgetComponent} from './todo/todo-gadget.component'; // todo gadget import {JobAnalysisGadgetComponent} from './job-analysis/job-analysis-gadget.component'; import {CPUService} from './cpu/service'; import {EdgeService} from './edge-service-list/service'; import {StatisticService} from './statistic/service'; import {DiskService} from './disk/service'; import {TrendService} from './trend/service'; import {PropertyListGadgetComponent} from './property-list/property-list-gadget.component'; import {DynamicFormModule} from '../dynamic-form/dynamic-form-module'; import {ServiceListGadgetComponent} from './service-list/service-list-gadget.component'; import {DndModule} from 'ng2-dnd'; import {NgxChartsModule} from '@swimlane/ngx-charts'; import {GadgetSharedModule} from './_common/gadget-shared.module'; import {ErrorHandlerModule} from '../error/error.module'; import {PortConnectionGadgetComponent} from './port-connection/port-connection-gadget.component'; import { MatButtonModule, MatCheckboxModule, MatExpansionModule, MatIconModule, MatInputModule, MatOptionModule, MatProgressBarModule, MatSelectModule } from '@angular/material'; import {FormsModule} from '@angular/forms'; import {StorageObjectListComponent} from './storage-object-list/storage-object-list.component'; import {StorageService} from './storage-object-list/service'; import {DataListModule} from '../datalist/data-list.module'; import {DonutGadgetComponent} from './donut/donut-gadget.component'; import {DonutService} from './donut/service'; import {APITokenService} from '../api-token/api-token.service'; import {DrillDownComponent} from './donut/drill-down-component'; import {FacetModule} from '../facet/facet.module'; import {TypeAheadInputModule} from '../typeahead-input/typeahead-input.module'; import {TodoService} from './todo/service'; import {ConnectionService} from './port-connection/service'; import {BubbleGadgetComponent} from "./bubble/bubble-gadget.component"; import {ResultViewComponent} from "./port-connection/result-view.component"; import {SolutionViewComponent} from "./port-connection/solution-view.component"; import {BarChartGadgetComponent} from "./barchart/barchart-gadget.component"; import {BarChartService} from "./barchart/service"; import {PieChartGadgetComponent} from "./piechart/piechart-gadget.component"; import {PieChartService} from "./piechart/service"; // todo gadget @NgModule({ imports: [ CommonModule, GadgetSharedModule, DndModule.forRoot(), DynamicFormModule, ErrorHandlerModule, NgxChartsModule, MatButtonModule, MatIconModule, MatCheckboxModule, MatInputModule, MatProgressBarModule, MatExpansionModule, MatOptionModule, MatSelectModule, FormsModule, FacetModule, TypeAheadInputModule, DataListModule ], declarations: [ TodoGadgetComponent, // todo gadget CPUGadgetComponent, CPUMGadgetComponent, DiskGadgetComponent, MemoryGadgetComponent, EdgeServiceListGadgetComponent, StatisticGadgetComponent, TrendGadgetComponent, TrendLineGadgetComponent, NewsGadgetComponent, JobAnalysisGadgetComponent, StatisticGadgetComponent, PropertyListGadgetComponent, ServiceListGadgetComponent, PortConnectionGadgetComponent, StorageObjectListComponent, DonutGadgetComponent, DrillDownComponent, BubbleGadgetComponent, ResultViewComponent, SolutionViewComponent, BarChartGadgetComponent, PieChartGadgetComponent ], providers: [TrendService, DiskService, StatisticService, EdgeService, CPUService, StorageService, DonutService, APITokenService, ConnectionService, TodoService, // todo gadget BarChartService, PieChartService ], exports: [ TodoGadgetComponent, // todo gadget CPUGadgetComponent, CPUMGadgetComponent, DiskGadgetComponent, MemoryGadgetComponent, EdgeServiceListGadgetComponent, StatisticGadgetComponent, TrendGadgetComponent, TrendLineGadgetComponent, NewsGadgetComponent, JobAnalysisGadgetComponent, StatisticGadgetComponent, PropertyListGadgetComponent, ServiceListGadgetComponent, PortConnectionGadgetComponent, StorageObjectListComponent, DonutGadgetComponent, BubbleGadgetComponent, BarChartGadgetComponent, PieChartGadgetComponent ] }) export class GadgetModule { } ================================================ FILE: src/app/gadgets/job-analysis/ja.css ================================================ .example-card { width: 400px; } .example-header-image { background-size: cover; } ================================================ FILE: src/app/gadgets/job-analysis/job-analysis-gadget.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {JobAnalysisService} from './service'; import {DomSanitizer} from '@angular/platform-browser'; import {MatIconRegistry} from '@angular/material'; import {OptionsService} from "../../configuration/tab-options/service"; declare var d3: any; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./ja.css'] }) export class JobAnalysisGadgetComponent extends GadgetBase { showOperationControls = false; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _jobAnalysisService: JobAnalysisService, iconRegistry: MatIconRegistry, sanitizer: DomSanitizer, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); iconRegistry.addSvgIcon( 'thumbs-up', sanitizer.bypassSecurityTrustResourceUrl('assets/images/svg-icons/ic_add_white_36px.svg')); this.run(); } public preRun(): void { } public run() { this.initializeRunState(true); } public stop() { this.setStopState(false); } public updateData(data: any[]) { } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } ================================================ FILE: src/app/gadgets/job-analysis/model.json ================================================ [ { "name": "IBM", "series": [ { "name": "January", "value": 3453 }, { "name": "February", "value": 3434434 }, { "name": "March", "value": 333335 }, { "name": "April", "value": 434 } ] }, { "name": "NetApp", "series": [ { "name": "January", "value": 78 }, { "name": "February", "value": 82700 }, { "name": "March", "value": 4333335 }, { "name": "April", "value": 8345 } ] }, { "name": "Pure", "series": [ { "name": "January", "value": 5043344 }, { "name": "February", "value": 5800 },{ "name": "March", "value": 333335 }, { "name": "April", "value": 4343333 } ] } ] ================================================ FILE: src/app/gadgets/job-analysis/service.ts ================================================ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class JobAnalysisService { constructor(private _http: HttpClient) { } get() { return this._http.get('../../plugins/procmon/components/gadgets/trend/model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/job-analysis/view.html ================================================
================================================ FILE: src/app/gadgets/memory/memory-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, OnDestroy} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {timer} from 'rxjs'; import {ObservableWebSocketService} from '../../services/websocket-service'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class MemoryGadgetComponent extends GadgetBase implements OnDestroy { // chart options view: any[]; colorScheme = { domain: ['#A13F51', '#5AA454', '#C7B42C'] }; currentValue = '0'; previousValue = '0'; webSocket: any; waitForConnectionDelay = 2000; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, private _changeDetectionRef: ChangeDetectorRef, private _webSocketService: ObservableWebSocketService, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { } public run() { this.initializeRunState(false); this.webSocket = this._webSocketService.createObservableWebSocket(this.getEndPoint().address).subscribe(data => { const dataObject = JSON.parse(data); try { let percent = dataObject.used / dataObject.total * 100; percent = Math.round(percent); this.updateGraph(percent); } catch (error) { this.handleError(error); } }, error => { console.log(error); this.handleError(error); }); const _timer = timer(this.waitForConnectionDelay); _timer.subscribe(t => { // todo test whether we are connected of not this._webSocketService.sendMessage('start'); this.setInRunState(); }); } public stop() { this.setStopState(true); try { this._webSocketService.sendMessage('stop'); this.webSocket.unsubscribe(); } catch (error) { this.handleError(error); } this.actionInitiated = false; } public updateData(data: any[]) { } public updateGraph(value: number) { if (Number(this.currentValue) > Number(this.previousValue)) { this.previousValue = this.currentValue; } this.currentValue = value + ''; this.showOperationControls = true; } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } public ngOnDestroy() { this.stop(); } } ================================================ FILE: src/app/gadgets/memory/view.html ================================================ ================================================ FILE: src/app/gadgets/news/news-gadget.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {GadgetBase} from '../_common/gadget-base'; import {NewsService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class NewsGadgetComponent extends GadgetBase { // runtime document subscription news: any; resource: string; gadgetHasOperationControls = false; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _newsService: NewsService, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.updateData(null); this.run(); } public run() { this.news = []; this.initializeRunState(true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._newsService.get().subscribe(news => { this.news = news; }, error => this.handleError(error)); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.updateData(null); } } ================================================ FILE: src/app/gadgets/news/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class NewsService { constructor(private _http: HttpClient) { } get() { return this._http.get('/assets/api/news-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/news/view.html ================================================

{{release.heading}}

{{detail.category}}

{{article.date}}
{{article.headline}}

{{article.description}}

================================================ FILE: src/app/gadgets/piechart/piechart-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, ElementRef, ViewChild} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {PieChartService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; import {startWith, switchMap} from "rxjs/operators"; import {interval} from "rxjs"; import {ConfigurationService} from "../../services/configuration.service"; declare var jQuery: any; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class PieChartGadgetComponent extends GadgetBase { @ViewChild('chartOptionsSideBar_tag') chartOptionsSideBarRef: ElementRef; chartOptionsSideBar:any; // chart options explodeSlices: boolean; showDonut: boolean; gradient: boolean; showLegend: boolean; showLabels: boolean; legendTitle = "Title"; view: any[]; colorScheme: any = { domain: ['#0d5481', '#0AFF16', '#4894FF','#F54B7D'] //todo - control color from property page }; ////////////////// data: any[] = []; subscription: any; state: string; RUN_STATE = 'run'; STOP_STATE = 'stop'; POLL_INTERVAL = 15000; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _pieChartService: PieChartService, private _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService, private _configService: ConfigurationService ) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun() { /** * the base class initializes the common property gadgets. Prerun gives * us a chance to initialize any of the gadgets unique properties. */ this.initializeTheRemainderOfTheProperties(); if (this.getPropFromPropertyPages('state') == this.RUN_STATE) { this.run(); } } initializeTheRemainderOfTheProperties() { this.explodeSlices = this.getPropFromPropertyPages('explodeSlices'); this.showDonut = this.getPropFromPropertyPages('showDonut'); this.gradient = this.getPropFromPropertyPages('gradient'); this.showLegend = this.getPropFromPropertyPages('showLegend'); this.showLabels = this.getPropFromPropertyPages('showLabels'); } public run() { this.clearChartData(); this.initializeRunState(true); this.updateData(null); this.saveState(this.RUN_STATE); } clearChartData() { this.data = []; } public stop() { this.stopWithoutStateSave(); this.saveState(this.STOP_STATE); } /** * The state is being saved to allow the board to load with the last state. Also, when the gadget is moved * within the board we need to carry the gadget's state along. * @param state */ public saveState(state: string) { this.updateProperties('{\"state\":\"' + state + '\"}'); this.persistTheChangeInInternalState(); } /** * When the gadget is destroyed (see ngOnDestroy) there is no need to * save the state. We just want to stop any API calls. */ public stopWithoutStateSave() { if (this.subscription) { this.subscription.unsubscribe(); } const data = []; Object.assign(this, {data}); this.setStopState(false); } public updateData(someData: any[]) { this.data.length = 0; /** * poll every 15 seconds * todo - change this to a websocket */ this.subscription = interval(this.POLL_INTERVAL).pipe( startWith(0), switchMap(() => this._pieChartService.getData(this.endpointObject.address))) .subscribe(data => { Object.assign(this, {data}); }, error => this.handleError(error)); } public drillDown(data) { // this._route.navigate(['/detail'], {}); } private setInternalProperties(updatedPropsObject: any) { this.state = updatedPropsObject.state; if (updatedPropsObject.title != undefined) { this.title = updatedPropsObject.title; this.explodeSlices = updatedPropsObject.explodeSlices; this.showDonut = updatedPropsObject.showDonut; this.gradient = updatedPropsObject.gradient; this.showLegend = updatedPropsObject.showLegend; this.showLabels = updatedPropsObject.showLabels; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } /** * todo * This is called from the dynamic property page form or when the internal running state changes * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ public updateProperties(updatedProperties: any) { const updatedPropsObject = JSON.parse(updatedProperties); /** * update this tools property pages */ this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); /** * update the tools internal state */ this.setInternalProperties(updatedPropsObject); } public ngOnDestroy() { this.stopWithoutStateSave(); } /** * todo - need to improve how internal state is saved to persistant store */ private persistTheChangeInInternalState() { let payLoad = "{\"instanceId\":" + this.instanceId + ",\"title\":\"" + this.title + "\",\"state\":\"" + this.state + "\",\"endpoint\":\"" + this.endpointObject.name + "\",\"explodeSlices\":" + this.explodeSlices + ",\"showDonut\":" + this.showDonut + ",\"gradient\":" + this.gradient + ",\"showLegend\":" + this.showLegend + ",\"showLabels\":" + this.showLabels + "}"; this._configService.notifyGadgetOnPropertyChange(payLoad, this.instanceId); } toggleChartProperties() { if(this.globalOptions.displayGadgetOptionsInSideBar == false){ this.toggleConfigMode(); return; } this.chartOptionsSideBar = jQuery(this.chartOptionsSideBarRef.nativeElement); this.chartOptionsSideBar.sidebar('setting', 'transition', 'overlay'); this.chartOptionsSideBar.sidebar('toggle'); } } ================================================ FILE: src/app/gadgets/piechart/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class PieChartService { constructor(private _http: HttpClient) { } getData(endpoint: string) { return this._http.get(endpoint) .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/piechart/view.html ================================================
================================================ FILE: src/app/gadgets/port-connection/port-connection-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, OnDestroy} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {GadgetBase} from '../_common/gadget-base'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {ConnectionService} from './service'; import {EndPointModel} from "./service.model"; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class PortConnectionGadgetComponent extends GadgetBase implements OnDestroy { host: string; port: string; endPoints: Array = []; testResultData: Array = []; topic: any; constructor(protected _procMonRuntimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, private _changeDetectionRef: ChangeDetectorRef, protected _connectionService: ConnectionService, protected _optionsService: OptionsService) { super(_procMonRuntimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.port = this.getPropFromPropertyPages('port'); this.host = this.getPropFromPropertyPages('host'); this.setTopic(); } public run() { /** todo - add a one second delay to give the appearance * of something happening when there are two subsequent tests that * have the same result */ this.setUpEndPoints(); this.initializeRunState(false); this.testConnection(); this.inRun = true; } public stop() { this.setStopState(false); } public testConnection() { const me = this; /** * todo:refactor - the requests that include multiple ports are submitted in a loop to avoid requests who's host will * fail due to a timeout. If there are multiple endpoints being tested at the same time * an accumulation of timeouts will exceed the 30 second connection response window that when hit * will result in a 503 from the server. */ this.endPoints.forEach(function (endpoint) { let endpointInstance = []; endpointInstance.push(endpoint); /** * todon- consider adding a bit of delay between requests */ me._connectionService.testConnectivity(endpointInstance).subscribe( data => { me.testResultData.push(...data); me.stop(); }, error => me.handleError(error)); }); } private clearState() { this.testResultData.length = 0; this.endPoints.length = 0; } public updateData(data: any[]) { } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.port = updatedPropsObject.port; this.host = updatedPropsObject.host; this.title = updatedPropsObject.title; this.showOperationControls = true; } public setUpEndPoints() { this.clearState(); let ports: Array; ports = this.port.split(","); const me = this; ports.forEach(function (port) { me.endPoints.push(new EndPointModel(me.host.trim(), port.trim())); }); } public setTopic() { this.topic = { "concept": { "id": "c_network_test_tool", "xml:lang": "en-us", "title": "Network Test Connectivity Tool", "shortdesc": "The Network Test Connectivity tool tests host addresses and ports to determine if a connection can be established. If a connection can be established, the tool returns a green checkmark. If a connection cannot be established, the raw error condition displays, along with possible causes and actions.", "conbody": { "p": [ "The Network Test Connectivity tool can provide guidance for the following error conditions:", { "ul": { "id": "ul_k5c_qdl_r2b", "li": [ "time out", "connection refused", "unknown host", "no route", "unreachable host" ] } } ] } } } } ngOnDestroy() { } } ================================================ FILE: src/app/gadgets/port-connection/port-connection.css ================================================ .input-width{ width:45% } ================================================ FILE: src/app/gadgets/port-connection/result-view.component.ts ================================================ import {Component, Input} from "@angular/core"; import {animate, style, transition, trigger} from "@angular/animations"; @Component({ selector: 'connection-result-view-component', moduleId: module.id, templateUrl: './result-view.html', styleUrls: ['../_common/styles-gadget.css'], animations: [ trigger( 'fade', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(1000, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(300, style({opacity: 0})) ]) ]) ] }) export class ResultViewComponent{ @Input() connectionResult: any; detailMessageOpen = false; public toggleMessageDetail(): void { this.detailMessageOpen = !this.detailMessageOpen; } } ================================================ FILE: src/app/gadgets/port-connection/result-view.html ================================================
Host Port Description Status
{{connectionResult.host}} {{connectionResult.port}} {{connectionResult.portDescription}}
{{connectionResult.exception}}
Possible causes Actions you can take
{{solution.cause}}
  • {{action.task}}
================================================ FILE: src/app/gadgets/port-connection/service.model.ts ================================================ export class EndPointModel { host: string; port: string; constructor(host: string, port: string) { this.host = host; this.port = port; } } ================================================ FILE: src/app/gadgets/port-connection/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient, HttpHeaders} from '@angular/common/http'; import {EndPointModel} from "./service.model"; import {environment} from "../../../environments/environment"; import {catchError} from "rxjs/operators"; @Injectable() export class ConnectionService { connectivityTestURL: string; constructor(private _http: HttpClient) { this.configure(); } /** * TODO - the connectivityTestURL should be based on the Endpoint object that currently * comes from the board configuration. However, in order for this to be more modular * the Endpoint object should be managed by the gadget config form. The URL, when run in production mode, is * currently assumes to be the microservice jar file that runs the framework. The GUI should allow * the user to define whether the URL is internal or remote. */ configure() { if (environment.production) { /** * todo - consider setting this value if a user checks and option in the gadget's form * that indicate the user elects to use the interal api call. */ this.connectivityTestURL = '/connectTest'; } else { /** * todo - consider sett this from the gadget form */ this.connectivityTestURL = 'http://localhost:8080/connectTest'; } } testConnectivity(endPoints: Array) { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Accept':'application/json' }) }; return this._http.post>(this.connectivityTestURL, endPoints, httpOptions) .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/port-connection/solution-view.component.ts ================================================ import {Component, Input} from "@angular/core"; @Component({ selector: 'solution-view-component', moduleId: module.id, templateUrl: './solution-view.html', styleUrls: ['./port-connection.css'] }) export class SolutionViewComponent { } ================================================ FILE: src/app/gadgets/port-connection/solution-view.html ================================================
add
================================================ FILE: src/app/gadgets/port-connection/view.html ================================================
>
Testing Host & Port(s)

{{host}} {{port}}




================================================ FILE: src/app/gadgets/property-list/property-list-gadget.component.ts ================================================ import {ChangeDetectorRef, Component, OnDestroy} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {GadgetBase} from '../_common/gadget-base'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class PropertyListGadgetComponent extends GadgetBase implements OnDestroy { gadgetHasOperationControls = false; showConfigurationControl = false; constructor(protected _procMonRuntimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, private _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { super(_procMonRuntimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { } public run() { this.initializeRunState(true); } public stop() { this.setStopState(false); } public updateData(data: any[]) { } public updateProperties(updatedProperties: any) { this.showOperationControls = true; } ngOnDestroy() { } } ================================================ FILE: src/app/gadgets/property-list/view.html ================================================
================================================ FILE: src/app/gadgets/service-list/service-list-gadget.component.ts ================================================ import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { style, trigger, animate, transition } from '@angular/animations'; import {RuntimeService} from '../../services/runtime.service'; import {serviceList} from './service-list'; import {GadgetInstanceService} from '../../grid/grid.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'], animations: [ trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(1000, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(1000, style({opacity: 0})) ]) ])] }) export class ServiceListGadgetComponent extends GadgetBase implements OnDestroy { // todo just realy on json serviceList: { active: boolean, applicationName: string, description: string, icon: string, pseudoName: string, processId: string }[] = []; constructor(protected _procMonRuntimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, private _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { super(_procMonRuntimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); this.run(); } public preRun(): void { } public run() { this.initializeRunState( true); Object.assign(this, {serviceList}); } public stop() { this.setStopState(false); } public updateData(data: any[]) { } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } ================================================ FILE: src/app/gadgets/service-list/service-list.ts ================================================ /** * Created by jayhamilton on 1/28/17. */ export const serviceList: { active: boolean, applicationName: string, description: string, icon: string, pseudoName: string, processId: string}[] = [ { active: false, applicationName: 'Application Container', description: 'This is a modular application container.', icon: 'images/donut.png', pseudoName: 'Virgo', processId: '1' }, { active: false, applicationName: 'Microservice Discover Server', description: 'This is a microservice discovery service.', icon: 'images/donut.png', pseudoName: 'Eureka', processId: '2' }, { active: false, applicationName: 'Relational Database Management System', description: 'This is a database server.', icon: 'images/donut.png', pseudoName: 'Postgres', processId: '3' }, { active: false, applicationName: 'NO Sql Database Server', description: 'This is a no sql database server.', icon: 'images/donut.png', pseudoName: 'Mongo', processId: '4' } ]; ================================================ FILE: src/app/gadgets/service-list/view.html ================================================ ================================================ FILE: src/app/gadgets/statistic/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class StatisticService { constructor(private _http: HttpClient) { } get(resourceType) { return this._http.get('/assets/api/stat-' + resourceType + '-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/statistic/statistic-gadget.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {GadgetBase} from '../_common/gadget-base'; import {StatisticService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class StatisticGadgetComponent extends GadgetBase { gadgetHasOperationControls = false; // runtime document subscription data: any; resource: string; constructor(protected _statisticService: StatisticService, protected _procMonRuntimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { super(_procMonRuntimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); this.run(); } public preRun(): void { this.resource = this.getPropFromPropertyPages('resource'); this.updateData(null); } public run() { this.data = []; this.initializeRunState(true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._statisticService.get(this.resource).subscribe(data => { this.data = data; }, error => this.handleError(error)); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.resource = updatedPropsObject.resource; this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.updateData(null); } } ================================================ FILE: src/app/gadgets/statistic/view.html ================================================
{{data.total}}  
{{data.name}}
================================================ FILE: src/app/gadgets/storage-object-list/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class StorageService { constructor(private _http: HttpClient) { } get() { return this._http.get('/assets/api/storage-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/storage-object-list/storage-object-list.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {GadgetBase} from '../_common/gadget-base'; import {StorageService} from './service'; import {animate, style, transition, trigger} from '@angular/animations'; import {Facet} from '../../facet/facet-model'; import {FacetTagProcessor} from '../../facet/facet-tag-processor'; import {OptionsService} from "../../configuration/tab-options/service"; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./style.css'], animations: [ trigger( 'showHideAnimation', [ transition(':enter', [ // :enter is alias to 'void => *' style({opacity: 0}), animate(750, style({opacity: 1})) ]), transition(':leave', [ // :leave is alias to '* => void' animate(750, style({opacity: 0})) ]) ]) ] }) export class StorageObjectListComponent extends GadgetBase { // runtime document subscription news: any; resource: string; objectList: any[] = []; facetTags: Array; objectTitleList: string[] = []; placeHolderText = 'Enter volume search string'; layoutColumnOneWidth = 'four'; layoutColumnTwoWidth = 'twelve'; gadgetHasOperationControls = false; constructor(protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _storageService: StorageService, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.updateData(null); this.run(); } public run() { this.news = []; this.initializeRunState( true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._storageService.get().subscribe(item => { item['volumes'].forEach(_data => { this.objectList.push(_data); this.objectTitleList.push(_data.name); }); const facetTagProcess = new FacetTagProcessor(this.objectList); this.facetTags = facetTagProcess.getFacetTags(); }, error => this.handleError(error)); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.updateData(null); } actionHandler(actionItem, actionName) { } } ================================================ FILE: src/app/gadgets/storage-object-list/style.css ================================================ .storage{ overflow-y:hidden; overflow-x:hidden; padding-right:5px; padding-left:5px; height:100%; min-height: 275px; text-align: left !important; background-color:#f7f7f7 } .chartLabel{ font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif; background-color: white; font-size: 1em; font-weight: normal; color: grey; text-align: center; } .ui.statistic>.label, .ui.statistics .statistic>.label{ font-weight: normal; text-transform: none; } ================================================ FILE: src/app/gadgets/storage-object-list/view.html ================================================
VOLUME
{{item.volumeId}}

Used Storage
   {{item.snapshotCount}}
Snapshots
   {{item.vmCount}}
Virtual Machines
   {{item.fileCount}}
Files
================================================ FILE: src/app/gadgets/todo/service.ts ================================================ /** * Created by jayhamilton on 6/24/17. Todo Service */ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class TodoService { constructor(private _http: HttpClient) { } get() { return this._http.get('/assets/api/todo-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/todo/todo-gadget.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetInstanceService} from '../../grid/grid.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {GadgetBase} from '../_common/gadget-base'; import {TodoService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; // todo component @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class TodoGadgetComponent extends GadgetBase { gadgetHasOperationControls = false; // runtime document subscription data: any; todo: string; todoList = ['todo 1']; constructor(protected _todoService: TodoService, protected _procMonRuntimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { super(_procMonRuntimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.run(); } public run() { this.data = []; this.initializeRunState(true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._todoService.get().subscribe(_data => { this.data = _data; }, error => this.handleError(error)); } public addTodo(todo: string) { this.todoList.push(todo); } public removeTodo(todoIx: number) { if (this.todoList.length) { this.todoList.splice(todoIx, 1); } } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.setEndPoint(updatedPropsObject.endpoint); this.updateData(null); } } ================================================ FILE: src/app/gadgets/todo/view.html ================================================
This is a very simple todo gadget!
Todo service mock data: {{data.value}}
# Item
{{i + 1}} {{todo}} clear
add
================================================ FILE: src/app/gadgets/trend/service.ts ================================================ import {Injectable} from '@angular/core'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class TrendService { constructor(private _http: HttpClient) { } get() { return this._http.get('/assets/api/trend-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/trend/trend-gadget.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {TrendService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; declare var d3: any; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class TrendGadgetComponent extends GadgetBase { // chart options showXAxis = true; showYAxis = true; gradient = true; showLegend = true; showXAxisLabel = true; showYAxisLabel = true; yAxisLabel = 'Trend Data'; xAxisLabel = 'Trend Time Line'; view: any[]; data: any[] = []; colorScheme: any = { domain: ['#7B7E81', '#0AFF16', '#FAFF16'] }; d3 = d3; constructor(protected _trendService: TrendService, protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.run(); } public run() { this.data = []; this.initializeRunState( true); this.updateData(null); } public stop() { this.setStopState(false); } public updateData(data: any[]) { this._trendService.get().subscribe(res => { this.data = res['data']; }, error => this.handleError(error)); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.showXAxis = updatedPropsObject.chart_properties; this.showYAxis = updatedPropsObject.chart_properties; this.gradient = updatedPropsObject.chart_properties; this.showLegend = updatedPropsObject.chart_properties; this.showXAxisLabel = updatedPropsObject.chart_properties; this.showYAxisLabel = updatedPropsObject.chart_properties; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; } } ================================================ FILE: src/app/gadgets/trend/view.html ================================================ ================================================ FILE: src/app/gadgets/trend-line/service.ts ================================================ import {Injectable} from '@angular/core'; import {Observable, timer} from 'rxjs'; import {RuntimeService} from '../../services/runtime.service'; import {HttpClient} from '@angular/common/http'; import {catchError} from "rxjs/operators"; @Injectable() export class TrendLineService { static seedData() { const array = []; for (let i = 0; i < 25; i++) { array.push({ 'name': i.toString(), 'value': 0 }); } return array; } static retrieveData() { const currentDate = new Date(); const time = TrendLineService.getDay( currentDate.getDay()) + ':' + currentDate.getHours() + ':' + currentDate.getMinutes() + ':' + currentDate.getSeconds(); return { 'name': time, 'value': TrendLineService.getRandomArbitrary(5, 20) }; } static getRandomArbitrary(min, max) { return Math.round(Math.random() * (max - min) + min); } static getDay(dayOfWeek: number) { switch (dayOfWeek) { case 0: return 'sun'; case 1: return 'mon'; case 2: return 'tue'; case 3: return 'wed'; case 4: return 'thur'; case 5: return 'fri'; case 6: return 'sat'; } } constructor(private _http: HttpClient) { } public get(collectors: any[]) { return new Observable(observer => { timer(500, 5000).subscribe(t => { const data = []; collectors.forEach(collector => { data.push(TrendLineService.retrieveData()); }); observer.next(data); }); }); } public stop(subscription: any) { subscription.unsubscribe(); } getHelpTopic() { return this._http.get('/assets/api/trendline-help-model.json') .pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/gadgets/trend-line/trend-line-gadget.component.ts ================================================ import {ChangeDetectorRef, Component} from '@angular/core'; import {GadgetInstanceService} from '../../grid/grid.service'; import {RuntimeService} from '../../services/runtime.service'; import {GadgetPropertyService} from '../_common/gadget-property.service'; import {EndPointService} from '../../configuration/tab-endpoint/endpoint.service'; import {GadgetBase} from '../_common/gadget-base'; import {TrendLineService} from './service'; import {OptionsService} from "../../configuration/tab-options/service"; declare var d3: any; @Component({ selector: 'app-dynamic-component', moduleId: module.id, templateUrl: './view.html', styleUrls: ['../_common/styles-gadget.css'] }) export class TrendLineGadgetComponent extends GadgetBase { topic: any; // chart options showXAxis = true; showYAxis = true; gradient = true; showLegend = true; showXAxisLabel = true; showYAxisLabel = true; yAxisLabel = 'IOPS'; xAxisLabel = 'Time'; autoScale = true; view: any[]; colorScheme: any = { domain: ['#2185D0', '#0AFF16'] }; d3 = d3; multi: any[] = []; collectors: Array = []; eventTimerSubscription: any; constructor(protected _trendLineService: TrendLineService, protected _runtimeService: RuntimeService, protected _gadgetInstanceService: GadgetInstanceService, protected _propertyService: GadgetPropertyService, protected _endPointService: EndPointService, protected _changeDetectionRef: ChangeDetectorRef, protected _optionsService: OptionsService) { super(_runtimeService, _gadgetInstanceService, _propertyService, _endPointService, _changeDetectionRef, _optionsService); } public preRun(): void { this.setHelpTopic(); /** * todo - get collectors from property page data * @type {[string,string]} */ this.collectors = ['read', 'write']; for (let y = 0; y < this.collectors.length; y++) { this.multi[y] = { 'name': this.collectors[y], 'series': TrendLineService.seedData() }; } } public run() { this.initializeRunState(true); this.updateData(); } public stop() { this.setStopState(false); this._trendLineService.stop(this.eventTimerSubscription); } public updateData() { this.eventTimerSubscription = this._trendLineService.get(this.collectors).subscribe(data => { for (let x = 0; x < this.collectors.length; x++) { this.multi[x].series.shift(); this.multi[x].series.push(data[x]); } this.multi = [...this.multi]; }, error => this.handleError(error)); } public updateProperties(updatedProperties: any) { /** * todo * A similar operation exists on the procmman-config-service * whenever the property page form is saved, the in memory board model * is updated as well as the gadget instance properties * which is what the code below does. This can be eliminated with code added to the * config service or the property page service. * * **/ const updatedPropsObject = JSON.parse(updatedProperties); this.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); this.title = updatedPropsObject.title; this.showXAxis = updatedPropsObject.chart_properties; this.showYAxis = updatedPropsObject.chart_properties; this.gradient = updatedPropsObject.chart_properties; this.showLegend = updatedPropsObject.chart_properties; this.showXAxisLabel = updatedPropsObject.chart_properties; this.showYAxisLabel = updatedPropsObject.chart_properties; this.setEndPoint(updatedPropsObject.endpoint); this.showOperationControls = true; /** * todo - adjust collectors from property page data * @type {[string,string]} */ } private setHelpTopic() { this._trendLineService.getHelpTopic().subscribe(data => { this.topic = data; }); } } ================================================ FILE: src/app/gadgets/trend-line/view.html ================================================ ================================================ FILE: src/app/grid/cell.component.ts ================================================ import {Component, Input, ViewContainerRef, OnInit, ComponentFactoryResolver} from '@angular/core'; import {GadgetInstanceService} from './grid.service'; import {GadgetFactory} from '../add-gadget/gadget-factory'; /* this class handles the dynamic creation of components */ @Component({ selector: 'app-grid-cell', template: '' }) export class CellComponent implements OnInit { @Input() gadgetType: string; @Input() gadgetConfig: any; @Input() gadgetInstanceId: number; @Input() gadgetTags: Array; constructor(private viewContainerRef: ViewContainerRef, private cfr: ComponentFactoryResolver, private gadgetInstanceService: GadgetInstanceService) { } ngOnInit() { /* create component instance dynamically */ const component: any = GadgetFactory.getComponentType(this.gadgetType); let compFactory: any = {}; let gadgetRef: any = {}; if (component) { compFactory = this.cfr.resolveComponentFactory(component); gadgetRef = this.viewContainerRef.createComponent(compFactory); /* we need to pass the input parameters (instance id and config) back into the newly created component. */ gadgetRef.instance.configureGadget(this.gadgetInstanceId, this.gadgetConfig, this .gadgetTags); /* add concrete component to service for tracking */ this.gadgetInstanceService.addInstance(gadgetRef); } } } ================================================ FILE: src/app/grid/grid.component.ts ================================================ import {Component, Output, EventEmitter} from '@angular/core'; import {GadgetInstanceService} from './grid.service'; import {ConfigurationService} from '../services/configuration.service'; import {GadgetConfigModel} from '../gadgets/_common/gadget-config-model'; import {AddGadgetService} from '../add-gadget/service'; import {ToastService} from '../toast/toast.service'; import {MenuEventService} from '../menu/menu-service'; @Component({ moduleId: module.id, selector: 'app-grid-component', templateUrl: './grid.html', styleUrls: ['./styles-grid.css'] }) export class GridComponent { @Output() boardUpdateEvent: EventEmitter = new EventEmitter(); model: any = {}; noGadgets = true; dashedStyle: {}; dropZone1: any = null; dropZone2: any = null; dropZone3: any = null; gadgetLibrary: any[] = []; /** todo * Temporary objects for experimenting with AI * @type */ gridInsertionPosition = { x: 0, y: 0 }; /** * Todo - split model and board operations. This class should really focus on an individual board model's operations * within the grid. The board specific operations should be moved to the board component. * @param _gadgetInstanceService * @param _procmonConfigurationService */ constructor(private _gadgetInstanceService: GadgetInstanceService, private _configurationService: ConfigurationService, private _gadgetLibraryService: AddGadgetService, private _toastService: ToastService, private _menuEventService: MenuEventService) { this.removeOldListeners(); this.setupEventListeners(); this.initializeBoard(); this.getGadgetLibrary(); } /** * todo - This is a temporary attempt to avoid emitting events from stale listeners. * Most severe symptom is when you drill down and then change the layout. * Multiple events are triggered per action due to the services not * getting destroyed when coming into the main board from a child route. The end result is multiple gadget instances * appearing. The following code improves the condition but there still are issues with multiple gadgets appearing * when changing the layout. * */ removeOldListeners(){ this._gadgetInstanceService.unSubscribeAll(); this._menuEventService.unSubscribeAll(); } setupEventListeners() { let gadgetRemoveEventSubscriber = this._gadgetInstanceService.listenForInstanceRemovedEventsFromGadgets().subscribe((message: string) => { this.saveBoard('Gadget Removed From Board: ' + message, false); }); let menuEventSubscriber = this._menuEventService.listenForMenuEvents().subscribe((event: IEvent) => { const edata = event['data']; switch (event['name']) { case 'boardChangeLayoutEvent': this.updateBoardLayout(edata); break; case 'boardSelectEvent': this.loadBoard(edata); break; case 'boardCreateEvent': this.createBoard(edata); break; case 'boardEditEvent': this.editBoard(edata); break; case 'boardDeleteEvent': this.deleteBoard(edata); break; case 'boardAddGadgetEvent': this.addGadget(edata); break; case 'boardAIAddGadgetEvent': this.addGadgetUsingArtificialIntelligence(edata); break; } }); this._gadgetInstanceService.addSubscriber(gadgetRemoveEventSubscriber); this._menuEventService.addSubscriber(menuEventSubscriber); } /** * * This is experimental code that deals with AI */ getGadgetLibrary() { this._gadgetLibraryService.getGadgetLibrary().subscribe(data => { this.gadgetLibrary.length = 0; const me = this; data.library.forEach(function (item) { me.gadgetLibrary.push(item); }); }); } getGadgetFromLibrary(gadgetType: string) { let gadgetObject = null; this.gadgetLibrary.forEach(gadget => { if (gadgetType.localeCompare(gadget['componentType']) === 0) { gadgetObject = gadget; } }); return gadgetObject; } addGadgetUsingArtificialIntelligence(aiObject: any) { /** todo * make confidence code configurable */ if (aiObject && aiObject.operation) { switch (aiObject.operation) { case 'get_storage': this.addGadget(this.getGadgetFromLibrary('StorageObjectListComponent')); break; case 'get_cpu': this.addGadget(this.getGadgetFromLibrary('CPUGadgetComponent')); break; } } } /** * This is the end of the experimental AI code. */ updateGadgetPositionInBoard($event, columnNumber, rowNumber, type) { let moveComplete = false; this.getModel().rows.forEach(row => { let colpos = 0; row.columns.forEach(column => { let gadgetpos = 0; if (column.gadgets) { column.gadgets.forEach(_gadget => { if (_gadget.instanceId === $event.dragData && !moveComplete) { const gadget = column.gadgets.splice(gadgetpos, 1); if (!this.getModel().rows[rowNumber].columns[columnNumber].gadgets) { this.getModel().rows[rowNumber].columns[columnNumber].gadgets = []; } this.getModel().rows[rowNumber].columns[columnNumber].gadgets.push(gadget[0]); this.saveBoard('drag drop operation', false); moveComplete = true; } gadgetpos++; }); colpos++; } }); }); } public createBoard(name: string) { this.loadNewBoard(name); } public editBoard(name: string) { } public deleteBoard(name: string) { this._configurationService.deleteBoard(name).subscribe(data => { this.initializeBoard(); }, error => console.error('Deletion error', error), () => console.debug('Board Deletion: ' + name)); } public addGadget(gadget: any) { //console.log("Adding Gadget!!!!!!!@#@##@#@#@#@#@#@@"); const _gadget = Object.assign({}, gadget); _gadget.instanceId = new Date().getTime(); _gadget.config = new GadgetConfigModel(gadget.config); this.setGadgetInsertPosition(); const x = this.gridInsertionPosition.x; const y = this.gridInsertionPosition.y; if (!this.getModel().rows[x].columns[y].gadgets) { this.getModel().rows[x].columns[y].gadgets = []; } this.getModel().rows[x].columns[y].gadgets.push(_gadget); this.saveBoard('Adding Gadget To The Board', false); } public updateBoardLayout(structure) { console.log("IN UPDATE BOARD LAYOUT"); // user selected the currently selected layout if (structure.id === this.getModel().id) { return; } // copy the current board's model const _model = Object.assign({}, this.getModel()); // get just the columns that contain gadgets from all rows const originalColumns: any[] = this.readColumnsFromOriginalModel(_model); // reset the copied model's rows, which include columns _model.rows.length = 0; // copy the contents of the requested structure into the temporary model // we now have a board model we can populate with the original board's gadgets Object.assign(_model.rows, structure.rows); _model.structure = structure.structure; _model.id = structure.id; let originalColumnIndexToStartProcessingFrom = 0; /* For each column from the original board, copy its gadgets to the new structure. The requested layout may have more or less columns than defined by the original layout. So the fillGridStructure method will copy column content into the target. If there are more columns than the target, the fillGridStructure will return the count of remaining columns to be processed and then process those. */ while (originalColumnIndexToStartProcessingFrom < originalColumns.length) { originalColumnIndexToStartProcessingFrom = this.fillGridStructure(_model, originalColumns, originalColumnIndexToStartProcessingFrom); } // This will copy the just processed model and present it to the board this.setModel(_model); // clear temporary object for (const member in _model) { delete _model[member]; } // persist the board change this.saveBoard('Grid Layout Update', false); } private updateGridState() { let gadgetCount = 0; if (this.getModel().rows) { this.getModel().rows.forEach(function (row) { row.columns.forEach(function (column) { if (column.gadgets) { column.gadgets.forEach(function (gadget) { gadgetCount++; }); } }); }); } this.noGadgets = !gadgetCount; this.dashedStyle = { 'border-style': this.noGadgets ? 'dashed' : 'none', 'border-width': this.noGadgets ? '2px' : 'none', 'border-color': this.noGadgets ? 'darkgray' : 'none', 'padding': this.noGadgets ? '5px' : 'none' }; } private readColumnsFromOriginalModel(_model) { const columns = []; _model.rows.forEach(function (row) { row.columns.forEach(function (col) { columns.push(col); }); }); return columns; } private fillGridStructure(destinationModelStructure, originalColumns: any[], counter: number) { const me = this; destinationModelStructure.rows.forEach(function (row) { row.columns.forEach(function (destinationColumn) { if (!destinationColumn.gadgets) { destinationColumn.gadgets = []; } if (originalColumns[counter]) { me.copyGadgets(originalColumns[counter], destinationColumn); counter++; } }); }); return counter; } private copyGadgets(source, target) { if (source.gadgets && source.gadgets.length > 0) { let w = source.gadgets.shift(); while (w) { target.gadgets.push(w); w = source.gadgets.shift(); } } } public enableConfigMode() { this._gadgetInstanceService.enableConfigureMode(); } private initializeBoard() { this._configurationService.getBoards().subscribe(board => { if (board && board instanceof Array && board.length) { const sortedBoard = board.sort(function (a, b) { return a.boardInstanceId - b.boardInstanceId; }); this.loadBoard(sortedBoard[0].title); } else { this.loadDefaultBoard(); } }); } private loadBoard(boardTitle: string) { this.clearGridModelAndGadgetInstanceStructures(); this._configurationService.getBoardByTitle(boardTitle).subscribe(board => { this.setModel(board); this.updateServicesAndGridWithModel(); this.boardUpdateEvent.emit(boardTitle); }, error => { console.error(error); this.loadDefaultBoard(); }); } private loadDefaultBoard() { this.clearGridModelAndGadgetInstanceStructures(); this._configurationService.getDefaultBoard().subscribe(board => { this.setModel(board); this.updateServicesAndGridWithModel(); this.saveBoard('Initialization of a default board', true); }); } private loadNewBoard(name: string) { this.clearGridModelAndGadgetInstanceStructures(); this._configurationService.getDefaultBoard().subscribe(res => { this.setModel(res); this.getModel().title = name; this.getModel().boardInstanceId = new Date().getTime(); this.updateServicesAndGridWithModel(); this.saveBoard('Initialization of a new board', true); }); } private updateServicesAndGridWithModel() { this._gadgetInstanceService.setCurrentModel(this.getModel()); this._configurationService.setCurrentModel(this.getModel()); this.updateGridState(); } private saveBoard(operation: string, alertBoardListenerThatTheMenuShouldBeUpdated: boolean) { this.updateServicesAndGridWithModel(); this._configurationService.saveBoard(this.getModel()).subscribe(result => { this._toastService.sendMessage(this.getModel().title + ' has been updated!', ''); if (alertBoardListenerThatTheMenuShouldBeUpdated) { this._menuEventService.raiseGridEvent({name: 'boardUpdateEvent', data: this.getModel().title}); } }, error => console.error('Error' + error), () => console.debug('Saving configuration to store!')); } private clearGridModelAndGadgetInstanceStructures() { // clear gadgetInstances this._gadgetInstanceService.clearAllInstances(); // clear current model for (const prop in this.getModel()) { if (this.model.hasOwnProperty(prop)) { delete this.model[prop]; } } } private setGadgetInsertPosition() { for (let x = 0; x < this.getModel().rows.length; x++) { for (let y = 0; y < this.getModel().rows[x].columns.length; y++) { if (this.getModel().rows[x].columns[y].gadgets && this.getModel().rows[x].columns[y].gadgets.length === 0) { this.gridInsertionPosition.x = x; this.gridInsertionPosition.y = y; return; } } } // we go here because the board is either empty or full // insert in the top left most cell this.gridInsertionPosition.y = 0; if (this.noGadgets) { // there are no gadgets so insert in top row this.gridInsertionPosition.x = 0; } else { // board is full so insert in the last row this.gridInsertionPosition.x = this.getModel().rows.length - 1; } } public setModel(model: any) { this.model = Object.assign({}, model); } public getModel() { return this.model; } } ================================================ FILE: src/app/grid/grid.html ================================================
Add Gadget!
================================================ FILE: src/app/grid/grid.module.ts ================================================ import {NgModule, ANALYZE_FOR_ENTRY_COMPONENTS} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormsModule} from '@angular/forms'; import {GridComponent} from './grid.component'; import {CellComponent} from './cell.component'; import {GadgetInstanceService} from './grid.service'; import {ConfigurationService} from '../services/configuration.service'; import {AddGadgetService} from '../add-gadget/service'; import {DndModule} from 'ng2-dnd'; import {NewsService} from '../gadgets/news/service'; import {JobAnalysisService} from '../gadgets/job-analysis/service'; import {TrendService} from '../gadgets/trend/service'; import {TrendLineService} from '../gadgets/trend-line/service'; import {EdgeService} from '../gadgets/edge-service-list/service'; import {CPUService} from '../gadgets/cpu/service'; import {HttpClientModule} from '@angular/common/http'; import {DonutService} from '../gadgets/donut/service'; import {TodoService} from '../gadgets/todo/service'; import {ToastModule} from '../toast/toast.module'; import {ConnectionService} from '../gadgets/port-connection/service'; import {BubbleService} from "../gadgets/bubble/service"; import {BarChartService} from "../gadgets/barchart/service"; import {PieChartService} from "../gadgets/piechart/service"; @NgModule({ imports: [ CommonModule, FormsModule, ToastModule, HttpClientModule, DndModule.forRoot() ], declarations: [ GridComponent, CellComponent ], exports: [ GridComponent ], providers: [ GadgetInstanceService, ConfigurationService, AddGadgetService, NewsService, JobAnalysisService, TrendLineService, TrendService, EdgeService, CPUService, DonutService, ConnectionService, TodoService // todo gadget ,BubbleService, BarChartService, PieChartService ] }) export class GridModule { static withComponents(components: any[]) { return { ngModule: GridModule, providers: [ {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: components, multi: true} ] }; } } ================================================ FILE: src/app/grid/grid.service.ts ================================================ /** * Created by jayhamilton on 1/28/17. */ import {Injectable} from '@angular/core'; import {Subject,Observable} from 'rxjs'; /** * todo - the name of this service does not represent the file name. This should be refactored. Consider moving this service to the gadget module instead. */ @Injectable() export class GadgetInstanceService { private concreteGadgetInstances: any[] = []; private model: any; private subject: Subject = new Subject(); private subscribers: Array> = []; constructor() { } addInstance(gadget: any) { let gadgetFound = false; for (let x = 0; x < this.concreteGadgetInstances.length; x++) { if (gadget.instance.instanceId == this.concreteGadgetInstances[x]['instance']['instanceId']) { gadgetFound = true; } } if (gadgetFound == false) { this.concreteGadgetInstances.push(gadget); } } enableConfigureMode() { this.concreteGadgetInstances.forEach(function (gadget) { gadget.instance.toggleConfigMode(); }); } removeInstance(id: number) { console.log("REMOVING GADGET"); // remove instance representation from model this.model.rows.forEach(function (row) { row.columns.forEach(function (column) { if (column.gadgets) { for (let i = column.gadgets.length - 1; i >= 0; i--) { if (column.gadgets[i].instanceId === id) { column.gadgets.splice(i, 1); break; } } } }); }); // removes concrete instance from service for (let x = this.concreteGadgetInstances.length - 1; x >= 0; x--) { if (this.concreteGadgetInstances[x].instance.instanceId === id) { const _gadget = this.concreteGadgetInstances.splice(x, 1); _gadget[0].destroy(); break; } } // raise an event indicating a gadget was removed this.subject.next('gadget id: ' + id); } getInstanceCount() { return this.concreteGadgetInstances.length; } /* this allows this service to update the board when a delete operation occurs */ setCurrentModel(model: any) { this.model = model; } /* raise an event that the grid.component is listening for when a gadget is removed. */ listenForInstanceRemovedEventsFromGadgets(): Observable { return this.subject.asObservable(); } addSubscriber(subscriber: any) { this.subscribers.push(subscriber); } unSubscribeAll() { this.subscribers.forEach(subscription => { subscription.unsubscribe(); }); this.subscribers.length = 0; this.clearAllInstances(); } clearAllInstances() { this.concreteGadgetInstances.length = 0; } } ================================================ FILE: src/app/grid/styles-grid.css ================================================ .ui.info.message { background-color: #C8CBCE; color: #ffffff } .ui.attached.info.message, .ui.info.message { box-shadow: 0 0 0 0 px #c8cbce inset, 0 0 0 0 transparent; } .short-row{ min-height:150px; } .tall-row{ min-height:370px; } ================================================ FILE: src/app/layout/layout-component.ts ================================================ /** * Created by jayhamilton on 1/24/17. */ import { AfterViewInit, Component, Output, EventEmitter, Input } from '@angular/core'; import {boardLayouts} from './model'; /** * Message Modal - clasable modal with message * * Selector message-modal * * Methods * popMessageModal - display a message modal for a sepcified duration * showMessageModal - show the message modal * hideMessageModal - hide the message modal */ @Component({ selector: 'app-board-layout-manager-modal', moduleId: module.id, templateUrl: './view.html', styleUrls: ['./styles.css'] }) export class BoardLayoutManagerComponent implements AfterViewInit { @Input() layoutId; @Output() boardLayoutChangeEvent: EventEmitter = new EventEmitter(); boardLayouts: any[]; modalHeader = 'Layout'; messageModal: any; constructor() { this.initializeLayouts(); } selectBoardLayout(layoutId: number) { for (let x = 0; x < this.boardLayouts.length; x++) { if (this.boardLayouts[x].id === layoutId) { this.boardLayoutChangeEvent.emit(this.boardLayouts[x]); this.layoutId = layoutId; break; } } } ngAfterViewInit() { } initializeLayouts() { Object.assign(this, {boardLayouts}); } } ================================================ FILE: src/app/layout/layout.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {BoardLayoutManagerComponent} from './layout-component'; @NgModule({ imports: [ CommonModule ], declarations: [BoardLayoutManagerComponent], exports: [BoardLayoutManagerComponent] }) export class LayoutModule { } ================================================ FILE: src/app/layout/model.ts ================================================ /** * Created by jayhamilton on 1/31/17. */ export const boardLayouts = [ { id: 0, boardInstanceId: 0, title: 'one-narrow', checked: false, structure: '8', rows: [{ columns: [ { styleClass: 'eight wide', }] }] }, { id: 1, boardInstanceId: 1, title: 'single', checked: false, structure: '14', rows: [{ columns: [ { styleClass: 'fourteen wide', }] }] }, { id: 2, boardId: 2, title: 'narrow-right', checked: false, structure: '10-6', rows: [{ columns: [ { styleClass: 'ten wide', }, { styleClass: 'six wide', }] }] }, { id: 3, boardInstanceId: 3, title: 'wide-center', checked: false, structure: '4-8-4', rows: [{ columns: [ { styleClass: 'four wide', }, { styleClass: 'eight wide', }, { styleClass: 'four wide', }] }] }, { id: 4, boardInstanceId: 4, title: 'narrow-left', checked: false, structure: '4-12', rows: [{ columns: [ { styleClass: 'four wide', }, { styleClass: 'twelve wide', }] }] }, { id: 5, boardInstanceId: 5, title: 'two-even', checked: true, structure: '6-6', rows: [{ columns: [ { styleClass: 'six wide', }, { styleClass: 'six wide', }] }] }, { id: 6, boardInstanceId: 6, title: 'three-even', checked: false, structure: '5-5-5', rows: [{ columns: [ { styleClass: 'four wide', }, { styleClass: 'four wide', }, { styleClass: 'four wide', } ] }] }, { id: 7, boardInstanceId: 7, title: 'wide-top', checked: false, structure: '16/8-8', rows: [ { columns: [ { styleClass: 'twelve wide' } ] }, { columns: [ { styleClass: 'six wide' }, { styleClass: 'six wide' } ] } ] }, { id: 8, boardInstanceId: 8, title: 'ngadmin', checked: false, structure: '4-4-4-4/8-4-4', rows: [ { columns: [ { styleClass: 'four wide' }, { styleClass: 'four wide' }, { styleClass: 'four wide' }, { styleClass: 'four wide' } ] }, { columns: [ { styleClass: 'eight wide' }, { styleClass: 'four wide' }, { styleClass: 'four wide' } ] } ] }, { id: 9, boardInstanceId: 9, title: 'google-layout', checked: false, structure: '3-6-3', rows: [ { columns: [ { styleClass: 'three wide' }, { styleClass: 'six wide' }, { styleClass: 'three wide' } ] } ] } ]; ================================================ FILE: src/app/layout/styles.css ================================================ .layout-selected { border-top: medium lawngreen solid; padding-top:5px; } ul{ text-align: center; } h2{ color:white !important; } ul, menu, dir { -webkit-padding-start: 5px; } ================================================ FILE: src/app/layout/view.html ================================================

{{modalHeader}}




================================================ FILE: src/app/menu/IEvent.ts ================================================ interface IEvent { name: string; data: any; } ================================================ FILE: src/app/menu/menu-service.ts ================================================ import {Injectable} from '@angular/core'; import {Subject, Observable} from 'rxjs'; /** The grid is primarily controlled by MenuService Events. You can change the behavior by changing the MenuComponent and have it call methods within the Grid component via Output Events within the Menu component or by using the @ViewChild approach. In the current code, the MenuService is shared by the GridComponent and MenuComponent. The MenuComponent relays events from the various components that make up the MenuComponent. The GridComponent listens for those events via an Observable. The GridComponent will also raise/emit an event that will be picked up by the MenuComponent via the MenuService through an Observable as well. */ @Injectable() export class MenuEventService { private menuSubject: Subject = new Subject(); private gridSubject: Subject = new Subject(); private subscribers: Array> =[]; constructor() { } raiseMenuEvent(event: IEvent) { this.menuSubject.next(event); } listenForMenuEvents(): Observable { return this.menuSubject.asObservable(); } raiseGridEvent(event: IEvent) { this.menuSubject.next(event); } listenForGridEvents(): Observable { return this.gridSubject.asObservable(); } addSubscriber(subscriber:any){ this.subscribers.push(subscriber); } unSubscribeAll(){ this.subscribers.forEach(subscription=>{ subscription.unsubscribe(); }); this.subscribers.length = 0; } } ================================================ FILE: src/app/menu/menu.component.ts ================================================ import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import {ConfigurationService} from '../services/configuration.service'; import {MenuEventService} from './menu-service'; import {environment} from '../../environments/environment'; declare var jQuery: any; /**a * Menu component * */ @Component({ moduleId: module.id, selector: 'app-menu-component', templateUrl: './view.html', styleUrls: ['./styles.css'], }) export class MenuComponent implements OnInit { host = window.location.host; dashboardList: any[] = []; selectedBoard = ''; placeHolderText = 'Ask the board to do something!'; searchList: Array = []; env: any; @ViewChild('notificationSideBar_tag') notificationSideBarRef: ElementRef; @ViewChild('layoutSideBar_tag') layoutSideBarRef: ElementRef; @ViewChild('aboutSideBar_tag') aboutSideBarRef: ElementRef; @ViewChild('stickymenu_tag') stickyMenuRef: ElementRef; notificationSideBar: any; layoutSideBar: any; aboutSideBar:any; stickyMenu: any; typeAheadIsInMenu = true; layoutId = 0; constructor(private _configurationService: ConfigurationService, private _menuEventService: MenuEventService) { this._menuEventService.unSubscribeAll(); this.setupEventListeners(); this.env = environment; } setupEventListeners() { let gridEventSubscription = this._menuEventService.listenForGridEvents().subscribe((event: IEvent) => { const edata = event['data']; switch (event['name']) { case 'boardUpdateEvent': this.updateDashboardMenu(edata); break; } }); this._menuEventService.addSubscriber(gridEventSubscription); } ngOnInit() { this.updateDashboardMenu(''); this.stickyMenu = jQuery(this.stickyMenuRef.nativeElement); this.stickyMenu.sticky(); } emitBoardChangeLayoutEvent(event) { this._menuEventService.raiseMenuEvent({name: 'boardChangeLayoutEvent', data: event}); } emitBoardSelectEvent(event) { this.boardSelect(event); this._menuEventService.raiseMenuEvent({name: 'boardSelectEvent', data: event}); } emitBoardCreateEvent(event) { this._menuEventService.raiseMenuEvent({name: 'boardCreateEvent', data: event}); this.updateDashboardMenu(event); } emitBoardEditEvent(event) { this._menuEventService.raiseMenuEvent({name: 'boardEditEvent', data: event}); } emitBoardDeleteEvent(event) { this._menuEventService.raiseMenuEvent({name: 'boardDeleteEvent', data: event}); this.updateDashboardMenu(''); } emitBoardAddGadgetEvent(event) { this._menuEventService.raiseMenuEvent({name: 'boardAddGadgetEvent', data: event}); } emitBoardAIAddGadgetEvent(event) { this._menuEventService.raiseMenuEvent({name: 'boardAIAddGadgetEvent', data: event}); } updateDashboardMenu(selectedBoard: string) { this._configurationService.getBoards().subscribe(data => { const me = this; if (data && data instanceof Array && data.length) { this.dashboardList.length = 0; // sort boards data.sort((a: any, b: any) => a.boardInstanceId - b.boardInstanceId); data.forEach(board => { me.dashboardList.push(board.title); }); if (selectedBoard === '') { this.boardSelect(this.dashboardList[0]); } else { this.boardSelect(selectedBoard); } } }); } boardSelect(selectedBoard: string) { this.selectedBoard = selectedBoard; } toggleLayoutSideBar() { this.layoutSideBar = jQuery(this.layoutSideBarRef.nativeElement); this.layoutSideBar.sidebar('setting', 'transition', 'overlay'); this.layoutSideBar.sidebar('toggle'); this.layoutId = this._configurationService.currentModel.id; } toggleNotificationSideBar() { this.notificationSideBar = jQuery(this.notificationSideBarRef.nativeElement); this.notificationSideBar.sidebar('setting', 'transition', 'overlay'); this.notificationSideBar.sidebar('toggle'); } toggleAboutSideBar() { this.aboutSideBar = jQuery(this.aboutSideBarRef.nativeElement); this.aboutSideBar.sidebar('setting', 'transition', 'overlay'); this.aboutSideBar.sidebar('toggle'); } public showDocumentation() { window.location.href = 'http://' + window.location.host + '/assets/documentation/index.html'; } } ================================================ FILE: src/app/menu/menu.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {MatButtonModule, MatIconModule} from '@angular/material'; import {DndModule} from 'ng2-dnd'; import {GadgetModule} from '../gadgets/gadget.module'; import {ConfigurationModule} from '../configuration/configuration.module'; import {LayoutModule} from '../layout/layout.module'; import {AddGadgetModule} from '../add-gadget/add-gadget.module'; import {NotificationModule} from '../notification/notification.module'; import {GadgetPropertyService} from '../gadgets/_common/gadget-property.service'; import {ConfigurationService} from '../services/configuration.service'; import {RuntimeService} from '../services/runtime.service'; import {EndPointService} from '../configuration/tab-endpoint/endpoint.service'; import {ObservableWebSocketService} from '../services/websocket-service'; import {TypeAheadInputModule} from '../typeahead-input/typeahead-input.module'; import {MenuComponent} from './menu.component'; import {MenuEventService} from './menu-service'; import {AboutModule} from "../about/about.module"; @NgModule({ imports: [ CommonModule, NotificationModule, AddGadgetModule, LayoutModule, AboutModule, ConfigurationModule, TypeAheadInputModule, GadgetModule, DndModule.forRoot(), MatButtonModule, MatIconModule, ], providers: [EndPointService, RuntimeService, ConfigurationService, GadgetPropertyService, ObservableWebSocketService, MenuEventService ], declarations: [ MenuComponent ], exports: [ MenuComponent ] }) export class MenuModule { } ================================================ FILE: src/app/menu/styles.css ================================================ .ui.secondary.pointing.menu .active.item { color: #f9f9f9 !important; font-weight: 500 !important; border-color: #00ff00 !important; border-width: medium !important; font-family: 'Roboto', sans-serif !important; font-size: 1.2em; } .ui.secondary.pointing.menu .item { color: rgba(128, 128, 128, 0.7) !important; font-weight: 500 !important; font-family: 'Roboto', sans-serif !important; font-size: 1.2em; } .ui.secondary.pointing.menu { border-bottom: none !important; } input { padding: 5px; font-weight: 300 !important; font-family: 'Roboto', sans-serif !important; font-size: 1.1em; outline: none !important; } .ui.floating.label{ top:.5em; margin:0; } .ui.menu .item>.floating.label{ padding:.3em .6em } .ui.menu{ border-radius:0px; } .ui.inverted.menu{ background-color: #1C2B36; } .ui.menu .item>i.icon { margin: 0; } ================================================ FILE: src/app/menu/view.html ================================================
================================================ FILE: src/app/notification/notification-component.css ================================================ .components{ border-bottom: solid !important; border-bottom-color: rgba(128, 128, 128, 0.69) !important; border-bottom-width: thin !important; } td.selected { background-color: #4fa6d3 !important; color: white; font-weight: lighter; } td{ line-height: 1.7; } .ui.table tr td{ border:none; } .ui.floating.label{ } tr { cursor: pointer; } ================================================ FILE: src/app/notification/notification-component.html ================================================

{{modalHeader}}

{{item.name}}
{{item.count}}
================================================ FILE: src/app/notification/notification-component.spec.ts ================================================ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NotificationComponent } from './notification-component'; describe('NotificationComponent', () => { let component: NotificationComponent; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ NotificationComponent ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(NotificationComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); }); ================================================ FILE: src/app/notification/notification-component.ts ================================================ import {AfterViewInit, Component, OnInit} from '@angular/core'; @Component({ moduleId: module.id, selector: 'app-notification-modal', templateUrl: './notification-component.html', styleUrls: ['./notification-component.css'] }) export class NotificationComponent implements OnInit, AfterViewInit { notificationFilters: any [] = []; currentNotificationFilter = { name: '', count: 0 }; notificationModal: any; modalHeader = 'Notifications'; constructor() { this.notificationFilters.push({name: 'Unacknowledged', count: 4}); this.notificationFilters.push({name: 'All Notifications', count: 4}); this.notificationFilters.push({name: 'Trend Gadget', count: 4}); this.notificationFilters.push({name: 'CPU Gadget', count: 0}); this.setSelectedNotificationFilter({name: 'Unacknowledged', count: 4}); } ngOnInit() { } ngAfterViewInit() { } setSelectedNotificationFilter(item) { this.currentNotificationFilter = item; } } ================================================ FILE: src/app/notification/notification-service.spec.ts ================================================ import { TestBed, inject } from '@angular/core/testing'; import { NotificationService } from './notification-service'; describe('NotificationService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [NotificationService] }); }); it('should ...', inject([NotificationService], (service: NotificationService) => { expect(service).toBeTruthy(); })); }); ================================================ FILE: src/app/notification/notification-service.ts ================================================ import { Injectable } from '@angular/core'; @Injectable() export class NotificationService { constructor() { } } ================================================ FILE: src/app/notification/notification.model.ts ================================================ export class Notification { public id: number; public name: string; public description: string; public state: string; public icon: string; public when: string; constructor(name: string, description: string) { this.name = name; this.description = description; } setState(state: string) { this.state = state; } getState() { return this.state; } setIcon(icon: string) { this.icon = icon; } getIcon() { return this.icon; } setWhen(when: string) { this.when = when; } getWhen() { return this.when; } } ================================================ FILE: src/app/notification/notification.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {NotificationComponent} from './notification-component'; import {NotificationService} from './notification-service'; import {NotificationDetailComponent} from './notificationDetail.component'; @NgModule({ imports: [ CommonModule ], declarations: [NotificationComponent, NotificationDetailComponent], providers: [NotificationService], exports: [NotificationComponent] }) export class NotificationModule { } ================================================ FILE: src/app/notification/notificationDetail.component.ts ================================================ /** * Created by jayhamilton on 5/16/17. */ import {Component} from '@angular/core'; import {Notification} from './notification.model'; @Component({ selector: 'app-notification-detail', moduleId: module.id, templateUrl: './notificationDetail.html' }) export class NotificationDetailComponent { notifications: Notification[]; constructor() { this.notifications = []; let notification = new Notification('CPU Gadget', 'this is a cpu gadget error'); notification.setWhen((new Date()).toString()); this.notifications.push(notification); notification = new Notification ('Trend Gadget', 'this is a trend gadget error2'); notification.setWhen((new Date()).toString()); this.notifications.push(notification); notification = new Notification ('Disk Gadget', 'Disk 1 alert'); notification.setWhen((new Date()).toString()); this.notifications.push(notification); notification = new Notification ('Disk Gadget', 'Disk 2 alert'); notification.setWhen((new Date()).toString()); this.notifications.push(notification); } } ================================================ FILE: src/app/notification/notificationDetail.html ================================================

  Details

{{notification.description}}
{{notification.when}}
================================================ FILE: src/app/routing.module.ts ================================================ import {NgModule} from '@angular/core'; import {Routes, RouterModule} from '@angular/router'; import {AppComponent} from './app.component'; import {BoardComponent} from './board/board.component'; import {DetailComponent} from './detail/detail.component'; export const routes: Routes = [ { path: '', redirectTo: 'main-board', pathMatch: 'full' }, { path: '', component: AppComponent }, { path: 'main-board', component: BoardComponent }, { path: 'detail', component: DetailComponent, runGuardsAndResolvers: 'always' }, ]; @NgModule({ imports: [RouterModule.forRoot(routes,{onSameUrlNavigation: 'reload'})], exports: [RouterModule] }) export class RoutingModule { } ================================================ FILE: src/app/services/configuration-sample-boards-prod.model.ts ================================================ export const sampleBoardCollectionProd = { "board": [{ "title": "Network Test", "structure": "8", "id": 0, "boardInstanceId": 1532575263046, "rows": [{ "columns": [{ "styleClass": "eight wide", "gadgets": [{ "componentType": "PortConnectionGadgetComponent", "name": "Port Connection Test", "description": "Test Connection To Endpoints.", "icon": "assets/images/response.png", "instanceId": 1533516521962, "tags": [{"facet": "Informational", "name": "port-connection"}], "config": { "propertyPages": [{ "displayName": "Run", "groupId": "run", "position": 10, "properties": [{ "value": "Connection Test", "key": "title", "label": "Title", "required": false, "order": 1, "controlType": "textbox" }, { "value": "", "key": "host", "label": "Host", "required": false, "order": 1, "controlType": "textbox" }, { "value": "", "key": "port", "label": "Port(s)", "required": false, "order": 3, "controlType": "dropdown", "options": [{ "key": "vSnap + vAdp (All Ports)", "value": "8900,8080,22,111,20048,2049" }, { "key": "SPP Single Appliance (All Ports)", "value": "443,8900,22,111,20048,2049" }, {"key": "SPP (443)", "value": "443"}, { "key": "SPP (8443)", "value": "8443" }, {"key": "SPP (3000)", "value": "3000"}, { "key": "LDAP (389)", "value": "389" }, {"key": "LDAP (636)", "value": "636"}, { "key": "VADP (8080)", "value": "8080" }, {"key": "WinRM (5985)", "value": "5985"}, { "key": "VSnap (8900)", "value": "8900" }, {"key": "SSH (22)", "value": "22"}, { "key": "ISCSI (3260)", "value": "3260" }, {"key": "NFS (2049)", "value": "2049"}, { "key": "NFS (20048)", "value": "20048" }, {"key": "NFS (111)", "value": "111"}, {"key": "ESXI (902)", "value": "902"}] }, { "value": 2, "key": "instanceId", "label": "", "required": false, "order": -1, "controlType": "hidden" }] }] }, "actions": [{"name": "Add"}] }] }] }] }] }; ================================================ FILE: src/app/services/configuration-sample-boards.model.ts ================================================ export const sampleBoardCollection = { 'board': [{ 'title': 'Board Sample 1', 'structure': '3-6-3', 'id': 9, 'boardInstanceId': 1, 'rows': [ { 'columns': [ { 'styleClass': 'three wide', 'gadgets': [ { 'componentType': 'NewsGadgetComponent', 'name': 'News', 'description': "What's new", 'icon': 'images/news.png', 'instanceId': 1500253814523, 'tags': [ { 'facet': 'Informational', 'name': 'news' }, { 'facet': 'List', 'name': 'news' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'news', 'key': 'endpoint', 'label': 'News URL', 'required': false, 'order': 3, 'controlType': 'dynamicdropdown' }, { 'value': 'News', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] } ] } } ] }, { 'styleClass': 'six wide', 'gadgets': [ { 'componentType': 'CPUGadgetComponent', 'name': 'CPU Chart', 'description': 'Monitors CPU utilization for CDM.', 'icon': 'images/cpu.png', 'instanceId': 1499912922910, 'tags': [ { 'facet': 'Performance', 'name': 'real-time' }, { 'facet': 'Chart', 'name': 'bar' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'CPU Utilization', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 3, 'controlType': 'dynamicdropdown' }, { 'value': 999, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'Chart', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } }, { 'componentType': 'TrendGadgetComponent', 'name': 'Trend', 'description': 'General trends.', 'icon': 'images/trend.png', 'instanceId': 1499912901569, 'tags': [ { 'facet': 'Performance', 'name': 'trend' }, { 'facet': 'Chart', 'name': 'area' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'Devappliance - ECX', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 2, 'controlType': 'dynamicdropdown' }, { 'value': 'Trend', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'Chart', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } } ] }, { 'styleClass': 'three wide', 'gadgets': [] } ] } ] }, { 'title': 'Board Sample 2', 'structure': '4-4-4-4/8-4-4', 'id': 8, 'boardInstanceId': 2, 'rows': [ { 'columns': [ { 'styleClass': 'four wide', 'gadgets': [ { 'componentType': 'StatisticGadgetComponent', 'name': 'Statistic', 'description': 'General statistic.', 'icon': 'images/statistic.png', 'instanceId': 1499912861630, 'tags': [ { 'facet': 'Statistic', 'name': 'counter' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'database', 'key': 'resource', 'label': 'Resource', 'required': false, 'order': 2, 'controlType': 'dropdown', 'options': [ { 'key': 'vm', 'value': 'vm' }, { 'key': 'database', 'value': 'database' }, { 'key': 'volume', 'value': 'volume' }, { 'key': 'job', 'value': 'job' } ] }, { 'value': 'Devappliance - ECX', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 3, 'controlType': 'dynamicdropdown' }, { 'value': 'Statistic', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': true, 'order': -1, 'controlType': 'hidden' } ] } ] } } ] }, { 'styleClass': 'four wide', 'gadgets': [ { 'componentType': 'StatisticGadgetComponent', 'name': 'Statistic', 'description': 'General statistic.', 'icon': 'images/statistic.png', 'instanceId': 1499912845375, 'tags': [ { 'facet': 'Statistic', 'name': 'counter' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'vm', 'key': 'resource', 'label': 'Resource', 'required': false, 'order': 2, 'controlType': 'dropdown', 'options': [ { 'key': 'vm', 'value': 'vm' }, { 'key': 'database', 'value': 'database' }, { 'key': 'volume', 'value': 'volume' }, { 'key': 'job', 'value': 'job' } ] }, { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 3, 'controlType': 'dynamicdropdown' }, { 'value': 'Statistic', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': true, 'order': -1, 'controlType': 'hidden' } ] } ] } } ] }, { 'styleClass': 'four wide', 'gadgets': [ { 'componentType': 'StatisticGadgetComponent', 'name': 'Statistic', 'description': 'General statistic.', 'icon': 'images/statistic.png', 'instanceId': 1500251223835, 'tags': [ { 'facet': 'Statistic', 'name': 'counter' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'volume', 'key': 'resource', 'label': 'Resource', 'required': false, 'order': 2, 'controlType': 'dropdown', 'options': [ { 'key': 'vm', 'value': 'vm' }, { 'key': 'database', 'value': 'database' }, { 'key': 'volume', 'value': 'volume' }, { 'key': 'job', 'value': 'job' } ] }, { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 3, 'controlType': 'dynamicdropdown' }, { 'value': 'Statistic', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': true, 'order': -1, 'controlType': 'hidden' } ] } ] } } ] }, { 'styleClass': 'four wide', 'gadgets': [ { 'componentType': 'StatisticGadgetComponent', 'name': 'Statistic', 'description': 'General statistic.', 'icon': 'images/statistic.png', 'instanceId': 1500251241829, 'tags': [ { 'facet': 'Statistic', 'name': 'counter' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'job', 'key': 'resource', 'label': 'Resource', 'required': false, 'order': 2, 'controlType': 'dropdown', 'options': [ { 'key': 'vm', 'value': 'vm' }, { 'key': 'database', 'value': 'database' }, { 'key': 'volume', 'value': 'volume' }, { 'key': 'job', 'value': 'job' } ] }, { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 3, 'controlType': 'dynamicdropdown' }, { 'value': 'Statistic', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': true, 'order': -1, 'controlType': 'hidden' } ] } ] } } ] } ] }, { 'columns': [ { 'styleClass': 'eight wide', 'gadgets': [ { 'componentType': 'TrendLineGadgetComponent', 'name': 'Realtime Performance', 'description': 'IOPs and Network Bandwidth.', 'icon': 'images/trend-line.png', 'instanceId': 1500251340757, 'tags': [ { 'facet': 'Performance', 'name': 'trend' }, { 'facet': 'Chart', 'name': 'line' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'Devappliance - ECX', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 2, 'controlType': 'dynamicdropdown' }, { 'value': 'Performance', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'Chart', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } }, { 'componentType': 'ServiceListGadgetComponent', 'name': 'Service Status', 'description': 'Monitors Service Status', 'icon': 'images/service.png', 'instanceId': 1500343727872, 'tags': [ { 'facet': 'List', 'name': 'health' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'Devappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 2, 'controlType': 'dynamicdropdown' }, { 'value': 'Service Status', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 999, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'List', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } } ] }, { 'styleClass': 'four wide', 'gadgets': [ { 'componentType': 'DiskGadgetComponent', 'name': 'Disk Utilization', 'description': 'Disk consumption information for ECX.', 'icon': 'images/donut.png', 'instanceId': 1500253741130, 'tags': [ { 'facet': 'Chart', 'name': 'pie' }, { 'facet': 'Performance', 'name': 'storage' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 4, 'controlType': 'dynamicdropdown' }, { 'value': 'Disk Consumption', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 50, 'key': 'threshold', 'label': 'Consumed Threshold', 'required': false, 'order': 3, 'controlType': 'number' }, { 'value': 'g3g3egegre', 'key': 'diskid', 'label': 'Disk Id', 'required': false, 'order': 2, 'controlType': 'dropdown', 'options': [ { 'key': '2344112gdfgdfg', 'value': '2344112gdfgdfg' }, { 'key': 'g3g3egegre', 'value': 'g3g3egegre' }, { 'key': '8435f34', 'value': '8435f34' } ] }, { 'value': 2, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'Thresholds', 'groupId': 'threshold', 'position': 10, 'properties': [ { 'value': 30, 'key': 'frequency', 'label': 'Frequency', 'required': false, 'order': 3, 'controlType': 'number' }, { 'value': 30, 'key': 'retentionH', 'label': 'Retention High', 'required': false, 'order': 4, 'controlType': 'number' }, { 'value': 180, 'key': 'retentionL', 'label': 'Retention Low', 'required': false, 'order': 4, 'controlType': 'number' } ] }, { 'displayName': 'Chart', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } } ] }, { 'styleClass': 'four wide', 'gadgets': [ { 'componentType': 'DiskGadgetComponent', 'name': 'Disk Utilization', 'description': 'Disk consumption information for ECX.', 'icon': 'images/donut.png', 'instanceId': 1500252483066, 'tags': [ { 'facet': 'Chart', 'name': 'pie' }, { 'facet': 'Performance', 'name': 'storage' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 4, 'controlType': 'dynamicdropdown' }, { 'value': 'Disk Consumption', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 50, 'key': 'threshold', 'label': 'Consumed Threshold', 'required': false, 'order': 3, 'controlType': 'number' }, { 'value': 'g3g3egegre', 'key': 'diskid', 'label': 'Disk Id', 'required': false, 'order': 2, 'controlType': 'dropdown', 'options': [ { 'key': '2344112gdfgdfg', 'value': '2344112gdfgdfg' }, { 'key': 'g3g3egegre', 'value': 'g3g3egegre' }, { 'key': '8435f34', 'value': '8435f34' } ] }, { 'value': 2, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'Thresholds', 'groupId': 'threshold', 'position': 10, 'properties': [ { 'value': 30, 'key': 'frequency', 'label': 'Frequency', 'required': false, 'order': 3, 'controlType': 'number' }, { 'value': 30, 'key': 'retentionH', 'label': 'Retention High', 'required': false, 'order': 4, 'controlType': 'number' }, { 'value': 180, 'key': 'retentionL', 'label': 'Retention Low', 'required': false, 'order': 4, 'controlType': 'number' } ] }, { 'displayName': 'Chart', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } }, { 'componentType': 'TrendGadgetComponent', 'name': 'Trend', 'description': 'General trends.', 'icon': 'images/trend.png', 'instanceId': 1500252536414, 'tags': [ { 'facet': 'Performance', 'name': 'trend' }, { 'facet': 'Chart', 'name': 'area' } ], 'config': { 'propertyPages': [ { 'displayName': 'Run', 'groupId': 'run', 'position': 10, 'properties': [ { 'value': 'Carlosappliance - Process Monitor', 'key': 'endpoint', 'label': 'API Endpoints', 'required': false, 'order': 2, 'controlType': 'dynamicdropdown' }, { 'value': 'Trend', 'key': 'title', 'label': 'Title', 'required': false, 'order': 1, 'controlType': 'textbox' }, { 'value': 2, 'key': 'instanceId', 'required': false, 'order': -1, 'controlType': 'hidden' } ] }, { 'displayName': 'Chart', 'groupId': 'chart', 'position': 11, 'properties': [ { 'value': true, 'key': 'chart_properties', 'label': 'Show chart details', 'required': false, 'order': 3, 'controlType': 'checkbox' } ] } ] } } ] } ] } ] } ] }; ================================================ FILE: src/app/services/configuration-sample-default-board.ts ================================================ export const defaultBoard = { title: 'Dashboard', structure: '6-6', id: 5, boardInstanceId: 0, rows: [ { columns: [ { styleClass: 'six wide', gadgets: [] }, { styleClass: 'six wide', gadgets: [] } ] } ] } ================================================ FILE: src/app/services/configuration.service.ts ================================================ /** * Created by jayhamilton on 2/7/17. */ import {Injectable} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; import {EMPTY, Observable} from 'rxjs'; import {defaultBoard} from './configuration-sample-default-board'; import {sampleBoardCollectionProd} from './configuration-sample-boards-prod.model'; import {sampleBoardCollection} from './configuration-sample-boards.model'; import {environment} from '../../environments/environment'; @Injectable() export class ConfigurationService { model: any; // todo review this object closely currentModel: any; // this object helps with updates to property page values demo = true; env: any; defaultBoard: any; sampleBoardCollection: any; sampleBoardCollectionProd:any; /** * todo - fix this hard coded store * @type {string} */ remoteConfigurationRepository = ''; constructor(private _http: HttpClient) { Object.assign(this, {defaultBoard}); Object.assign(this, {sampleBoardCollection}); Object.assign(this, {sampleBoardCollectionProd}); this.env = environment; this.seedLocalStorageWithSampleBoardCollection(); } private seedLocalStorageWithSampleBoardCollection() { if (localStorage.getItem('board') === null) { if (this.env.production == true) { localStorage.setItem('board', JSON.stringify(this.sampleBoardCollectionProd)); } else { localStorage.setItem('board', JSON.stringify(this.sampleBoardCollection)); } } } public getBoardByTitle(title: string) { if (this.demo) { return new Observable(observer => { const board_collection = JSON.parse(localStorage.getItem('board')); let data = ''; board_collection['board'].forEach(boardModel => { if (boardModel.title === title) { data = boardModel; } }); observer.next(data); return () => { }; }); } else { return this._http.get(this.remoteConfigurationRepository + '/' + name); } } public getBoards() { if (this.demo) { return new Observable(observer => { let data = JSON.parse(localStorage.getItem('board')); if (!data) { data = {board: []}; } observer.next(data.board); return () => { }; }); } else { /** * todo - this call is based on an internal representation (admin console) of something called a store. * That concept requires refactoring. */ return this._http.get(this.remoteConfigurationRepository); } } public saveBoard(board: any) { this.model = board; if (Object.keys(board).length === 0 && board.constructor === Object) { return EMPTY; } if (this.demo) { return new Observable(observer => { let board_collection; // find and remove board from storage this.deleteBoardFromLocalStore(board.title); // get a collection object and add board to it if ((board_collection = JSON.parse(localStorage.getItem('board'))) == null) { board_collection = { board: [] }; } board_collection['board'].push(board); // save localStorage.setItem('board', JSON.stringify(board_collection)); observer.next({}); return () => { }; }); } else { /** * todo - a delete must happen here * */ const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; return this._http.post(this.remoteConfigurationRepository + '?id=' + board.title, JSON.stringify(board), httpOptions); } } private delete(board_collection: any) { localStorage.removeItem('board'); localStorage.setItem('board', JSON.stringify(board_collection)); } private deleteBoardFromLocalStore(boardTitle: string) { const board_collection = JSON.parse(localStorage.getItem('board')); let index; if (board_collection && (index = board_collection['board'].findIndex(item => { return item.title === boardTitle; })) >= 0) { board_collection['board'].splice(index, 1); this.delete(board_collection); } } public deleteBoard(boardTitle: string) { if (this.demo) { return new Observable(observer => { this.deleteBoardFromLocalStore(boardTitle); observer.next({}); return () => { }; }); } else { return this._http.delete(this.remoteConfigurationRepository + '/' + boardTitle); } } public getDefaultBoard() { return new Observable(observer => { observer.next(this.defaultBoard); return () => { }; }); } /* when a gadget instance's property page is updated and saved, the change gets communicated to all gadgets. The gadget instance id that caused the change will update their current instance. todo - this might be able to be improved. For now the utility of this approach allows the configuration service to capture the property page change in a way that allows us to update the persisted board model. */ notifyGadgetOnPropertyChange(gadgetConfig: string, instanceId: number) { this.savePropertyPageConfigurationToStore(gadgetConfig, instanceId); } setCurrentModel(_currentModel: any) { this.currentModel = _currentModel; } savePropertyPageConfigurationToStore(gadgetConfig: string, instanceId: number) { this.currentModel.rows.forEach(row => { row.columns.forEach(column => { if (column.gadgets) { column.gadgets.forEach(gadget => { this.updateProperties(gadgetConfig, gadget, instanceId); }); } }); }); this.saveBoard(this.currentModel).subscribe(result => { /** * todo - create popup/toast to show configuration saved message */ console.debug('The following configuration model was saved!'); }, error => console.error('Error' + error), () => console.debug('Saving configuration to store!')); } updateProperties(updatedProperties: any, gadget: any, instanceId: number) { const updatedPropsObject = JSON.parse(updatedProperties); if (gadget.instanceId === instanceId) { gadget.config.propertyPages.forEach(function (propertyPage) { for (let x = 0; x < propertyPage.properties.length; x++) { for (const prop in updatedPropsObject) { if (updatedPropsObject.hasOwnProperty(prop)) { if (prop === propertyPage.properties[x].key) { propertyPage.properties[x].value = updatedPropsObject[prop]; } } } } }); } } } ================================================ FILE: src/app/services/runtime.service.ts ================================================ /** * Created by jayhamilton on 1/18/17. */ import {Injectable} from '@angular/core'; import {throwError,Observable} from 'rxjs'; import {catchError} from "rxjs/operators"; import {ErrorHandler} from '../error/error-handler'; import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http'; import {environment} from '../../environments/environment'; @Injectable() export class RuntimeService { watsonMicroserviceURL: string; connectivityTestURL: string; serviceVersionURL: string; testURL = "http://localhost:8080"; static handleError(err: HttpErrorResponse | any) { const errMsg: any = { status: '-1', statusText: '', resource: '' }; if (err.error instanceof Error) { errMsg.statusText = err.error.message; console.log('Client error'); } else { errMsg.status = err.status; errMsg.statusText = 'A backend error occurred. In all likelihood the server/api service is not running.'; errMsg.resource = err.url; } return throwError(ErrorHandler.getErrorObject(errMsg)); } constructor(private _http: HttpClient) { this.configure(); } configure() { if (environment.production) { this.watsonMicroserviceURL = '/classify'; this.connectivityTestURL = '/connectTest'; this.serviceVersionURL = '/version' } else { this.connectivityTestURL = this.testURL + '/connectTest'; this.watsonMicroserviceURL = this.testURL + '/classify'; this.serviceVersionURL = this.testURL + '/version'; } } testURLResponse(url: string) { return this._http.get(url, {responseType: 'text'}) .pipe( catchError(RuntimeService.handleError) ); } testConnectivity(host: string, port: string) { let p = new HttpParams(); p = p.append('host', host); p = p.append('port', port); return this._http.get(this.connectivityTestURL, {params: p}) .pipe( catchError(RuntimeService.handleError) ); } /** * Wit AI can be called directly from a web server using JSONP * @param {string} aiStatement * @returns {Observable} */ callWitAI(aiStatement: string) { console.log('running WitAi'); if (!environment.production) { let p = new HttpParams(); if (!localStorage.getItem('Wit.aiToken')) { return; } p = p.append('v', '20171128'); p = p.append('q', aiStatement); p = p.append('access_token', localStorage.getItem('Wit.aiToken')); return this._http.jsonp('https://api.wit.ai/message?' + p.toString(), 'callback' ).pipe( catchError(RuntimeService.handleError) ); } } callback(data) { // console.log(data); } /** * Watson does not support JSONP for some of their APIs. This call relies on a backend that implements * a Watson SDK. Im my configuration I am using a Spring Boot backend with a simple REST API which calls into * the Watson JAVA SDK. The SDK requires a user, password and classifier, which is managed by the board configuration. * Here is what the backend test SDK code looks like. @RestController class WatsonClassifier{ ....... @CrossOrigin @RequestMapping("/classify") Classification processWatson(@RequestParam(value = "userid", defaultValue = "") String userid, @RequestParam(value = "password", defaultValue = "") String password, @RequestParam(value = "classifier_id", defaultValue = "") String classifierId, @RequestParam(value = "data", defaultValue = "") String data) { NaturalLanguageClassifier service = new NaturalLanguageClassifier(); service.setUsernameAndPassword(userid, password); Classification classification = service.classify(classifierId, data).execute(); return classification; } } * * @param {string} aiStatement * @returns {Observable} */ callWatsonAI(aiStatement: string) { console.log('running Watson'); if (!localStorage.getItem('ibmwatsoncid')) { return; } let p = new HttpParams(); p = p.append('userid', localStorage.getItem('ibmwatsonuid')); p = p.append('password', localStorage.getItem('ibmwatsonpwd')); p = p.append('classifier_id', localStorage.getItem('ibmwatsoncid')); p = p.append('data', aiStatement); return this._http.get(this.watsonMicroserviceURL, { params: p }).pipe( catchError(RuntimeService.handleError) ); } } ================================================ FILE: src/app/services/websocket-service.ts ================================================ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; @Injectable() export class ObservableWebSocketService { ws: WebSocket; createObservableWebSocket(url: string): Observable { this.ws = new WebSocket(url); return new Observable(observer => { this.ws.onmessage = (event) => observer.next(event.data); this.ws.onerror = (event) => observer.error(event); this.ws.onclose = (event) => observer.complete(); }); } sendMessage(message: string) { this.ws.send(message); } } ================================================ FILE: src/app/toast/message.ts ================================================ export class Message { id: number; content: string; style: string; dismissed = false; constructor(content, id, style?) { this.content = content; this.style = style || 'info'; this.id = id; } } ================================================ FILE: src/app/toast/reverse.pipe.spec.ts ================================================ import { ReversePipe } from './reverse.pipe'; describe('ReversePipe', () => { it('create an instance', () => { const pipe = new ReversePipe(); expect(pipe).toBeTruthy(); }); }); ================================================ FILE: src/app/toast/reverse.pipe.ts ================================================ import {Pipe, PipeTransform} from '@angular/core'; import {filter, reverse} from 'lodash'; @Pipe({ name: 'reverse' }) export class ReversePipe implements PipeTransform { transform(value: any, args?: any): any { value = filter(value, ['dismissed', false]); return reverse(value); } } ================================================ FILE: src/app/toast/toast.component.css ================================================ .wrapper { position: absolute; top: 50px; right: 10px; width: 400px; z-index: 1000; } .toast { width: inherit; } .toast-icon { background-color: #384b5f; text-align: center; } .ui[class*="top right attached"].label { background-color: transparent; } .toast-message{ height: 40px; display:table-cell; vertical-align: middle; } ================================================ FILE: src/app/toast/toast.component.html ================================================
{{message.content}}
================================================ FILE: src/app/toast/toast.component.spec.ts ================================================ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ToastComponent } from './toast.component'; describe('ToastComponent', () => { let component: ToastComponent; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ ToastComponent ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(ToastComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); }); ================================================ FILE: src/app/toast/toast.component.ts ================================================ import {Component, OnInit} from '@angular/core'; import {ToastService} from './toast.service'; import {Message} from './message'; import {animate, style, transition, trigger} from '@angular/animations'; import {timer} from 'rxjs'; @Component({ selector: 'app-toast', templateUrl: './toast.component.html', styleUrls: ['./toast.component.css'], animations: [ trigger('showHide', [ transition(':enter', [ style({opacity: 0}), animate('500ms', style({opacity: 1})) ]), transition(':leave', [ style({opacity: 1}), animate('500ms', style({opacity: 0})) ]) ])] }) export class ToastComponent implements OnInit { messages: Array; constructor(private _toastService: ToastService) { } ngOnInit() { this.messages = this._toastService.getMessages(); timer(7000, 7000).subscribe(t => { this.messages.forEach(message => { this.dismiss(message.id); }); }); } dismiss(id) { this._toastService.dismissMessage(id); } } ================================================ FILE: src/app/toast/toast.module.ts ================================================ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {ToastComponent} from './toast.component'; import {ReversePipe} from './reverse.pipe'; import {ToastService} from './toast.service'; @NgModule({ imports: [ CommonModule ], declarations: [ToastComponent, ReversePipe], providers: [ToastService], exports: [ToastComponent] }) export class ToastModule { } ================================================ FILE: src/app/toast/toast.service.spec.ts ================================================ import { TestBed, inject } from '@angular/core/testing'; import { ToastService } from './toast.service'; describe('ToastService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ToastService] }); }); it('should be created', inject([ToastService], (service: ToastService) => { expect(service).toBeTruthy(); })); }); ================================================ FILE: src/app/toast/toast.service.ts ================================================ import {Injectable} from '@angular/core'; import {Message} from './message'; @Injectable() export class ToastService { messages: Array = []; constructor() { } getMessages() { return this.messages; } sendMessage(content, style) { const message = new Message(content, this.messages.length + 1, style); this.messages.push(message); } dismissMessage(messageKey) { this.purgeDismissedMessages(); this.messages.forEach(message => { if (message.id === messageKey) { message.dismissed = true; } }); } purgeDismissedMessages () { this.messages.forEach(function (message, index, object) { if (message.dismissed) { object.splice(index, 1); } }); } } ================================================ FILE: src/app/typeahead-input/styles.css ================================================ .full-width { width: 100%; } /deep/ .ui.menu .item>.input input{ font-size: 1.1em !important; } /deep/ .mat-form-field-underline{ height: 0 !important; } ================================================ FILE: src/app/typeahead-input/typeahead-input.component.ts ================================================ import {Component, ElementRef, EventEmitter, Input, Output} from '@angular/core'; import {RuntimeService} from '../services/runtime.service'; /** * Created by jayhamilton on 2/26/17. */ @Component({ moduleId: module.id, selector: 'app-typeahead-input', templateUrl: './view.html', styleUrls: ['./styles.css'], }) export class TypeAheadInputComponent { @Input() searchList: string[]; @Input() placeHolderText; @Input() typeAheadIsInMenu: boolean; @Output() selectionEvent = new EventEmitter(); @Output() ArtificialIntelligenceEventEmitter: EventEmitter = new EventEmitter(); requestCounter = 0; maxAttempts = 5; public query = ''; public filteredList = []; public elementRef; constructor(myElement: ElementRef, private _runtimeService: RuntimeService) { this.elementRef = myElement; } filter() { if (this.query !== '') { this.filteredList = this.searchList.filter(function (el) { return el.toLowerCase().indexOf(this.query.toLowerCase()) > -1; }.bind(this)); } else { this.filteredList = []; } this.selectionEvent.emit(this.query); this.requestCounter++; if (this.requestCounter === this.maxAttempts) { this.processAIString(this.query); this.requestCounter = 0; } } select(item) { this.query = item; this.filteredList = []; this.selectionEvent.emit(item); } processAIString(aiStatement: string) { console.log('Processing statement: ' + aiStatement); const engine = localStorage.getItem('ai_engine'); if (engine.localeCompare('watson') === 0) { this._runtimeService.callWatsonAI(aiStatement).subscribe(data => { this.ArtificialIntelligenceEventEmitter.emit(this.getAIIntentFromWatson(data)); }); } if (engine.localeCompare('witai') === 0) { this._runtimeService.callWitAI(aiStatement).subscribe(data => { this.ArtificialIntelligenceEventEmitter.emit(this.getAIIntentFromWitAI(data)); }); } } getAIIntentFromWitAI(aiObject: any) { let operation = ''; let confidence = 0; if (aiObject && aiObject.entities) { if (aiObject.entities.intent && aiObject.entities.intent.length) { operation = aiObject.entities.intent[0].value; confidence = aiObject.entities.intent[0].confidence; } } return { 'operation': operation, 'confidence': confidence }; } getAIIntentFromWatson(aiObject: any) { let operation = ''; let confidence = 0; if (aiObject && aiObject.classes) { if (aiObject.classes && aiObject.classes.length) { operation = aiObject.classes[0].name; confidence = aiObject.classes[0].confidence; } } return { 'operation': operation, 'confidence': confidence }; } } ================================================ FILE: src/app/typeahead-input/typeahead-input.module.ts ================================================ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {TypeAheadInputComponent} from './typeahead-input.component'; import {FormsModule} from '@angular/forms'; @NgModule({ imports: [ CommonModule, FormsModule ], declarations: [ TypeAheadInputComponent ], providers: [ ], exports: [ TypeAheadInputComponent ] }) export class TypeAheadInputModule { } ================================================ FILE: src/app/typeahead-input/view.html ================================================
================================================ FILE: src/assets/.gitkeep ================================================ ================================================ FILE: src/assets/api/chart-mock-bar-model.json ================================================ [ { "name": "metric 1", "series": [ { "name": "available", "value": 25 },{ "name": "used", "value": 75 } ] }, { "name": "metric 2", "series": [ { "name": "available", "value": 77 },{ "name": "used", "value": 23 } ] } ] ================================================ FILE: src/assets/api/chart-mock-bubble-model.json ================================================ [ { "name": "Germany", "series": [ { "name": "2010", "x": 40632, "y": 80.3, "r": 80.4 }, { "name": "2000", "x": 36953, "y": 80.3, "r": 78 }, { "name": "1990", "x": 31476, "y": 75.4, "r": 79 } ] }, { "name": "USA", "series": [ { "name": "2010", "x": 49737, "y": 78.8, "r": 310 }, { "name": "2000", "x": 45986, "y": 76.9, "r": 283 }, { "name": "1990", "x": 3706, "y": 75.4, "r": 253 } ] }, { "name": "France", "series": [ { "name": "2010", "x": 36745, "y": 81.4, "r": 63 }, { "name": "2000", "x": 34774, "y": 79.1, "r": 59.4 }, { "name": "1990", "x": 29476, "y": 77.2, "r": 56.9 } ] }, { "name": "United Kingdom", "series": [ { "name": "2010", "x": 36240, "y": 80.2, "r": 62.7 }, { "name": "2000", "x": 32543, "y": 77.8, "r": 58.9 }, { "name": "1990", "x": 26424, "y": 75.7, "r": 57.1 } ] } ] ================================================ FILE: src/assets/api/chart-mock-pie-model.json ================================================ [ { "name": "a", "value": 45 }, { "name": "b", "value": 344 }, { "name": "c", "value": 75 }, { "name": "d", "value": 23 } ] ================================================ FILE: src/assets/api/connection-model.json ================================================ [ { "event": "unreachable", "title": "Network is unreachable", "summary": "The application failed to connect to the host due to inability to reach the host.", "solution": [ { "cause": "The target address may be IPV6.", "action": [ { "id": 1, "task": "Contact support to review this issue." } ] }, { "cause": "There may be a stale entry within the DNS server.", "action": [ { "id": 2, "task": "You may want to contact your network administrator to see if the issue is related to a stale DNS entries." } ] }, { "cause": "General connectivity issue.", "action": [ { "id": 2, "task": "Verify your network connections, proxies and firewalls." } ] } ] },{ "event": "time-out", "title": "Network request timed out", "summary": "The application link was able to contact the remote application but could not establish a connection.", "solution": [ { "cause": "The remote application is in deadlock.", "action": [ { "id": 1, "task": "Check that the remote application is operational, by accessing it as usual from the browser." } ] }, { "cause": "There is a network connection problem.", "action": [ { "id": 2, "task": "Use tools like ping, telnet or Wire Shark to check the connection to the remote machine. You may want to contact your network administrator." } ] } ] }, { "event": "connection-refuse", "title":"The remote application is not responding", "summary": "The application was attempting to connect to the remote application but the connection was refused.", "solution": [ { "cause": "You may have the correct URL but the wrong Port.", "action": [ { "id": 1, "task": "Double check an make sure you are using the correct port if a port is required. Review the documentation that describes which ports are required to open." } ] }, { "cause": "The remote application is down.", "action": [ { "id": 2, "task": "Check that the remote application is operational, by accessing it from the browser using the application URL (or the display URL if the remote application is behind a reverse proxy)." }, { "id": 3, "task": "If the remote application is not accessible with the browser, ping the remote application URL from the local server – the remote application must be accessible using the application URL for the application link to work." } ] }, { "cause": "The application URL is mis-configured.", "action": [ { "id": 1, "task": "Does the application URL use the correct port? If you're using the IP address for the application URL, is that correct?" }, { "id": 2, "task": "Does the application URL match the base URL of the remote application (or the display URL if the remote application is behind a reverse proxy )?" }, { "id": 3, "task": "Does the application URL use the correct context path?" } ] }, { "cause": "A firewall is blocking access.", "action": [ { "id": 1, "task": "Check that the firewall configuration allows access on the required port." } ] } ] }, { "event": "unknown-host", "title":"Unable to reach the remote application", "summary": "The application link was attempting to connect to the remote application but the hostname of the remote application couldn't be resolved.", "solution": [ { "cause": "Misconfigured DNS.", "action": [ { "id": 1, "task": "Check your DNS configuration, particularly for private networks.." } ] }, { "cause": "The application URL for the remote application is mis-configured, or may have changed recently.", "action": [ { "id": 1, "task": "Is the host name correct?" }, { "id": 2, "task": "Is the port correct?" }, { "id": 3, "task": "Is the context path correct?" }, { "id": 4, "task": "Does the application URL match the base URL for the remote application (or the display URL if the remote application is behind a reverse proxy)?" } ] } ] }, { "event": "no-route", "title":"Unable to locate the remote application", "summary": "The application link was attempting to connect to the remote application but couldn't even contact it.", "solution": [ { "cause": "A firewall is blocking requests.", "action": [ { "id": 1, "task": "Check that the firewall configuration allows access on the required port. Use a tool like traceroute to show the path of traffic between the local and remote machines" } ] }, { "cause": "An intermediate router is down.", "action": [ { "id": 1, "task": "Use a tool like traceroute to show the path of traffic between the local and remote machines." } ] } ] } ] ================================================ FILE: src/assets/api/cpu-model.json ================================================ [ { "name": "CPU 1", "series": [ { "name": "used", "value": 75 }, { "name": "available", "value": 100 } ] }, { "name": "CPU 2", "series": [ { "name": "used", "value": 23 }, { "name": "available", "value": 77 } ] }, { "name": "CPU 3", "series": [ { "name": "used", "value": 7 }, { "name": "available", "value": 93 } ] } ] ================================================ FILE: src/assets/api/disk-help-model.json ================================================ { "heading": "Disk Information", "summary": "", "details": [ { "category": "Disk1", "articles": [ { "headline": "IOPS", "description": "ECX enables automated seeding of your SQL Server AlwaysON availability group by leveraging Storage snapshot technology to minimize the data movement between primary and secondary replica.", "benefit": "Automatically and efficiently initialize AlwaysOn availability group.", "id": "id string", "date": "09/10/2017" } ] }, { "category": "Disk2", "articles": [ { "headline": "IOPS", "description": "ECX 2.7 introduces CDM support for SAP HANA 2.0 SPS01. Single tenant configuration can now be automatically protected using storage snapshot.", "benefit": "Protect your latest version of SAP HANA using ECX Copy Data Management", "id": "id string", "date": "09/10/2017" } ] } ] } ================================================ FILE: src/assets/api/disk-model.json ================================================ [ { "name": "used", "value": 45 } ] ================================================ FILE: src/assets/api/donut-model.json ================================================ [ { "name": "compliant", "value": 6, "detail": [ { "id": "1234", "name": "20GA_Exchange", "type": "vm", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "2345", "name": "Windows Prod1", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "2346", "name": "Red Hat test 1", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "2347", "name": "Centos test 1", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "2348", "name": "Ubuntu Prod1", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "2349", "name": "Ubuntu Prod2", "tags": [ { "facet": "Informational", "name": "storage" } ] } ] }, { "name": "staged", "value": 4, "detail": [ { "id": "5678", "name": "20GA_SQL", "type": "vm", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "9101", "name": "Windows 2016 Prod1", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "9102", "name": "Oracle Version a", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "9103", "name": "Catalog VM Server1", "tags": [ { "facet": "Informational", "name": "storage" } ] } ] }, { "name": "non-compliant", "value": 2, "detail": [ { "id": "1213", "name": "MY SQL", "type": "vm", "tags": [ { "facet": "Informational", "name": "storage" } ] }, { "id": "1415", "name": "QA System 1", "tags": [ { "facet": "Informational", "name": "storage" } ] } ] } ] ================================================ FILE: src/assets/api/gadget-library-model-prod.json ================================================ { "library": [ { "componentType": "PortConnectionGadgetComponent", "name": "Port Connection Test", "description": "Test Connection To Endpoints.", "icon": "assets/images/response.png", "instanceId": -1, "tags": [ { "facet": "Informational", "name": "port-connection" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Connection Test", "required": true, "order": 1 }, { "controlType": "textbox", "key": "host", "label": "Host", "value": "", "required": true, "order": 1 }, { "controlType": "dropdown", "key": "port", "label": "Port(s)", "value": "", "required": true, "order": 3, "options": [ { "key": "vSnap + vAdp (All Ports)", "value": "8900,8098,8080,22,111,20048,2049" }, { "key": "SPP Single Appliance (All Ports)", "value": "443,8900,22,111,20048,2049" }, { "key": "SPP (443)", "value": "443" }, { "key": "SPP (8443)", "value": "8443" }, { "key": "SPP (3000)", "value": "3000" }, { "key": "LDAP (389)", "value": "389" }, { "key": "LDAP (636)", "value": "636" }, { "key": "VADP (8080)", "value": "8080" }, { "key": "VADP on vSnap (8098)", "value": "8098" }, { "key": "WinRM (5985)", "value": "5985" }, { "key": "VSnap (8900)", "value": "8900" }, { "key": "SSH (22)", "value": "22" }, { "key": "ISCSI (3260)", "value": "3260" }, { "key": "NFS (2049)", "value": "2049" }, { "key": "NFS (20048)", "value": "20048" }, { "key": "NFS (111)", "value": "111" }, { "key": "ESXI (902)", "value": "902" } ] }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "BarChartGadgetComponent", "name": "Bar Chart", "description": "Bar Char", "icon": "assets/images/cpu.png", "instanceId": -1, "tags": [ { "facet": "Chart", "name": "metric" }, { "facet": "Chart", "name": "bar" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Bar Chart Data", "required": true, "order": 1 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "hidden", "key": "state", "label": "", "value": "stop", "required": true, "order": -1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -2 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "dropdown", "type": "dropdown", "key": "barChartType", "label": "Bar Chart Type", "value": "normalizedHorizontal", "required": true, "order": 1, "options": [ { "key": "Normalized Horizontal", "value": "normalizedHorizontal" }, { "key": "Normalized Vertical", "value": "normalizedVertical" }, { "key": "Stacked Horizontal", "value": "stackedHorizontal" }, { "key": "Stacked Vertical", "value": "stackedVertical" } ] }, { "controlType": "textbox", "key": "yAxisLabel", "label": "Y Axis Label", "value": "", "required": false, "order": 2 }, { "controlType": "textbox", "key": "xAxisLabel", "label": "X Axis Label", "value": "", "required": false, "order": 3 }, { "controlType": "checkbox", "type": "checkbox", "key": "gradient", "label": "Show colors in gradient", "value": true, "required": true, "order": 4 }, { "controlType": "checkbox", "type": "checkbox", "key": "showDataLabel", "label": "Show data label", "value": true, "required": false, "order": 5 }, { "controlType": "checkbox", "type": "checkbox", "key": "showXAxis", "label": "Show x axis", "value": true, "required": false, "order": 6 }, { "controlType": "checkbox", "type": "checkbox", "key": "showYAxis", "label": "Show y axis", "value": true, "required": false, "order": 7 }, { "controlType": "checkbox", "type": "checkbox", "key": "showLegend", "label": "Show chart legend", "value": true, "required": false, "order": 8 }, { "controlType": "checkbox", "type": "checkbox", "key": "showXAxisLabel", "label": "Show x axis label", "value": true, "required": false, "order": 9 }, { "controlType": "checkbox", "type": "checkbox", "key": "showYAxisLabel", "label": "Show y axis label", "value": false, "required": false, "order": 10 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "PieChartGadgetComponent", "name": "Pie Chart", "description": "Pie Chart", "icon": "assets/images/pie.png", "instanceId": -1, "tags": [ { "facet": "Chart", "name": "metric" }, { "facet": "Chart", "name": "pie" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Pie Chart", "required": true, "order": 1 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 }, { "controlType": "hidden", "key": "state", "label": "", "value": "stop", "required": true, "order": -2 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "explodeSlices", "label": "Explode slices", "value": true, "required": false, "order": 1 },{ "controlType": "checkbox", "type": "checkbox", "key": "showDonut", "label": "Show donut", "value": true, "required": false, "order": 2 }, { "controlType": "checkbox", "type": "checkbox", "key": "gradient", "label": "Show colors in gradient", "value": true, "required": true, "order": 3 }, { "controlType": "checkbox", "type": "checkbox", "key": "showLegend", "label": "Show chart legend", "value": true, "required": false, "order": 4 }, { "controlType": "checkbox", "type": "checkbox", "key": "showLabels", "label": "Show labels", "value": true, "required": false, "order": 5 } ] } ] }, "actions": [ { "name": "Add" } ] } ] } ================================================ FILE: src/assets/api/gadget-library-model.json ================================================ { "library": [ { "componentType": "BarChartGadgetComponent", "name": "Bar Chart", "description": "Bar Char", "icon": "assets/images/cpu.png", "instanceId": -1, "tags": [ { "facet": "Chart", "name": "metric" }, { "facet": "Chart", "name": "bar" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Bar Chart Data", "required": true, "order": 1 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "hidden", "key": "state", "label": "", "value": "stop", "required": true, "order": -1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -2 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "dropdown", "type": "dropdown", "key": "barChartType", "label": "Bar Chart Type", "value": "normalizedHorizontal", "required": true, "order": 1, "options": [ { "key": "Normalized Horizontal", "value": "normalizedHorizontal" }, { "key": "Normalized Vertical", "value": "normalizedVertical" }, { "key": "Stacked Horizontal", "value": "stackedHorizontal" }, { "key": "Stacked Vertical", "value": "stackedVertical" } ] }, { "controlType": "textbox", "key": "yAxisLabel", "label": "Y Axis Label", "value": "", "required": false, "order": 2 }, { "controlType": "textbox", "key": "xAxisLabel", "label": "X Axis Label", "value": "", "required": false, "order": 3 }, { "controlType": "checkbox", "type": "checkbox", "key": "gradient", "label": "Show colors in gradient", "value": true, "required": true, "order": 4 }, { "controlType": "checkbox", "type": "checkbox", "key": "showDataLabel", "label": "Show data label", "value": true, "required": false, "order": 5 }, { "controlType": "checkbox", "type": "checkbox", "key": "showXAxis", "label": "Show x axis", "value": true, "required": false, "order": 6 }, { "controlType": "checkbox", "type": "checkbox", "key": "showYAxis", "label": "Show y axis", "value": true, "required": false, "order": 7 }, { "controlType": "checkbox", "type": "checkbox", "key": "showLegend", "label": "Show chart legend", "value": true, "required": false, "order": 8 }, { "controlType": "checkbox", "type": "checkbox", "key": "showXAxisLabel", "label": "Show x axis label", "value": true, "required": false, "order": 9 }, { "controlType": "checkbox", "type": "checkbox", "key": "showYAxisLabel", "label": "Show y axis label", "value": false, "required": false, "order": 10 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "PieChartGadgetComponent", "name": "Pie Chart", "description": "Pie Chart", "icon": "assets/images/pie.png", "instanceId": -1, "tags": [ { "facet": "Chart", "name": "metric" }, { "facet": "Chart", "name": "pie" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Pie Chart", "required": true, "order": 1 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 }, { "controlType": "hidden", "key": "state", "label": "", "value": "stop", "required": true, "order": -2 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "explodeSlices", "label": "Explode slices", "value": true, "required": false, "order": 1 }, { "controlType": "checkbox", "type": "checkbox", "key": "showDonut", "label": "Show donut", "value": true, "required": false, "order": 2 }, { "controlType": "checkbox", "type": "checkbox", "key": "gradient", "label": "Show colors in gradient", "value": true, "required": true, "order": 3 }, { "controlType": "checkbox", "type": "checkbox", "key": "showLegend", "label": "Show chart legend", "value": true, "required": false, "order": 4 }, { "controlType": "checkbox", "type": "checkbox", "key": "showLabels", "label": "Show labels", "value": true, "required": false, "order": 5 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "BubbleGadgetComponent", "name": "Bubble", "description": "Bubble Gadget", "icon": "assets/images/bubble.png", "instanceId": -1, "tags": [ { "facet": "Bubble", "name": "bubble" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 11, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Bubble", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": false, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "StorageObjectListComponent", "name": "Storage Information", "description": "Provides a storage view.", "icon": "assets/images/volume.png", "instanceId": -1, "tags": [ { "facet": "Informational", "name": "storage" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Data Source", "value": "vSnap", "required": true, "order": 3 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Storage - Volume Analysis", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "CPUMGadgetComponent", "name": "CPUM Chart", "description": "Monitors CPU utilization.", "icon": "assets/images/cpu.png", "instanceId": -1, "tags": [ { "facet": "Performance", "name": "real-time" }, { "facet": "Chart", "name": "bar" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "EXP CPU Monitor utilization", "required": true, "order": 1 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 } ] }, { "displayName": "Threshold", "groupId": "alert", "position": 11, "properties": [ { "controlType": "number", "key": "cpuErrorThreshold", "label": "Utilization Error Threshold (%)", "value": 90, "required": true, "order": 2 }, { "controlType": "number", "key": "cpuWarningThreshold", "label": "Utilization Warning Threshold (%)", "value": 75, "required": true, "order": 2 }, { "controlType": "number", "key": "cpuThresholdTime", "label": "Maximum Exceed Threshold Time (minutes)", "value": 10, "required": true, "order": 3 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "textbox", "type": "textbox", "key": "orientation", "label": "Orientation", "value": "horizontal", "required": true, "order": 2 }, { "controlType": "checkbox", "type": "checkbox", "key": "xAxis", "label": "Show X Axis", "value": true, "required": true, "order": 3 }, { "controlType": "checkbox", "type": "checkbox", "key": "yAxis", "label": "Show Y Axis", "value": true, "required": true, "order": 4 }, { "controlType": "checkbox", "type": "checkbox", "key": "legend", "label": "Show Legend", "value": true, "required": true, "order": 5 }, { "controlType": "checkbox", "type": "checkbox", "key": "gradient", "label": "Use Gradient", "value": true, "required": true, "order": 6 }, { "controlType": "checkbox", "type": "checkbox", "key": "showGridLines", "label": "Show Grid Lines", "value": true, "required": true, "order": 7 }, { "controlType": "checkbox", "type": "checkbox", "key": "showXAxisLabel", "label": "Show X Axis Label", "value": true, "required": true, "order": 8 }, { "controlType": "checkbox", "type": "checkbox", "key": "showYAxisLabel", "label": "Show Y Axis Label", "value": true, "required": true, "order": 9 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "PortConnectionGadgetComponent", "name": "Port Connection Test", "description": "Test Connection To Endpoints.", "icon": "assets/images/response.png", "instanceId": -1, "tags": [ { "facet": "Informational", "name": "port-connection" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Connection Test", "required": true, "order": 1 }, { "controlType": "textbox", "key": "host", "label": "Host", "value": "", "required": true, "order": 1 }, { "controlType": "dropdown", "key": "port", "label": "Port(s)", "value": "", "required": true, "order": 3, "options": [ { "key": "vSnap + vAdp (All Ports)", "value": "8900,8098, 8080,22,111,20048,2049" }, { "key": "SPP Single Appliance (All Ports)", "value": "443,8900,22,111,20048,2049" }, { "key": "SPP (443)", "value": "443" }, { "key": "SPP (8443)", "value": "8443" }, { "key": "SPP (3000)", "value": "3000" }, { "key": "LDAP (389)", "value": "389" }, { "key": "LDAP (636)", "value": "636" }, { "key": "VADP (8080)", "value": "8080" }, { "key": "VADP on vSnap (8098)", "value": "8098" }, { "key": "WinRM (5985)", "value": "5985" }, { "key": "VSnap (8900)", "value": "8900" }, { "key": "SSH (22)", "value": "22" }, { "key": "ISCSI (3260)", "value": "3260" }, { "key": "NFS (2049)", "value": "2049" }, { "key": "NFS (20048)", "value": "20048" }, { "key": "NFS (111)", "value": "111" }, { "key": "ESXI (902)", "value": "902" } ] }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "EdgeServiceListGadgetComponent", "name": "Edge Service Status", "description": "Monitors edge service status", "icon": "assets/images/edge-service.png", "instanceId": -1, "tags": [ { "facet": "List", "name": "health" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "Carlosappliance", "required": true, "order": 2 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Proxy service status", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 } ] }, { "displayName": "List", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "TrendGadgetComponent", "name": "Trend", "description": "General trends.", "icon": "assets/images/trend.png", "instanceId": -1, "tags": [ { "facet": "Performance", "name": "trend" }, { "facet": "Chart", "name": "area" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 2 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Trend", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "TrendLineGadgetComponent", "name": "Realtime Performance", "description": "Monitors IOPs and network bandwidth.", "icon": "assets/images/trend-line.png", "instanceId": -1, "tags": [ { "facet": "Performance", "name": "trend" }, { "facet": "Chart", "name": "line" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 2 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Performance", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "CPUGadgetComponent", "name": "CPU Chart", "description": "Monitors CPU utilization.", "icon": "assets/images/cpu.png", "instanceId": -1, "tags": [ { "facet": "Performance", "name": "real-time" }, { "facet": "Chart", "name": "bar" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "CPU utilization", "required": true, "order": 1 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "MemoryGadgetComponent", "name": "Memory Chart", "description": "Monitors memory utilization", "icon": "assets/images/memory.png", "instanceId": -1, "tags": [ { "facet": "Performance", "name": "real-time" }, { "facet": "Chart", "name": "gauge" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 2 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Memory Utilization", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "ServiceListGadgetComponent", "name": "Service Status", "description": "Monitors service status", "icon": "assets/images/service.png", "instanceId": -1, "tags": [ { "facet": "List", "name": "health" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Data Source", "value": "", "required": true, "order": 2 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Service Status", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 } ] }, { "displayName": "List", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "PropertyListGadgetComponent", "name": "Tuning Parameters", "description": "System related tuning parameters.", "icon": "assets/images/property.png", "instanceId": -1, "tags": [ { "facet": "List", "name": "tuning" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "Tuning", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 999, "required": true, "order": -1 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "textbox", "key": "title", "label": "Title", "value": "CPU Utilization", "required": true, "order": 1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "DiskGadgetComponent", "name": "Disk Utilization", "description": "Disk consumption information.", "icon": "assets/images/donut.png", "instanceId": -1, "tags": [ { "facet": "Chart", "name": "pie" }, { "facet": "Performance", "name": "storage" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 4 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Disk Consumption", "required": true, "order": 1 }, { "controlType": "number", "key": "threshold", "label": "Consumed Threshold", "value": 50, "required": true, "order": 3 }, { "controlType": "dropdown", "key": "diskid", "label": "Disk Id", "value": "", "required": true, "order": 2, "options": [ { "key": "2344112gdfgdfg", "value": "2344112gdfgdfg" }, { "key": "g3g3egegre", "value": "g3g3egegre" }, { "key": "8435f34", "value": "8435f34" } ] }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] }, { "displayName": "Thresholds", "groupId": "threshold", "position": 10, "properties": [ { "controlType": "number", "key": "frequency", "label": "Frequency", "value": 30, "required": true, "order": 3 }, { "controlType": "number", "key": "retentionH", "label": "Retention High", "value": 30, "required": true, "order": 4 }, { "controlType": "number", "key": "retentionL", "label": "Retention Low", "value": 180, "required": true, "order": 4 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "DonutGadgetComponent", "name": "Smart Protection", "description": "Ai based protection.", "icon": "assets/images/smart-protect.png", "instanceId": -1, "tags": [ { "facet": "Chart", "name": "pie" }, { "facet": "Performance", "name": "storage" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 4 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Smart Protection", "required": true, "order": 1 }, { "controlType": "number", "key": "frequency", "label": "Compliant Check Frequency", "value": 50, "required": true, "order": 3 }, { "controlType": "checkbox", "type": "checkbox", "key": "auto", "label": "Use AI to automate compliance", "value": true, "required": true, "order": 3 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] }, { "displayName": "Chart", "groupId": "chart", "position": 11, "properties": [ { "controlType": "checkbox", "type": "checkbox", "key": "chart_properties", "label": "Show chart details", "value": true, "required": true, "order": 3 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "StatisticGadgetComponent", "name": "Statistic", "description": "General statistic.", "icon": "assets/images/statistic.png", "instanceId": -1, "tags": [ { "facet": "Statistic", "name": "counter" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dropdown", "key": "resource", "label": "Resource", "value": "vm", "required": true, "order": 2, "options": [ { "key": "vm", "value": "vm" }, { "key": "database", "value": "database" }, { "key": "volume", "value": "volume" }, { "key": "job", "value": "job" } ] }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Statistic", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": false, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "NewsGadgetComponent", "name": "News", "description": "What's new", "icon": "assets/images/news.png", "instanceId": -1, "tags": [ { "facet": "Informational", "name": "news" }, { "facet": "List", "name": "news" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "News URL", "value": "news", "required": true, "order": 3 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "News", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "JobAnalysisGadgetComponent", "name": "Job Analyzer", "description": "Performs operation analysis.", "icon": "assets/images/job-analysis.png", "instanceId": -1, "tags": [ { "facet": "Informational", "name": "job" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 10, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Application URL", "value": "", "required": true, "order": 3 }, { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Device URL", "value": "", "required": true, "order": 3 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Job Anaylyzer", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": true, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] }, { "componentType": "TodoGadgetComponent", "name": "Todo", "description": "Todo Gadget", "icon": "assets/images/todo.png", "instanceId": -1, "tags": [ { "facet": "Todo", "name": "todo" } ], "config": { "propertyPages": [ { "displayName": "Run", "groupId": "run", "position": 11, "properties": [ { "controlType": "dynamicdropdown", "key": "endpoint", "label": "Chart Data Source", "value": "", "required": true, "order": 3 }, { "controlType": "textbox", "key": "title", "label": "Title", "value": "Todo", "required": true, "order": 1 }, { "controlType": "hidden", "key": "instanceId", "label": "", "value": 2, "required": false, "order": -1 } ] } ] }, "actions": [ { "name": "Add" } ] } ] } ================================================ FILE: src/assets/api/http-codes.json ================================================ [ { "code": "1xx", "phrase": "**Informational**", "description": "\"indicates an interim response for communicating connection status or request progress prior to completing the requested action and sending a final response.\" ~ [sure](http://www.urbandictionary.com/define.php?term=sure)", "spec_title": "RFC7231#6.2", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.2" }, { "code": "100", "phrase": "Continue", "description": "\"indicates that the initial part of a request has been received and has not yet been rejected by the server.\"", "spec_title": "RFC7231#6.2.1", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.2.1" }, { "code": "101", "phrase": "Switching Protocols", "description": "\"indicates that the server understands and is willing to comply with the client's request, via the Upgrade header field, for a change in the application protocol being used on this connection.\"", "spec_title": "RFC7231#6.2.2", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.2.2" }, { "code": "2xx", "phrase": "**Successful**", "description": "\"indicates that the client's request was successfully received, understood, and accepted.\" ~ [cool](https://twitter.com/DanaDanger/status/183316183494311936)", "spec_title": "RFC7231#6.3", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3" }, { "code": "200", "phrase": "OK", "description": "\"indicates that the request has succeeded.\"", "spec_title": "RFC7231#6.3.1", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3.1" }, { "code": "201", "phrase": "Created", "description": "\"indicates that the request has been fulfilled and has resulted in one or more new resources being created.\"", "spec_title": "RFC7231#6.3.2", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3.2" }, { "code": "202", "phrase": "Accepted", "description": "\"indicates that the request has been accepted for processing, but the processing has not been completed.\"", "spec_title": "RFC7231#6.3.3", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3.3" }, { "code": "203", "phrase": "Non-Authoritative Information", "description": "\"indicates that the request was successful but the enclosed payload has been modified from that of the origin server's 200 (OK) response by a transforming proxy.\"", "spec_title": "RFC7231#6.3.4", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3.4" }, { "code": "204", "phrase": "No Content", "description": "\"indicates that the server has successfully fulfilled the request and that there is no additional content to send in the response payload body.\"", "spec_title": "RFC7231#6.3.5", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3.5" }, { "code": "205", "phrase": "Reset Content", "description": "\"indicates that the server has fulfilled the request and desires that the user agent reset the \"document view\", which caused the request to be sent, to its original state as received from the origin server.\"", "spec_title": "RFC7231#6.3.6", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.3.6" }, { "code": "206", "phrase": "Partial Content", "description": "\"indicates that the server is successfully fulfilling a range request for the target resource by transferring one or more parts of the selected representation that correspond to the satisfiable ranges found in the requests's Range header field.\"", "spec_title": "RFC7233#4.1", "spec_href": "https://tools.ietf.org/html/rfc7233#section-4.1" }, { "code": "3xx", "phrase": "**Redirection**", "description": "\"indicates that further action needs to be taken by the user agent in order to fulfill the request.\" ~ [ask that dude over there](https://twitter.com/DanaDanger/status/183316183494311936)", "spec_title": "RFC7231#6.4", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4" }, { "code": "300", "phrase": "Multiple Choices", "description": "\"indicates that the target resource has more than one representation, each with its own more specific identifier, and information about the alternatives is being provided so that the user (or user agent) can select a preferred representation by redirecting its request to one or more of those identifiers.\"", "spec_title": "RFC7231#6.4.1", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4.1" }, { "code": "301", "phrase": "Moved Permanently", "description": "\"indicates that the target resource has been assigned a new permanent URI and any future references to this resource ought to use one of the enclosed URIs.\"", "spec_title": "RFC7231#6.4.2", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4.2" }, { "code": "302", "phrase": "Found", "description": "\"indicates that the target resource resides temporarily under a different URI.\"", "spec_title": "RFC7231#6.4.3", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4.3" }, { "code": "303", "phrase": "See Other", "description": "\"indicates that the server is redirecting the user agent to a different resource, as indicated by a URI in the Location header field, that is intended to provide an indirect response to the original request.\"", "spec_title": "RFC7231#6.4.4", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4.4" }, { "code": "304", "phrase": "Not Modified", "description": "\"indicates that a conditional GET request has been received and would have resulted in a 200 (OK) response if it were not for the fact that the condition has evaluated to false.\"", "spec_title": "RFC7232#4.1", "spec_href": "https://tools.ietf.org/html/rfc7232#section-4.1" }, { "code": "305", "phrase": "Use Proxy", "description": "*deprecated*", "spec_title": "RFC7231#6.4.5", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4.5" }, { "code": "307", "phrase": "Temporary Redirect", "description": "\"indicates that the target resource resides temporarily under a different URI and the user agent MUST NOT change the request method if it performs an automatic redirection to that URI.\"", "spec_title": "RFC7231#6.4.7", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.4.7" }, { "code": "4xx", "phrase": "**Client Error**", "description": "\"indicates that the client seems to have erred.\" ~ [*you* fucked up](https://twitter.com/DanaDanger/status/183316183494311936)", "spec_title": "RFC7231#6.5", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5" }, { "code": "400", "phrase": "Bad Request", "description": "\"indicates that the server cannot or will not process the request because the received syntax is invalid, nonsensical, or exceeds some limitation on what the server is willing to process.\"", "spec_title": "RFC7231#6.5.1", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.1" }, { "code": "401", "phrase": "Unauthorized", "description": "\"indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.\"", "spec_title": "RFC7235#6.3.1", "spec_href": "https://tools.ietf.org/html/rfc7235#section-3.1" }, { "code": "402", "phrase": "Payment Required", "description": "*reserved*", "spec_title": "RFC7231#6.5.2", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.2" }, { "code": "403", "phrase": "Forbidden", "description": "\"indicates that the server understood the request but refuses to authorize it.\"", "spec_title": "RFC7231#6.5.3", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.3" }, { "code": "404", "phrase": "Not Found", "description": "\"indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists.\"", "spec_title": "RFC7231#6.5.4", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.4" }, { "code": "405", "phrase": "Method Not Allowed", "description": "\"indicates that the method specified in the request-line is known by the origin server but not supported by the target resource.\"", "spec_title": "RFC7231#6.5.5", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.5" }, { "code": "406", "phrase": "Not Acceptable", "description": "\"indicates that the target resource does not have a current representation that would be acceptable to the user agent, according to the proactive negotiation header fields received in the request, and the server is unwilling to supply a default representation.\"", "spec_title": "RFC7231#6.5.6", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.6" }, { "code": "407", "phrase": "Proxy Authentication Required", "description": "\"is similar to 401 (Unauthorized), but indicates that the client needs to authenticate itself in order to use a proxy.\"", "spec_title": "RFC7235#3.2", "spec_href": "https://tools.ietf.org/html/rfc7235#section-3.2" }, { "code": "408", "phrase": "Request Timeout", "description": "\"indicates that the server did not receive a complete request message within the time that it was prepared to wait.\"", "spec_title": "RFC7231#6.5.7", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.7" }, { "code": "409", "phrase": "Conflict", "description": "\"indicates that the request could not be completed due to a conflict with the current state of the resource.\"", "spec_title": "RFC7231#6.5.8", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.8" }, { "code": "410", "phrase": "Gone", "description": "\"indicates that access to the target resource is no longer available at the origin server and that this condition is likely to be permanent.\"", "spec_title": "RFC7231#6.5.9", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.9" }, { "code": "411", "phrase": "Length Required", "description": "\"indicates that the server refuses to accept the request without a defined Content-Length.\"", "spec_title": "RFC7231#6.5.10", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.10" }, { "code": "412", "phrase": "Precondition Failed", "description": "\"indicates that one or more preconditions given in the request header fields evaluated to false when tested on the server.\"", "spec_title": "RFC7232#4.2", "spec_href": "https://tools.ietf.org/html/rfc7232#section-4.2" }, { "code": "413", "phrase": "Payload Too Large", "description": "\"indicates that the server is refusing to process a request because the request payload is larger than the server is willing or able to process.\"", "spec_title": "RFC7231#6.5.11", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.11" }, { "code": "414", "phrase": "URI Too Long", "description": "\"indicates that the server is refusing to service the request because the request-target is longer than the server is willing to interpret.\"", "spec_title": "RFC7231#6.5.12", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.12" }, { "code": "415", "phrase": "Unsupported Media Type", "description": "\"indicates that the origin server is refusing to service the request because the payload is in a format not supported by the target resource for this method.\"", "spec_title": "RFC7231#6.5.13", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.13" }, { "code": "416", "phrase": "Range Not Satisfiable", "description": "\"indicates that none of the ranges in the request's Range header field overlap the current extent of the selected resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive request of small or overlapping ranges.\"", "spec_title": "RFC7233#4.4", "spec_href": "https://tools.ietf.org/html/rfc7233#section-4.4" }, { "code": "417", "phrase": "Expectation Failed", "description": "\"indicates that the expectation given in the request's Expect header field could not be met by at least one of the inbound servers.\"", "spec_title": "RFC7231#6.5.14", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.14" }, { "code": "418", "phrase": "I'm a teapot", "description": "\"Any attempt to brew coffee with a teapot should result in the error code 418 I'm a teapot.\"", "spec_title": "RFC2324#2.3.1", "spec_href": "https://tools.ietf.org/html/rfc2324#section-2.3.1" }, { "code": "426", "phrase": "Upgrade Required", "description": "\"indicates that the server refuses to perform the request using the current protocol but might be willing to do so after the client upgrades to a different protocol.\"", "spec_title": "RFC7231#6.5.15", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.5.15" }, { "code": "5xx", "phrase": "**Server Error**", "description": "\"indicates that the server is aware that it has erred or is incapable of performing the requested method.\" ~ [*we* fucked up](https://twitter.com/DanaDanger/status/183316183494311936)", "spec_title": "RFC7231#6.6", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6" }, { "code": "500", "phrase": "Internal Server Error", "description": "\"indicates that the server encountered an unexpected condition that prevented it from fulfilling the request.\"", "spec_title": "RFC7231#6.6.1", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6.1" }, { "code": "501", "phrase": "Not Implemented", "description": "\"indicates that the server does not support the functionality required to fulfill the request.\"", "spec_title": "RFC7231#6.6.2", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6.2" }, { "code": "502", "phrase": "Bad Gateway", "description": "\"indicates that the server, while acting as a gateway or proxy, received an invalid response from an inbound server it accessed while attempting to fulfill the request.\"", "spec_title": "RFC7231#6.6.3", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6.3" }, { "code": "503", "phrase": "Service Unavailable", "description": "\"indicates that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.\"", "spec_title": "RFC7231#6.6.4", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6.4" }, { "code": "504", "phrase": "Gateway Time-out", "description": "\"indicates that the server, while acting as a gateway or proxy, did not receive a timely response from an upstream server it needed to access in order to complete the request.\"", "spec_title": "RFC7231#6.6.5", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6.5" }, { "code": "505", "phrase": "HTTP Version Not Supported", "description": "\"indicates that the server does not support, or refuses to support, the protocol version that was used in the request message.\"", "spec_title": "RFC7231#6.6.6", "spec_href": "https://tools.ietf.org/html/rfc7231#section-6.6.6" }, { "code": "102", "phrase": "Processing", "description": "\"is an interim response used to inform the client that the server has accepted the complete request, but has not yet completed it.\"", "spec_title": "RFC5218#10.1", "spec_href": "https://tools.ietf.org/html/rfc2518#section-10.1" }, { "code": "207", "phrase": "Multi-Status", "description": "\"provides status for multiple independent operations.\"", "spec_title": "RFC5218#10.2", "spec_href": "https://tools.ietf.org/html/rfc2518#section-10.2" }, { "code": "226", "phrase": "IM Used", "description": "\"The server has fulfilled a GET request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.\"", "spec_title": "RFC3229#10.4.1", "spec_href": "https://tools.ietf.org/html/rfc3229#section-10.4.1" }, { "code": "308", "phrase": "Permanent Redirect", "description": "\"The target resource has been assigned a new permanent URI and any future references to this resource outght to use one of the enclosed URIs. [...] This status code is similar to 301 Moved Permanently (Section 7.3.2 of rfc7231), except that it does not allow rewriting the request method from POST to GET.\"", "spec_title": "RFC7538", "spec_href": "https://tools.ietf.org/html/rfc7538" }, { "code": "422", "phrase": "Unprocessable Entity", "description": "\"means the server understands the content type of the request entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad Request) status code is inappropriate) but was unable to process the contained instructions.\"", "spec_title": "RFC5218#10.3", "spec_href": "https://tools.ietf.org/html/rfc2518#section-10.3" }, { "code": "423", "phrase": "Locked", "description": "\"means the source or destination resource of a method is locked.\"", "spec_title": "RFC5218#10.4", "spec_href": "https://tools.ietf.org/html/rfc2518#section-10.4" }, { "code": "424", "phrase": "Failed Dependency", "description": "\"means that the method could not be performed on the resource because the requested action depended on another action and that action failed.\"", "spec_title": "RFC5218#10.5", "spec_href": "https://tools.ietf.org/html/rfc2518#section-10.5" }, { "code": "428", "phrase": "Precondition Required", "description": "\"indicates that the origin server requires the request to be conditional.\"", "spec_title": "RFC6585#3", "spec_href": "https://tools.ietf.org/html/rfc6585#section-3" }, { "code": "429", "phrase": "Too Many Requests", "description": "\"indicates that the user has sent too many requests in a given amount of time (\"rate limiting\").\"", "spec_title": "RFC6585#4", "spec_href": "https://tools.ietf.org/html/rfc6585#section-4" }, { "code": "431", "phrase": "Request Header Fields Too Large", "description": "\"indicates that the server is unwilling to process the request because its header fields are too large.\"", "spec_title": "RFC6585#5", "spec_href": "https://tools.ietf.org/html/rfc6585#section-5" }, { "code": "451", "phrase": "Unavailable For Legal Reasons", "description": "\"This status code indicates that the server is denying access to the resource in response to a legal demand.\"", "spec_title": "draft-ietf-httpbis-legally-restricted-status", "spec_href": "https://tools.ietf.org/html/draft-ietf-httpbis-legally-restricted-status" }, { "code": "506", "phrase": "Variant Also Negotiates", "description": "\"indicates that the server has an internal configuration error: the chosen variant resource is configured to engage in transparent content negotiation itself, and is therefore not a proper end point in the negotiation process.\"", "spec_title": "RFC2295#8.1", "spec_href": "https://tools.ietf.org/html/rfc2295#section-8.1" }, { "code": "507", "phrase": "Insufficient Storage", "description": "\"means the method could not be performed on the resource because the server is unable to store the representation needed to successfully complete the request.\"", "spec_title": "RFC5218#10.6", "spec_href": "https://tools.ietf.org/html/rfc2518#section-10.6" }, { "code": "511", "phrase": "Network Authentication Required", "description": "\"indicates that the client needs to authenticate to gain network access.\"", "spec_title": "RFC6585#6", "spec_href": "https://tools.ietf.org/html/rfc6585#section-6" }, { "code": "7xx", "phrase": "**Developer Error**", "description": "[err](http://www.urbandictionary.com/define.php?term=err)", "spec_title": "7xx-rfc", "spec_href": "http://documentup.com/joho/7XX-rfc" } ] ================================================ FILE: src/assets/api/news-model.json ================================================ [ { "heading": "", "summary": "", "details": [ { "category": "Application A Enhancements", "articles": [ { "headline": "Automated Seeding for Always On", "description": "Enables automated seeding of your Application A AlwaysON availability group by leveraging Storage snapshot technology to minimize the data movement between primary and secondary replica.", "benefit": "Automatically and efficiently initialize AlwaysOn availability group.", "id": "id string", "date": "09/10/2017" } ] }, { "category": "Application B Enhancements", "articles": [ { "headline": "Application B support", "description": "Introduces CDM support for Application B. Single tenant configuration can now be automatically protected using storage snapshot.", "benefit": "Protect your latest version of Application B using Product Copy Data Management", "id": "id string", "date": "09/10/2017" }, { "headline": "Recovery improvements", "description": "Product now enables Instant recovery of Application B database to original location from the UI portal.", "benefit": "Flexible recovery options", "id": "id string", "date": "09/10/2017" } ] }, { "category": "Application C support", "articles": [ { "headline": "Application C support", "description": "Product now supports Application C configurations running 9 and above.", "benefit": "Automatically and efficiently initialize AlwaysOn availability group.", "id": "id string", "date": "09/10/2017" } ] } ] } ] ================================================ FILE: src/assets/api/port-model.json ================================================ [ { "serviceName": "Secure Shell (SSH) ", "portNumber": "22", "transportProtocol": "tcp", "description": "Secure Shell (SSH)" }, { "serviceName": "sunrpc", "portNumber": "111", "transportProtocol": "tcp", "description": "Remote Procedure Call (RPC) port binding." }, { "serviceName": "LDAP", "portNumber": "389", "transportProtocol": "tcp", "description": "Default LDAP port." }, { "serviceName": "Default TLS port", "portNumber": "443", "transportProtocol": "tcp", "description": "Default web server secure port used with SPP, VCenter and ESX Hosts" }, { "serviceName": "LDAP (TLS)", "portNumber": "636", "transportProtocol": "tcp", "description": "Default LDAP support that encrypts data in flight using TLS." }, { "serviceName": "nfs", "portNumber": "2049", "transportProtocol": "tcp", "description": "Used for NFS data transfer to/from vSnap." }, { "serviceName": "iSCSI", "portNumber": "3260", "transportProtocol": "tcp", "description": "Used for iSCSI data transfer to/from vSnap." }, { "serviceName": "mountd", "portNumber": "20048", "transportProtocol": "tcp", "description": "NFS mount protocol" }, { "serviceName": "VADP Proxy", "portNumber": "8080", "transportProtocol": "tcp", "description": "VMware VM data mover management interface. " }, { "serviceName": "vSnap", "portNumber": "8900", "transportProtocol": "tcp", "description": "VSnap management service" }, { "serviceName": "VADP Proxy on vSnap node", "portNumber": "8098", "transportProtocol": "tcp", "description": "VMware VM data mover management interface running on vSnap" }, { "serviceName": "ESXI Network File Copy (NFC)", "portNumber": "902", "transportProtocol": "tcp", "description": "ESXI Network File Copy (NFC)" }, { "serviceName": "Discovery Service", "portNumber": "8761", "transportProtocol": "tcp", "description": "Automatically discovers VADP proxies and is used by IBM Spectrum Protect Plus VM backup operations." }, { "serviceName": "Rabbit MQ", "portNumber": "5671", "transportProtocol": "tcp", "description": "Message framework used for communicating asynchronous events." } ] ================================================ FILE: src/assets/api/stat-database-model.json ================================================ { "name":"Databases", "total":200, "protected":100, "percentProtected":40, "icon":"database icon", "color":"orange" } ================================================ FILE: src/assets/api/stat-job-model.json ================================================ { "name":"Jobs", "total":50, "protected":50, "percentProtected":40, "icon":"settings icon", "color":"blue" } ================================================ FILE: src/assets/api/stat-undefined-model.json ================================================ { "name":"Virtual Machines", "total":565, "protected":200, "percentProtected":40, "icon":"desktop icon" } ================================================ FILE: src/assets/api/stat-vm-model.json ================================================ { "name":"Virtual Machines", "total":565, "protected":200, "percentProtected":40, "icon":"desktop icon", "color":"green" } ================================================ FILE: src/assets/api/stat-volume-model.json ================================================ { "name":"Volumes", "total":252, "protected":70, "percentProtected":30, "icon":"hdd outline icon", "color":"purple" } ================================================ FILE: src/assets/api/storage-model.json ================================================ { "volumes": [ { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1356808419", "poolId": "510143747", "policyId": "Policy_200", "protocolType": "NFS", "total": 699, "allocated": 559, "available": 140, "fileCount": 73743181, "vmCount": 20, "snapshotCount": 94, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "50336434", "poolId": "510143747", "policyId": "Policy_272", "protocolType": "CIFS", "total": 131, "allocated": 104, "available": 27, "fileCount": 140916696, "vmCount": 65, "snapshotCount": 164, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1400372106", "poolId": "510143747", "policyId": "Policy_525", "protocolType": "MIXED", "total": 554, "allocated": 443, "available": 111, "fileCount": 674658385, "vmCount": 27, "snapshotCount": 133, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "mixed" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "438801716", "poolId": "510143747", "policyId": "Policy_961", "protocolType": "NFS", "total": 1688, "allocated": 1350, "available": 338, "fileCount": 509518383, "vmCount": 100, "snapshotCount": 51, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "179172896", "poolId": "510143747", "policyId": "Policy_673", "protocolType": "CIFS", "total": 188, "allocated": 150, "available": 38, "fileCount": 1423354710, "vmCount": 41, "snapshotCount": 170, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1863489116", "poolId": "510143747", "policyId": "Policy_494", "protocolType": "MIXED", "total": 1478, "allocated": 1182, "available": 296, "fileCount": 2055981538, "vmCount": 24, "snapshotCount": 218, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "mixed" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "679542288", "poolId": "510143747", "policyId": "Policy_141", "protocolType": "NFS", "total": 1576, "allocated": 1260, "available": 316, "fileCount": 484598180, "vmCount": 91, "snapshotCount": 91, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1448008057", "poolId": "510143747", "policyId": "Policy_865", "protocolType": "CIFS", "total": 187, "allocated": 149, "available": 38, "fileCount": 1455150084, "vmCount": 69, "snapshotCount": 29, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "107331726", "poolId": "510143747", "policyId": "Policy_506", "protocolType": "MIXED", "total": 1060, "allocated": 848, "available": 212, "fileCount": 888401401, "vmCount": 96, "snapshotCount": 121, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "mixed" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1696309046", "poolId": "684805883", "policyId": "Policy_807", "protocolType": "NFS", "total": 490, "allocated": 392, "available": 98, "fileCount": 981393962, "vmCount": 82, "snapshotCount": 175, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "467763111", "poolId": "684805883", "policyId": "Policy_434", "protocolType": "CIFS", "total": 464, "allocated": 371, "available": 93, "fileCount": 1683837460, "vmCount": 40, "snapshotCount": 41, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "892564621", "poolId": "684805883", "policyId": "Policy_383", "protocolType": "MIXED", "total": 1981, "allocated": 1584, "available": 397, "fileCount": 705071166, "vmCount": 61, "snapshotCount": 241, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "mixed" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1805386240", "poolId": "684805883", "policyId": "Policy_644", "protocolType": "NFS", "total": 823, "allocated": 658, "available": 165, "fileCount": 1184142679, "vmCount": 77, "snapshotCount": 42, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "744675109", "poolId": "2072746734", "policyId": "Policy_927", "protocolType": "NFS", "total": 2022, "allocated": 1617, "available": 405, "fileCount": 1047060306, "vmCount": 19, "snapshotCount": 145, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1984542573", "poolId": "2072746734", "policyId": "Policy_940", "protocolType": "CIFS", "total": 386, "allocated": 308, "available": 78, "fileCount": 1535796442, "vmCount": 19, "snapshotCount": 111, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1594459696", "poolId": "2072746734", "policyId": "Policy_812", "protocolType": "MIXED", "total": 1303, "allocated": 1042, "available": 261, "fileCount": 1423752387, "vmCount": 51, "snapshotCount": 92, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "mixed" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1119961550", "poolId": "2072746734", "policyId": "Policy_211", "protocolType": "NFS", "total": 656, "allocated": 524, "available": 132, "fileCount": 1425203788, "vmCount": 31, "snapshotCount": 188, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1888422731", "poolId": "2072746734", "policyId": "Policy_239", "protocolType": "CIFS", "total": 791, "allocated": 632, "available": 159, "fileCount": 917272960, "vmCount": 74, "snapshotCount": 10, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "1639523587", "poolId": "2072746734", "policyId": "Policy_391", "protocolType": "MIXED", "total": 1197, "allocated": 957, "available": 240, "fileCount": 1765944241, "vmCount": 20, "snapshotCount": 166, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "mixed" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "396800785", "poolId": "1367767837", "policyId": "Policy_177", "protocolType": "NFS", "total": 1350, "allocated": 1080, "available": 270, "fileCount": 940273680, "vmCount": 60, "snapshotCount": 66, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "2082371931", "poolId": "1367767837", "policyId": "Policy_53", "protocolType": "CIFS", "total": 2016, "allocated": 1612, "available": 404, "fileCount": 1899545025, "vmCount": 94, "snapshotCount": 36, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "cifs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] }, { "type": "ZFSVOLUME", "timestamp": 1511668600434, "volumeId": "387140983", "poolId": "1367767837", "policyId": "Policy_142", "protocolType": "MIXED", "total": 1318, "allocated": 1054, "available": 264, "fileCount": 128550758, "vmCount": 69, "snapshotCount": 202, "compressionRatio": 2.5, "dedupeEfficiency": 25, "name": "ZFSVOLUME", "description": "ZFS Volume Description.", "icon": "assets/images/volume.png", "tags": [ { "facet": "Protocol", "name": "nfs" }, { "facet": "Range", "name": "allocated" } ], "actions": [ { "name": "view policy" } ] } ] } ================================================ FILE: src/assets/api/todo-model.json ================================================ { "name":"Todo", "value":"Example service data!" } ================================================ FILE: src/assets/api/trend-model.json ================================================ { "data": [ { "name": "Resource A", "series": [ { "name": "January", "value": 3453 }, { "name": "February", "value": 3434434 }, { "name": "March", "value": 333335 }, { "name": "April", "value": 434 } ] }, { "name": "Resource B", "series": [ { "name": "January", "value": 78 }, { "name": "February", "value": 82700 }, { "name": "March", "value": 4333335 }, { "name": "April", "value": 8345 } ] }, { "name": "Resource C", "series": [ { "name": "January", "value": 5043344 }, { "name": "February", "value": 5800 }, { "name": "March", "value": 333335 }, { "name": "April", "value": 4343333 } ] } ] } ================================================ FILE: src/assets/api/trendline-help-model.json ================================================ { "heading": "Trends", "summary": "", "details": [ { "category": "IOPS", "articles": [ { "headline": "IOPS", "description": "ECX enables automated seeding of your SQL Server AlwaysON availability group by leveraging Storage snapshot technology to minimize the data movement between primary and secondary replica.", "benefit": "Automatically and efficiently initialize AlwaysOn availability group.", "id": "id string", "date": "09/10/2017" } ] }, { "category": "CPU", "articles": [ { "headline": "SAP HANA 2.0 support", "description": "ECX 2.7 introduces CDM support for SAP HANA 2.0 SPS01. Single tenant configuration can now be automatically protected using storage snapshot.", "benefit": "Protect your latest version of SAP HANA using ECX Copy Data Management", "id": "id string", "date": "09/10/2017" }, { "headline": "Recovery improvements", "description": "ECX now enables Instant recovery of SAP HANA database to original location from the UI portal.", "benefit": "Flexible recovery options", "id": "id string", "date": "09/10/2017" } ] } ] } ================================================ FILE: src/assets/api/version-model.json ================================================ [ { "name": "metric 1", "series": [ { "name": "used", "value": 75 }, { "name": "available", "value": 100 } ] }, { "name": "metric 2", "series": [ { "name": "used", "value": 23 }, { "name": "available", "value": 77 } ] } ] ================================================ FILE: src/environments/environment.prod.ts ================================================ export const environment = { production: true, productName: "Dynamic Dashboard Framework", productVersion: "0.0.4", menu:{ documentation:false, ai: false, notification:false }, boardConfiguration:{ board:true, ai:false, endpoint:true } }; ================================================ FILE: src/environments/environment.ts ================================================ // The file contents for the current environment will overwrite these during build. // The build system defaults to the dev environment which uses `environment.ts`, but if you do // `ng build --env=prod` then `environment.prod.ts` will be used instead. // The list of which env maps to which file can be found in `.angular-cli.json`. export const environment = { production: false, productName: "Dynamic Dashboard Framework", productVersion: "0.0.5", menu:{ documentation:true, aiSearch: true, notification:true }, boardConfiguration:{ board:true, ai:true, endpoint:true } }; ================================================ FILE: src/index.html ================================================ NGX Dynamic Dashboard Framework
================================================ FILE: src/main.ts ================================================ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule); ================================================ FILE: src/polyfills.ts ================================================ /** * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. * * This file is divided into 2 sections: * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. * 2. Application imports. Files imported after ZoneJS that should be loaded before your main * file. * * The current setup is for so-called "evergreen" browsers; the last versions of browsers that * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. * * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html */ /*************************************************************************************************** * BROWSER POLYFILLS */ /** IE9, IE10 and IE11 requires all of the following polyfills. **/ // import 'core-js/es6/symbol'; // import 'core-js/es6/object'; // import 'core-js/es6/function'; // import 'core-js/es6/parse-int'; // import 'core-js/es6/parse-float'; // import 'core-js/es6/number'; // import 'core-js/es6/math'; // import 'core-js/es6/string'; // import 'core-js/es6/date'; // import 'core-js/es6/array'; // import 'core-js/es6/regexp'; // import 'core-js/es6/map'; // import 'core-js/es6/set'; /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. /** IE10 and IE11 requires the following to support `@angular/animation`. */ // import 'web-animations-js'; // Run `npm install --save web-animations-js`. /** Evergreen browsers require these. **/ import 'core-js/es6/reflect'; /** ALL Firefox browsers require the following to support `@angular/animation`. **/ // import 'web-animations-js'; // Run `npm install --save web-animations-js`. /*************************************************************************************************** * Zone JS is required by Angular itself. */ import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ /** * Date, currency, decimal and percent pipes. * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 */ // import 'intl'; // Run `npm install --save intl`. ================================================ FILE: src/styles.css ================================================ /* You can add global styles to this file, and also import other style files */ @import "~@angular/material/prebuilt-themes/indigo-pink.css"; body{ background-color: whitesmoke !important; font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important; color:darkgray !important; } h1, h2{ color: #6e6e6e !important; } ================================================ FILE: src/test.ts ================================================ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'zone.js/dist/long-stack-trace-zone'; import 'zone.js/dist/proxy.js'; import 'zone.js/dist/sync-test'; import 'zone.js/dist/jasmine-patch'; import 'zone.js/dist/async-test'; import 'zone.js/dist/fake-async-test'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. declare var __karma__: any; declare var require: any; // Prevent Karma from running prematurely. __karma__.loaded = function () {}; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting() ); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); // And load the modules. context.keys().map(context); // Finally, start Karma to run the tests. __karma__.start(); ================================================ FILE: src/tsconfig.app.json ================================================ { "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", "module": "es2015", "baseUrl": "", "types": [ ] }, "exclude": [ "test.ts", "**/*.spec.ts" ] } ================================================ FILE: src/tsconfig.spec.json ================================================ { "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/spec", "module": "commonjs", "target": "es5", "baseUrl": "", "types": [ "jasmine", "node" ] }, "files": [ "test.ts", "polyfills.ts" ], "include": [ "**/*.spec.ts", "**/*.d.ts" ] } ================================================ FILE: src/typings.d.ts ================================================ /* SystemJS module definition */ declare var module: NodeModule; interface NodeModule { id: string; } declare module 'sockjs-client'; declare module 'stompjs'; ================================================ FILE: tsconfig.json ================================================ { "compileOnSave": false, "compilerOptions": { "importHelpers": true, "outDir": "./dist/out-tsc", "baseUrl": "src", "sourceMap": true, "declaration": false, "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "es5", "typeRoots": [ "node_modules/@types" ], "lib": [ "es2016", "dom" ] } } ================================================ FILE: tslint.json ================================================ { "rulesDirectory": [ "node_modules/codelyzer" ], "rules": { "callable-types": true, "class-name": true, "comment-format": [ true, "check-space" ], "curly": true, "eofline": true, "forin": true, "import-blacklist": [true], "import-spacing": true, "indent": [ true, "spaces" ], "interface-over-type-literal": true, "label-position": true, "max-line-length": [ true, 140 ], "member-access": false, "member-ordering": [ true, "static-before-instance", "variables-before-functions" ], "no-arg": true, "no-bitwise": true, "no-console": [ false, "debug", "info", "time", "timeEnd", "trace" ], "no-construct": true, "no-debugger": true, "no-duplicate-variable": true, "no-empty": false, "no-empty-interface": true, "no-eval": true, "no-inferrable-types": [true, "ignore-params"], "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, "no-unused-expression": true, "no-use-before-declare": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [ true, "check-open-brace", "check-catch", "check-else", "check-whitespace" ], "prefer-const": true, "quotemark": [ true, "single" ], "radix": true, "semicolon": [ "always" ], "triple-equals": [ true, "allow-null-check" ], "typedef-whitespace": [ true, { "call-signature": "nospace", "index-signature": "nospace", "parameter": "nospace", "property-declaration": "nospace", "variable-declaration": "nospace" } ], "typeof-compare": true, "unified-signatures": true, "variable-name": false, "whitespace": [ true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type" ], "directive-selector": [true, "attribute", "app", "camelCase"], "component-selector": [true, "element", "app", "kebab-case"], "use-input-property-decorator": true, "use-output-property-decorator": true, "use-host-property-decorator": true, "no-input-rename": true, "no-output-rename": true, "use-life-cycle-interface": true, "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, "no-access-missing-member": true, "templates-use-public": true, "invoke-injectable": true } }