├── .bowerrc ├── .cfignore ├── .eslintrc.json ├── .gitignore ├── .jshintrc ├── HISTORY.md ├── LICENSE.md ├── OSS_Notice.pdf ├── README.md ├── bower.json ├── gulpfile.js ├── manifest.yml ├── manifest.yml.template ├── package-lock.json ├── package.json ├── polymer.json ├── public ├── _index-inline-loading-script.js ├── _index.html ├── docs │ ├── ABOUT.md │ └── images │ │ ├── PredixWebAppStarterOverview.png │ │ ├── SeedRuntimeBrowserDetails.png │ │ ├── SeedRuntimeMicroservicesDetails.png │ │ ├── SeedRuntimeOverview.png │ │ ├── SeedRuntimePredixDetails.png │ │ └── SeedRuntimeServerDetails.png ├── downloads │ ├── csv │ │ └── AssetDashboard_Download.csv │ └── zip │ │ ├── AssetModel.zip │ │ └── CompressorAsset.zip ├── elements │ ├── asset-browser │ │ └── asset-browser.html │ ├── dev-guide │ │ ├── dev-guide-bootstrap.js │ │ ├── dev-guide-config.js │ │ ├── dev-guide-imports.html │ │ ├── hopscotch_custom.css │ │ ├── img │ │ │ ├── sprite-green.png │ │ │ └── sprite-orange.png │ │ └── mock-tour.js │ ├── ge-svg-logo │ │ └── ge-svg-logo.html │ ├── kpi-bar │ │ ├── kpi-bar-meter.html │ │ ├── kpi-bar.html │ │ └── kpi-bar.scss │ ├── predix-logo │ │ └── predix-logo.html │ ├── seed-app │ │ ├── seed-app.html │ │ └── seed-app.scss │ ├── seed-table │ │ ├── asset-name-validator.html │ │ └── seed-table.html │ ├── timeseries-chart │ │ ├── timeseries-chart.html │ │ └── timeseries-chart.scss │ └── views │ │ ├── about-view.html │ │ ├── rmd-view.html │ │ ├── rmd-view.scss │ │ └── simple-asset-view.html ├── images │ └── favicon.png ├── index-inline.scss └── seed-theme.scss ├── scripts ├── quickstart-predix-webapp-starter.bat └── quickstart-predix-webapp-starter.sh ├── server ├── app.js ├── localConfig.json ├── passport-config.js ├── predix-config.js ├── routes │ ├── data-exchange.js │ ├── docs.js │ ├── learning-paths.js │ ├── mock-asset.js │ ├── mock-live-data.js │ ├── mock-rmd-datasource.js │ ├── mock-time-series.js │ ├── proxy.js │ └── user-info.js └── sample-data │ ├── data-exchange │ ├── PutFieldDataCreateAsset-DataMap.json │ └── PutFieldDataUpdatehiLoThreshold-rmq.json │ ├── predix-asset │ ├── Compressor-CMMS-Compressor-2018-Scaled.json │ ├── Compressor-CMMS-Compressor-2018-clone.json │ ├── Compressor-CMMS-Compressor-2018.json │ ├── asset-model-metadata.json │ ├── asset-model.json │ ├── edge-asset.json │ └── groups.json │ ├── rmd-datasource │ ├── datagrid-compressor.json │ ├── datagrid-refinery.json │ ├── summary-compressor.json │ └── summary-refinery.json │ └── time-series │ ├── 2-tags.json │ └── compressor-2017-compression-ratio.json ├── tasks ├── compile.index.js ├── compile.sass.js ├── dist.clean.js ├── dist.copy.js ├── serve.dev.start.js ├── serve.dist.start.js └── watch.public.js ├── test ├── asset-browser-test.html ├── index.html ├── kpi-bar-test.html ├── seed-table-test.html ├── seed-test.html └── server │ └── learningpaths-spec.js ├── tutorials └── USERAUTH.md ├── version.json ├── wct.conf.js └── yarn.lock /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/bower_components", 3 | "registry": "https://registry.bower.io" 4 | } 5 | -------------------------------------------------------------------------------- /.cfignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "jsx": true 11 | }, 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | "no-const-assign": "warn", 16 | "no-this-before-super": "warn", 17 | "no-undef": "warn", 18 | "no-unreachable": "warn", 19 | "no-unused-vars": "warn", 20 | "constructor-super": "warn", 21 | "valid-typeof": "warn" 22 | } 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/bower_components 3 | public/index.html 4 | public/**/*-styles.html 5 | public/index-inline.css 6 | npm-debug.log 7 | *.log 8 | .DS_Store 9 | temp 10 | dist 11 | build 12 | .github/ 13 | .vscode/ 14 | predix-scripts/ 15 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "jquery": true, 5 | "devel": true, 6 | "esnext": true, 7 | "bitwise": true, 8 | "camelcase": true, 9 | "curly": true, 10 | "eqeqeq": true, 11 | "immed": true, 12 | "indent": 4, 13 | "latedef": true, 14 | "newcap": true, 15 | "noarg": true, 16 | "quotmark": "single", 17 | "regexp": true, 18 | "undef": true, 19 | "unused": true, 20 | "strict": true, 21 | "trailing": true, 22 | "smarttabs": true, 23 | "white": false, 24 | "globals": { 25 | /* requirejs */ 26 | "window": false, 27 | "define": false, 28 | "vRuntime": false, 29 | "Promise": false, 30 | /* ANGULAR MOCKS */ 31 | "inject": false, 32 | "module": false, 33 | "expect": false, 34 | "element": false, 35 | /* PROTRACTOR */ 36 | "protractor": false, 37 | "browser": false, 38 | "by" : false, 39 | /* MOCHA */ 40 | "describe": false, 41 | "xdescribe": false, 42 | "it": false, 43 | "xit": false, 44 | "before": false, 45 | "beforeEach": false, 46 | "after": false, 47 | "afterEach": false, 48 | "jasmine": false 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | v3.0.1 2 | =================== 3 | * added the import once command to sass to avoid import loop. 4 | 5 | v3.0.0 6 | =================== 7 | * released Polymer Only seed 8 | v1.4.2 9 | =================== 10 | * Fix dist build issue stalling on promise polyfill. 11 | 12 | v1.4.1 13 | =================== 14 | * Upgrade to Polymer 1.5.0 and latest dev dependencies. 15 | 16 | v1.4.0 17 | =================== 18 | * Upgrade to Polymer 1.4.0 19 | 20 | v1.2.17 21 | =================== 22 | * updated bower repos in response to GH Issue 23 | 24 | v1.2.16 25 | =================== 26 | * forced install of vulcanize to 1.14.8, since 1.14.9 wouldn't work. 27 | 28 | v1.2.14 29 | =================== 30 | * Updated License 31 | 32 | v1.2.13 33 | =================== 34 | * resolved merge conflict 35 | 36 | v1.2.12 37 | =================== 38 | * started merge of develop and master 39 | 40 | v1.2.11 41 | =================== 42 | * uPatch version 43 | 44 | v1.2.10 45 | =================== 46 | * Updated manifest to not have logstash and newrelic in it. 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ### GE Software Development License Agreement – General Release 2 | 3 | THIS SOFTWARE LICENSE AGREEMENT (the “License”) describes the rights granted by the General Electric Company, operating through GE Digital (also referred to as “GE Software”), located at 2623 Camino Ramon, San Ramon, CA 94583 (herein referred to as “Licensor”) to any entity (the “Licensee”) receiving a copy of any of the following GE Digital development materials: Predix DevBox; Predix Reference Application (“RefApp”); Predix Dashboard Seed; Predix Px, Predix Security Service redistributable .jar files; Predix Machine redistributable .jar files; and Predix Machine SDK . These materials may include scripts, compiled code, supporting components, and documentation and are collectively referred to as the “Licensed Programs”. Both Licensor and Licensee are referred to hereinafter as a “Party” and collectively as the “Parties” to this License 4 | 5 | ### Section 1 – Conditional Grant. 6 | 7 | No Licensee is required to accept this License for use of the Licensed Programs. In the absence of a signed license agreement between Licensor and Licensee specifying alternate terms, any use of the Licensed Programs by the Licensee shall be considered acceptance of these terms. The Licensed Programs are copyrighted and are licensed, not sold, to you. If you are not willing to be bound by the terms of this License, do not install, copy or use the Licensed Programs. If you received this software from any source other than the Licensor, your access to the Licensed Programs is NOT permitted under this License, and you must delete the software and any copies from your systems. 8 | 9 | ### Section 2 – Warranty Disclaimer. 10 | 11 | NO WARRANTIES. LICENSOR AND OUR AFFILIATES, RESELLERS, DISTRIBUTORS, AND VENDORS, MAKE NO WARRANTIES, EXPRESS OR IMPLIED, GUARANTEES OR CONDITIONS WITH RESPECT TO USE OF THE LICENSED PROGRAMS. LICENSEE’S USE OF ALL SUCH PROGRAMS ARE AT LICENSEE’S AND CUSTOMERS’ OWN RISK. LICENSOR PROVIDES THE LICENSED PROGRAMS ON AN “AS IS” BASIS “WITH ALL FAULTS” AND “AS AVAILABLE.” LICENSOR DOES NOT GUARANTEE THE ACCURACY OR TIMELINESS OF INFORMATION AVAILABLE FROM, OR PROCESSED BY, THE LICENSED PROGRAMS. TO THE EXTENT PERMITTED UNDER LAW, LICENSOR EXCLUDES ANY IMPLIED WARRANTIES, INCLUDING FOR MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, WORKMANLIKE EFFORT, AND NON-INFRINGEMENT. NO GUARANTEE OF UNINTERRUPTED, TIMELY, SECURE, OR ERROR-FREE OPERATION IS MADE. 12 | 13 | THESE LICENSED PROGRAMS MAY BE USED AS PART OF A DEVELOPMENT ENVIRONMENT, AND MAY BE COMBINED WITH OTHER CODE BY END-USERS. LICENSOR IS NOT ABLE TO GUARANTEE THAT THE LICENSED PROGRAMS WILL OPERATE WITHOUT DEFECTS WHEN USED IN COMBINATION WITH END-USER SOFTWARE. LICENSEE IS ADVISED TO SAFEGUARD IMPORTANT DATA, TO USE CAUTION, AND NOT TO RELY IN ANY WAY ON THE CORRECT FUNCTIONING OR PERFORMANCE OF ANY COMBINATION OF END-USER SOFTWARE AND THE LICENSED PROGRAMS AND/OR ACCOMPANYING MATERIALS. LICENSEE IS ADVISED NOT TO USE ANY COMBINATION OF LICENSED PROGRAMS AND END-USER PROVIDED SOFTWARE IN A PRODUCTION ENVIRONMENT WITHOUT PRIOR SUITABILITY AND DEFECT TESTING. 14 | 15 | ### Section 3 – Feedback. 16 | 17 | It is expressly understood, acknowledged and agreed that you may provide GE reasonable suggestions, comments and feedback regarding the Software, including but not limited to usability, bug reports and test results, with respect to Software testing (collectively, "Feedback"). If you provide such Feedback to GE, you shall grant GE the following worldwide, non-exclusive, perpetual, irrevocable, royalty free, fully paid up rights: 18 | 19 | A). to make, use, copy, modify, sell, distribute, sub-license, and create derivative works of, the Feedback as part of any product, technology, service, specification or other documentation developed or offered by GE or any of its affiliates (individually and collectively, "GE Products"); 20 | 21 | B). to publicly perform or display, import, broadcast, transmit, distribute, license, offer to sell, and sell, rent, lease or lend copies of the Feedback (and derivative works thereof) as part of any GE Product; 22 | 23 | C). solely with respect to Licensee's copyright and trade secret rights, to sublicense to third parties the foregoing rights, including the right to sublicense to further third parties; and d. to sublicense to third parties any claims of any patents owned or licensable by Licensee that are necessarily infringed by a third party product, technology or service that uses, interfaces, interoperates or communicates with the Feedback or portion thereof incorporated into a GE Product, technology or service. Further, you represent and warrant that your Feedback is not subject to any license terms that would purport to require GE to comply with any additional obligations with respect to any GE Products that incorporate any Feedback. 24 | 25 | ### Section 4 – Reserved 26 | 27 | ### Section 5 – Limitation of Liability. 28 | 29 | LIABILITY ARISING UNDER THIS LICENSE, WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), SHALL BE LIMITED TO DIRECT, OBJECTIVELY MEASURABLE DAMAGES. LICENSOR SHALL HAVE NO LIABILITY TO THE OTHER PARTY OR TO ANY THIRD PARTY, FOR ANY INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. LIABILITY FOR ANY SOFTWARE LICENSED FROM THIRD PARTIES FOR USE WITH THE SERVICES IS EXPLICILTLY DISCLAIMED AND LIMITED TO THE MAXIMUM EXTENT PERMITTED BY LAW. 30 | 31 | Notwithstanding anything to the contrary, the aggregate liability of Licensor and its suppliers under this License shall not exceed the total amounts paid by Licensee to Licensor hereunder during the one-year period immediately preceding the event which gave rise to the claims. 32 | 33 | ### Section 6 – License. 34 | 35 | A). License Grant. Subject to the terms and conditions of this License, Licensor hereby grants Licensee a worldwide, perpetual, royalty-free, non-exclusive license to: 36 | 37 | i) install the Licensed Programs on Licensee’s premises, and permit Licensee’s users to use the Licensed Programs so installed, solely for Licensee’s own development, testing, demonstration, staging, and production of Licensee’s own software that makes use of the Licensed Programs in a way that adds substantial functionality not present in the Licensed Programs (the result, a “Licensee Application”); 38 | 39 | ii) permit Licensee to permit third-party hosts (“Hosts”) to install the Licensee Application on such Hosts’ respective premises on Licensee’s behalf, and permit Licensee’s users to access and use the Licensed Programs so installed, solely for Licensee’s own development, testing, demonstration, staging and production purposes 40 | 41 | iii) install the Licensee Application on Licensee’s own premises and permit its own users to use the Licensee Application so installed on the same terms as sub-sections (i) and (ii) above. 42 | 43 | B). For the purposes of this License, the right to “use” the Licensed Programs shall include the right to utilize, run, access, store, copy, test or display the Licensed Programs. No right or license is granted or agreed to be granted to disassemble or decompile any Licensed Programs furnished in object code form, and Licensee agrees not to engage in any such conduct unless permitted by law. Reverse engineering of Licensed Programs provided in object code form is prohibited, unless such a right is explicitly granted by any explicit license subject to sub-section (d) below or as a matter of law, and then only to the extent explicitly permitted. Licensor shall have no obligation to support any such reverse engineering, any product or derivative of such reverse engineering, or any use of the Licensed Programs with any modified versions of any of their components under this License. 44 | 45 | C). Licensee shall ensure that any Licensee Applications incorporate the Licensed Programs in such a way as to prevent third parties (other than Hosts) from viewing the code of the Licensed Programs or gaining access to any programmatic interface or other hidden aspect of the Licensed Programs. Licensee shall also restrict distribution of the Licensed Programs, including as part of Licensee Applications, to only those parties who are notified of, and subject to, an enforceable obligation to refrain from any of the prohibited activities listed herein, such as reverse engineering or disassembling the Licensed Programs. 46 | 47 | D). Use of some open source and third party software applications or components included in or accessed through the Licensed Programs may be subject to other terms and conditions found in a separate license agreement, terms of use or “Notice” file located at the download page. The Licensed Programs are accompanied by additional software components solely to enable the Licensed Programs to operate as designed. Licensee is not permitted to use such additional software independently of the Licensed Programs unless Licensee secures a separate license for use from the named vendor. Do not use any third party code unless you agree with the applicable license terms for that code. 48 | 49 | E). Title. Title to and ownership of the Licensed Programs shall at all times remain with Licensor. 50 | 51 | ### Section 7 – Termination. 52 | 53 | A). The Licensor reserves the right to cease distribution and grant of further licenses to any or all of the Licensed Programs at any time in its sole discretion. 54 | 55 | B). The Licensor reserves the right to at any time and at its sole discretion provide updated versions of any or all of the Licensed Programs that supercede and replace the prior version of that Licensed Program. 56 | 57 | C). Your license rights under Section 6 are effective until terminated as described below: 58 | 59 | i). This license and all rights under it will terminate or cease to be effective without notice if Licensee breaches the terms of the License and does not correct or remedy such breach promptly. 60 | 61 | ii). Notwithstanding the foregoing, Licensee may terminate this License at any time for any reason or no reason by providing the Licensor written notice thereof. 62 | 63 | D). Upon any expiration or termination of this License, the rights and licenses granted to you under this License shall immediately terminate, and you shall immediately cease using and delete the Licensed Programs. Licensee Applications based upon the Licensed Programs (see Section 6(a) above) are not subject to this limitation. 64 | 65 | In the event of any expiration or termination of this Licensee, any Confidentiality provision, disclaimers of GE’s representations and warranties, choice of applicable law and limitations of GE’s liability shall survive. 66 | 67 | ### Section 8 – Applicable Law. 68 | 69 | The License shall be governed by and interpreted in accordance with the substantive law of the State of California, U.S.A., excluding its conflicts of law provisions, and by the courts of that state. 70 | -------------------------------------------------------------------------------- /OSS_Notice.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/OSS_Notice.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Predix WebApp Starter 2 | 3 | *NOTE:* This project was formerly known as the "Predix Seed." Code for old versions of this project can still be found in the old Github repository: https://github.com/predixdev/predix-seed 4 | 5 | ## What is the Predix WebApp Starter? 6 | The Predix WebApp Starter ("Starter") is a web application starter kit aimed to accelerate Predix application development. It comes in the form of a simple web application, with code examples on features such as branding, theming, layout, navigation, responsiveness, organization of views, data presentation and micro-services integration, to name some. These working code samples can be straightforwardly customized and adapted to specific application needs. Predix application projects can directly use, remove from or add to these features to achieve prototype or production state much faster than through building everything from scratch. This speeds up application development, letting developers focus on functionality, instead of having to make boilerplate concerns work. 7 | 8 | As its name indicates the Starter is built on [Polymer](http://www.polymer-project.org). Based on the [Web Component API](https://developer.mozilla.org/en-US/docs/Web/Web_Components), Polymer is a component framework that prefers the browser's native capabilities over HTML and JavaScript enhancements, wherever possible. And where there are differences in currently available features, polyfills are provided towards consistent cross-browser behavior. By adopting the Polymer strategy the Starter ensures high consistency of application behavior across browsers, and the best chances of compatibility with future browser versions. 9 | 10 | Most of the frontend components provided in the Starter are from [Predix UI Components](https://www.predix-ui.com), which are also built on Polymer. These re-usable UI building blocks have been researched and designed to address the most common UI patterns. Both built upon Polymer, the Starter and Px Components work together out-of-the-box. Px Components can be used independently, or in combination with one another and with the Starter. This achieves consistent behavior, look-and-feel, and high code re-use. 11 | 12 | The backend of the Starter is implemented as a NodeJS/Express web server. It presently includes a minimal set of public modules and a couple of Predix-specific modules (for session and proxy concerns, for example). Similar to the frontend, it is also straightforwardly customizable, even replaceable by another server application, if so desired. [NodeJS](http://nodejs.org) is a server-side application framework based on JavaScript. It enjoys strong growth and huge adoption in the server applications community. 13 | 14 | Many features offered by the Starter are from open-source component projects, many of which are actively discussed and contributed to. This provides developers with available documentation and help in using such components for their projects. 15 | 16 | ## Getting Started 17 | 18 | ### Get the source code 19 | Make a directory for your project. Clone or download and extract the starter in that directory. 20 | ``` 21 | git clone https://github.com/PredixDev/predix-webapp-starter.git 22 | cd predix-webapp-starter 23 | ``` 24 | 25 | ### Install tools 26 | If you don't have them already, you'll need node, bower and gulp to be installed globally on your machine. 27 | 28 | 1. Install [node](https://nodejs.org/en/download/). This includes npm - the node package manager. 29 | 2. Install [bower](https://bower.io/) globally `npm install bower -g` 30 | 3. Install [gulp](http://gulpjs.com/) globally `npm install gulp-cli -g` 31 | 32 | ### Install the dependencies 33 | Change directory into the new project you just cloned, then install dependencies. 34 | ``` 35 | npm install 36 | bower install 37 | ``` 38 | 39 | ## Running the app locally 40 | The default gulp task will start a local web server. Just run this command: 41 | ``` 42 | gulp 43 | ``` 44 | Browse to http://localhost:5000. 45 | Initially, the app will use mock data for the views service, asset service, and time series service. 46 | Later you can connect your app to real instances of these services. 47 | 48 | ## Running in Predix Cloud 49 | With a few commands you can build a distribution version of the app, and deploy it to the cloud. 50 | 51 | ### Create a distribution version 52 | Use gulp to create a distribution version of your app, which contains vulcanized files for more efficient serving. 53 | You will need to run this command every time before you deploy to the Cloud. 54 | ``` 55 | gulp dist 56 | ``` 57 | 58 | ## Push to the Cloud 59 | 60 | ### Pre-Requisites 61 | Pushing (deploying) to a cloud environment requires knowledge of the commands involved and a valid user account with the environment. GE uses Cloud Foundry for its cloud platform. For information on Cloud Foundry, refer to this [link](http://docs.cloudfoundry.org/cf-cli/index.html). 62 | 63 | ### Steps 64 | The simplest way to push the Starter application to a cloud environment is by modifying the default manifest file (manifest.yml) and using the **cf push** command, as follows: 65 | 66 | 1. Update manifest.yml 67 | 68 | Change the name field in your manifest.yml. This is all you need to do! Skip down to step 2, and you'll see the app running with mock data. 69 | If you want to connect to real Predix services, you'll need to do the following: 70 | Uncomment the services section, and change the names to match your service instances. 71 | Uncomment the two base64ClientCredential environment variables and enter the correct values for your UAA clients. 72 | The loginBase64ClientCredential should use authorization_code grant type to allow users to log in to your app. 73 | The base64ClientCredential should use client_credentials grant type to allow your app to access back end services. 74 | (app_client_id will have the scopes set up to access time series, asset, etc. login_client_id will not have any of those scopes.) 75 | 76 | ``` 77 | --- 78 | applications: 79 | - name: my-predix-starter 80 | memory: 64M 81 | buildpack: nodejs_buildpack 82 | command: node server/app.js 83 | #services: 84 | # - -secure-uaa-instance 85 | # - -timeseries-instance 86 | # - -asset-instance 87 | env: 88 | node_env: cloud 89 | uaa_service_label : predix-uaa 90 | # Add these values for authentication in the cloud 91 | #base64ClientCredential: dWFhLWNsaWVudC1pZDp1YWEtY2xpZW50LWlkLXNlY3JldA== 92 | #loginBase64ClientCredential: bG9naW5fY2xpZW50X2lkOnNlY3JldA== 93 | ``` 94 | 95 | 2. Push to the cloud. 96 | 97 | ``` 98 | cf push 99 | ``` 100 | 101 | 3. Access the cloud deployment of your Starter application 102 | 103 | The output of the **cf push** command includes the URL to which your application was deployed. Below is an example: 104 | 105 | ``` 106 | Showing health and status for app my-predix-starter in org my-org / space dev as developer@gmail.com... 107 | OK 108 | 109 | requested state: started 110 | instances: 1/1 111 | usage: 128M x 1 instances 112 | urls: my-predix-starter.run.aws-usw02-pr.ice.predix.io 113 | last uploaded: Mon Apr 17 18:35:03 UTC 2017 114 | stack: cflinuxfs2 115 | buildpack: nodejs_buildpack 116 | 117 | state since cpu memory disk details 118 | #0 running 2017-04-17 11:35:40 AM 0.0% 63.5M of 128M 90.9M of 1G 119 | ``` 120 | 121 | Access your Starter application by adding "https://" to the beginning of the URL, and loading that URL in a web browser. 122 | 123 | ## Support and Further Information 124 | 125 | Ask questions and file tickets on https://www.predix.io/community. 126 | 127 | This application also serves as the UI for the Predix RMD Reference App. For more information: 128 | - [Predix RMD Reference App Dev Guide](https://www.predix.io/resources/tutorials/journey.html#1610) 129 | - [predix-rmd-ref-app Github repo](https://github.com/predixdev/predix-rmd-ref-app) 130 | 131 | # Copyright 132 | Copyright © 2015, 2016, 2017, 2018 GE Global Research. All rights reserved. 133 | 134 | The copyright to the computer software herein is the property of 135 | GE Global Research. The software may be used and/or copied only 136 | with the written permission of GE Global Research or in accordance 137 | with the terms and conditions stipulated in the agreement/contract 138 | under which the software has been supplied. 139 | 140 | [![Analytics](https://predix-beacon.appspot.com/UA-82773213-1/predix-webapp-starter/readme?pixel)](https://github.com/PredixDev) 141 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "predix-webapp-starter", 3 | "version": "^3.1.0", 4 | "private": true, 5 | "ignore": [ 6 | "**/.*", 7 | "node_modules", 8 | "bower_components", 9 | "public/bower_components", 10 | "test", 11 | "sass", 12 | "tutorials" 13 | ], 14 | "dependencies": { 15 | "hopscotch": "^0.2.6", 16 | "polymer": "^1.9.0", 17 | "px-view": "^1.1.1", 18 | "app-route": "PolymerElements/app-route#^0.9.1", 19 | "px": "^1.4.3", 20 | "px-card": "^1.0.0", 21 | "px-login": "^2.0.0", 22 | "iron-ajax": "^2.0.0", 23 | "px-data-table": "^4.0.0", 24 | "es6-promise": "~3.3.1", 25 | "moment": "^2.17.1", 26 | "px-rangepicker": "^2.0.0", 27 | "px-dropdown": "^4.0.0", 28 | "px-vis-timeseries": "^3.0.0", 29 | "marked-element": "^1.3.1", 30 | "px-branding-bar": "^1.0.6", 31 | "px-key-value-pair": "^1.1.2", 32 | "iron-resizable-behavior": "^2.0.1", 33 | "px-validation": "^1.0.6", 34 | "px-alert-message": "^1.1.2", 35 | "px-chip": "^1.0.8", 36 | "px-context-browser": "^2.0.14", 37 | "px-app-nav": "^2.1.1", 38 | "px-breadcrumbs": "^1.1.1", 39 | "px-app-helpers": "^2.1.0", 40 | "px-modal": "^2.0.0", 41 | "px-theme": "~3.0.0", 42 | "px-dark-theme": "~2.0.0", 43 | "px-spacing-design": "^1.0.0", 44 | "px-buttons-design": "^2.0.0", 45 | "px-widths-responsive-design": "^1.0.0", 46 | "px-starter-kit-design": "^1.0.0", 47 | "px-layout-design": "^1.0.0", 48 | "px-headings-design": "^1.0.3", 49 | "px-meta-lists-design": "^1.0.1", 50 | "px-forms-design": "^2.0.6", 51 | "px-datetime-picker": "1.2.0" 52 | }, 53 | "resolutions": { 54 | "px-app-helpers": "^2.0.0", 55 | "webcomponentsjs": "^0.7.24" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const gulp = require('gulp'); 3 | const plugins = require('gulp-load-plugins')(); 4 | const gulpSequence = require('gulp-sequence'); 5 | const exec = require('child_process').exec; 6 | 7 | var dev = process.argv.indexOf('--dist') < 0; 8 | 9 | // ----------------------------------------------------------------------------- 10 | // Task: Polymer CLI - use the Polymer CLI for bundling and JS minification 11 | // See polymer.json for Polymer CLI build options. 12 | // ----------------------------------------------------------------------------- 13 | gulp.task('polymer:cli', function (cb) { 14 | exec('node ./node_modules/polymer-cli/bin/polymer.js build', function (err, stdout, stderr) { 15 | console.log(stdout); 16 | console.log(stderr); 17 | cb(err); 18 | }); 19 | }); 20 | 21 | // ----------------------------------------------------------------------------- 22 | // getTask() loads external gulp task script functions by filename 23 | // ----------------------------------------------------------------------------- 24 | function getTask(task) { 25 | return require('./tasks/' + task)(gulp, plugins); 26 | } 27 | 28 | // ----------------------------------------------------------------------------- 29 | // Task: Compile : Scripts, Sass, EJS, All 30 | // ----------------------------------------------------------------------------- 31 | gulp.task('compile:sass', getTask('compile.sass')); 32 | gulp.task('compile:index', ['compile:sass'], getTask('compile.index')); 33 | 34 | // ----------------------------------------------------------------------------- 35 | // Task: Serve : Start 36 | // ----------------------------------------------------------------------------- 37 | gulp.task('serve:dev:start', getTask('serve.dev.start')); 38 | gulp.task('serve:dist:start', ['dist'], getTask('serve.dist.start')); 39 | 40 | // ----------------------------------------------------------------------------- 41 | // Task: Watch : Source, Public, All 42 | // ----------------------------------------------------------------------------- 43 | gulp.task('watch:public', getTask('watch.public')); 44 | 45 | // ----------------------------------------------------------------------------- 46 | // Task: Dist (Build app ready for deployment) 47 | // clean, compile:sass, compile:index, bundle (using polymer cli), copy 48 | // ----------------------------------------------------------------------------- 49 | gulp.task('dist', function(cb) { 50 | gulpSequence( 51 | 'dist:clean', 52 | 'compile:index', 53 | 'polymer:cli', 54 | 'dist:copy' 55 | )(cb); 56 | }); 57 | 58 | // ----------------------------------------------------------------------------- 59 | // Task: Dist : Copy extra files for deployment, that weren't bundled. 60 | // ----------------------------------------------------------------------------- 61 | gulp.task('dist:copy', getTask('dist.copy')); 62 | 63 | // ----------------------------------------------------------------------------- 64 | // Task: Dist : Clean 'dist/'' folder 65 | // ----------------------------------------------------------------------------- 66 | gulp.task('dist:clean', getTask('dist.clean')); 67 | 68 | // ----------------------------------------------------------------------------- 69 | // Task: Default (compile source, start server, watch for changes) 70 | // run "gulp --dist" to serve up files from the build directory. 71 | // ----------------------------------------------------------------------------- 72 | gulp.task('default', function (cb) { 73 | if (dev) { 74 | gulpSequence('compile:index', 'watch:public', 'serve:dev:start')(cb); 75 | } else { 76 | gulpSequence('serve:dist:start')(cb); 77 | } 78 | }); 79 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: predix-webapp-starter 4 | memory: 128M 5 | timeout: 180 6 | buildpack: nodejs_buildpack 7 | command: node server/app.js 8 | path: build/es5-basic 9 | #services: 10 | # - your-name-uaa 11 | # - your-name-time-series 12 | # - your-name-asset 13 | env: 14 | node_env: cloud 15 | uaa_service_label : predix-uaa 16 | # Add these values for authentication in the cloud 17 | #base64ClientCredential: dWFhLWNsaWVudC1pZDp1YWEtY2xpZW50LWlkLXNlY3JldA== 18 | #loginBase64ClientCredential: bG9naW5fY2xpZW50X2lkOnNlY3JldA== 19 | # Use a full wss:// URL for actual web socket server. use /livestream for mock data. 20 | websocketServerURL: /livestream 21 | timeSeriesOnly: false 22 | assetModel: "sample-data/predix-asset/Compressor-CMMS-Compressor-2018.json" 23 | #rmdDatasourceURL: {URL of your RMD datasource microservice} 24 | rmdDocsURL: "https://raw.githubusercontent.com/PredixDev/predix-rmd-ref-app/master/README.md" 25 | #dataExchangeURL: {URL of your data exchange microservice} 26 | -------------------------------------------------------------------------------- /manifest.yml.template: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: predix-webapp-starter 4 | memory: 128M 5 | timeout: 180 6 | buildpack: nodejs_buildpack 7 | command: node server/app.js 8 | path: build/es5-basic 9 | #services: 10 | # - your-name-uaa 11 | # - your-name-time-series 12 | # - your-name-asset 13 | env: 14 | node_env: cloud 15 | uaa_service_label : predix-uaa 16 | # Add these values for authentication in the cloud 17 | #base64ClientCredential: dWFhLWNsaWVudC1pZDp1YWEtY2xpZW50LWlkLXNlY3JldA== 18 | #loginBase64ClientCredential: bG9naW5fY2xpZW50X2lkOnNlY3JldA== 19 | # Use a full wss:// URL for actual web socket server. use /livestream for mock data. 20 | websocketServerURL: /livestream 21 | timeSeriesOnly: false 22 | assetModel: "sample-data/predix-asset/Compressor-CMMS-Compressor-2018.json" 23 | #rmdDatasourceURL: {URL of your RMD datasource microservice} 24 | rmdDocsURL: "https://raw.githubusercontent.com/PredixDev/predix-rmd-ref-app/master/README.md" 25 | #dataExchangeURL: {URL of your data exchange microservice} 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "predix-webapp-starter", 3 | "version": "2.0.92", 4 | "private": true, 5 | "engines": { 6 | "node": ">=0.10.0" 7 | }, 8 | "author": { 9 | "name": "Predix Adoption", 10 | "email": "predix.adoption@ge.com" 11 | }, 12 | "scripts": { 13 | "start": "node server/app.js", 14 | "test": "node_modules/mocha/bin/mocha test/server" 15 | }, 16 | "keywords": [ 17 | "predix-ui-seed", 18 | "predix-seed", 19 | "predix-webapp-starter" 20 | ], 21 | "devDependencies": { 22 | "chai": "^3.5.0", 23 | "gulp": "^3.9.0", 24 | "gulp-autoprefixer": "^3.1.0", 25 | "gulp-clean": "^0.3.2", 26 | "gulp-inline-source": "^2.1.0", 27 | "gulp-load-plugins": "^1.1.0", 28 | "gulp-nodemon": "^2.4.1", 29 | "gulp-rename": "^1.2.2", 30 | "gulp-replace": "^0.5.4", 31 | "gulp-sass": "^3.1.0", 32 | "gulp-sequence": "^0.4.6", 33 | "gulp-style-modules": "^0.1.0", 34 | "merge-stream": "^1.0.0", 35 | "mocha": "^5.2.0", 36 | "node-sass": "^4.7.0", 37 | "node-sass-import-once": "^1.2.0", 38 | "polymer-cli": "^1.9.7", 39 | "postcss": "^5.0.21", 40 | "postcss-cli": "^2.5.2", 41 | "sass-import-modules": "^2.1.0", 42 | "sinon": "^1.17.7" 43 | }, 44 | "dependencies": { 45 | "body-parser": "^1.15.2", 46 | "compression": "^1.6.2", 47 | "connect-redis": "^3.2.0", 48 | "cookie-parser": "^1.4.3", 49 | "express": "^4.14.0", 50 | "express-http-proxy": "^0.10.0", 51 | "express-session": "^1.14.1", 52 | "https-proxy-agent": "^2.2.1", 53 | "json-server": "^0.8.23", 54 | "passport": "^0.3.2", 55 | "passport-oauth2-middleware": "^1.0.2", 56 | "passport-predix-oauth": "^0.1.2", 57 | "predix-uaa-client": "^1.3.1", 58 | "request": "^2.87.0", 59 | "ws": "^5.2.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /polymer.json: -------------------------------------------------------------------------------- 1 | { 2 | "entrypoint": "public/index.html", 3 | "shell": "public/elements/seed-app/seed-app.html", 4 | "fragments": [ 5 | "public/elements/views/about-view.html", 6 | "public/elements/views/rmd-view.html", 7 | "public/elements/timeseries-chart/timeseries-chart.html", 8 | "public/elements/views/simple-asset-view.html" 9 | ], 10 | "sources": [ 11 | "public/elements/**/*", 12 | "public/index*", 13 | "public/seed*", 14 | "!public/elements/dev-guide/**" 15 | ], 16 | "builds": [ 17 | { 18 | "name": "es5-basic", 19 | "js": {"compile": true, "minify": true}, 20 | "bundle": true 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /public/_index-inline-loading-script.js: -------------------------------------------------------------------------------- 1 | // Below is a fast app loading script 2 | // The script async & lazy-loads webcomponents-lite polyfill 3 | // then checks for Polymer element 4 | // then removes #splash div to reveal application UI in #app 5 | (function() { 6 | 7 | // Wait for async loading of elements.html bundle 8 | var onWebComponentsLoaded = function() { 9 | var mainElementLink = document.querySelector('#main-element-import'); 10 | if (mainElementLink.import && mainElementLink.import.readyState === 'complete') { 11 | onMainElementLoaded(); 12 | } else { 13 | mainElementLink.addEventListener('load', onMainElementLoaded); 14 | } 15 | }; 16 | 17 | // Remove #splash div and 'loading' class from body 18 | var onMainElementLoaded = function() { 19 | // Fade splash screen, then remove 20 | var splashEl = document.getElementById('splash'); 21 | splashEl.parentNode.removeChild(splashEl); 22 | }; 23 | 24 | // load webcomponents polyfills 25 | if ('registerElement' in document && 'import' in document.createElement('link') && 'content' in 26 | document.createElement('template')) { 27 | // browser has web components, no need to load webcomponents polyfill 28 | onWebComponentsLoaded(); 29 | } else { 30 | // polyfill web components 31 | var polyfill = document.createElement('script'); 32 | polyfill.async = true; 33 | polyfill.src = '../../bower_components/webcomponentsjs/webcomponents-lite.min.js'; 34 | polyfill.onload = onWebComponentsLoaded; 35 | document.head.appendChild(polyfill); 36 | } 37 | 38 | }()); 39 | -------------------------------------------------------------------------------- /public/_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Predix WebApp Starter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 33 | 34 | 37 | 42 | 43 | 44 | 48 | 49 | 50 | 52 |
53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /public/docs/ABOUT.md: -------------------------------------------------------------------------------- 1 | 4 | The Predix WebApp Starter ("Starter") is a starter kit that accelerates development of modern web applications integrated with Predix™. Developers can get faster to a Predix web application by building upon the Starter and its components than starting one on their own. The Starter makes use of best practices and class-leading technologies to provide a highly extensible and configurable base web application that is suitable for both prototying and official development. 5 | 6 |

7 | 8 |

9 | 10 | ### Pre-built Features 11 | The Starter accelerates application development by providing pre-built features in the User Interface, Middleware and Service layers, as well as configurations, automated testing, build and deployment setup. Some of these pre-built features include: 12 | 13 | 14 | #### User Interface 15 | Layout, Branding, Navigation, Page Routing, Styles and Theming, Views, Header, Footer and Content elements 16 | 17 | 18 | #### Middleware and Services 19 | Module Configuration, Server-side Routes, Predix Services and Custom Microservice Integration 20 | 21 | 22 | #### Mock Data 23 | JSON files for UAA, Asset and Time Series Data. 24 | 25 | 26 | #### Testing and Deployment Automation 27 | Test Framework and Examples, Cloud Manifest files 28 | 29 | 30 |
31 | ### Terminology 32 | With the advent of single-page apps the lines between what is client and what is server can get confusing. Let's take a look at the Libraries, Frameworks and Runtimes used by the predix-webapp-starter. 33 | 34 | |Client | |Server | | | 35 | |:--------------|:----------------|:--------------|:--------------|:--------------| 36 | |`Library` |`Framework` |`Runtime` |`Libary` |`Framework` | 37 | | Polymer | none | Node.js | Passport | Express | 38 | 39 | You also might be thinking of some other libraries, frameworks and runtimes. Predix can run these as well and we may add starters for the popular libraries and frameworks. Predix-UI Web Components are equally at home with any of these other libraries, frameworks, and runtimes. 40 | 41 | |Client | |Server | | | 42 | |:--------------|:----------------|:--------------|:----------------|:----------------| 43 | |`Other Library`|`Other Framework`|`Other Runtime`|`Other Library` |`Other Framework`| 44 | | jQuery | AngularJS | Apache | Socket.io | Hapi | 45 | | React | Backbone | Nginx | | | 46 | | | Ember | | | | 47 | | etc | etc | etc | etc | etc | 48 | 49 |
50 | ### Technologies 51 | The Predix WebApp Starter presently uses the following technologies backed by industry best practices and alignment with Predix product strategies. 52 | 53 | 54 | #### User Interface 55 | ##### Web Components 56 | "A set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps". More information on http://www.webcomponents.org. 57 | 58 | 59 | ##### Polymer 60 | "Polymer is a JavaScript library that helps you create custom reusable HTML elements, and use them to build performant, maintainable apps. Polymer helps developers build modern Progressive Web Apps, taking full advantage of cutting-edge features like Web Components, Service Workers and HTTP/2." More information on http://www.polymer-project.org. 61 | 62 | 63 | ##### Predix UI Components 64 | "Predix UI Components are a set of user interface pieces that can be quickly snapped together to build an application. Predix Web Components give you the basic building blocks of an Industrial IOT web app, freeing up your time to spend on more important development tasks. Polymer is not required for Predix UI Components, but they do work well together." More information on http://www.predix-ui.com. 65 | 66 | 67 | #### Middleware and Services 68 | ##### NodeJS, ExpressJS and other Popular Node Modules 69 | "Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world." More information on http://www.nodejs.org. 70 | 71 | Express is the most popular web server that runs in the Node.js environment. More information on http://expressjs.com. 72 | 73 | 74 | ##### Predix™ Services 75 | "Predix helps you develop, deploy, and operate industrial apps at the edge and in the cloud. Securely connect machines, data, and analytics to improve operational efficiency." Predix offers a catalog of applications and data services geared towards the Industrial Internet space. More information on http://www.predix.io. 76 | 77 | 78 | ##### Custom Microservices 79 | Microservices created by other developers can be straightforwardly integrated with the Starter. Microservice architecture promotes encapsulation, specialization and loose coupling in implementing well-defined business capabilities. More information on Microservice Templates. 80 | 81 | While the Starter makes use of the above technology stack, it remains flexible with alternatives. For example, the User Interface layer can (and has been shown to) work with a Python-based web server instead of the NodeJS server. It is also agnostic on technology implementations for Predix™ Services and Custom Microservices. 82 | 83 |
84 | ### Runtime Overview 85 | The diagram below shows an overview of the main runtime components of a Predix web application built on the Starter. It shows the main process blocks ("Browser", "Server", "Predix" and "Custom Microservices") and the type of components and dependencies that make up each. 86 | 87 | ![WebApp Starter Runtime Overview](../docs/images/SeedRuntimeOverview.png "WebApp Starter Runtime Overview") 88 | 89 | 'Browser' will be running on the end-user's client computer. 'Server' will be the machine where middleware will execute and serve files to the Browser. 'Predix' is the collection of Predix services running in their designated cloud space. 'Microservices' is the collection of custom microservices running from their own cloud deployments as well. The arrows show the direct communications between the blocks, which will mostly be in HTTP/* . 90 | 91 | Although the Starter is presently configured so that the web server process and middleware from the same machine, there are no hard requirements for this deployment configuration. The Starter can be modified so that the web server and middleware reside in different machines. 92 | 93 | The diagram also shows the areas of the Starter where developers make changes when they extend and customize it for their web application project. These are the areas in red boxes. Changes in the 'Seed UI Components' come in the form of custom Polymer elements, JavaScript code, HTML and CSS. Changes in 'Seed Node Modules' include custom Node modules and other server code written in JavaScript, as well as configuration changes. 94 | 95 | 96 | ### Runtime Details 97 | Below are more detailed diagrams for each of the main runtime process blocks mentioned. A more complete list of the pre-built features are shown mapped into each of the Browser and Server blocks. Dependency among component types within a block are also indicated by placing dependent component types on top of the other types they depend on. For example, Seed Custom UI Components depend on Predix UI Components, and both directly depend on Polymer Elements and on the Polymer library. All of the preceding depend on the support for Web Components implemented in compatible browsers. 98 | 99 | 100 | #### Browser (User Interface) 101 | ![User Interface Layer](../docs/images/SeedRuntimeBrowserDetails.png "Browser Components") 102 | 103 | 104 | #### Server (Middleware) 105 | ![Middleware Layer](../docs/images/SeedRuntimeServerDetails.png "Server Components") 106 | 107 | 108 | #### Predix (Services) 109 | ![Services Layer - Predix](../docs/images/SeedRuntimePredixDetails.png "Predix Services") 110 | 111 | 112 | #### Microservices (Custom Services) 113 | ![Services Layer - Microservices](../docs/images/SeedRuntimeMicroservicesDetails.png "Custom Microservices") 114 | 115 | ### Reference Applications 116 | 117 | #### What is a Reference Application? 118 | A reference application is a complete example of a secure, scalable application that you can use as a model for your applications. A reference app includes many features such as Predix services, custom microservices, security, session storage, user interface, and more. The Predix WebApp Starter acts as the user interface component of the RMD Reference App. For more information on the RMD Reference app, see the github repository: https://github.com/predixdev/predix-rmd-ref-app. 119 | 120 | ### Learn More 121 | * RMD Reference App tutorials 122 | * Predix WebApp Starter tutorials 123 | * Predix Toolkit (US West) 124 | * Predix.io forum -------------------------------------------------------------------------------- /public/docs/images/PredixWebAppStarterOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/docs/images/PredixWebAppStarterOverview.png -------------------------------------------------------------------------------- /public/docs/images/SeedRuntimeBrowserDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/docs/images/SeedRuntimeBrowserDetails.png -------------------------------------------------------------------------------- /public/docs/images/SeedRuntimeMicroservicesDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/docs/images/SeedRuntimeMicroservicesDetails.png -------------------------------------------------------------------------------- /public/docs/images/SeedRuntimeOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/docs/images/SeedRuntimeOverview.png -------------------------------------------------------------------------------- /public/docs/images/SeedRuntimePredixDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/docs/images/SeedRuntimePredixDetails.png -------------------------------------------------------------------------------- /public/docs/images/SeedRuntimeServerDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/docs/images/SeedRuntimeServerDetails.png -------------------------------------------------------------------------------- /public/downloads/csv/AssetDashboard_Download.csv: -------------------------------------------------------------------------------- 1 | deviceId,uri,userGroup,location,updateDate,expirationDate,deviceType,deviceName,deviceGroup,deviceAddress,createdDate,activationDate,airportId,currentDecibels,currentAbatementAlarm,amountSaved,industrialAsset 2 | WRIDPFA28,/device/WR-IDP-FA28,/userGroup/e621893c-8573-4fe7-8c77-585a48c8828e,,11/28/2017 07:27:11 PM,01/27/2018 07:27:11 PM,NUC,Hatch-PredixEdge-01,/deviceGroup/Predix,WR-IDP-FA28,11/28/2017 07:27:11 PM,11/28/2017 07:27:11 PM,SFO,75,FALSE,60, 3 | -------------------------------------------------------------------------------- /public/downloads/zip/AssetModel.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/downloads/zip/AssetModel.zip -------------------------------------------------------------------------------- /public/downloads/zip/CompressorAsset.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/downloads/zip/CompressorAsset.zip -------------------------------------------------------------------------------- /public/elements/asset-browser/asset-browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 32 | 170 | -------------------------------------------------------------------------------- /public/elements/dev-guide/dev-guide-bootstrap.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | // Instanciate window.predix object/namespace if 3 | window.predix = window.predix || {}; 4 | 5 | // Define window-level function that checks if string value 6 | // is the name of a defined custom element 7 | window.predix.isRegistered = function(elementName) { 8 | switch(document.createElement(elementName).constructor) { 9 | case HTMLElement: return false; 10 | case HTMLUnknownElement: return undefined; 11 | } 12 | return true; 13 | } 14 | 15 | // Define window-level utility function for fetching the URL parameter 16 | function getURLParameter(theParameter) { 17 | var params = window.location.search.substr(1).split('&'); 18 | 19 | for (var i = 0; i < params.length; i++) { 20 | var p=params[i].split('='); 21 | if (p[0] == theParameter) { 22 | return decodeURIComponent(p[1]); 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | // If URL flag for developer coaching is found, 29 | // then create and show path guide (attach to element) 30 | var coaching = getURLParameter('coach'); 31 | 32 | 33 | // Called when the main element is loaded and ready, 34 | // which means that the main learning overlay can be installed 35 | function onMainElementLoaded() { 36 | // Polymer.importHref(['/elements/dev-coach/dev-coach.html'], function(){}); 37 | var link = document.createElement('link'); 38 | link.setAttribute('rel', 'import'); 39 | link.setAttribute('href', '/elements/dev-guide/dev-guide-imports.html'); 40 | link.onload = fetchLearningStatus; 41 | document.head.appendChild(link); 42 | } 43 | 44 | // Fetches the state of the developer's learning activity by quering the 45 | // installed services using the /learningpaths server route; an object should 46 | // be returned with the following structure: 47 | // 48 | // { 49 | // appMode: 'default', 50 | // learningSequence: ['base', 'uaa', 'asset', timeseries], 51 | // services: { 52 | // base: true, 53 | // uaa: false, 54 | // asset: false, 55 | // timeseries: false 56 | // } 57 | // } 58 | // 59 | // The JSON above is used to construct the 'steps' attribute for the 60 | // path guide overlay element 61 | function fetchLearningStatus() { 62 | var lp = window.predix.learningPaths = window.predix.learningPaths || {}; 63 | lp.httpR = new XMLHttpRequest(); 64 | if (!lp.httpR) { 65 | console.error('ERROR: learning paths - unable to create an XMLHTTPRequest instance'); 66 | return; 67 | } 68 | lp.httpR.onreadystatechange = onLearningPathResponse; 69 | lp.httpR.open('POST', '/learningpaths'); 70 | lp.httpR.send(); 71 | } 72 | 73 | // utility function to return TRUE the first time and that only time, 74 | // and return FALSE on any subsequent call 75 | var usedOnce = false; 76 | function useOnce() { 77 | if (!usedOnce) { 78 | usedOnce = true; 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | // handler function for the return of the /learningpaths call 85 | function onLearningPathResponse() { 86 | var httpR = window.predix.learningPaths.httpR; 87 | if (httpR.readyState === XMLHttpRequest.DONE) { 88 | if (httpR.status === 200) { 89 | window.predix.learningPaths.steps = []; 90 | var step; 91 | var ls = window.predix.learningPaths.learningState = JSON.parse(httpR.responseText); 92 | for (var i=0, sl = ls.learningSequence.length; i < sl; i++) { 93 | step = { 94 | id: i, 95 | label: ls.learningSequence[i], 96 | started: ls.services[ls.learningSequence[i]], 97 | completed: ls.services[ls.learningSequence[i]], 98 | current: !ls.services[ls.learningSequence[i]] && useOnce() 99 | }; 100 | window.predix.learningPaths.steps.push(step); 101 | } 102 | onCoachingLoaded(); 103 | } else { 104 | console.error('ERROR: learning paths - ' + httpR.statusText); 105 | } 106 | } 107 | } 108 | 109 | // callback handler for a click on each of the path guide nodes, 110 | // launches the tour for that node 111 | function tourFeature(event) { 112 | var tourID = event.payload.id; 113 | if (window.predix.isTouring) { 114 | window.predix.hopscotch.endTour(false); 115 | window.predix.isTouring = false; 116 | } 117 | window.predix.hopscotch.startTour(window.predix.hopscotchTour['tour' + tourID]); 118 | window.predix.isTouring = true; 119 | } 120 | 121 | // constructs the path guide visual overlay 122 | // to be called when the learning state has been fetched from 123 | // the /learningpaths server route 124 | function onCoachingLoaded() { 125 | // if "px-path-guide" was registered 126 | if ( window.predix.isRegistered && window.predix.isRegistered('px-path-guide')) { 127 | // create a px-path-guide custom element, 128 | // configure it and attach it to the element 129 | var pathGuideEl = document.createElement('px-path-guide'); 130 | 131 | var configProps = ['id', 'completedStepIcon', 'currentStepIcon', 'stepClickEventName']; 132 | 133 | pathGuideEl.id = "path-guide"; 134 | pathGuideEl.completedStepIcon = "fa-diamond"; 135 | pathGuideEl.currentStepIcon = "fa-circle"; 136 | pathGuideEl.stepClickEventName = window.predix.featureTourEventName; 137 | pathGuideEl.pathLabel = 'Learning Path:' 138 | 139 | for (var i=0; i < configProps.length; i++) { 140 | pathGuideEl[configProps[i]] = window.predix.pathGuideConfig[configProps[i]] || pathGuideEl[configProps[i]]; 141 | } 142 | 143 | pathGuideEl.steps = window.predix.learningPaths.steps; 144 | 145 | /* Default Styles */ 146 | pathGuideEl.style.position = 'absolute'; 147 | var width = pathGuideEl.clientWidth; 148 | var halfWidth = width / 2; 149 | pathGuideEl.style.left = "50%"; 150 | pathGuideEl.style.marginLeft = "-" + halfWidth + "px"; 151 | pathGuideEl.style.marginLeft = "-450px"; // <-------- temporary code 152 | pathGuideEl.style.top = '13px'; 153 | 154 | pathGuideEl.customStyle['--px-path-guide-icon-size'] = '16px'; 155 | pathGuideEl.customStyle['--px-path-guide-icon-color'] = '#3ab4d4'; 156 | pathGuideEl.customStyle['--px-path-guide-icon-top'] = '-8px'; 157 | pathGuideEl.customStyle['--px-path-guide-icon-left'] = '-8px'; 158 | pathGuideEl.customStyle['--px-path-guide-current-icon-size'] = '20px'; 159 | pathGuideEl.customStyle['--px-path-guide-current-icon-color'] = '#3ab4d4'; 160 | pathGuideEl.customStyle['--px-path-guide-current-icon-top'] = '-10px'; 161 | pathGuideEl.customStyle['--px-path-guide-current-icon-left'] = '-9px'; 162 | 163 | pathGuideEl.customStyle['--px-path-guide-connector-color'] = '#3ab4d4'; 164 | pathGuideEl.customStyle['--px-path-guide-connector-length'] = '150px'; 165 | 166 | pathGuideEl.customStyle['--px-path-guide-step-diameter'] = '24px'; 167 | pathGuideEl.customStyle['--px-path-guide-step-radius'] = '12px'; 168 | pathGuideEl.customStyle['--px-path-guide-step-border-color'] = '#3ab4d4'; 169 | pathGuideEl.customStyle['--px-path-guide-step-fill-color'] = '#2c404c'; 170 | pathGuideEl.customStyle['--px-path-guide-current-step-diameter'] = '30px'; 171 | pathGuideEl.customStyle['--px-path-guide-current-step-radius'] = '15px'; 172 | pathGuideEl.customStyle['--px-path-guide-current-step-border-color'] = '#fff'; 173 | pathGuideEl.customStyle['--px-path-guide-current-step-fill-color'] = '#2c404c'; 174 | 175 | pathGuideEl.customStyle['--px-path-guide-step-label-color'] = '#999'; 176 | pathGuideEl.customStyle['--px-path-guide-step-label-font-weight'] = 'normal'; 177 | pathGuideEl.customStyle['--px-path-guide-current-step-label-color'] = '#fff'; 178 | pathGuideEl.customStyle['--px-path-guide-current-step-label-font-weight'] = 'bold'; 179 | pathGuideEl.customStyle['--px-path-guide-step-label-width'] = '80px'; 180 | pathGuideEl.customStyle['--px-path-guide-step-label-top'] = '18px'; 181 | pathGuideEl.customStyle['--px-path-guide-step-label-font-size'] = '11px'; 182 | pathGuideEl.customStyle['--px-path-guide-path-label-color'] = '#3ab4d4'; 183 | /* End Default Styles */ 184 | 185 | /* Any Custom Styles specified in window.predix.pathGuideConfig */ 186 | var styles = window.predix.pathGuideConfig.styles; 187 | for (var style in styles) { 188 | if( styles.hasOwnProperty(style) && style !== 'vars') { 189 | pathGuideEl.style[style] = styles[style]; 190 | } 191 | } 192 | 193 | var styleVars = window.predix.pathGuideConfig.styles.vars; 194 | for (var styleVar in styleVars) { 195 | if( styleVars.hasOwnProperty(styleVar) ) { 196 | pathGuideEl.customStyle['--' + styleVar] = styleVars[styleVar]; 197 | } 198 | } 199 | 200 | pathGuideEl.updateStyles(); 201 | 202 | window.predix.pathGuideElement = pathGuideEl; 203 | document.body.appendChild(pathGuideEl); 204 | 205 | window.addEventListener(window.predix.featureTourEventName, tourFeature); 206 | } 207 | } 208 | 209 | if (coaching === 'true') { 210 | var mainElement = document.querySelector('#main-element-import'); 211 | if (mainElement.import && mainElement.import.readyState === 'complete') { 212 | onMainElementLoaded(); 213 | } else { 214 | mainElement.addEventListener('load', onMainElementLoaded); 215 | } 216 | } 217 | }()); 218 | -------------------------------------------------------------------------------- /public/elements/dev-guide/dev-guide-config.js: -------------------------------------------------------------------------------- 1 | window.predix = window.predix || {}; 2 | window.predix.devGuideURL = ''; 3 | window.predix.hopscotch = hopscotch; 4 | window.predix.isTouring = false; 5 | window.predix.featureTourEventName = 'FEATURE_TOUR'; 6 | window.predix.pathGuideConfig = { 7 | id : "path-guide", 8 | completedStepIcon : "fa-check", 9 | currentStepIcon : "fa-circle", 10 | stepClickEventName : window.predix.featureTourEventName, 11 | styles: { 12 | position : 'absolute', 13 | left : "800px", 14 | top : '13px', 15 | vars: { 16 | /* icon styles */ 17 | 'px-path-guide-icon-size': '14px', 18 | 'px-path-guide-icon-color': '#3ab4d4', 19 | 'px-path-guide-icon-top': '-8px', 20 | 'px-path-guide-icon-left': '-8px', 21 | 'px-path-guide-current-icon-size': '20px', 22 | 'px-path-guide-current-icon-color': '#3ab4d4', 23 | 'px-path-guide-current-icon-top': '-10px', 24 | 'px-path-guide-current-icon-left': '-9px', 25 | 26 | /* connector styles */ 27 | 'px-path-guide-connector-color': '#3ab4d4', 28 | 'px-path-guide-connector-length': '150px', 29 | 30 | /* node/step styles */ 31 | 'px-path-guide-step-diameter': '20px', 32 | 'px-path-guide-step-radius': '12px', 33 | 'px-path-guide-step-border-color': '#3ab4d4', 34 | 'px-path-guide-step-fill-color': '#2c404c', 35 | 'px-path-guide-current-step-diameter': '30px', 36 | 'px-path-guide-current-step-radius': '15px', 37 | 'px-path-guide-current-step-border-color': '#fff', 38 | 'px-path-guide-current-step-fill-color': '#2c404c', 39 | 40 | /* label styles */ 41 | 'px-path-guide-step-label-color': '#999', 42 | 'px-path-guide-step-label-font-weight': 'normal', 43 | 'px-path-guide-current-step-label-color': '#fff', 44 | 'px-path-guide-current-step-label-font-weight': 'bold', 45 | 'px-path-guide-step-label-width': '80px', 46 | 'px-path-guide-step-label-top': '18px', 47 | 'px-path-guide-step-label-font-size': '11px' 48 | } 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /public/elements/dev-guide/dev-guide-imports.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/elements/dev-guide/img/sprite-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/elements/dev-guide/img/sprite-green.png -------------------------------------------------------------------------------- /public/elements/dev-guide/img/sprite-orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/elements/dev-guide/img/sprite-orange.png -------------------------------------------------------------------------------- /public/elements/dev-guide/mock-tour.js: -------------------------------------------------------------------------------- 1 | window.predix = window.predix || {}; 2 | window.predix.hopscotchTour = { 3 | 'tour0' : { 4 | id: "basic-application", 5 | i18n: { 6 | stepNums: ['1', '1.1', '1.2', '1.3', '1.4', '1.5'] 7 | }, 8 | steps: [ 9 | { 10 | title: "Basic Application", 11 | content: "Congratulations! You have successfully installed a basic form of the Predix UI Seed, with essential features like header, navigation, content area and footer. The corresponding documentaion for this step is here", 12 | target: "step-item-0", 13 | xOffset: -30, 14 | yOffset: 30, 15 | placement: "bottom", 16 | }, 17 | { 18 | title: "Seed Header", 19 | content: "Page header for the Predix UI Seed", 20 | target: "seed-header", 21 | placement: "bottom" 22 | }, 23 | { 24 | title: "App Navigation", 25 | content: "Navigation using the 'px-app-nav' Predix UI Component", 26 | target: "app-nav", 27 | placement: "right", 28 | }, 29 | { 30 | title: "Content Area", 31 | content: "Main content area", 32 | target: "content-area", 33 | xOffset: "center", 34 | yOffset: "center", 35 | placement: "top", 36 | }, 37 | { 38 | title: "Sample View", 39 | content: "A Predix UI component for generic view encapsulation", 40 | target: "sample-view", 41 | xOffset: "center", 42 | placement: "bottom", 43 | }, 44 | { 45 | title: "Footer", 46 | content: "Application footer", 47 | target: "seed-footer", 48 | placement: "top", 49 | } 50 | ] 51 | }, 52 | 'tour1' : { 53 | id: 'uaa-integration', 54 | i18n: { 55 | stepNums: ['2', '2.1', '2.2'] 56 | }, 57 | steps: [ 58 | { 59 | title: "UAA Integration", 60 | content: "Congratulations! You have successfully integrated your application frontend with UAA service. This will allow you to secure all or specific portions of your application. You can follow this link to review the relevant guide for this step.", 61 | target: "step-item-1", 62 | xOffset: -30, 63 | yOffset: 30, 64 | placement: "bottom", 65 | }, 66 | { 67 | title: "Context Browser", 68 | content: "A Predix UI component geared for asset tree representation", 69 | target: "contextBrowser", 70 | xOffset: "center", 71 | placement: "bottom", 72 | }, 73 | { 74 | title: "Sample Visualizations", 75 | content: "Example Predix UI visualization components", 76 | target: "widgetsx3", 77 | xOffset: 500, 78 | placement: "top", 79 | } 80 | ] 81 | }, 82 | 'tour2' : { 83 | id: 'asset-data-integration', 84 | i18n: { 85 | stepNums: ['3'] 86 | }, 87 | steps: [ 88 | { 89 | title: "Asset Data Integration", 90 | content: "You are currently at a stage of the Predix UI Seed where you will be adding features for data integration, specifically with an instance of the Asset Data Service.", 91 | target: "step-item-2", 92 | xOffset: -30, 93 | yOffset: 30, 94 | placement: "bottom", 95 | } 96 | ] 97 | }, 98 | 'tour3' : { 99 | id: 'analytics-features', 100 | i18n: { 101 | stepNums: ['4'] 102 | }, 103 | steps: [ 104 | { 105 | title: "Analytics", 106 | content: "Your next step will be the installation of analytics features. Upon completion of your current step, these features will be enabled.", 107 | target: "step-item-3", 108 | xOffset: -30, 109 | yOffset: 30, 110 | placement: "bottom", 111 | } 112 | ] 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /public/elements/ge-svg-logo/ge-svg-logo.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | -------------------------------------------------------------------------------- /public/elements/kpi-bar/kpi-bar-meter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 21 | 68 | 69 | -------------------------------------------------------------------------------- /public/elements/kpi-bar/kpi-bar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 33 | 53 | 54 | -------------------------------------------------------------------------------- /public/elements/kpi-bar/kpi-bar.scss: -------------------------------------------------------------------------------- 1 | // Settings 2 | 3 | // Generic 4 | 5 | // Base 6 | @import 'px-flexbox-design/_base.flexbox.scss'; 7 | 8 | // Meta 9 | 10 | // Objects 11 | 12 | .kpi-bar-wrapper { 13 | width: 100%; 14 | min-width: 810px; 15 | height: 67px; 16 | max-height: var(--kpi-bar-wrapper-height, 80px); 17 | } 18 | 19 | .kpi-bar-meter-wrapper { 20 | display: inline-block; 21 | height: 100%; 22 | margin: 0 auto; 23 | } 24 | 25 | .kpi-bar-meter-bar-wrapper { 26 | display: inline-block; 27 | width: 4px; 28 | height: 60%; 29 | background-color: #ccc; 30 | border-radius: 1px; 31 | overflow: hidden; 32 | max-height: 60px; 33 | } 34 | 35 | .kpi-bar-meter-bar-content { 36 | height: 100%; 37 | background-color: var(--kpi-datapoint-bar-meter-color, rgb(75, 133, 169)); 38 | } 39 | 40 | // Trumps 41 | $inuit-enable-paddings--huge : true; 42 | @import "px-spacing-design/_trumps.spacing.scss"; 43 | @import "px-widths-responsive-design/_trumps.widths-responsive.scss"; -------------------------------------------------------------------------------- /public/elements/predix-logo/predix-logo.html: -------------------------------------------------------------------------------- 1 | 2 | 45 | -------------------------------------------------------------------------------- /public/elements/seed-app/seed-app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 65 | 140 | 141 | -------------------------------------------------------------------------------- /public/elements/seed-app/seed-app.scss: -------------------------------------------------------------------------------- 1 | // Settings 2 | @import "px-colors-design/_settings.colors.scss"; 3 | 4 | // Generic 5 | @import "px-normalize-design/_generic.normalize.scss"; 6 | @import "px-box-sizing-design/_generic.box-sizing.scss"; 7 | @import "px-helpers-design/_generic.helpers.scss"; 8 | 9 | // px-viewport configuration variables are documented in the px-viewport Readme.md: 10 | // https://github.com/predixdev/px-viewport-design 11 | $desk-wide-width: auto; // defaults to 100% 12 | $desk-wide-max-width: none; // defaults to 80rem 13 | $viewport-margin: 0; // defaults to auto 14 | 15 | // Base 16 | @import "px-viewport-design/_base.viewport.scss"; 17 | @import "px-flexbox-design/_base.flexbox.scss"; 18 | @import "px-headings-design/_base.headings.scss"; 19 | 20 | // Meta 21 | @import "px-meta-lists-design/_meta.lists.scss"; 22 | 23 | // Objects 24 | $inuit-enable-btn--bare : true; 25 | 26 | @import "px-buttons-design/_objects.buttons.scss"; 27 | 28 | $inuit-enable-layout--small : true; 29 | $inuit-enable-layout--flush : true; 30 | $inuit-enable-layout--full : true; 31 | 32 | @import "px-layout-design/_objects.layout.scss"; 33 | 34 | @import "../../seed-theme.scss"; 35 | 36 | :root { 37 | --px-app-nav-background-color: $gray19; 38 | --px-app-nav-link-selected-color: #364C59; 39 | --px-branding-bar-background-color: rgb(8, 41, 57); 40 | } 41 | 42 | px-login { 43 | --px-btn-bare-color: #b7c3cd; 44 | } 45 | 46 | header > div.viewport { 47 | margin: 0 15px; 48 | } 49 | 50 | main { 51 | min-height: calc(100vh - #{calculateRem(120px)}); 52 | // margin-left: calculateRem(60px); 53 | background-color: var(--px-seed-app-main-content-area-background-color, $px-page-main-content-area-background-color); 54 | overflow-x: hidden; 55 | flex: 1; 56 | } 57 | 58 | px-app-nav { 59 | z-index: 5; 60 | } 61 | 62 | // Trumps 63 | $inuit-enable-margins--tiny : true; 64 | $inuit-enable-margins--small : true; 65 | $inuit-enable-margins--large : true; 66 | $inuit-enable-margins--huge : true; 67 | $inuit-enable-margins--none : true; 68 | $inuit-enable-margins--rtl : true; 69 | $inuit-enable-margins--negative-tiny : true; 70 | $inuit-enable-paddings : true; 71 | $inuit-enable-paddings--tiny : true; 72 | $inuit-enable-paddings--small : true; 73 | $inuit-enable-paddings--large : true; 74 | $inuit-enable-paddings--huge : true; 75 | $inuit-enable-paddings--none : true; 76 | 77 | @import "px-spacing-design/_trumps.spacing.scss"; 78 | @import "px-widths-design/_trumps.widths.scss"; 79 | @import "px-widths-responsive-design/_trumps.widths-responsive.scss"; 80 | -------------------------------------------------------------------------------- /public/elements/seed-table/asset-name-validator.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /public/elements/timeseries-chart/timeseries-chart.scss: -------------------------------------------------------------------------------- 1 | // Settings 2 | 3 | // Generic 4 | 5 | // Base 6 | @import 'px-flexbox-design/_base.flexbox.scss'; 7 | 8 | // Meta 9 | 10 | // Objects 11 | #refreshTableBtnTS { 12 | margin-top: 4px; 13 | cursor: pointer; 14 | } 15 | 16 | @import "px-buttons-design/_objects.buttons.scss"; 17 | 18 | // Trumps 19 | $inuit-enable-paddings : true; 20 | $inuit-enable-paddings--tiny : true; 21 | @import "px-spacing-design/_trumps.spacing.scss"; 22 | @import "px-widths-responsive-design/_trumps.widths-responsive.scss"; -------------------------------------------------------------------------------- /public/elements/views/about-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 33 | 48 | 49 | -------------------------------------------------------------------------------- /public/elements/views/rmd-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 50 | 232 | 233 | -------------------------------------------------------------------------------- /public/elements/views/rmd-view.scss: -------------------------------------------------------------------------------- 1 | // Settings 2 | @import "px-colors-design/_settings.colors.scss"; 3 | 4 | // Generic 5 | 6 | // Base 7 | @import 'px-flexbox-design/_base.flexbox.scss'; 8 | @import "px-headings-design/_base.headings.scss"; 9 | @import "px-forms-design/_base.forms.scss"; 10 | 11 | // Meta 12 | 13 | // Objects 14 | :host { 15 | background: $gray16; 16 | } 17 | 18 | #refreshTableBtn { 19 | margin-top: 4px; 20 | cursor: pointer; 21 | } 22 | 23 | #exportBtn { 24 | line-height: inherit; 25 | color: #b6c3cc; 26 | height: 25px; 27 | vertical-align: middle; 28 | background-color: #086e87; 29 | cursor: pointer; 30 | padding: 0; 31 | margin: -12px 0 0 10px; 32 | padding: 0 5px 0 0; 33 | iron-icon { 34 | height: 15px; 35 | } 36 | } 37 | 38 | // Trumps 39 | $inuit-enable-paddings : true; 40 | $inuit-enable-paddings--huge : true; 41 | @import "px-spacing-design/_trumps.spacing.scss"; 42 | @import "px-widths-responsive-design/_trumps.widths-responsive.scss"; 43 | -------------------------------------------------------------------------------- /public/elements/views/simple-asset-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 32 | 86 | 87 | -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PredixDev/predix-webapp-starter/f129d205b28c6770cfdac08f908a1865f02cf189/public/images/favicon.png -------------------------------------------------------------------------------- /public/index-inline.scss: -------------------------------------------------------------------------------- 1 | // Seed Theme 2 | @import "seed-theme.scss"; 3 | 4 | // Settings 5 | // Tools 6 | 7 | // Generic 8 | @import "px-normalize-design/_generic.normalize.scss"; 9 | 10 | // Base 11 | $inuit-webfont-path: "/bower_components/px-typography-design/type"; 12 | @import "px-typography-design/_base.typography.scss"; 13 | @import "px-headings-design/_base.headings.scss"; 14 | 15 | // Meta 16 | // Objects 17 | // Components 18 | // Trumps 19 | 20 | // Page Styles to be inserted into index.html 21 | html { 22 | position: relative; 23 | min-height: 100%; 24 | } 25 | body { 26 | margin-bottom: calculateRem(60px); 27 | } 28 | html,body { 29 | margin: 0; 30 | background: var(--px-seed-header-background-color, $gray19); 31 | font-family: $predix-font-family; 32 | font-weight: normal; 33 | } 34 | body.loading #splash { 35 | opacity: 1; 36 | } 37 | #splash { 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | right: 0; 42 | bottom: 0; 43 | transition: opacity 500ms cubic-bezier(0,0,0.2,1); 44 | opacity: 0; 45 | will-change: opacity; 46 | z-index: 1; 47 | background: #3b3b3f; 48 | } 49 | -------------------------------------------------------------------------------- /public/seed-theme.scss: -------------------------------------------------------------------------------- 1 | // Settings 2 | @import "px-defaults-design/_settings.defaults.scss"; 3 | @import "px-colors-design/_settings.colors.scss"; 4 | 5 | // Tools 6 | @import "px-mixins-design/_tools.mixins.scss"; 7 | 8 | // theme variables 9 | $predix-font-family : "GE Inspira Sans", sans-serif !default; 10 | $px-page-html-background-color: $gray9; 11 | $px-page-link-color: $gray5; 12 | $px-page-spine-border-color: $gray8; 13 | $px-page-spine-text-color: $white; 14 | $px-page-bold-text-color: $gray3; 15 | $px-page-card-border-color: $gray3; 16 | $px-page-card-background-color: $white; 17 | $px-page-card-text-color: $gray15; 18 | $px-page-card-paragraph-text-color: $gray15; 19 | $px-page-banner-text-color: $gray4; 20 | $px-page-banner-meatball-color : $gray4; 21 | $px-page-main-content-area-background-color: $gray9; 22 | -------------------------------------------------------------------------------- /scripts/quickstart-predix-webapp-starter.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION 3 | set CURRENTDIR=%cd% 4 | echo "currentdir=!CURRENTDIR!" 5 | SET FILE_NAME=%0 6 | SET BRANCH=master 7 | SET SKIP_SETUP=FALSE 8 | SET CF_URL="" 9 | 10 | :GETOPTS 11 | IF /I [%1] == [--skip-setup] SET SKIP_SETUP=TRUE 12 | rem Here we call SHIFT twice to remove the switch and value, 13 | rem since these are not needed by the .sh script. 14 | IF /I [%1] == [-b] SET BRANCH=%2& SHIFT & SHIFT 15 | IF /I [%1] == [--branch] SET BRANCH=%2& SHIFT & SHIFT 16 | IF /I [%1] == [--cf-url] SET CF_URL=%2& SHIFT & SHIFT 17 | IF /I [%1] == [--cf-user] SET CF_USER=%2& SHIFT & SHIFT 18 | IF /I [%1] == [--cf-password] SET CF_PASSWORD=%2& SHIFT & SHIFT 19 | IF /I [%1] == [--cf-org] SET CF_ORG=%2& SHIFT & SHIFT 20 | IF /I [%1] == [--cf-space] SET CF_SPACE=%2& SHIFT & SHIFT 21 | 22 | SET QUICKSTART_ARGS=!QUICKSTART_ARGS! %1 23 | SHIFT & IF NOT [%1]==[] GOTO :GETOPTS 24 | GOTO :AFTERGETOPTS 25 | 26 | CALL :GETOPTS %* 27 | :AFTERGETOPTS 28 | 29 | ECHO SKIP_SETUP=!SKIP_SETUP! 30 | 31 | IF [!BRANCH!]==[] ( 32 | ECHO "Usage: %FILE_NAME% -b/--branch " 33 | EXIT /b 1 34 | ) 35 | 36 | SET IZON_BAT=https://raw.githubusercontent.com/PredixDev/izon/1.2.0/izon.bat 37 | SET TUTORIAL=https://www.predix.io/resources/tutorials/tutorial-details.html?tutorial_id=1475^&tag^=1719^&journey^=Predix%%20UI%%20Seed^&resources^=1475,1569,1523 38 | SET REPO_NAME=predix-webapp-starter 39 | SET SHELL_SCRIPT_NAME=quickstart-predix-webapp-starter.sh 40 | SET APP_DIR="webapp-starter" 41 | SET APP_NAME=Predix WebApp Starter 42 | SET TOOLS=Cloud Foundry CLI, Git, Node.js, Predix CLI 43 | SET TOOLS_SWITCHES=/cf /git /nodejs /predixcli 44 | 45 | SET SHELL_SCRIPT_URL=https://raw.githubusercontent.com/PredixDev/!REPO_NAME!/!BRANCH!/scripts/!SHELL_SCRIPT_NAME! 46 | SET VERSION_JSON_URL=https://raw.githubusercontent.com/PredixDev/!REPO_NAME!/!BRANCH!/version.json 47 | 48 | GOTO START 49 | 50 | :CHECK_DIR 51 | IF not "!CURRENTDIR!"=="!CURRENTDIR:System32=!" ( 52 | ECHO. 53 | ECHO. 54 | ECHO Exiting tutorial. Looks like you are in the system32 directory, please change directories, e.g. \Users\your-login-name 55 | EXIT /b 1 56 | ) 57 | GOTO :eof 58 | 59 | :CHECK_FAIL 60 | IF NOT !errorlevel! EQU 0 ( 61 | CALL :MANUAL 62 | ) 63 | GOTO :eof 64 | 65 | :MANUAL 66 | ECHO. 67 | ECHO. 68 | ECHO Exiting tutorial. You can manually go through the tutorial steps here 69 | ECHO !TUTORIAL! 70 | GOTO :eof 71 | 72 | :CHECK_PERMISSIONS 73 | echo Administrative permissions required. Detecting permissions... 74 | 75 | net session >nul 2>&1 76 | if %errorLevel% == 0 ( 77 | echo Success: Administrative permissions confirmed. 78 | ) else ( 79 | echo Failure: Current permissions inadequate. This script installs tools, ensure you are launching Windows Command window by Right clicking and choosing 'Run as Administrator'. 80 | EXIT /b 1 81 | ) 82 | GOTO :eof 83 | 84 | :INIT 85 | IF not "!CURRENTDIR!"=="!CURRENTDIR:System32=!" ( 86 | ECHO. 87 | ECHO. 88 | ECHO Exiting tutorial. Looks like you are in the system32 directory, please change directories, e.g. \Users\your-login-name 89 | EXIT /b 1 90 | ) 91 | IF not "!CURRENTDIR!"=="!CURRENTDIR:\scripts=!" ( 92 | ECHO. 93 | ECHO. 94 | ECHO Exiting tutorial. Please launch the script from the root dir of the project 95 | EXIT /b 1 96 | ) 97 | 98 | mkdir !APP_DIR! 99 | PUSHD !APP_DIR! 100 | cd 101 | 102 | ECHO Let's start by verifying that you have the required tools installed. 103 | SET /p answer=Should we install the required tools if not already installed (!TOOLS!)? 104 | IF "!answer!"=="" ( 105 | SET /p answer=Specify yes/no - 106 | ) 107 | IF "!answer:~0,1!"=="y" SET doInstall=Y 108 | IF "!answer:~0,1!"=="Y" echo doInstall=Y 109 | 110 | if "!doInstall!"=="Y" ( 111 | CALL :CHECK_PERMISSIONS 112 | IF NOT !errorlevel! EQU 0 EXIT /b !errorlevel! 113 | 114 | CALL :GET_DEPENDENCIES 115 | 116 | ECHO Calling setup-windows.bat 117 | CALL "setup-windows.bat" !TOOLS_SWITCHES! 118 | IF NOT !errorlevel! EQU 0 ( 119 | ECHO. 120 | ECHO "Unable to install tools. Is there a proxy server? Perhaps if you go on a regular internet connection (turning off any proxy variables), the tools portion of the install will succeed. Please see detailed proxy instructions at https://www.predix.io/resources/tutorials/tutorial-details.html?tutorial_id=1565" 121 | EXIT /b !errorlevel! 122 | ) 123 | ECHO. 124 | ECHO The required tools have been installed. Now you can proceed with the tutorial. 125 | pause 126 | ) 127 | 128 | GOTO :eof 129 | 130 | :GET_DEPENDENCIES 131 | ECHO Getting Dependencies 132 | 133 | powershell -Command "(new-object net.webclient).DownloadFile('!IZON_BAT!','izon.bat')" 134 | powershell -Command "(new-object net.webclient).DownloadFile('!VERSION_JSON_URL!','version.json')" 135 | CALL izon.bat READ_DEPENDENCY local-setup LOCAL_SETUP_URL LOCAL_SETUP_BRANCH %cd% 136 | ECHO "LOCAL_SETUP_BRANCH=!LOCAL_SETUP_BRANCH!" 137 | SET SETUP_WINDOWS=https://raw.githubusercontent.com/PredixDev/local-setup/!LOCAL_SETUP_BRANCH!/setup-windows.bat 138 | rem SET SETUP_WINDOWS=https://raw.githubusercontent.com/PredixDev/local-setup/!LOCAL_SETUP_BRANCH!/setup-windows.bat 139 | 140 | ECHO !SETUP_WINDOWS! 141 | powershell -Command "(new-object net.webclient).DownloadFile('!SETUP_WINDOWS!','setup-windows.bat')" 142 | 143 | GOTO :eof 144 | 145 | :START 146 | 147 | CALL :CHECK_DIR 148 | 149 | ECHO. 150 | ECHO Welcome to the !APP_NAME! Quickstart. 151 | ECHO -------------------------------------------------------------- 152 | ECHO. 153 | ECHO This is an automated script which will guide you through the tutorial. 154 | ECHO. 155 | 156 | if "!SKIP_SETUP!"=="FALSE" ( 157 | CALL :INIT 158 | ) 159 | 160 | CALL :CHECK_FAIL 161 | IF NOT !errorlevel! EQU 0 EXIT /b !errorlevel! 162 | 163 | if !CF_URL!=="" ( 164 | ECHO CF_URL=!CF_URL! 165 | ) else ( 166 | rem this is here so jenkins can non-interactively log in to the cloud 167 | cf login -a !CF_URL! -u !CF_USER! -p !CF_PASSWORD! -o !CF_ORG! -s !CF_SPACE! 168 | ) 169 | 170 | powershell -Command "(new-object net.webclient).DownloadFile('!SHELL_SCRIPT_URL!','!CURRENTDIR!\!SHELL_SCRIPT_NAME!')" 171 | ECHO Running the !CURRENTDIR!\%SHELL_SCRIPT_NAME% script using Git-Bash 172 | cd !CURRENTDIR! 173 | ECHO. 174 | "%PROGRAMFILES%\Git\bin\bash" --login -i -- "!CURRENTDIR!\%SHELL_SCRIPT_NAME%" -b !BRANCH! --skip-setup !QUICKSTART_ARGS! 175 | -------------------------------------------------------------------------------- /scripts/quickstart-predix-webapp-starter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | function local_read_args() { 4 | while (( "$#" )); do 5 | opt="$1" 6 | case $opt in 7 | -h|-\?|--\?--help) 8 | PRINT_USAGE=1 9 | QUICKSTART_ARGS="$SCRIPT $1" 10 | break 11 | ;; 12 | -b|--branch) 13 | BRANCH="$2" 14 | QUICKSTART_ARGS+=" $1 $2" 15 | shift 16 | ;; 17 | -o|--override) 18 | QUICKSTART_ARGS=" $SCRIPT" 19 | ;; 20 | --skip-setup) 21 | SKIP_SETUP=true 22 | ;; 23 | *) 24 | QUICKSTART_ARGS+=" $1" 25 | #echo $1 26 | ;; 27 | esac 28 | shift 29 | done 30 | 31 | if [[ -z $BRANCH ]]; then 32 | echo "Usage: $0 -b/--branch [--skip-setup]" 33 | exit 1 34 | fi 35 | } 36 | 37 | # default settings 38 | BRANCH="master" 39 | PRINT_USAGE=0 40 | SKIP_SETUP=false 41 | IZON_SH="https://raw.githubusercontent.com/PredixDev/izon/1.5.0/izon2.sh" 42 | GITHUB_RAW="https://raw.githubusercontent.com" 43 | PREDIX_SCRIPTS_ORG="PredixDev" 44 | PREDIX_SCRIPTS=predix-scripts 45 | VERSION_JSON="version.json" 46 | 47 | #ASSET_MODEL="-amrmd predix-ui-seed/server/sample-data/predix-asset/asset-model-metadata.json predix-ui-seed/server/sample-data/predix-asset/asset-model.json" 48 | SCRIPT="-script build-basic-app.sh -script-readargs build-basic-app-readargs.sh" 49 | QUICKSTART_ARGS="-ps $SCRIPT" 50 | GITHUB_ORG="PredixDev" 51 | REPO_NAME=predix-webapp-starter 52 | APP_DIR="webapp-starter" 53 | APP_NAME="Predix UI Polymer Starter" 54 | SCRIPT_NAME="quickstart-predix-webapp-starter.sh" 55 | TOOLS="Cloud Foundry CLI, Git, jq, Node.js, Predix CLI, yq" 56 | TOOLS_SWITCHES="--cf --git --jq --nodejs --predixcli --yq" 57 | 58 | # Process switches 59 | local_read_args $@ 60 | 61 | #variables after processing switches 62 | SCRIPT_LOC="$GITHUB_RAW/$GITHUB_ORG/$REPO_NAME/$BRANCH/scripts/$SCRIPT_NAME" 63 | VERSION_JSON_URL="$GITHUB_RAW/$GITHUB_ORG/$REPO_NAME/$BRANCH/version.json" 64 | 65 | function check_internet() { 66 | set +e 67 | echo "" 68 | echo "Checking internet connection..." 69 | curl "http://google.com" > /dev/null 2>&1 70 | if [ $? -ne 0 ]; then 71 | echo "Unable to connect to internet, make sure you are connected to a network and check your proxy settings if behind a corporate proxy" 72 | echo "If you are behind a corporate proxy, set the 'http_proxy' and 'https_proxy' environment variables." 73 | exit 1 74 | fi 75 | echo "OK" 76 | echo "" 77 | set -e 78 | } 79 | 80 | function init() { 81 | currentDir=$(pwd) 82 | if [[ $currentDir == *"scripts" ]]; then 83 | echo 'Please launch the script from the root dir of the project' 84 | exit 1 85 | fi 86 | 87 | check_internet 88 | 89 | #get the script that reads version.json 90 | eval "$(curl -s -L $IZON_SH)" 91 | 92 | #download the script and cd 93 | #getUsingCurl $SCRIPT_LOC 94 | #chmod 755 $SCRIPT_NAME; 95 | if [[ ! $currentDir == *"$REPO_NAME" ]]; then 96 | mkdir -p $APP_DIR 97 | cd $APP_DIR 98 | fi 99 | 100 | 101 | getVersionFile 102 | getLocalSetupFuncs $GITHUB_RAW $PREDIX_SCRIPTS_ORG 103 | } 104 | 105 | if [[ $PRINT_USAGE == 1 ]]; then 106 | init 107 | __print_out_standard_usage 108 | else 109 | if $SKIP_SETUP; then 110 | init 111 | else 112 | init 113 | __standard_mac_initialization 114 | fi 115 | fi 116 | 117 | getPredixScripts 118 | #clone the repo itself if running from oneclick script 119 | getCurrentRepo 120 | 121 | echo "quickstart_args=$QUICKSTART_ARGS" 122 | source $PREDIX_SCRIPTS/bash/quickstart.sh $QUICKSTART_ARGS 123 | 124 | __append_new_line_log "Successfully completed $APP_NAME installation!" "$quickstartLogDir" 125 | __append_new_line_log "" "$quickstartLogDir" 126 | -------------------------------------------------------------------------------- /server/localConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "note": "Out of the box, the predix-webapp-starter app uses mock data, so these values are not required. Set these values for connecting to real Predix services.", 4 | "uaaURL": " https://6689be5f-1bdf-4fc0-bbda-dcea2b371b77.predix-uaa.run.aws-usw02-pr.ice.predix.io", 5 | "base64ClientCredential": "YXBwX2NsaWVudF9pZDpzZWNyZXQ=", 6 | "loginBase64ClientCredential": "bG9naW5fY2xpZW50X2lkOnNlY3JldA==", 7 | "appURL": "http://localhost:5000", 8 | "timeseriesURL": "https://time-series-store-predix.run.aws-usw02-pr.ice.predix.io/v1/datapoints", 9 | "timeseriesZoneId": "85665ade-c0e7-4e27-920a-ae38a5b02347", 10 | "assetURL": "https://predix-asset.run.aws-usw02-pr.ice.predix.io", 11 | "assetZoneId": "6b6121b0-81e3-4297-8c35-007ebf74faef", 12 | "windServiceURL": "{URL of the microservice -winddata-timeseries-service}, e.g. https://your-name-winddata-timeseries-service.run.asw-usw02-pr.predix.io", 13 | "websocketServerURL": "/livestream", 14 | "rmdDatasourceURL": "{URL of your RMD datasource microservice}", 15 | "rmdDocsURL": "https://raw.githubusercontent.com/PredixDev/predix-rmd-ref-app/master/README.md", 16 | "dataExchangeURL": "{URL of your data exchange microservice}", 17 | "timeSeriesOnly": "false", 18 | "assetModel": "sample-data/predix-asset/Compressor-CMMS-Compressor-2018.json" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/passport-config.js: -------------------------------------------------------------------------------- 1 | var passport = require('passport'); 2 | var CloudFoundryStrategy = require('passport-predix-oauth').Strategy; 3 | var OAuth2RefreshTokenStrategy = require('passport-oauth2-middleware').Strategy; 4 | var cfStrategy; 5 | 6 | /********************************************************************* 7 | PASSPORT PREDIX STRATEGY SETUP 8 | **********************************************************************/ 9 | function configurePassportStrategy(predixConfig) { 10 | 'use strict'; 11 | var refreshStrategy = new OAuth2RefreshTokenStrategy({ 12 | refreshWindow: 10, // Time in seconds to perform a token refresh before it expires 13 | userProperty: 'ticket', // Active user property name to store OAuth tokens 14 | authenticationURL: '/login', // URL to redirect unathorized users to 15 | callbackParameter: 'callback' //URL query parameter name to pass a return URL 16 | }); 17 | 18 | passport.use('main', refreshStrategy); //Main authorization strategy that authenticates 19 | //user with stored OAuth access token 20 | //and performs a token refresh if needed 21 | 22 | // Passport session setup. 23 | // To support persistent login sessions, Passport needs to be able to 24 | // serialize users into and deserialize users out of the session. Typically, 25 | // this will be as simple as storing the user ID when serializing, and finding 26 | // the user by ID when deserializing. However, since this example does not 27 | // have a database of user records, the complete CloudFoundry profile is 28 | // serialized and deserialized. 29 | passport.serializeUser(function(user, done) { 30 | // console.log("From USER-->"+JSON.stringify(user)); 31 | done(null, user); 32 | }); 33 | passport.deserializeUser(function(obj, done) { 34 | done(null, obj); 35 | }); 36 | 37 | cfStrategy = new CloudFoundryStrategy({ 38 | clientID: predixConfig.getClientIdFromEncodedString(predixConfig.loginBase64ClientCredential), 39 | clientSecret: predixConfig.getSecretFromEncodedString(predixConfig.loginBase64ClientCredential), 40 | callbackURL: predixConfig.callbackURL, 41 | authorizationURL: predixConfig.uaaURL, 42 | tokenURL: predixConfig.tokenURL 43 | },refreshStrategy.getOAuth2StrategyCallback() //Create a callback for OAuth2Strategy 44 | /* TODO: implement if needed. 45 | function(accessToken, refreshToken, profile, done) { 46 | token = accessToken; 47 | done(null, profile); 48 | }*/); 49 | 50 | passport.use(cfStrategy); 51 | //Register the OAuth strategy to perform OAuth2 refresh token workflow 52 | refreshStrategy.useOAuth2Strategy(cfStrategy); 53 | 54 | return passport; 55 | } 56 | 57 | function reset() { 58 | 'use strict'; 59 | cfStrategy.reset(); 60 | } 61 | 62 | module.exports = { 63 | configurePassportStrategy: configurePassportStrategy, 64 | reset: reset 65 | }; 66 | -------------------------------------------------------------------------------- /server/predix-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | This module reads config settings from localConfig.json when running locally, 3 | or from the VCAPS environment variables when running in Cloud Foundry. 4 | */ 5 | 6 | var settings = {}; 7 | 8 | // checking NODE_ENV to load cloud properties from VCAPS 9 | // or development properties from config.json. 10 | var node_env = process.env.node_env || 'development'; 11 | if(node_env === 'development') { 12 | // use localConfig file 13 | var devConfig = require('./localConfig.json')[node_env]; 14 | // console.log(devConfig); 15 | settings.base64ClientCredential = devConfig.base64ClientCredential; 16 | settings.loginBase64ClientCredential = devConfig.loginBase64ClientCredential; 17 | settings.uaaURL = devConfig.uaaURL; 18 | settings.tokenURL = devConfig.uaaURL; 19 | settings.appURL = devConfig.appURL; 20 | settings.callbackURL = devConfig.appURL + '/callback'; 21 | 22 | settings.assetURL = devConfig.assetURL; 23 | settings.assetZoneId = devConfig.assetZoneId; 24 | settings.timeseriesZoneId = devConfig.timeseriesZoneId; 25 | settings.timeseriesURL = devConfig.timeseriesURL; 26 | settings.websocketServerURL = devConfig.websocketServerURL; 27 | settings.rmdDatasourceURL = devConfig.rmdDatasourceURL; 28 | settings.rmdDocsURL = devConfig.rmdDocsURL; 29 | settings.dataExchangeURL = devConfig.dataExchangeURL; 30 | settings.timeSeriesOnly = devConfig.timeSeriesOnly; 31 | settings.assetModel = devConfig.assetModel; 32 | } else { 33 | // read VCAP_SERVICES 34 | var vcapsServices = JSON.parse(process.env.VCAP_SERVICES); 35 | var uaaService = vcapsServices[process.env.uaa_service_label]; 36 | var assetService = vcapsServices['predix-asset']; 37 | var timeseriesService = vcapsServices['predix-timeseries']; 38 | 39 | if(uaaService) { 40 | settings.uaaURL = uaaService[0].credentials.uri; 41 | settings.tokenURL = uaaService[0].credentials.uri; 42 | } 43 | if(assetService) { 44 | settings.assetURL = assetService[0].credentials.uri + '/' + process.env.assetMachine; 45 | settings.assetZoneId = assetService[0].credentials.zone['http-header-value']; 46 | } 47 | if(timeseriesService) { 48 | settings.timeseriesZoneId = timeseriesService[0].credentials.query['zone-http-header-value']; 49 | settings.timeseriesURL = timeseriesService[0].credentials.query.uri; 50 | } 51 | 52 | // read VCAP_APPLICATION 53 | var vcapsApplication = JSON.parse(process.env.VCAP_APPLICATION); 54 | settings.appURL = 'https://' + vcapsApplication.uris[0]; 55 | settings.callbackURL = settings.appURL + '/callback'; 56 | settings.base64ClientCredential = process.env.base64ClientCredential; 57 | settings.loginBase64ClientCredential = process.env.loginBase64ClientCredential; 58 | settings.websocketServerURL = process.env.websocketServerURL; 59 | settings.rmdDatasourceURL = process.env.rmdDatasourceURL; 60 | settings.rmdDocsURL = process.env.rmdDocsURL; 61 | settings.dataExchangeURL = process.env.dataExchangeURL; 62 | settings.timeSeriesOnly = process.env.timeSeriesOnly; 63 | settings.assetModel = process.env.assetModel; 64 | } 65 | // console.log('config settings: ' + JSON.stringify(settings)); 66 | 67 | // This vcap object is used by the proxy module. 68 | settings.buildVcapObjectFromLocalConfig = function(config) { 69 | 'use strict'; 70 | // console.log('local config: ' + JSON.stringify(config)); 71 | var vcapObj = {}; 72 | if (config.uaaURL) { 73 | vcapObj['predix-uaa'] = [{ 74 | credentials: { 75 | uri: config.uaaURL 76 | } 77 | }]; 78 | } 79 | if (config.timeseriesURL) { 80 | vcapObj['predix-timeseries'] = [{ 81 | credentials: { 82 | query: { 83 | uri: config.timeseriesURL, 84 | 'zone-http-header-value': config.timeseriesZoneId 85 | } 86 | } 87 | }]; 88 | } 89 | if (config.assetURL) { 90 | vcapObj['predix-asset'] = [{ 91 | credentials: { 92 | uri: config.assetURL, 93 | zone: { 94 | 'http-header-value': config.assetZoneId 95 | } 96 | } 97 | }]; 98 | } 99 | return vcapObj; 100 | }; 101 | 102 | settings.isUaaConfigured = function() { 103 | return settings.uaaURL && 104 | settings.uaaURL.indexOf('https') === 0 && 105 | settings.base64ClientCredential && 106 | settings.base64ClientCredential.indexOf('client') < 0 && 107 | settings.loginBase64ClientCredential && 108 | settings.loginBase64ClientCredential.indexOf('client') < 0; 109 | }; 110 | 111 | settings.isAssetConfigured = function() { 112 | return settings.assetURL && 113 | settings.assetURL.indexOf('https') === 0 && 114 | settings.assetZoneId && 115 | settings.assetZoneId.indexOf('{') !== 0; 116 | } 117 | 118 | settings.isTimeSeriesConfigured = function() { 119 | return settings.timeseriesURL && 120 | settings.timeseriesURL.indexOf('https') === 0 && 121 | settings.timeseriesZoneId && 122 | settings.timeseriesZoneId.indexOf('{') !== 0; 123 | } 124 | 125 | settings.isDataExchangeConfigured = function() { 126 | return settings.dataExchangeURL && 127 | settings.dataExchangeURL.indexOf('https') === 0; 128 | } 129 | 130 | function getValueFromEncodedString(encoded, index) { 131 | if (!encoded) { 132 | return ''; 133 | } 134 | var decoded = new Buffer(encoded, 'base64').toString(); 135 | // console.log('DECODED: ' + decoded); 136 | var values = decoded.split(':'); 137 | if (values.length !== 2) { 138 | throw "base64 encoded client credential is not correct. \n It should be the base64 encoded value of: 'client:secret' \n Set in localConfig.json for local dev, or environment variable in the cloud."; 139 | } 140 | return values[index]; 141 | } 142 | 143 | settings.getSecretFromEncodedString = function(encoded) { 144 | return getValueFromEncodedString(encoded, 1); 145 | } 146 | 147 | settings.getClientIdFromEncodedString = function(encoded) { 148 | return getValueFromEncodedString(encoded, 0); 149 | } 150 | 151 | settings.getRedisCredentials = function() { 152 | var vcaps = JSON.parse(process.env.VCAP_SERVICES || '{}'); 153 | var creds; 154 | Object.keys(vcaps).forEach(function(vcap) { 155 | if (vcap.indexOf('predix-cache') > -1) { 156 | creds = vcaps[vcap][0].credentials; 157 | } 158 | }); 159 | return creds; 160 | } 161 | 162 | module.exports = settings; 163 | -------------------------------------------------------------------------------- /server/routes/data-exchange.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require("fs"); 3 | const path = require('path'); 4 | const request = require('request'); 5 | const config = require('../predix-config'); 6 | const assettemplatefile = "../sample-data/data-exchange/PutFieldDataCreateAsset-DataMap.json"; 7 | 8 | const cloneAsset = function(req, res) { 9 | var assetName = req.body[0].assetname; 10 | console.log("user entered asset name:", assetName); 11 | var now = Date.now(); 12 | 13 | var rawClone = fs.readFileSync(path.join(__dirname, assettemplatefile), 'utf8'); 14 | var uniqueRequestString = req.ip.replace(/:/g, '_') + '-' + now; 15 | var result = rawClone.replace(/Compressor-Clone/g, uniqueRequestString); 16 | var putFieldDataRequest = JSON.parse(result); 17 | putFieldDataRequest.putFieldDataCriteria[0].fieldData.data.map[0].description = assetName; 18 | putFieldDataRequest.putFieldDataCriteria[0].fieldData.data.map[0].createdDate = now; 19 | console.log('putFieldDataRequest', JSON.stringify(putFieldDataRequest)); 20 | 21 | var options = { 22 | method: 'POST', 23 | url: config.dataExchangeURL + '/services/fdhrouter/fielddatahandler/putfielddata', 24 | headers: { 25 | 'cache-control': 'no-cache', 26 | 'Authorization' : req.headers['Authorization'], 27 | 'content-type': 'application/json' 28 | }, 29 | json: putFieldDataRequest 30 | }; 31 | 32 | request(options, function(err, response, body) { 33 | if (err) { 34 | console.error(err.message + err.stack); 35 | res.status(500).send(err.message); 36 | } else if (body && body.errorEvent && body.errorEvent.length > 0) { 37 | console.error('Error from data-exchange.', body.errorEvent[0]); 38 | res.status(500).send(body.errorEvent[0]); 39 | } else if (response.statusCode == 200 || response.statusCode == 204) { 40 | console.log('response from putfielddata:', body); 41 | res.status = response.statusCode; 42 | res.send({clonedAssetId: uniqueRequestString}); 43 | } else { 44 | console.error('ERROR cloning asset: ' + JSON.stringify(response)); 45 | res.sendStatus(response.statusCode); 46 | } 47 | }); 48 | }; 49 | 50 | module.exports = { 51 | cloneAsset: cloneAsset 52 | }; -------------------------------------------------------------------------------- /server/routes/docs.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const url = require('url'); 3 | const proxy = require('./proxy'); 4 | 5 | module.exports = (predixConfig) => { 6 | if (predixConfig.isTimeSeriesConfigured()) { 7 | // console.log('rmdDocsURL', predixConfig.rmdDocsURL); 8 | return proxy.customProxyMiddleware('/docs', predixConfig.rmdDocsURL, url.parse(predixConfig.rmdDocsURL).path); 9 | } else { 10 | return (req, res, next) => { 11 | req.url = path.join(req.url, '/ABOUT.md'); 12 | // console.log('req.url', req.url); 13 | next(); 14 | }; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /server/routes/learning-paths.js: -------------------------------------------------------------------------------- 1 | module.exports = (predixConfig) => { 2 | return (req, res, next) => { 3 | const response = { 4 | "appMode": "default", 5 | "learningSequence": ['base', 'uaa', 'asset', 'timeseries'], // sequence of learning features 6 | "services": {} 7 | }; 8 | response.services.base = true, 9 | response.services.uaa = predixConfig.isUaaConfigured(); 10 | response.services.asset = predixConfig.isAssetConfigured(); 11 | response.services.timeseries = predixConfig.isTimeSeriesConfigured(); 12 | res.send(response); 13 | next(); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /server/routes/mock-asset.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var config = require('../predix-config'); 3 | 4 | 5 | // export the routes to be used in express/json-server in app.js 6 | module.exports = function() { 7 | // mock asset data contains an extra "filter" property, so we can easily match the Predix API. 8 | const routes = {}; 9 | 10 | // http://localhost:5000/mock-api/predix-asset/asset?filter=group=/group/plant-richmond-refinery 11 | const compressorJson = require(path.resolve(__dirname, '../' + config.assetModel)); 12 | //const compressorJson = require(path.resolve(__dirname, '../sample-data/predix-asset/Compressor-CMMS-Compressor-2018.json')); 13 | routes["asset/Compressor-CMMS-Compressor-2018"] = compressorJson; 14 | 15 | // http://localhost:5000/mock-api/predix-asset/group?filter=parent=/group/enterprise-predix 16 | const groupsJson = require(path.resolve(__dirname, '../sample-data/predix-asset/groups.json')); 17 | routes["group"] = groupsJson; 18 | return routes; 19 | }; 20 | -------------------------------------------------------------------------------- /server/routes/mock-live-data.js: -------------------------------------------------------------------------------- 1 | const ws = require('ws'); 2 | 3 | // OLD Ref app: 4 | // wss://release-oct-20-websocket-server.run.aws-usw02-pr.ice.predix.io/livestream/Compressor-2015:CompressionRatio 5 | // {"name":"Compressor-2015:CompressionRatio","datapoints":[[1490043819965,2.6873587396621494]],"attributes":{"assetId":"compressor-2015","sourceTagId":"CompressionRatio"}} 6 | 7 | // hardcode thresholds for demonstration: 8 | const thresholds = { 9 | "Compressor-CMMS-Compressor-2018:CompressionRatio": [2.5, 3], 10 | "Compressor-CMMS-Compressor-2018:DischargePressure": [0, 23], 11 | "Compressor-CMMS-Compressor-2018:SuctionPressure": [0, 0.21], 12 | "Compressor-CMMS-Compressor-2018:MaximumPressure": [22, 26], 13 | "Compressor-CMMS-Compressor-2018:Velocity": [0, 0.07], 14 | "Compressor-CMMS-Compressor-2018:Temperature": [65, 80] 15 | }; 16 | 17 | function getRandomData(tag) { 18 | let threshold = thresholds[tag] || thresholds['Compressor-CMMS-Compressor-2018:CompressionRatio']; 19 | let range = (threshold[1] - threshold[0]) * 1.2; 20 | let low = threshold[0] - ((threshold[1] - threshold[0]) * 0.1); 21 | let value = Math.random() * range + low; 22 | return JSON.stringify({"name": tag, "datapoints": [[(new Date()).getTime(), value]]}); 23 | } 24 | 25 | function createWebSocketServer(httpServer) { 26 | const wsServer = new ws.Server({server: httpServer}); 27 | 28 | wsServer.on('connection', function connection(socket, req) { 29 | console.log('connection opened: ', req.url); 30 | let path = req.url || ""; 31 | if (path === "" || path === "/") { 32 | path = "/Compressor-CMMS-Compressor-2018:CompressionRatio"; 33 | } 34 | const tagName = path.substring(path.lastIndexOf("/") + 1); 35 | console.log('web socket opened for tag:', tagName); 36 | // send one datapoint right away: 37 | socket.send(getRandomData(tagName)); 38 | let intervalId = setInterval(function() { 39 | if (socket.readyState === socket.OPEN) { 40 | socket.send(getRandomData(tagName)); 41 | } 42 | }, 1000); 43 | // socket.on('message', function incoming(msg) { 44 | // console.log('received message:', msg); 45 | // }); 46 | socket.on('close', function() { 47 | console.log('web socket closed'); 48 | clearInterval(intervalId); 49 | }) 50 | }); 51 | } 52 | 53 | module.exports = createWebSocketServer; -------------------------------------------------------------------------------- /server/routes/mock-rmd-datasource.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | 3 | // export the routes to be used in express/json-server in app.js 4 | module.exports = function() { 5 | const routes = {}; 6 | const compressorJson = require(path.resolve(__dirname, '../sample-data/rmd-datasource/datagrid-compressor.json')); 7 | const summaryJson = require(path.resolve(__dirname, '../sample-data/rmd-datasource/summary-compressor.json')); 8 | // http://localhost:5000/mock-api/datagrid/asset/Compressor-CMMS-Compressor-2018 9 | routes["asset/:id"] = compressorJson; 10 | 11 | // http://localhost:5000/mock-api/datagrid/asset/Compressor-CMMS-Compressor-2018/summary 12 | routes["summary"] = summaryJson; 13 | routes["asset/:id/summary"] = summaryJson; 14 | 15 | // http://localhost:5000/mock-api/datagrid/group/plant-richmond-refinery 16 | routes["group/:id"] = require(path.resolve(__dirname, '../sample-data/rmd-datasource/datagrid-refinery.json')); 17 | 18 | // http://localhost:5000/mock-api/datagrid/group/plant-richmond-refinery/summary 19 | routes["group/:id/summary"] = require(path.resolve(__dirname, '../sample-data/rmd-datasource/summary-refinery.json')); 20 | return routes; 21 | }; 22 | -------------------------------------------------------------------------------- /server/routes/mock-time-series.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var express = require("express") 3 | var router = express.Router(); 4 | 5 | router.use(['/v1/datapoints', '/compression-ratio'], function(req, res) { 6 | // TODO: Add support to return different data based on request body. use body-parser 7 | // const compressorJson = require(path.resolve(__dirname, '../sample-data/time-series/Compressor-CMMS-Compressor-2018-compression-ratio.json')); 8 | const compressorJson = require(path.resolve(__dirname, '../sample-data/time-series/2-tags.json')); 9 | res.send(compressorJson); 10 | }); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /server/routes/proxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module can be used to set up reverse proxying from client to Predix services. 3 | * It assumes only one UAA instance, one UAA client, and one instance of each service. 4 | * Use setUaaConfig() and setServiceConfig() for local development. 5 | * In cloud foundry, set the following environment vars: base64ClientCredential 6 | * Info for bound services is read from VCAP environment variables. 7 | */ 8 | 9 | var url = require('url'); 10 | var express = require('express'); 11 | var expressProxy = require('express-http-proxy'); 12 | var HttpsProxyAgent = require('https-proxy-agent'); 13 | var predixUaaClient = require('predix-uaa-client'); 14 | var predixConfig = require('../predix-config'); 15 | var router = express.Router(); 16 | var vcapServices = {}; 17 | 18 | var corporateProxyServer = process.env.http_proxy || process.env.HTTP_PROXY || process.env.https_proxy || process.env.HTTPS_PROXY; 19 | var corporateProxyAgent; 20 | if (corporateProxyServer) { 21 | corporateProxyAgent = new HttpsProxyAgent(corporateProxyServer); 22 | } 23 | 24 | var clientId = predixConfig.getClientIdFromEncodedString(process.env.base64ClientCredential); 25 | var base64ClientCredential = process.env.base64ClientCredential; 26 | var uaaURL = (function() { 27 | var vcapsServices = process.env.VCAP_SERVICES ? JSON.parse(process.env.VCAP_SERVICES) : {}; 28 | var uaaService = vcapsServices[process.env.uaa_service_label || 'predix-uaa']; 29 | var uaaURL; 30 | 31 | if(uaaService) { 32 | uaaURL = uaaService[0].credentials.uri; 33 | } 34 | return uaaURL; 35 | }) (); 36 | 37 | // Pass a VCAPS object here if desired, for local config. 38 | // Otherwise, this module reads from VCAP_SERVICES environment variable. 39 | var setServiceConfig = function(vcaps) { 40 | vcapServices = vcaps; 41 | setProxyRoutes(); 42 | }; 43 | 44 | var setUaaConfig = function(options) { 45 | if (predixConfig.isUaaConfigured()) { 46 | uaaURL = options.uaaURL || uaaURL; 47 | base64ClientCredential = options.base64ClientCredential || base64ClientCredential; 48 | clientId = predixConfig.getClientIdFromEncodedString(base64ClientCredential); 49 | } 50 | }; 51 | 52 | var getClientToken = function(successCallback, errorCallback) { 53 | var clientSecret = predixConfig.getSecretFromEncodedString(base64ClientCredential); 54 | predixUaaClient.getToken(uaaURL + '/oauth/token', clientId, clientSecret).then(function (token) { 55 | successCallback(token.token_type + ' ' + token.access_token); 56 | }).catch(function (err) { 57 | console.error('ERROR fetching client token:', err); 58 | if (errorCallback) { 59 | errorCallback(err); 60 | } 61 | }); 62 | }; 63 | 64 | function cleanResponseHeaders (rsp, data, req, res, cb) { 65 | res.removeHeader('Access-Control-Allow-Origin'); 66 | cb(null, data); 67 | } 68 | 69 | function buildDecorator(zoneId) { 70 | var decorator = function(req) { 71 | if (corporateProxyAgent) { 72 | req.agent = corporateProxyAgent; 73 | } 74 | req.headers['Content-Type'] = 'application/json'; 75 | if (zoneId) { 76 | req.headers['Predix-Zone-Id'] = zoneId; 77 | } 78 | return req; 79 | }; 80 | return decorator; 81 | } 82 | 83 | function isValidUrl(str) { 84 | var urlObj = url.parse(str); 85 | return urlObj.protocol === 'https:' && urlObj.host; 86 | } 87 | 88 | function getEndpointAndZone(key, credentials) { 89 | var out = {}; 90 | // ugly code needed since vcap service variables are not consistent across services 91 | // TODO: all the other predix services 92 | if (key === 'predix-uaa') { 93 | // do nothing. authentication handled by the passport module. 94 | // return here, so we don't display a confusing log message. 95 | return out; 96 | } else if (key === 'predix-asset') { 97 | out.serviceEndpoint = isValidUrl(credentials.uri) ? credentials.uri : null; 98 | out.zoneId = credentials.zone['http-header-value']; 99 | } else if (key === 'predix-timeseries') { 100 | var urlObj = url.parse(credentials.query.uri); 101 | out.serviceEndpoint = urlObj.host ? urlObj.protocol + '//' + urlObj.host : null; 102 | out.zoneId = credentials.query['zone-http-header-value']; 103 | } 104 | if (!out.serviceEndpoint) { 105 | console.log('no proxy set for service: ' + key); 106 | } 107 | return out; 108 | } 109 | 110 | var setProxyRoute = function(key, credentials) { 111 | // console.log(JSON.stringify(credentials)); 112 | var routeOptions = getEndpointAndZone(key, credentials); 113 | if (!routeOptions.serviceEndpoint) { 114 | return; 115 | } 116 | console.log('setting proxy route for key: ' + key); 117 | console.log('serviceEndpoint: ' + routeOptions.serviceEndpoint); 118 | // console.log('zone id: ' + routeOptions.zoneId); 119 | var decorator = buildDecorator(routeOptions.zoneId); 120 | 121 | router.use('/' + key, expressProxy(routeOptions.serviceEndpoint, { 122 | https: true, 123 | forwardPath: function (req) { 124 | console.log('req.url: ' + req.url); 125 | return req.url; 126 | }, 127 | intercept: cleanResponseHeaders, 128 | decorateRequest: decorator 129 | })); 130 | }; 131 | 132 | // Fetches client token, adds to request headers, and stores in session. 133 | // Returns 403 if no session. 134 | // Use this middleware to proxy a request to a secure service, using a client token. 135 | var addClientTokenMiddleware = function(req, res, next) { 136 | function errorHandler(errorString) { 137 | var err = new Error(errorString); 138 | err.status = 500; 139 | next(err); 140 | } 141 | // console.log('proxy root route'); 142 | if (req.session) { 143 | // console.log('session found.'); 144 | // console.log('fetching client token'); 145 | // getClientToken will returned a cached token if it's not expired 146 | // or renew if it has expired. 147 | getClientToken(function(token) { 148 | req.headers['Authorization'] = token; 149 | next(); 150 | }, errorHandler); 151 | } else { 152 | next(res.sendStatus(403).send('Forbidden')); 153 | } 154 | }; 155 | 156 | router.use(['/'], addClientTokenMiddleware); 157 | 158 | // Adds user authorization token from passport to request 159 | var addAccessTokenMiddleware = function (req, res, next) { 160 | if (req.session) { 161 | req.headers['Authorization'] = 'bearer ' + req.session.passport.user.ticket.access_token; 162 | next(); 163 | } else { 164 | next(res.sendStatus(403).send('Forbidden')); 165 | } 166 | }; 167 | 168 | // TODO: Support for multiple instances of the same service. 169 | var setProxyRoutes = function() { 170 | var vcapString = process.env.VCAP_SERVICES; 171 | var serviceKeys = []; 172 | vcapServices = vcapString ? JSON.parse(vcapString) : vcapServices; 173 | console.log('vcaps: ' + JSON.stringify(vcapServices)); 174 | 175 | serviceKeys = Object.keys(vcapServices); 176 | serviceKeys.forEach(function(key) { 177 | setProxyRoute(key, vcapServices[key][0].credentials); 178 | }); 179 | }; 180 | // TODO: only call this, if we find a vcapstring in environment? 181 | setProxyRoutes(); 182 | 183 | // Use this to set up your own proxy route to your custom microservice. 184 | // Path and arguments after the pathPrefix will be passed on to the target endpoint. 185 | // pathPrefix: the path that clients will call in your express app. 186 | // endpoint: the URL of your custom microservice. 187 | // targetPath: optional path to proxy to. if set, the pathPrefix will be replaced with targetPath. 188 | // example usage: 189 | // customProxyMiddleware('/my-custom-api', 'https://my-custom-service.run.aws-usw02-pr.ice.predix.io') 190 | // customProxyMiddleware('/another-api', 'https://another-api.run.aws-usw02-pr.ice.predix.io', '/v3/special-api-path') 191 | var customProxyMiddleware = function(pathPrefix, endpoint, targetPath) { 192 | console.log('custom endpoint: ' + endpoint); 193 | return expressProxy(endpoint, { 194 | https: true, 195 | forwardPath: function (req) { 196 | var path = req.url.replace(pathPrefix, targetPath || ''); 197 | console.log('proxying to:', path); 198 | return path; 199 | }, 200 | intercept: cleanResponseHeaders, 201 | decorateRequest: buildDecorator() 202 | }); 203 | }; 204 | 205 | module.exports = { 206 | router: router, 207 | setServiceConfig: setServiceConfig, 208 | setUaaConfig: setUaaConfig, 209 | customProxyMiddleware: customProxyMiddleware, 210 | addClientTokenMiddleware: addClientTokenMiddleware, 211 | addAccessTokenMiddleware: addAccessTokenMiddleware, 212 | expressProxy: expressProxy 213 | }; -------------------------------------------------------------------------------- /server/routes/user-info.js: -------------------------------------------------------------------------------- 1 | //custom middleware to get the user information from UAA 2 | //function below hits the UAA endpoint with the access token and fetches user information 3 | var request = require('request'); 4 | 5 | var getUserInfo = function (accessToken, uaaURL, callback) { 6 | 7 | var options = { 8 | method: 'GET', 9 | url: uaaURL + '/userinfo', 10 | headers: { 11 | 'Authorization': 'Bearer ' + accessToken 12 | } 13 | }; 14 | 15 | request(options, function (err, response, body) { 16 | if (!err) { 17 | var userDetails = JSON.parse(body); 18 | callback(userDetails); 19 | } else { 20 | console.error(response.statusCode); 21 | console.error('ERROR fetching client token: ' + err); 22 | } 23 | }); 24 | }; 25 | 26 | module.exports = function(uaaURL){ 27 | return function(req, res, next){ 28 | if (!req.user.details){ 29 | getUserInfo(req.session.passport.user.ticket.access_token, uaaURL, function(userDetails){ 30 | // console.log(userDetails); 31 | req.user.details = userDetails; 32 | next(); 33 | }); 34 | } 35 | else{ 36 | next(); 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /server/sample-data/data-exchange/PutFieldDataUpdatehiLoThreshold-rmq.json: -------------------------------------------------------------------------------- 1 | { 2 | "putFieldDataCriteria": [ 3 | { 4 | "namespaces": [], 5 | "fieldData": { 6 | "field": [ 7 | { 8 | "fieldIdentifier": { 9 | "complexType": "FieldIdentifier", 10 | "id": "/asset/assetTag/crank-frame-velocity/hiAlarmThreshold", 11 | "source": "PREDIX_ASSET" 12 | }, 13 | "parents": [] 14 | } 15 | ], 16 | "data": { 17 | "complexType": "OsaData", 18 | "dataEvent": { 19 | "complexType": "DMReal", 20 | "id": 0, 21 | "numAlerts": [], 22 | "value": 64.72 23 | } 24 | } 25 | }, 26 | "filter": { 27 | "complexType": "AssetFilter", 28 | "uri": "/asset/Compressor-CMMS-Compressor-2018" 29 | } 30 | }, 31 | { 32 | "namespaces": [], 33 | "fieldData": { 34 | "field": [ 35 | { 36 | "fieldIdentifier": { 37 | "complexType": "FieldIdentifier", 38 | "id": "/asset/assetTag/crank-frame-velocity/loAlarmThreshold", 39 | "source": "PREDIX_ASSET" 40 | }, 41 | "parents": [] 42 | } 43 | ], 44 | "data": { 45 | "complexType": "OsaData", 46 | "dataEvent": { 47 | "complexType": "DMReal", 48 | "id": 0, 49 | "numAlerts": [], 50 | "value": 2.97 51 | } 52 | } 53 | }, 54 | "filter": { 55 | "complexType": "AssetFilter", 56 | "uri": "/asset/Compressor-CMMS-Compressor-2018" 57 | } 58 | }, 59 | { 60 | "namespaces": [], 61 | "fieldData": { 62 | "field": [ 63 | { 64 | "fieldIdentifier": { 65 | "complexType": "FieldIdentifier", 66 | "source": "RABBITMQ_QUEUE" 67 | }, 68 | "parents": [] 69 | } 70 | ], 71 | "data": { 72 | "complexType": "PredixString", 73 | "string": "{\n\t\"fieldChangedList\": {\n\t\t\"fieldChanged\": [{\n\t\t\t\"assetList\": {\n\t\t\t\t\"asset\": [{\n\t\t\t\t\t\"uri\": \"/asset/Compressor-CMMS-Compressor-2018\",\n\t\t\t\t\t\"assetType\": \"asset\",\n\t\t\t\t\t\"fieldList\": {\n\t\t\t\t\t\t\"field\": [{\n\t\t\t\t\t\t\t\"fieldKey\": \"/asset/assetTag/crank-frame-velocity\",\n\t\t\t\t\t\t\t\"fieldValue\": \"\",\n\t\t\t\t\t\t\t\"fieldType\": \"assetTag\",\n\t\t\t\t\t\t\t\"timeChanged\": \"2017-07-11T07:16:13.000Z\"\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t}]\n\t\t\t},\n\t\t\t\"externalAttributeMap\": {\n\t\t\t\t\"entry\": [{\n\t\t\t\t\t\"key\": \"\",\n\t\t\t\t\t\"value\": \"\"\n\t\t\t\t}]\n\t\t\t}\n\t\t}]\n\t}\n}" 74 | } 75 | } 76 | } 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /server/sample-data/predix-asset/Compressor-CMMS-Compressor-2018-Scaled.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "complexType": "Asset", 4 | "CMMS_Id": "Compressor-CMMS", 5 | "uri": "/asset/Compressor-CMMS-Compressor-2018", 6 | "classificationUri": "/classification/GE_COMPRESSOR_CYLINDER", 7 | "assetId": "Compressor-CMMS-Compressor-2018", 8 | "description": "Reciprocating Compressor", 9 | "filter": "group=/group/plant-richmond-refinery", 10 | "isMock": true, 11 | "group": "/group/plant-richmond-refinery", 12 | "assetTag": { 13 | "crank-frame-compressionratio": { 14 | "complexType": "AssetTag", 15 | "label": "Compression Ratio", 16 | "tagUri": "/tag/crank-frame-compressionratio", 17 | "hiAlarmThreshold": 3, 18 | "loAlarmThreshold": 2.5, 19 | "isKpi": true, 20 | "timeseriesDatasource": { 21 | "complexType": "TimeseriesDatasource", 22 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-compressionratio.scaled_x_1000" 23 | }, 24 | "edgeDatasource": { 25 | "complexType": "EdgeDatasource", 26 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-compressionratio", 27 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 28 | } 29 | }, 30 | "crank-frame-dischargepressure": { 31 | "complexType": "AssetTag", 32 | "label": "Discharge Pressure", 33 | "tagUri": "/tag/crank-frame-dischargepressure", 34 | "hiAlarmThreshold": 23, 35 | "loAlarmThreshold": 0, 36 | "isKpi": true, 37 | "timeseriesDatasource": { 38 | "complexType": "TimeseriesDatasource", 39 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-dischargepressure.scaled_x_1000" 40 | }, 41 | "edgeDatasource": { 42 | "complexType": "EdgeDatasource", 43 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-dischargepressure", 44 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 45 | }, 46 | "alertStatusUri": "/asset/Compressor-CMMS-Compressor-2018.alert-status.crank-frame-discharge-pressure" 47 | }, 48 | "crank-frame-suctionpressure": { 49 | "complexType": "AssetTag", 50 | "label": "Suction Pressure", 51 | "tagUri": "/tag/crank-frame-suctionpressure", 52 | "hiAlarmThreshold": 0.21, 53 | "loAlarmThreshold": 0, 54 | "timeseriesDatasource": { 55 | "complexType": "TimeseriesDatasource", 56 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-suctionpressure.scaled_x_1000" 57 | }, 58 | "edgeDatasource": { 59 | "complexType": "EdgeDatasource", 60 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-suctionpressure", 61 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 62 | } 63 | }, 64 | "crank-frame-maxpressure": { 65 | "complexType": "AssetTag", 66 | "label": "Max Pressure", 67 | "tagUri": "/tag/crank-frame-maxpressure", 68 | "hiAlarmThreshold": 26, 69 | "loAlarmThreshold": 22, 70 | "timeseriesDatasource": { 71 | "complexType": "TimeseriesDatasource", 72 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-maximumpressure.scaled_x_1000" 73 | }, 74 | "edgeDatasource": { 75 | "complexType": "EdgeDatasource", 76 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-maximumpressure", 77 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 78 | } 79 | }, 80 | "crank-frame-velocity": { 81 | "complexType": "AssetTag", 82 | "label": "Velocity", 83 | "tagUri": "/tag/crank-frame-velocity", 84 | "hiAlarmThreshold": 0.07, 85 | "loAlarmThreshold": 0, 86 | "timeseriesDatasource": { 87 | "complexType": "TimeseriesDatasource", 88 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-velocity.scaled_x_1000" 89 | }, 90 | "edgeDatasource": { 91 | "complexType": "EdgeDatasource", 92 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-velocity", 93 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 94 | } 95 | }, 96 | "crank-frame-temperature": { 97 | "complexType": "AssetTag", 98 | "label": "Temperature", 99 | "tagUri": "/tag/crank-frame-temperature", 100 | "hiAlarmThreshold": 80, 101 | "loAlarmThreshold": 65, 102 | "timeseriesDatasource": { 103 | "complexType": "TimeseriesDatasource", 104 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-temperature.scaled_x_1000" 105 | }, 106 | "edgeDatasource": { 107 | "complexType": "EdgeDatasource", 108 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-temperature", 109 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 110 | } 111 | } 112 | }, 113 | "attributes": { 114 | "machineControllerId": { 115 | "complexType": "Attribute", 116 | "type": "string", 117 | "enumeration": [], 118 | "value": [ 119 | "/asset/Bently.Nevada.3500.Rack1" 120 | ] 121 | }, 122 | "summaryTag": { 123 | "complexType": "Attribute", 124 | "type": "string", 125 | "enumeration": [], 126 | "value": [ 127 | "crank-frame-velocity", 128 | "crank-frame-compressionratio" 129 | ] 130 | } 131 | } 132 | } 133 | ] 134 | -------------------------------------------------------------------------------- /server/sample-data/predix-asset/Compressor-CMMS-Compressor-2018.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "complexType": "Asset", 4 | "CMMS_Id": "Compressor-CMMS", 5 | "uri": "/asset/Compressor-CMMS-Compressor-2018", 6 | "classificationUri": "/classification/GE_COMPRESSOR_CYLINDER", 7 | "assetId": "Compressor-CMMS-Compressor-2018", 8 | "description": "Reciprocating Compressor", 9 | "filter": "group=/group/plant-richmond-refinery", 10 | "isMock": true, 11 | "group": "/group/plant-richmond-refinery", 12 | "assetTag": { 13 | "crank-frame-compressionratio": { 14 | "complexType": "AssetTag", 15 | "label": "Compression Ratio", 16 | "tagUri": "/tag/crank-frame-compressionratio", 17 | "hiAlarmThreshold": 3, 18 | "loAlarmThreshold": 2.5, 19 | "isKpi": true, 20 | "timeseriesDatasource": { 21 | "complexType": "TimeseriesDatasource", 22 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-compressionratio" 23 | }, 24 | "edgeDatasource": { 25 | "complexType": "EdgeDatasource", 26 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-compressionratio", 27 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 28 | } 29 | }, 30 | "crank-frame-dischargepressure": { 31 | "complexType": "AssetTag", 32 | "label": "Discharge Pressure", 33 | "tagUri": "/tag/crank-frame-dischargepressure", 34 | "hiAlarmThreshold": 23, 35 | "loAlarmThreshold": 0, 36 | "isKpi": true, 37 | "timeseriesDatasource": { 38 | "complexType": "TimeseriesDatasource", 39 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-dischargepressure" 40 | }, 41 | "edgeDatasource": { 42 | "complexType": "EdgeDatasource", 43 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-dischargepressure", 44 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 45 | }, 46 | "alertStatusUri": "/asset/Compressor-CMMS-Compressor-2018.alert-status.crank-frame-discharge-pressure" 47 | }, 48 | "crank-frame-suctionpressure": { 49 | "complexType": "AssetTag", 50 | "label": "Suction Pressure", 51 | "tagUri": "/tag/crank-frame-suctionpressure", 52 | "hiAlarmThreshold": 0.21, 53 | "loAlarmThreshold": 0, 54 | "timeseriesDatasource": { 55 | "complexType": "TimeseriesDatasource", 56 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-suctionpressure" 57 | }, 58 | "edgeDatasource": { 59 | "complexType": "EdgeDatasource", 60 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-suctionpressure", 61 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 62 | } 63 | }, 64 | "crank-frame-maxpressure": { 65 | "complexType": "AssetTag", 66 | "label": "Max Pressure", 67 | "tagUri": "/tag/crank-frame-maxpressure", 68 | "hiAlarmThreshold": 26, 69 | "loAlarmThreshold": 22, 70 | "timeseriesDatasource": { 71 | "complexType": "TimeseriesDatasource", 72 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-maximumpressure" 73 | }, 74 | "edgeDatasource": { 75 | "complexType": "EdgeDatasource", 76 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-maximumpressure", 77 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 78 | } 79 | }, 80 | "crank-frame-velocity": { 81 | "complexType": "AssetTag", 82 | "label": "Velocity", 83 | "tagUri": "/tag/crank-frame-velocity", 84 | "hiAlarmThreshold": 0.07, 85 | "loAlarmThreshold": 0, 86 | "timeseriesDatasource": { 87 | "complexType": "TimeseriesDatasource", 88 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-velocity" 89 | }, 90 | "edgeDatasource": { 91 | "complexType": "EdgeDatasource", 92 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-velocity", 93 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 94 | } 95 | }, 96 | "crank-frame-temperature": { 97 | "complexType": "AssetTag", 98 | "label": "Temperature", 99 | "tagUri": "/tag/crank-frame-temperature", 100 | "hiAlarmThreshold": 80, 101 | "loAlarmThreshold": 65, 102 | "timeseriesDatasource": { 103 | "complexType": "TimeseriesDatasource", 104 | "tag": "Compressor-CMMS-Compressor-2018.crank-frame-temperature" 105 | }, 106 | "edgeDatasource": { 107 | "complexType": "EdgeDatasource", 108 | "nodeName": "Compressor-CMMS-Compressor-2018.crank-frame-temperature", 109 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 110 | } 111 | } 112 | }, 113 | "attributes": { 114 | "machineControllerId": { 115 | "complexType": "Attribute", 116 | "type": "string", 117 | "enumeration": [], 118 | "value": [ 119 | "/asset/Bently.Nevada.3500.Rack1" 120 | ] 121 | }, 122 | "summaryTag": { 123 | "complexType": "Attribute", 124 | "type": "string", 125 | "enumeration": [], 126 | "value": [ 127 | "crank-frame-velocity", 128 | "crank-frame-compressionratio" 129 | ] 130 | } 131 | } 132 | } 133 | ] 134 | -------------------------------------------------------------------------------- /server/sample-data/predix-asset/asset-model-metadata.json: -------------------------------------------------------------------------------- 1 | {"putFieldDataCriteria":[{"namespaces":[],"fieldData":{"field":[{"fieldIdentifier":{"complexType":"FieldIdentifier","source":"PREDIX_ASSET"},"parents":[]}],"data":{"complexType":"MetaData","name":"Compressor-CMMS-Compressor-2018","description":"Industrial Compressor for RMD Reference App","source":"PREDIX_ASSET","tags":["Oil & Gas","Compressor"]}}},{"namespaces":[],"fieldData":{"field":[{"fieldIdentifier":{"complexType":"FieldIdentifier","source":"PREDIX_ASSET"},"parents":[]}],"data":{"complexType":"DataFile"}}}]} 2 | -------------------------------------------------------------------------------- /server/sample-data/predix-asset/edge-asset.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "complexType": "Asset", 4 | "uri": "/asset/Compressor-CMMS-Compressor-2018", 5 | "classificationUri": "/classification/GE_COMPRESSOR_CYLINDER", 6 | "assetId": "Compressor-CMMS-Compressor-2018", 7 | "description": "Reciprocating Compressor", 8 | "filter": "group=/group/plant-richmond-refinery", 9 | "isMock": true, 10 | "group": "/group/plant-richmond-refinery", 11 | "assetTag": { 12 | "crank-frame-compressionratio": { 13 | "complexType": "AssetTag", 14 | "label": "Compression Ratio", 15 | "tagUri": "/tag/crank-frame-compressionratio", 16 | "hiAlarmThreshold": 3, 17 | "loAlarmThreshold": 2.5, 18 | "isKpi": true, 19 | "timeseriesDatasource": { 20 | "complexType": "TimeseriesDatasource", 21 | "tag": "My.App.DOUBLE1" 22 | }, 23 | "edgeDatasource": { 24 | "complexType": "EdgeDatasource", 25 | "nodeName": "Compressor-CMMS-Compressor-2018:CompressionRatio", 26 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 27 | } 28 | }, 29 | "crank-frame-dischargepressure": { 30 | "complexType": "AssetTag", 31 | "label": "Discharge Pressure", 32 | "tagUri": "/tag/crank-frame-dischargepressure", 33 | "hiAlarmThreshold": 23, 34 | "loAlarmThreshold": 0, 35 | "isKpi": true, 36 | "timeseriesDatasource": { 37 | "complexType": "TimeseriesDatasource", 38 | "tag": "My.App.DOUBLE2" 39 | }, 40 | "edgeDatasource": { 41 | "complexType": "EdgeDatasource", 42 | "nodeName": "Compressor-CMMS-Compressor-2018:DischargePressure", 43 | "controllerUri": "/asset/Bently.Nevada.3500.Rack1" 44 | }, 45 | "alertStatusUri": "/asset/Compressor-CMMS-Compressor-2018.alert-status.crank-frame-discharge-pressure" 46 | } 47 | }, 48 | "attributes": { 49 | "machineControllerId": { 50 | "complexType": "Attribute", 51 | "type": "string", 52 | "enumeration": [], 53 | "value": [ 54 | "/asset/Bently.Nevada.3500.Rack1" 55 | ] 56 | }, 57 | "summaryTag": { 58 | "complexType": "Attribute", 59 | "type": "string", 60 | "enumeration": [], 61 | "value": [ 62 | "crank-frame-velocity", 63 | "crank-frame-compressionratio" 64 | ] 65 | } 66 | } 67 | } 68 | ] 69 | -------------------------------------------------------------------------------- /server/sample-data/predix-asset/groups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "filter": "parent=/group/root", 4 | "parent": "/group/root", 5 | "description": "Predix Energy", 6 | "uri": "/group/enterprise-predix", 7 | "level": "enterprise", 8 | "name": "Predix Energy" 9 | }, 10 | { 11 | "filter": "parent=/group/enterprise-predix", 12 | "parent": "/group/enterprise-predix", 13 | "description": "Richmond Refinery", 14 | "uri": "/group/plant-richmond-refinery", 15 | "level": "plant", 16 | "name": "Richmond Refinery 1" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /server/sample-data/rmd-datasource/datagrid-compressor.json: -------------------------------------------------------------------------------- 1 | { 2 | "tableData": [{ 3 | "tag": "crank-frame-compressionratio", 4 | "tagUri": "/tag/crank-frame-compressionratio", 5 | "tag_isKpi": true, 6 | "tag_isPM": true, 7 | "alertStatus": false, 8 | "alertStatusType": null, 9 | "currentValue": 2.7387545914849944, 10 | "unit": null, 11 | "lastTagReading": 1489103997210, 12 | "thresholdMin": 2.5, 13 | "thresholdMax": 3.0, 14 | "deltaThreshold": 8.708180283833517, 15 | "deltaThresholdColor": "GREEN", 16 | "deltaThresholdLevel": "HIGH" 17 | }, { 18 | "tag": "crank-frame-dischargepressure", 19 | "tagUri": "/tag/crank-frame-dischargepressure", 20 | "tag_isKpi": true, 21 | "tag_isPM": true, 22 | "alertStatus": false, 23 | "alertStatusType": null, 24 | "currentValue": 16.767715215208693, 25 | "unit": "psi", 26 | "lastTagReading": 1489103997210, 27 | "thresholdMin": 0.0, 28 | "thresholdMax": 23.0, 29 | "deltaThreshold": 27.09689036865786, 30 | "deltaThresholdColor": "GREEN", 31 | "deltaThresholdLevel": "HIGH" 32 | }, { 33 | "tag": "crank-frame-suctionpressure", 34 | "tagUri": "/tag/crank-frame-suctionpressure", 35 | "tag_isKpi": false, 36 | "tag_isPM": true, 37 | "alertStatus": false, 38 | "alertStatusType": null, 39 | "currentValue": 0.0435274216737174, 40 | "unit": "psi", 41 | "lastTagReading": 1489103997210, 42 | "thresholdMin": 0.0, 43 | "thresholdMax": 0.21, 44 | "deltaThreshold": 20.72734365415114, 45 | "deltaThresholdColor": "GREEN", 46 | "deltaThresholdLevel": "LOW" 47 | }, { 48 | "tag": "crank-frame-maxpressure", 49 | "tagUri": "/tag/crank-frame-maxpressure", 50 | "tag_isKpi": false, 51 | "tag_isPM": true, 52 | "alertStatus": false, 53 | "alertStatusType": null, 54 | "currentValue": 23.095171627593004, 55 | "unit": "psi", 56 | "lastTagReading": 1489103997210, 57 | "thresholdMin": 22.0, 58 | "thresholdMax": 26.0, 59 | "deltaThreshold": 11.172416816949985, 60 | "deltaThresholdColor": "GREEN", 61 | "deltaThresholdLevel": "HIGH" 62 | }, { 63 | "tag": "crank-frame-velocity", 64 | "tagUri": "/tag/crank-frame-velocity", 65 | "tag_isKpi": true, 66 | "tag_isPM": true, 67 | "alertStatus": false, 68 | "alertStatusType": null, 69 | "currentValue": 0.03788935796103903, 70 | "unit": "m/s", 71 | "lastTagReading": 1489103997210, 72 | "thresholdMin": 0.0, 73 | "thresholdMax": 0.07, 74 | "deltaThreshold": 45.872345769944246, 75 | "deltaThresholdColor": "GREEN", 76 | "deltaThresholdLevel": "HIGH" 77 | }, { 78 | "tag": "crank-frame-temperature", 79 | "tagUri": "/tag/crank-frame-temperature", 80 | "tag_isKpi": false, 81 | "tag_isPM": true, 82 | "alertStatus": true, 83 | "alertStatusType": null, 84 | "currentValue": 81.45810117216645, 85 | "unit": "F", 86 | "lastTagReading": 1489103997210, 87 | "thresholdMin": 65.0, 88 | "thresholdMax": 80.0, 89 | "deltaThreshold": 4.427373534791936, 90 | "deltaThresholdColor": "RED", 91 | "deltaThresholdLevel": "HIGH" 92 | }], 93 | "columns": [{ 94 | "field": "tag", 95 | "type": "string", 96 | "inputType": "text", 97 | "inputSize": 25.0, 98 | "label": "Tag Name", 99 | "uom": null 100 | }, { 101 | "field": "alertStatus", 102 | "type": "string", 103 | "inputType": "text", 104 | "inputSize": 20.0, 105 | "label": "Alert Status", 106 | "uom": null 107 | }, { 108 | "field": "currentValue", 109 | "type": "number", 110 | "inputType": "text", 111 | "inputSize": 20.0, 112 | "label": "Current value", 113 | "uom": null 114 | }, { 115 | "field": "unit", 116 | "type": "number", 117 | "inputType": "text", 118 | "inputSize": 10.0, 119 | "label": "Units", 120 | "uom": null 121 | }, { 122 | "field": "lastTagReading", 123 | "type": "string", 124 | "inputType": "text", 125 | "inputSize": 10.0, 126 | "label": "Last Tag Reading", 127 | "uom": null 128 | }, { 129 | "field": "thresholdMin", 130 | "type": "number", 131 | "inputType": "text", 132 | "inputSize": 10.0, 133 | "label": "Threshold Minimum", 134 | "uom": null 135 | }, { 136 | "field": "thresholdMax", 137 | "type": "number", 138 | "inputType": "text", 139 | "inputSize": 10.0, 140 | "label": "Threshold Maximum", 141 | "uom": null 142 | }, { 143 | "field": "deltaThreshold", 144 | "type": "string", 145 | "inputType": "text", 146 | "inputSize": 25.0, 147 | "label": "Delta Threshold ", 148 | "uom": null 149 | }] 150 | } -------------------------------------------------------------------------------- /server/sample-data/rmd-datasource/datagrid-refinery.json: -------------------------------------------------------------------------------- 1 | { 2 | "tableData": [{ 3 | "tag": "crank-frame-compressionratio", 4 | "tagUri": "/tag/crank-frame-compressionratio", 5 | "tag_isKpi": true, 6 | "tag_isPM": true, 7 | "alertStatus": false, 8 | "alertStatusType": null, 9 | "currentValue": 2.8296469609264805, 10 | "unit": null, 11 | "lastTagReading": 1489786617310, 12 | "asset": "Bently Compressor", 13 | "assetUri": "/asset/compressor-2015" 14 | }, { 15 | "tag": "crank-frame-dischargepressure", 16 | "tagUri": "/tag/crank-frame-dischargepressure", 17 | "tag_isKpi": true, 18 | "tag_isPM": true, 19 | "alertStatus": false, 20 | "alertStatusType": null, 21 | "currentValue": 7.2320851255672505, 22 | "unit": "psi", 23 | "lastTagReading": 1489786617310, 24 | "asset": "Bently Compressor", 25 | "assetUri": "/asset/compressor-2015" 26 | }, { 27 | "tag": "crank-frame-velocity", 28 | "tagUri": "/tag/crank-frame-velocity", 29 | "tag_isKpi": true, 30 | "tag_isPM": true, 31 | "alertStatus": false, 32 | "alertStatusType": null, 33 | "currentValue": 0.025494921193207055, 34 | "unit": "m/s", 35 | "lastTagReading": 1489786617310, 36 | "asset": "Bently Compressor", 37 | "assetUri": "/asset/compressor-2015" 38 | }], 39 | "columns": [{ 40 | "field": "asset", 41 | "type": "string", 42 | "inputType": "text", 43 | "inputSize": 25.0, 44 | "label": "Asset", 45 | "uom": null 46 | }, { 47 | "field": "tag", 48 | "type": "string", 49 | "inputType": "text", 50 | "inputSize": 20.0, 51 | "label": "Tag", 52 | "uom": null 53 | }, { 54 | "field": "alertStatus", 55 | "type": "number", 56 | "inputType": "text", 57 | "inputSize": 20.0, 58 | "label": "Alert Status", 59 | "uom": null 60 | }, { 61 | "field": "currentValue", 62 | "type": "number", 63 | "inputType": "text", 64 | "inputSize": 10.0, 65 | "label": "Current Reading", 66 | "uom": null 67 | }, { 68 | "field": "unit", 69 | "type": "number", 70 | "inputType": "text", 71 | "inputSize": 10.0, 72 | "label": "Units", 73 | "uom": null 74 | }, { 75 | "field": "lastTagReading", 76 | "type": "datetime", 77 | "inputType": "text", 78 | "inputSize": 10.0, 79 | "label": "Last Tag Reading", 80 | "uom": null 81 | }] 82 | } -------------------------------------------------------------------------------- /server/sample-data/rmd-datasource/summary-compressor.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "kpis": [{ 3 | "value": "0.049", 4 | "label": "Output (avg)", 5 | "uom": "m/s" 6 | }, { 7 | "value": "2.75", 8 | "label": "crank-frame-compressionratio (avg)", 9 | "uom": null 10 | }, { 11 | "value": "60.0", 12 | "label": "Reliability", 13 | "uom": "%" 14 | }, { 15 | "value": "85.0", 16 | "label": "Availability", 17 | "uom": "%" 18 | }], 19 | "overall": { 20 | "title": "KPI Health", 21 | "subtitle": "Compressor-CMMS-Compressor-2018", 22 | "percentage": 85.0 23 | } 24 | }] -------------------------------------------------------------------------------- /server/sample-data/rmd-datasource/summary-refinery.json: -------------------------------------------------------------------------------- 1 | { 2 | "kpis": [{ 3 | "value": "10.0", 4 | "label": "Output (avg)", 5 | "uom": "mb/d" 6 | }, { 7 | "value": "10.0", 8 | "label": "Octane Number (avg)", 9 | "uom": null 10 | }, { 11 | "value": "60.0", 12 | "label": "Reliability", 13 | "uom": "%" 14 | }, { 15 | "value": "100.0", 16 | "label": "Availability", 17 | "uom": "%" 18 | }], 19 | "overall": { 20 | "title": "KPI Health", 21 | "subtitle": "plant-richmond-refinery", 22 | "percentage": 100.0 23 | } 24 | } -------------------------------------------------------------------------------- /server/sample-data/time-series/2-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": [{ 3 | "name": "Compressor-CMMS-Compressor-2018:CompressionRatio", 4 | "results": [{ 5 | "groups": [{ 6 | "name": "type", 7 | "type": "number" 8 | }], 9 | "values": [ 10 | [1489615201706, 2.790201188714474, 3], 11 | [1489615232949, 2.777595425042259, 3], 12 | [1489615262095, 2.757900075726165, 3], 13 | [1489615293324, 2.7387557351287115, 3], 14 | [1489615324652, 2.867478344883653, 3], 15 | [1489615352884, 2.7277724075659746, 3], 16 | [1489615384208, 2.7566573698329866, 3], 17 | [1489615412225, 2.7540674658329505, 3], 18 | [1489615443558, 2.7053423997287345, 3], 19 | [1489615471730, 2.695539677232941, 3], 20 | [1489615502957, 2.6587629675583133, 3], 21 | [1489615534391, 2.749814878556744, 3], 22 | [1489615562498, 2.7211945100283095, 3], 23 | [1489615593718, 2.717825434812348, 3], 24 | [1489615622012, 2.7023015482613952, 3], 25 | [1489615653262, 2.7322397663493034, 3], 26 | [1489615684583, 2.7938893310629274, 3], 27 | [1489615712735, 2.8193213064153704, 3], 28 | [1489615743992, 2.714217275885393, 3], 29 | [1489615772169, 2.841475054440882, 3], 30 | [1489615803454, 2.804752998913334, 3], 31 | [1489615834792, 2.7900135387191303, 3], 32 | [1489615862999, 2.7535261660783674, 3], 33 | [1489615894331, 2.7660247137525173, 3], 34 | [1489615922567, 2.752702173356181, 3], 35 | [1489615953982, 2.7555175053461842, 3], 36 | [1489615982178, 2.8456213790937754, 3], 37 | [1489616013533, 2.8339198866080597, 3], 38 | [1489616041747, 2.8044532246815477, 3], 39 | [1489616073061, 2.711538349992279, 3], 40 | [1489616104442, 2.7800083787704972, 3], 41 | [1489616132569, 2.745942614745533, 3], 42 | [1489616163920, 2.6828880842589014, 3], 43 | [1489616192167, 2.6546305729554245, 3] 44 | ], 45 | "attributes": { 46 | "assetId": ["Compressor-CMMS-Compressor-2018"], 47 | "sourceTagId": ["CompressionRatio"] 48 | } 49 | }], 50 | "stats": { 51 | "rawCount": 326 52 | } 53 | }, { 54 | "name": "Compressor-CMMS-Compressor-2018:DischargePressure", 55 | "results": [{ 56 | "groups": [{ 57 | "name": "type", 58 | "type": "number" 59 | }], 60 | "values": [ 61 | [1489615201706, 10.26758225618985, 3], 62 | [1489615232949, 7.86844756674142, 3], 63 | [1489615262095, 15.871590495249801, 3], 64 | [1489615293324, 14.385112013767445, 3], 65 | [1489615324652, 13.648875728903038, 3], 66 | [1489615352884, 13.764177352213412, 3], 67 | [1489615384208, 8.267804749097156, 3], 68 | [1489615412225, 11.776820309165451, 3], 69 | [1489615443558, 11.94323002582114, 3], 70 | [1489615471730, 17.229249919120626, 3], 71 | [1489615502957, 10.705465971333112, 3], 72 | [1489615534391, 10.961226606669118, 3], 73 | [1489615562498, 16.08690732898673, 3], 74 | [1489615593718, 15.883460717351324, 3], 75 | [1489615622012, 10.924517724843472, 3], 76 | [1489615653262, 9.034299593333369, 3], 77 | [1489615684583, 14.773572860888306, 3], 78 | [1489615712735, 8.848219665481375, 3], 79 | [1489615743992, 8.22582450023226, 3], 80 | [1489615772169, 9.27774435693377, 3], 81 | [1489615803454, 14.070250356857533, 3], 82 | [1489615834792, 10.912374355282502, 3], 83 | [1489615862999, 15.042916292025325, 3], 84 | [1489615894331, 11.713298783751789, 3], 85 | [1489615922567, 12.530243787913538, 3], 86 | [1489615953982, 12.938059810665283, 3], 87 | [1489615982178, 12.261504491232607, 3], 88 | [1489616013533, 13.106670299358198, 3], 89 | [1489616041747, 11.352448047264271, 3], 90 | [1489616073061, 9.855299849696204, 3], 91 | [1489616104442, 14.144995962350935, 3], 92 | [1489616132569, 12.79202407290183, 3], 93 | [1489616163920, 13.335738376151696, 3], 94 | [1489616192167, 10.735634597454709, 3] 95 | ], 96 | "attributes": { 97 | "assetId": ["Compressor-CMMS-Compressor-2018"], 98 | "sourceTagId": ["DischargePressure"] 99 | } 100 | }], 101 | "stats": { 102 | "rawCount": 326 103 | } 104 | }] 105 | } -------------------------------------------------------------------------------- /tasks/compile.index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // ------------------------------------- 4 | // Task: Compile: Inline Index Source 5 | // ------------------------------------- 6 | const inlinesource = require('gulp-inline-source'); 7 | const rename = require('gulp-rename'); 8 | 9 | module.exports = function(gulp) { 10 | return function() { 11 | return gulp.src('./public/_index.html') 12 | .pipe(inlinesource()) 13 | .pipe(rename('index.html')) 14 | .pipe(gulp.dest('./public')); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /tasks/compile.sass.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // ------------------------------------- 4 | // Task: Compile: Sass 5 | // ------------------------------------- 6 | const stylemod = require('gulp-style-modules'); 7 | const autoprefixer = require('gulp-autoprefixer'); 8 | const path = require('path'); 9 | const importOnce = require('node-sass-import-once'); 10 | 11 | var getName = function(file) { 12 | return path.basename(file.path, path.extname(file.path)); 13 | }; 14 | 15 | var styleModuleDest = function(file) { 16 | return file.base; 17 | // console.log(path.basename(file.path)); 18 | // var name = getName(file); 19 | // return './temp/styles.html'; 20 | }; 21 | 22 | module.exports = function(gulp, plugins) { 23 | return function() { 24 | 25 | return gulp.src([ 26 | './public/*.scss', 27 | './public/elements/*.scss', 28 | './public/elements/**/*.scss' 29 | ]) 30 | .pipe(plugins.sass({ 31 | includePaths: './public/bower_components/', 32 | importer: importOnce, 33 | importOnce: { 34 | index: true, 35 | bower: true 36 | } 37 | }) 38 | .on('error', plugins.sass.logError)) 39 | .pipe(autoprefixer()) 40 | .pipe(stylemod({ 41 | // All files will be named 'styles.html' 42 | filename: function(file) { 43 | var name = getName(file) + '-styles'; 44 | return name; 45 | }, 46 | // Use '-css' suffix instead of '-styles' for module ids 47 | moduleId: function(file) { 48 | return getName(file) + '-styles'; 49 | } 50 | })) 51 | .pipe(gulp.dest(styleModuleDest)); 52 | 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /tasks/dist.clean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const clean = require('gulp-clean'); 3 | 4 | // --------------------------- 5 | // Task: Clean 'build' folder 6 | // --------------------------- 7 | 8 | module.exports = function(gulp) { 9 | return function() { 10 | return gulp.src('build', {read: false}) 11 | .pipe(clean()); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /tasks/dist.copy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const merge = require('merge-stream'); 3 | 4 | // ------------------------------------------------ 5 | // Task: Copy extra files for deployment 6 | // Most files are included in the bundle, these are exceptions. 7 | // ------------------------------------------------ 8 | 9 | module.exports = function(gulp) { 10 | return function() { 11 | var outputDir = './build/es5-basic/'; 12 | // These directories contain files that are not included in the polymer build output for various reasons. 13 | // (Whenever possible files should be imported using HTML imports, so they're included in the polymer build.) 14 | var extraDirectories = [ 15 | 'public/bower_components/polymer', 16 | 'public/bower_components/webcomponentsjs', 17 | 18 | 'public/bower_components/px-typography-design', 19 | 'public/bower_components/px-theme', 20 | 'public/bower_components/px-dark-theme', 21 | 'public/bower_components/px-vis', 22 | 'public/bower_components/pxd3', 23 | 'public/downloads', 24 | 25 | 'public/elements/dev-guide' 26 | ]; 27 | 28 | var extraStreams = []; 29 | 30 | extraDirectories.forEach(function(bc) { 31 | extraStreams.push(gulp.src([bc + '/**/*.*']).pipe(gulp.dest(outputDir + bc))); 32 | }); 33 | 34 | var docsFiles = gulp.src(['public/docs/**/*.*']).pipe(gulp.dest(outputDir + 'public/docs')); 35 | var server = gulp.src(['server/**/*.*']).pipe(gulp.dest(outputDir + '/server')) 36 | var packageFile = gulp.src(['package.json']).pipe(gulp.dest(outputDir)); 37 | var imageFiles = gulp.src(['public/images/**/*.*']).pipe(gulp.dest(outputDir + 'public/images')); 38 | 39 | return merge(server, packageFile, extraStreams, docsFiles, imageFiles); 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /tasks/serve.dev.start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // ------------------------------------- 4 | // Task: Serve raw unbundled files from /public 5 | // ------------------------------------- 6 | const nodemon = require('gulp-nodemon'); 7 | 8 | module.exports = function() { 9 | return function() { 10 | nodemon({ 11 | script: 'server/app.js', 12 | ignore: ['/../public/*'], 13 | env: { 'base-dir' : '/../public'} 14 | }) 15 | .on('restart', function() { 16 | console.log('app.js restarted'); 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /tasks/serve.dist.start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // ------------------------------------- 4 | // Task: Serve bundled files from /build 5 | // ------------------------------------- 6 | const nodemon = require('gulp-nodemon'); 7 | 8 | module.exports = function() { 9 | return function() { 10 | nodemon({ 11 | script: 'server/app.js', 12 | ignore: ['*'], 13 | env: { 'base-dir' : '/../build/es5-basic/public'} 14 | }) 15 | .on('restart', function() { 16 | console.log('app.js restarted'); 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /tasks/watch.public.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // ------------------------------------- 4 | // Task: Watch: Public 5 | // ------------------------------------- 6 | 7 | module.exports = function(gulp) { 8 | return function() { 9 | gulp.watch(['public/elements/**/*.scss', 'public/*.scss'], ['compile:sass']); 10 | gulp.watch(['./public/_index.html','./public/_index-inline-loading-script.js','./public/index-inline.scss'], 11 | ['compile:index']); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /test/asset-browser-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | seed-test-01 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | Tests 9 | 10 | 11 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/kpi-bar-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KPI Bar Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /test/seed-table-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | seed-test-01 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /test/seed-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | seed-test-01 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /test/server/learningpaths-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const expect = require('chai').expect; 3 | const sinon = require('sinon'); 4 | const lp = require('../../server/routes/learning-paths'); 5 | const predixConfig = require('../../server/predix-config'); 6 | const stubConfig = {}; 7 | const expressResponse = {}; 8 | const baseResponse = { 9 | "appMode": "default", 10 | "learningSequence": ['base', 'uaa', 'asset', 'timeseries'], 11 | "services": {"base": true, "uaa": false, "asset": false, "timeseries": false} 12 | }; 13 | let expectedResponse; 14 | 15 | beforeEach(() => { 16 | expressResponse.send = sinon.spy() 17 | stubConfig.isUaaConfigured = sinon.stub().returns(false); 18 | stubConfig.isAssetConfigured = sinon.stub().returns(false); 19 | stubConfig.isTimeSeriesConfigured = sinon.stub().returns(false); 20 | expectedResponse = baseResponse; 21 | }); 22 | 23 | describe('Learning Paths', () => { 24 | it('returns default object when nothing is configured', () => { 25 | const mw = lp(stubConfig); 26 | mw(null, expressResponse, function(){}); 27 | expect(expressResponse.send.calledOnce).to.be.true; 28 | expect(expressResponse.send.calledWith(expectedResponse)).to.be.true; 29 | }); 30 | 31 | it('returns correct object if only uaa is configured', () => { 32 | stubConfig.isUaaConfigured.returns(true); 33 | const mw = lp(stubConfig); 34 | mw(null, expressResponse, function(){}); 35 | expectedResponse.services.uaa = true; 36 | expect(expressResponse.send.calledOnce).to.be.true; 37 | expect(expressResponse.send.calledWith(expectedResponse)).to.be.true; 38 | }); 39 | 40 | it('returns correct object if uaa and asset are configured', () => { 41 | stubConfig.isUaaConfigured.returns(true); 42 | stubConfig.isAssetConfigured.returns(true); 43 | const mw = lp(stubConfig); 44 | mw(null, expressResponse, function(){}); 45 | expectedResponse.services.uaa = true; 46 | expectedResponse.services.asset = true; 47 | expect(expressResponse.send.calledOnce).to.be.true; 48 | expect(expressResponse.send.calledWith(expectedResponse)).to.be.true; 49 | }); 50 | 51 | it('returns correct object if uaa, asset, and ts are configured', () => { 52 | stubConfig.isUaaConfigured.returns(true); 53 | stubConfig.isAssetConfigured.returns(true); 54 | stubConfig.isTimeSeriesConfigured.returns(true); 55 | const mw = lp(stubConfig); 56 | mw(null, expressResponse, function(){}); 57 | expectedResponse.services.uaa = true; 58 | expectedResponse.services.asset = true; 59 | expectedResponse.services.timeseries = true; 60 | expect(expressResponse.send.calledOnce).to.be.true; 61 | expect(expressResponse.send.calledWith(expectedResponse)).to.be.true; 62 | }); 63 | }); -------------------------------------------------------------------------------- /tutorials/USERAUTH.md: -------------------------------------------------------------------------------- 1 | # Tutorial : User Authentication with UAA 2 | 3 | ## Introduction 4 | Web applications often involve securing access to information. For this reason one commonly needed feature is that of user authentication. Requiring login to access restricted portions is an essential feature of a secure web application. 5 | 6 | This tutorial shows how to enable authentication for the Predix Web App Starter. Specifically, we use the Predix UAA service, which has its own user interface, for authentication. Accessing any restricted page of the application with a browser in unauthenticated state should trigger redirection to the UAA login user interface. Upon successful login the browser is redirected back to the application. From there the user is able to access the application's restricted information. This authenticated session lasts until the user logs out or the session expires. 7 | 8 | We first show how to require authentication for all routes and pages of the application. Then we show how to make specific routes require authentication. 9 | 10 | If you prefer a video version (on which this written version is based), one is available [**here**](https://youtu.be/AiJ2IFJoTHg?list=PLibNgo_CBeuujvRV26_uLTksm1ezh7oGd). 11 | 12 | ### Pre-Requisites 13 | This tutorial requires an existing UAA instance. Please refer to this [**page**](https://www.predix.io/resources/tutorials/tutorial-details.html?tutorial_id=1544&tag=1605&journey=Build%20a%20basic%20application&resources=1580,1569,1523,1544,1547,1549,1556,1553,1570) for information on creating an instance and selecting a set of credentials. This step produces the following values: 14 | 15 | - URL 16 | - clientId 17 | - secret 18 | - username / password (credentials) 19 | 20 | Save these values for use in the later configuration steps below. 21 | 22 | This tutorial also requires knowledge of and practical experience with the Predix Web App Starter (this project). You should have been able to install, minimally configure, and deploy the app prior to performing this tutorial. Please refer to the README document of this project for this requirement. 23 | 24 | ## Steps 25 | ### Configure for Authentication 26 | 1. Install the Predix Web App Starter and have it running locally by following the steps in the **README** document of this project. 27 | 2. With the app running locally, access the */secure* route ( For example: *https://localhost:5000/secure* ). You should see a page that says the site is unavailable ( browser shows page that says *cannot GET /secure* ). This is because the route has not been defined yet. 28 | 3. Find the *localConfig.json* file under the *server* folder. From this file locate the 3 configuration variables: 29 | - **uaaURL** 30 | - **base64ClientCredential** 31 | - **loginBase64ClientCredential** 32 | 4. Replace the values of these variables with the following: 33 | 34 | #### uaaURL 35 | This is the URL of the UAA instance that was created in the **Pre-Requisites** section. With the service running and a set of credentials in hand (user and password), use the service URL as the value for this variable. 36 | 37 | #### base64ClientCredential 38 | This is a [**Base64**](https://en.wikipedia.org/wiki/Base64) encoding of the string '*\*:*\*', where '*\*' is the client ID configured for access to back end microservices, and '*\*' is the client secret for that UAA client. 39 | 40 | In a Mac OS or Unix environment, you can get this value by running the following command sequence (for example, using the string literals '*app_client_id*' and '*secret*' for **clientId** and **secret** values, respectively): 41 | ``` 42 | echo -n app_client_id:secret | base64 43 | ``` 44 | In a Windows environment, [**certutil**](https://technet.microsoft.com/en-us/library/cc732443\(v=ws.11\).aspx) utility can be used to generate the same value. 45 | 46 | After running the above command sequence in your chosen environment, copy the output string into the variable. 47 | 48 | #### loginBase64ClientCredential 49 | This is a [**Base64**](https://en.wikipedia.org/wiki/Base64) encoding of the string '*\*:*\*', where '*\*' is the client ID configured for user access to this application, and '*\*' is the client secret for that UAA client. 50 | 51 | Here is an example of all three configuration variables in *server/localConfig.json* populated with their respective values : 52 | 53 | ``` 54 | ... 55 | "uaaURL": "https://162665f2-e477-488a-93d1-bb33ccb3d568.predix-uaa.run.aws-usw02-pr.ice.predix.io", 56 | "base64ClientCredential": "YXBwX2NsaWVudF9pZDpzZWNyZXQ=", 57 | "loginBase64ClientCredential": "bG9naW5fY2xpZW50X2lkOnNlY3JldA==" 58 | ... 59 | ``` 60 | 61 | ### Verify Authentication 62 | 1. After modifying localConfig.json, the local web server should restart. If not, you can restart the running server by typing `rs` into the terminal where the server is running. 63 | 64 | 2. By default, the app is configured to require login for all routes in the app. If you try to access http://localhost:5000 you should be redirected to the UAA login screen. After successful login, the browser is redirected back to the app running on your localhost. 65 | 66 | 3. Click your user name in the top right, then click the "Sign Out" link. This directs your browser to the /logout route, which ends your session and redirects you back to the application. Since all pages in the app require authentication, you'll be immediately redirected back to the UAA login screen. 67 | 68 | ### Authenticating Only Specific Routes (Optional) 69 | The previous sections show how authentication can be added to the entire application. In some cases, you might want to allow unauthenticated access to some parts of your application. To do that, we'll need to modify the code below in server/app.js. 70 | 71 | ``` 72 | //Use this route to make the entire app secure. This forces login for any path in the entire app. 73 | app.use('/', passport.authenticate('main', { 74 | noredirect: false //Don't redirect a user to the authentication page, just show an error 75 | }), 76 | express.static(path.join(__dirname, process.env['base-dir'] ? process.env['base-dir'] : '../public')) 77 | ); 78 | ``` 79 | 1. Remove the passport middleware function from the '/' route, so Express will no longer require authentication on all routes. The route should now look like this: 80 | ``` 81 | app.use('/', 82 | express.static(path.join(__dirname, process.env['base-dir'] ? process.env['base-dir'] : '../public')) 83 | ); 84 | ``` 85 | 2. After the local server restarts, refresh your browser and visit the "About" page: [http://localhost:5000/#/about]() (You can access the main */#/rmd* route now, but you'll get errors since the back end service routes still require authentication.) 86 | 87 | 3. Access the */secure* route. Notice that the browser now returns a page that says *Unauthorized*, instead of not being able to find the page (as in the previous section). This is because that route has now been defined, as an authenticated route (other routes that have also been defined are */login*, */callback*, */predix-api* and */logout*). At this point authentication is in place, and your browser session is in an unauthenticated state. Consequently, accessing this route results in "Unauthorized" (with the exception of */login*, which redirects to the authentication service's page). 88 | 89 | 4. Access the */login* route. Notice that the browser is redirected to the UAA login page. 90 | 91 | 5. Enter valid credentials in the login page and click on Submit. Upon successful login, the browser is redirected back to the main */#/rmd* route, which now shows data. 92 | 93 | 6. Visit the */secure* route again, and you should see the text **This is a sample secure route**. At this point, the browser session is now in the authenticated state, and access to such route is now authorized. We have just shown how to integrate UAA with authentication-requiring routes/pages in an instance of the webapp starter. 94 | 95 | 7. Access the */logout* route. This will put the browser session back to the unauthenticated state. 96 | 97 | 8. Access the */secure* route once more. Notice that we get the *Unauthorized* result again, because the browser session is now back to being unauthenticated. 98 | 99 | ### Deploying to the Cloud 100 | The previous steps showed how authentication is enabled in a local instance of the Predix Web App Starter application. Ultimately, we want the authentication feature to be part of deployments to the Cloud. To achieve this, perform these steps: 101 | 102 | In the *manifest.yml* file (or your designated manifest file), enable services by uncommenting the *services* section, and enter the name of the UAA instance that will be used. For example: 103 | 104 | ``` 105 | services 106 | - my-uaa-service 107 | ``` 108 | 109 | In the same file, enter the values for **clientId** and **base64ClientCredential** that were used in the previous sections. For Example: 110 | 111 | ``` 112 | env: 113 | base64ClientCredential: YXBwX2NsaWVudF9pZDpzZWNyZXQ= 114 | loginBase64ClientCredential: bG9naW5fY2xpZW50X2lkOnNlY3JldA== 115 | ``` 116 | 117 | From the command terminal, and in the main folder of the application, run 118 | 119 | ``` 120 | gulp dist 121 | ``` 122 | 123 | to include the configuration in the distribution package for the application. 124 | 125 | Deploy to the Cloud as usual. 126 | 127 | Perform the same steps above to verify that authentication is working. 128 | 129 | ## Conclusion 130 | 131 | This document has shown how to add UAA authentication to an instance of the Predix Web App Starter, and apply the feature to all routes or specific routes only. It has also shown how to apply the feature in both local and cloud deployments. 132 | 133 | ## Support and Further Information 134 | 135 | For more information on this tutorial, you can ask questions and file tickets on https://www.predix.io/community. 136 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Predix WebApp Starter", 3 | "version": "2.0.92", 4 | "private": true, 5 | "dependencies": { 6 | "local-setup": "https://github.com/PredixDev/local-setup#1.0.106", 7 | "predix-scripts": "https://github.com/PredixDev/predix-scripts#1.1.241", 8 | "predix-webapp-starter": "https://github.com/PredixDev/predix-webapp-starter.git#2.0.92" 9 | }, 10 | "author": "gedesigntech" 11 | } 12 | -------------------------------------------------------------------------------- /wct.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: false, 3 | persistent: false, 4 | plugins: { 5 | local: { 6 | browsers: ['chrome'] 7 | }, 8 | sauce: { 9 | disabled: true, 10 | "browsers": [{ 11 | "browserName": "microsoftedge", 12 | "platform": "Windows 10", 13 | "version": "" 14 | }, { 15 | "browserName": "internet explorer", 16 | "platform": "Windows 8.1", 17 | "version": "11" 18 | }, { 19 | "browserName": "safari", 20 | "platform": "OS X 10.11", 21 | "version": "9" 22 | }, { 23 | "browserName": "safari", 24 | "platform": "OS X 10.10", 25 | "version": "8" 26 | }] 27 | } 28 | }, 29 | suites: [ 30 | 'test/index.html' 31 | ] 32 | }; 33 | --------------------------------------------------------------------------------