├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── NOTICE.txt
├── README.md
├── deployment
├── build-s3-dist.sh
├── custom-deployment
│ ├── bin
│ │ └── smart-product-solution.ts
│ ├── cdk-manifest.json
│ ├── cdk.json
│ ├── lib
│ │ ├── name-generator.ts
│ │ ├── smart-product-api.ts
│ │ ├── smart-product-device-defender.ts
│ │ ├── smart-product-event.ts
│ │ ├── smart-product-jitr.ts
│ │ ├── smart-product-owner-web-app.ts
│ │ ├── smart-product-solution-stack.ts
│ │ └── smart-product-telemetry.ts
│ ├── package.json
│ └── tsconfig.json
├── manifest-generator
│ ├── app.js
│ └── package.json
├── run-unit-tests.sh
└── smart-product-solution.template
└── source
├── console
├── jsconfig.json
├── package.json
├── public
│ ├── apple-icon.png
│ ├── assets
│ │ └── smart_product_config.js
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
│ ├── assets
│ ├── css
│ │ ├── animate.min.css
│ │ ├── demo.css
│ │ ├── light-bootstrap-dashboard-react.css
│ │ ├── light-bootstrap-dashboard-react.css.map
│ │ ├── light-bootstrap-dashboard-react.min.css
│ │ └── pe-icon-7-stroke.css
│ ├── fonts
│ │ ├── Pe-icon-7-stroke.eot
│ │ ├── Pe-icon-7-stroke.svg
│ │ ├── Pe-icon-7-stroke.ttf
│ │ ├── Pe-icon-7-stroke.woff
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ ├── img
│ │ └── logo-aws.png
│ └── sass
│ │ ├── lbd
│ │ ├── _alerts.scss
│ │ ├── _buttons.scss
│ │ ├── _cards.scss
│ │ ├── _chartist.scss
│ │ ├── _checkbox-radio-switch.scss
│ │ ├── _custom.scss
│ │ ├── _dropdown.scss
│ │ ├── _footers.scss
│ │ ├── _inputs.scss
│ │ ├── _misc.scss
│ │ ├── _mixins.scss
│ │ ├── _navbars.scss
│ │ ├── _responsive.scss
│ │ ├── _sidebar-and-main-panel.scss
│ │ ├── _tables.scss
│ │ ├── _typography.scss
│ │ ├── _variables.scss
│ │ └── mixins
│ │ │ ├── _buttons.scss
│ │ │ ├── _cards.scss
│ │ │ ├── _chartist.scss
│ │ │ ├── _icons.scss
│ │ │ ├── _inputs.scss
│ │ │ ├── _labels.scss
│ │ │ ├── _morphing-buttons.scss
│ │ │ ├── _navbars.scss
│ │ │ ├── _social-buttons.scss
│ │ │ ├── _tabs.scss
│ │ │ ├── _transparency.scss
│ │ │ └── _vendor-prefixes.scss
│ │ └── light-bootstrap-dashboard-react.scss
│ ├── components
│ ├── Card
│ │ └── Card.jsx
│ ├── CustomCheckbox
│ │ └── CustomCheckbox.jsx
│ ├── Footer
│ │ └── Footer.jsx
│ ├── Navbars
│ │ ├── AdminNavbar.jsx
│ │ └── AdminNavbarLinks.jsx
│ └── Sidebar
│ │ └── Sidebar.jsx
│ ├── index.js
│ ├── layouts
│ └── Admin.jsx
│ ├── routes.js
│ ├── variables
│ ├── Variables.jsx
│ └── configurations.js
│ └── views
│ ├── Alerts.jsx
│ ├── DeviceDetail.jsx
│ ├── DeviceRegistration.jsx
│ ├── Devices.jsx
│ ├── History.jsx
│ └── UserSetting.jsx
├── resources
├── authorizer
│ ├── auth.common.js
│ ├── package.json
│ └── test-setup.spec.js
├── cicd
│ ├── index.js
│ └── package.json
├── cognito
│ ├── index.js
│ ├── index.spec.js
│ └── package.json
├── helper
│ ├── index.js
│ ├── lib
│ │ ├── dynamodb-helper.js
│ │ ├── dynamodb-helper.spec.js
│ │ ├── iot-helper.js
│ │ ├── iot-helper.spec.js
│ │ ├── s3-helper.js
│ │ └── test-setup.spec.js
│ └── package.json
├── logger
│ ├── logger.common.js
│ ├── package.json
│ └── test-setup.spec.js
├── usage-metrics
│ ├── metrics.common.js
│ ├── package.json
│ └── test-setup.spec.js
└── utils
│ ├── package.json
│ ├── test-setup.spec.js
│ └── utils.common.js
└── services
├── api
├── admin
│ ├── index.js
│ ├── lib
│ │ ├── admin.js
│ │ ├── admin.spec.js
│ │ ├── app.js
│ │ └── test-setup.spec.js
│ └── package.json
├── command
│ ├── index.js
│ ├── lib
│ │ ├── app.js
│ │ ├── command.js
│ │ └── command.spec.js
│ └── package.json
├── device
│ ├── index.js
│ ├── lib
│ │ ├── app.js
│ │ ├── device.js
│ │ ├── device.spec.js
│ │ └── test-setup.spec.js
│ └── package.json
├── event
│ ├── index.js
│ ├── lib
│ │ ├── app.js
│ │ ├── event.js
│ │ ├── event.spec.js
│ │ └── test-setup.spec.js
│ └── package.json
├── registration
│ ├── index.js
│ ├── lib
│ │ ├── app.js
│ │ ├── registration.js
│ │ └── registration.spec.js
│ └── package.json
└── status
│ ├── index.js
│ ├── lib
│ ├── app.js
│ ├── status.js
│ ├── status.spec.js
│ └── test-setup.spec.js
│ └── package.json
├── command-status
├── index.js
├── lib
│ ├── index.js
│ ├── index.spec.js
│ ├── message.js
│ └── message.spec.js
└── package.json
├── event-proxy
├── index.js
├── lib
│ ├── index.js
│ ├── index.spec.js
│ ├── message.js
│ ├── message.spec.js
│ └── test-setup.spec.js
└── package.json
├── jitr
├── index.js
├── lib
│ ├── index.js
│ ├── jitrHelper.js
│ └── jitrHelper.spec.js
└── package.json
├── notification
├── index.js
├── lib
│ ├── alert.js
│ ├── alert.spec.js
│ └── index.js
└── package.json
└── telemetry
├── index.js
├── lib
├── index.js
└── index.spec.js
└── package.json
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | **/dist
3 | **/open-source
4 | **/.zip
5 | **/tmp
6 | **/out-tsc
7 | **/global-s3-assets
8 | **/regional-s3-assets
9 |
10 | # dependencies
11 | **/node_modules
12 |
13 | # e2e
14 | **/e2e/*.js
15 | **/e2e/*.map
16 |
17 | # misc
18 | **/npm-debug.log
19 | **/testem.log
20 | **/package-lock.json
21 | **/.vscode
22 | **.nyc_output
23 | **.pem
24 |
25 | # System Files
26 | **/.DS_Store
27 |
28 | # Amplify Configuration
29 | **/variables/aws_exports.js
30 | **/build
31 |
32 | # CDK Configuration
33 | **/cdk.out
34 | **/cdk.staging
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [1.0.2] - 2020-01-28
8 | ### Updated
9 | - Fix for CDK breaking change *cloudfront: (experimental module) S3OriginConfig.originAccessIdentityId or type string has been removed in favor of S3OriginConfig.originAccessIdentity of type IOriginAccessIdentity*
10 | - Lock CDK version to 1.22.0
11 | - Update JITR microservice to use ```x509``` library
12 | - Update ```react-scripts``` version to fix vulnerability issue: https://npmjs.com/advisories/1426
13 |
14 | ### Removed
15 | - Remove some unnecessary library
16 | - Delete ```template.yml``` files
17 |
18 | ## [1.0.1] - 2019-12-16
19 | ### Updated
20 | - Update Node.JS version from 8 to 12
21 | - Update CDK dependency version to 1.18.0
22 | - Add adm-zip on CI/CD helper Lambda function to work on Node.JS 12.x platform
23 | - Update custom resource helper: ```then().catch()``` to ```async/await```.
24 | - Remove ```\n``` from README
25 | - Update device microservice unit tests
26 | - Remove ```finally``` statement from all unit tests
27 | - Update console package version to fix vulnerability
28 | - Add try/catch for sending anonymous data
29 | - Add API Gateway access logging configuration
30 |
31 | ## [1.0.0] - 2019-09-12
32 | ### Added
33 | - Smart Product Solution release
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/awslabs/aws-smart-product/issues), or [recently closed](https://github.com/awslabs/aws-smart-product/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-smart-product/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/awslabs/aws-smart-product/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Smart Product Reference Architecture
2 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
16 | **********************
17 | THIRD PARTY COMPONENTS
18 | **********************
19 | This software includes third party software subject to the following copyrights:
20 |
21 | AWS SDK under the Apache License Version 2.0
22 | aws-amplify under the Apache License Version 2.0
23 | aws-amplify-react under the Apache License Version 2.0
24 | aws-serverless-express under the Apache License Version 2.0
25 | express under the Massachusetts Institute of Technology (MIT) license
26 | cors under the Massachusetts Institute of Technology (MIT) license
27 | bootstrap under the Massachusetts Institute of Technology (MIT) license
28 | body-parser under the Massachusetts Institute of Technology (MIT) license
29 | node-sass under the Massachusetts Institute of Technology (MIT) license
30 | react under the Massachusetts Institute of Technology (MIT) license
31 | react-bootstrap under the Massachusetts Institute of Technology (MIT) license
32 | react-dom under the Massachusetts Institute of Technology (MIT) license
33 | react-notification-system under the Massachusetts Institute of Technology (MIT) license
34 | react-router under the Massachusetts Institute of Technology (MIT) license
35 | react-router-dom under the Massachusetts Institute of Technology (MIT) license
36 | react-scripts under the Massachusetts Institute of Technology (MIT) license
37 | react-toggle under the Massachusetts Institute of Technology (MIT) license
38 | underscore.js under the Massachusetts Institute of Technology (MIT) license
39 | moment.js under the Massachusetts Institute of Technology (MIT) license
40 | js-base64 under the Massachusetts Institute of Technology (MIT) license
41 | jsonwebtoken under the Massachusetts Institute of Technology (MIT) license
42 | jwk-to-pem under the Apache License Version 2.0
43 | font-awesome under the Massachusetts Institute of Technology (MIT) license
44 | uuid under the Massachusetts Institute of Technology (MIT) license
45 | request-promise under the ISC License
46 | request under the Apache License Version 2.0
47 | @fidm/x509 under the Massachusetts Institute of Technology (MIT) license
48 |
49 | The licenses for these third party components are included in LICENSE.txt
50 |
--------------------------------------------------------------------------------
/deployment/custom-deployment/bin/smart-product-solution.ts:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | import cdk = require('@aws-cdk/core');
15 | import { SmartProductSolutionStack } from '../lib/smart-product-solution-stack';
16 |
17 | const app = new cdk.App();
18 | new SmartProductSolutionStack(app, 'SmartProductSolutionStack');
--------------------------------------------------------------------------------
/deployment/custom-deployment/cdk-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": {
3 | "version": "v0.0.1",
4 | "sendAnonymousUsage": true,
5 | "api": {
6 | "deploy": true
7 | },
8 | "defender": {
9 | "deploy": true
10 | },
11 | "events": {
12 | "deploy": true,
13 | "env": {
14 | "eventTopic": "smartproduct/event"
15 | }
16 | },
17 | "jitr": {
18 | "deploy": true
19 | },
20 | "ownerapp": {
21 | "deploy": true
22 | },
23 | "telemetry": {
24 | "deploy": true,
25 | "env": {
26 | "telemetryTopic": "smartproduct/telemetry"
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/deployment/custom-deployment/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "npx ts-node bin/smart-product-solution.ts"
3 | }
4 |
--------------------------------------------------------------------------------
/deployment/custom-deployment/lib/name-generator.ts:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | const uuidv4 = require('uuid/v4');
15 |
16 | export function generateName(prefix: string, maxlength: number): string {
17 | const nameSufix = `-${uuidv4().split("-").pop()}`;
18 | if (maxlength > nameSufix.length)
19 | return `${prefix}`.substring(0, maxlength - nameSufix.length) + nameSufix
20 | else
21 | return `${nameSufix}`.substring(0, maxlength)
22 | }
--------------------------------------------------------------------------------
/deployment/custom-deployment/lib/smart-product-device-defender.ts:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | import cdk = require('@aws-cdk/core');
15 | import sns = require('@aws-cdk/aws-sns');
16 | import iam = require('@aws-cdk/aws-iam');
17 | import cfn = require('@aws-cdk/aws-cloudformation');
18 |
19 | export interface SmartProductDeviceDefenderProps {
20 | helperFunction: cfn.CustomResourceProvider;
21 | helperFunctionPolicy: iam.Policy;
22 | }
23 |
24 | export class SmartProductDeviceDefender extends cdk.Construct {
25 | public readonly response: string;
26 |
27 | constructor(parent: cdk.Construct, name: string, props: SmartProductDeviceDefenderProps) {
28 | super(parent, name);
29 |
30 | //=============================================================================================
31 | // Resources
32 | //=============================================================================================
33 | const deviceDefenderSNS = new sns.Topic(this, 'SNS', {
34 | displayName: "SmartProductDeviceDefenderSNS",
35 | topicName: "SmartProductDeviceDefenderSNS"
36 | })
37 |
38 | const auditNotifyRole = new iam.Role(this, 'AuditNotifyRole', {
39 | assumedBy: new iam.ServicePrincipal('iot.amazonaws.com')
40 | })
41 |
42 | const auditNotifyPolicy = new iam.Policy(this, 'AuditNotifyPolicy', {
43 | statements: [new iam.PolicyStatement({
44 | actions: [
45 | 'iot:GetLoggingOptions',
46 | 'iot:GetV2LoggingOptions',
47 | 'iot:ListCACertificates',
48 | 'iot:ListCertificates',
49 | 'iot:DescribeCACertificate',
50 | 'iot:DescribeCertificate',
51 | 'iot:ListPolicies',
52 | 'iot:GetPolicy',
53 | 'iot:GetEffectivePolicies',
54 | 'cognito-identity:GetIdentityPoolRoles',
55 | 'iam:ListRolePolicies',
56 | 'iam:ListAttachedRolePolicies',
57 | 'iam:GetPolicy',
58 | 'iam:GetPolicyVersion',
59 | 'iam:GetRolePolicy'
60 | ],
61 | resources: [`*`]
62 | }),
63 | new iam.PolicyStatement({
64 | actions: ['sns:Publish'],
65 | resources: [deviceDefenderSNS.topicArn]
66 | })]
67 | })
68 | const auditNotifyPolicyResource = auditNotifyPolicy.node.findChild('Resource') as iam.CfnPolicy;
69 | auditNotifyPolicyResource.cfnOptions.metadata = {
70 | cfn_nag: {
71 | rules_to_suppress: [{
72 | id: 'W12',
73 | reason: `The * resource allows ${auditNotifyRole.roleName} to audit IoT devices.`
74 | }]
75 | }
76 | }
77 | auditNotifyPolicy.attachToRole(auditNotifyRole);
78 |
79 | const _updateDefender = new cfn.CustomResource(this, 'UpdateIoTDeviceDefender', {
80 | provider: props.helperFunction,
81 | resourceType: 'Custom::UpdateIoTDeviceDefender',
82 | properties: {
83 | Region: `${cdk.Aws.REGION}`,
84 | CustomAction: 'updateIoTDeviceDefender',
85 | SnsRoleArn: auditNotifyRole.roleArn,
86 | SnsTargetArn: deviceDefenderSNS.topicArn,
87 | AuditRoleArn: auditNotifyRole.roleArn
88 | }
89 | })
90 | _updateDefender.node.addDependency(auditNotifyPolicy.node.findChild('Resource') as cdk.Resource)
91 | _updateDefender.node.addDependency(props.helperFunctionPolicy.node.findChild('Resource') as cdk.Resource)
92 |
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/deployment/custom-deployment/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-solution",
3 | "version": "1.0.0",
4 | "bin": {
5 | "smart-product-solution": "bin/smart-product-solution.ts"
6 | },
7 | "scripts": {
8 | "build": "tsc",
9 | "watch": "tsc -w",
10 | "cdk": "cdk"
11 | },
12 | "devDependencies": {
13 | "@types/node": "8.10.45",
14 | "typescript": "^3.3.3333",
15 | "ts-node": "^8.1.0"
16 | },
17 | "dependencies": {
18 | "@aws-cdk/aws-apigateway": "1.22.0",
19 | "@aws-cdk/aws-cloudfront": "1.22.0",
20 | "@aws-cdk/aws-cognito": "1.22.0",
21 | "@aws-cdk/aws-dynamodb": "1.22.0",
22 | "@aws-cdk/aws-iam": "1.22.0",
23 | "@aws-cdk/aws-iot": "1.22.0",
24 | "@aws-cdk/aws-iotanalytics": "1.22.0",
25 | "@aws-cdk/aws-lambda": "1.22.0",
26 | "@aws-cdk/aws-s3": "1.22.0",
27 | "@aws-cdk/aws-sns": "1.22.0",
28 | "aws-cdk": "1.22.0",
29 | "uuid": "^3.3.2"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/deployment/custom-deployment/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2018",
4 | "module": "commonjs",
5 | "lib": [
6 | "es2016",
7 | "es2017.object",
8 | "es2017.string"
9 | ],
10 | "declaration": true,
11 | "strict": true,
12 | "noImplicitAny": true,
13 | "strictNullChecks": true,
14 | "noImplicitThis": true,
15 | "alwaysStrict": true,
16 | "noUnusedLocals": false,
17 | "noUnusedParameters": false,
18 | "noImplicitReturns": true,
19 | "noFallthroughCasesInSwitch": false,
20 | "inlineSourceMap": true,
21 | "inlineSources": true,
22 | "experimentalDecorators": true,
23 | "strictPropertyInitialization": false,
24 | "resolveJsonModule": true,
25 | "esModuleInterop": true,
26 | }
27 | }
--------------------------------------------------------------------------------
/deployment/manifest-generator/app.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const fs = require('fs');
21 | const path = require('path');
22 | const args = require('minimist')(process.argv.slice(2));
23 |
24 | let getFileList = function(path) {
25 | let fileInfo;
26 | let filesFound;
27 | let fileList = [];
28 |
29 | filesFound = fs.readdirSync(path);
30 | for (let i = 0; i < filesFound.length; i++) {
31 | fileInfo = fs.lstatSync([path, filesFound[i]].join('/'));
32 | if (fileInfo.isFile()) {
33 | fileList.push(filesFound[i]);
34 | }
35 |
36 | if (fileInfo.isDirectory()) {
37 | console.log([path, filesFound[i]].join('/'));
38 | }
39 | }
40 |
41 | return fileList;
42 | };
43 |
44 | // List all files in a directory in Node.js recursively in a synchronous fashion
45 | let walkSync = function(dir, filelist) {
46 | // let filelist = []; //getFileList('./temp/site');
47 | let files = fs.readdirSync(dir);
48 | filelist = filelist || [];
49 | files.forEach(function(file) {
50 | if (fs.statSync(path.join(dir, file)).isDirectory()) {
51 | filelist = walkSync(path.join(dir, file), filelist);
52 | } else {
53 | filelist.push(path.join(dir, file));
54 | }
55 | });
56 |
57 | return filelist;
58 | };
59 |
60 | let _filelist = [];
61 | let _manifest = {
62 | files: [],
63 | };
64 |
65 | if (!args.hasOwnProperty('target')) {
66 | console.log(
67 | '--target parameter missing. This should be the target directory containing content for the manifest.'
68 | );
69 | process.exit(1);
70 | }
71 |
72 | if (!args.hasOwnProperty('output')) {
73 | console.log(
74 | '--ouput parameter missing. This should be the out directory where the manifest file will be generated.'
75 | );
76 | process.exit(1);
77 | }
78 |
79 | console.log(
80 | `Generating a manifest file ${args.output} for directory ${args.target}`
81 | );
82 |
83 | walkSync(args.target, _filelist);
84 |
85 | for (let i = 0; i < _filelist.length; i++) {
86 | _manifest.files.push(_filelist[i].replace(`${args.target}/`, ''));
87 | }
88 |
89 | fs.writeFileSync(args.output, JSON.stringify(_manifest, null, 4));
90 | console.log(`Manifest file ${args.output} generated.`);
91 |
--------------------------------------------------------------------------------
/deployment/manifest-generator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "manifest-generator",
3 | "version": "0.0.0",
4 | "private": true,
5 | "description": "Helper utility to create smart product site manifest for deployment",
6 | "main": "app.js",
7 | "author": {
8 | "name": "aws-solutions-builder"
9 | },
10 | "dependencies": {
11 | "minimist": "*"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/source/console/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src",
4 | "paths": {
5 | "*": ["src/*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/source/console/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-smart-product",
3 | "version": "1.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.2.4",
7 | "aws-amplify-react": "^2.5.4",
8 | "bootstrap": "^3.4.1",
9 | "node-sass": "^4.13.0",
10 | "react": "^16.11.0",
11 | "react-bootstrap": "^0.32.4",
12 | "react-dom": "^16.11.0",
13 | "react-notification-system": "^0.2.17",
14 | "react-router": "^5.1.2",
15 | "react-router-dom": "^5.1.2",
16 | "react-scripts": "^3.3.0",
17 | "react-toggle": "^4.1.1"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^16.9.11",
21 | "typescript": "3.4.3"
22 | },
23 | "scripts": {
24 | "start": "react-scripts start",
25 | "build": "react-scripts build",
26 | "test": "react-scripts test --env=jsdom",
27 | "eject": "react-scripts eject",
28 | "install:clean": "rm -rf node_modules/ && rm -rf package-lock.json && npm install && npm start",
29 | "compile-sass": "node-sass src/assets/sass/light-bootstrap-dashboard-react.scss src/assets/css/light-bootstrap-dashboard-react.css",
30 | "minify-sass": "node-sass src/assets/sass/light-bootstrap-dashboard-react.scss src/assets/css/light-bootstrap-dashboard-react.min.css --output-style compressed",
31 | "map-sass": "node-sass src/assets/sass/light-bootstrap-dashboard-react.scss src/assets/css/light-bootstrap-dashboard-react.css --source-map true"
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/source/console/public/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/public/apple-icon.png
--------------------------------------------------------------------------------
/source/console/public/assets/smart_product_config.js:
--------------------------------------------------------------------------------
1 | const smart_product_config = {
2 | "aws_project_region": "YOUR_REGION",
3 | "aws_user_pools_id": "YOUR_COGNITO_USER_POOL_ID",
4 | "aws_user_pools_web_client_id": "YOUR_COGNITO_WEB_CLIENT_ID",
5 | "oauth": {},
6 | "aws_cloud_logic_custom": [
7 | {
8 | "name": "smart-product-api",
9 | "endpoint": "YOUR_ENDPOINT",
10 | "region": "YOUR_REGION"
11 | }
12 | ],
13 | };
14 |
--------------------------------------------------------------------------------
/source/console/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/public/favicon.ico
--------------------------------------------------------------------------------
/source/console/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
25 | AWS Smart Product
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/source/console/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "LBDR",
3 | "name": "AWS Smart Product",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/Pe-icon-7-stroke.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/Pe-icon-7-stroke.eot
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/Pe-icon-7-stroke.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/Pe-icon-7-stroke.ttf
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/Pe-icon-7-stroke.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/Pe-icon-7-stroke.woff
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/source/console/src/assets/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/source/console/src/assets/img/logo-aws.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/smart-product-solution/827fb190390477037409cb5635d3facc0c4ffe6a/source/console/src/assets/img/logo-aws.png
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_alerts.scss:
--------------------------------------------------------------------------------
1 | .alert{
2 | border: 0;
3 | border-radius: 0;
4 | color: #FFFFFF;
5 | padding: 10px 15px;
6 | font-size: 14px;
7 |
8 | .container &{
9 | border-radius: 4px;
10 |
11 | }
12 | .navbar &{
13 | border-radius: 0;
14 | left: 0;
15 | position: absolute;
16 | right: 0;
17 | top: 85px;
18 | width: 100%;
19 | z-index: 3;
20 | }
21 | .navbar:not(.navbar-transparent) &{
22 | top: 70px;
23 | }
24 |
25 | span[data-notify="icon"]{
26 | font-size: 30px;
27 | display: block;
28 | left: 15px;
29 | position: absolute;
30 | top: 50%;
31 | margin-top: -15px;
32 | }
33 |
34 | button.close{
35 | position: absolute;
36 | right: 10px;
37 | top: 50%;
38 | margin-top: -13px;
39 | z-index: 1033;
40 | background-color: #FFFFFF;
41 | display: block;
42 | border-radius: 50%;
43 | opacity: .4;
44 | line-height: 20px;
45 | font-size: 12px;
46 | width: 25px;
47 | height: 25px;
48 | outline: 0 !important;
49 | text-align: center;
50 | padding: 3px;
51 | font-weight: 300;
52 |
53 | &:hover{
54 | opacity: .55;
55 | }
56 | }
57 |
58 | .close ~ span{
59 | display: block;
60 | max-width: 89%;
61 | }
62 |
63 | &[data-notify="container"]{
64 | padding: 10px 10px 10px 20px;
65 | border-radius: $border-radius-base;
66 | }
67 |
68 | &.alert-with-icon{
69 | padding-left: 65px;
70 | }
71 | a, a:hover, a:focus{
72 | text-decoration: underline;
73 | color: white;
74 | }
75 | }
76 | .alert-info{
77 | background-color: $azure-navbar;
78 | }
79 | .alert-success {
80 | background-color: $green-navbar;
81 | }
82 | .alert-warning {
83 | background-color: $orange-navbar;
84 | }
85 | .alert-danger {
86 | background-color: $red-navbar;
87 | }
88 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_buttons.scss:
--------------------------------------------------------------------------------
1 | .btn{
2 | border-width: $border-thick;
3 | background-color: $transparent-bg;
4 | font-weight: $font-weight-normal;
5 | border-radius: $border-radius-default;
6 |
7 | @include opacity(.8);
8 | padding: $padding-base-vertical $padding-base-horizontal;
9 |
10 | @include btn-styles($default-color, $default-states-color);
11 |
12 | &:hover,
13 | &:focus{
14 | @include opacity(1);
15 | outline: 0 !important;
16 | }
17 | &:active,
18 | &.active,
19 | .open > &.dropdown-toggle {
20 | @include box-shadow(none);
21 | outline: 0 !important;
22 | }
23 |
24 | &.btn-icon{
25 | padding: $padding-base-vertical;
26 | }
27 |
28 | }
29 |
30 | // Apply the mixin to the buttons
31 | .btn-default { @include btn-styles($default-color, $default-states-color); }
32 | .btn-primary { @include btn-styles($primary-color, $primary-states-color); }
33 | .btn-success { @include btn-styles($success-color, $success-states-color); }
34 | .btn-info { @include btn-styles($info-color, $info-states-color); }
35 | .btn-warning { @include btn-styles($warning-color, $warning-states-color); }
36 | .btn-danger { @include btn-styles($danger-color, $danger-states-color); }
37 | .btn-neutral {
38 | @include btn-styles($white-color, $white-color);
39 |
40 | &:active,
41 | &.active,
42 | .open > &.dropdown-toggle{
43 | background-color: $white-color;
44 | color: $default-color;
45 | }
46 |
47 | &.btn-fill,
48 | &.btn-fill:hover,
49 | &.btn-fill:focus{
50 | color: $default-color;
51 | }
52 |
53 | &.btn-simple:active,
54 | &.btn-simple.active{
55 | background-color: transparent;
56 | }
57 | }
58 |
59 | .btn{
60 | &:disabled,
61 | &[disabled],
62 | &.disabled{
63 | @include opacity(.5);
64 | }
65 | }
66 | .btn-round{
67 | border-width: $border-thin;
68 | border-radius: $btn-round-radius !important;
69 | padding: $padding-round-vertical $padding-round-horizontal;
70 |
71 | &.btn-icon{
72 | padding: $padding-round-vertical;
73 | }
74 | }
75 | .btn-simple{
76 | border: $none;
77 | font-size: $font-size-medium;
78 | padding: $padding-base-vertical $padding-base-horizontal;
79 |
80 | &.btn-icon{
81 | padding: $padding-base-vertical;
82 | }
83 | }
84 | .btn-lg{
85 | @include btn-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $border-radius-large);
86 | font-weight: $font-weight-normal;
87 | }
88 | .btn-sm{
89 | @include btn-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $border-radius-small);
90 | }
91 | .btn-xs {
92 | @include btn-size($padding-xs-vertical, $padding-xs-horizontal, $font-size-small, $border-radius-small);
93 | }
94 | .btn-wd {
95 | min-width: 140px;
96 | }
97 |
98 | .btn-group.select{
99 | width: 100%;
100 | }
101 | .btn-group.select .btn{
102 | text-align: left;
103 | }
104 | .btn-group.select .caret{
105 | position: absolute;
106 | top: 50%;
107 | margin-top: -1px;
108 | right: 8px;
109 | }
110 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_cards.scss:
--------------------------------------------------------------------------------
1 | .card{
2 | border-radius: $border-radius-base;
3 | box-shadow: 0 1px 2px rgba(0,0,0,.05),0 0 0 1px rgba(63,63,68,.1);
4 | background-color: #FFFFFF;
5 | margin-bottom: 30px;
6 |
7 | .image{
8 | width: 100%;
9 | overflow: hidden;
10 | height: 260px;
11 | border-radius: $border-radius-base $border-radius-base 0 0;
12 | position: relative;
13 | -webkit-transform-style: preserve-3d;
14 | -moz-transform-style: preserve-3d;
15 | transform-style: preserve-3d;
16 |
17 | img {
18 | width: 100%;
19 | }
20 | }
21 | .filter{
22 | position: absolute;
23 | z-index: 2;
24 | background-color: rgba(0,0,0,.68);
25 | top: 0;
26 | left: 0;
27 | width: 100%;
28 | height: 100%;
29 | text-align: center;
30 |
31 | @include opacity(0);
32 |
33 | .btn{
34 | @include vertical-align();
35 | }
36 | }
37 | &:hover .filter{
38 | @include opacity(1);
39 | }
40 | .btn-hover{
41 | @include opacity(0);
42 | }
43 | &:hover .btn-hover{
44 | @include opacity(1);
45 | }
46 | .content{
47 | padding: 15px 15px 10px 15px;
48 | }
49 | .header{
50 | padding: 15px 15px 0;
51 | }
52 | .category,
53 | label{
54 | font-size: $font-size-base;
55 | font-weight: $font-weight-normal;
56 | color: $dark-gray;
57 | margin-bottom: 0px;
58 |
59 | i{
60 | font-size: $font-paragraph;
61 | }
62 | }
63 |
64 | label{
65 | font-size: $font-size-small;
66 | margin-bottom: 5px;
67 | text-transform: uppercase;
68 | }
69 |
70 | .title{
71 | margin: $none;
72 | color: $black-color;
73 | font-weight: $font-weight-light;
74 | }
75 | .avatar{
76 | width: 30px;
77 | height: 30px;
78 | overflow: hidden;
79 | border-radius: 50%;
80 | margin-right: 5px;
81 | }
82 | .description{
83 | font-size: $font-size-base;
84 | color: #333;
85 | }
86 | .footer{
87 | padding: 0;
88 | background-color: $transparent-bg;
89 | line-height: 30px;
90 |
91 | .legend{
92 | padding: 5px 0;
93 | }
94 |
95 | hr{
96 | margin-top: 5px;
97 | margin-bottom: 5px;
98 | }
99 | }
100 | .stats{
101 | color: #a9a9a9;
102 | }
103 | .footer div{
104 | display: inline-block;
105 | }
106 |
107 | .author{
108 | font-size: $font-size-small;
109 | font-weight: $font-weight-bold;
110 | text-transform: uppercase;
111 | }
112 | .author i{
113 | font-size: $font-size-base;
114 | }
115 | h6{
116 | font-size: $font-size-small;
117 | margin: 0;
118 | }
119 | &.card-separator:after{
120 | height: 100%;
121 | right: -15px;
122 | top: 0;
123 | width: 1px;
124 | background-color: $medium-gray;
125 | content: "";
126 | position: absolute;
127 | }
128 |
129 | .ct-chart{
130 | margin: 30px 0 30px;
131 | height: 245px;
132 | }
133 |
134 | .table{
135 | tbody td:first-child,
136 | thead th:first-child{
137 | padding-left: 15px;
138 | }
139 |
140 | tbody td:last-child,
141 | thead th:last-child{
142 | padding-right: 15px;
143 | }
144 | }
145 |
146 | .alert{
147 | border-radius: $border-radius-base;
148 | position: relative;
149 |
150 | &.alert-with-icon{
151 | padding-left: 65px;
152 | }
153 | }
154 | }
155 | .card-user{
156 | .image{
157 | height: 110px;
158 | }
159 | .image-plain{
160 | height: 0;
161 | margin-top: 110px;
162 | }
163 | .author{
164 | text-align: center;
165 | text-transform: none;
166 | margin-top: -70px;
167 | }
168 | .avatar{
169 | width: 124px;
170 | height: 124px;
171 | border: 5px solid #FFFFFF;
172 | position: relative;
173 | margin-bottom: 15px;
174 |
175 | &.border-gray{
176 | border-color: #EEEEEE;
177 | }
178 | }
179 | .title{
180 | line-height: 24px;
181 | }
182 | .content{
183 | min-height: 240px;
184 | }
185 | }
186 |
187 | .card-user,
188 | .card-price{
189 | .footer{
190 | padding: 5px 15px 10px;
191 | }
192 | hr{
193 | margin: 5px 15px;
194 | }
195 | }
196 | .card-plain{
197 | background-color: transparent;
198 | box-shadow: none;
199 | border-radius: 0;
200 |
201 | .image{
202 | border-radius: 4px;
203 | }
204 | }
205 |
206 | .card-stats{
207 | .icon-big{
208 | font-size: 3em;
209 | min-height: 64px;
210 | i{
211 | font-weight: bold;
212 | line-height: 59px;
213 | }
214 | }
215 | .numbers{
216 | font-size: 2em;
217 | text-align: right;
218 | p {
219 | margin: 0;
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_custom.scss:
--------------------------------------------------------------------------------
1 | .custom_div {
2 | position: relative;
3 | display: block;
4 | width: 100%;
5 | }
6 |
7 | .custom_list_item {
8 | position: relative;
9 | padding: 10px 10px 10px 10px;
10 | border-bottom: 1px solid #e1e4ea;
11 | }
12 |
13 | .custom_mobile_title {
14 | display: block;
15 | overflow: hidden;
16 | }
17 |
18 | .mobile_title {
19 | font-weight: bold;
20 | text-overflow: ellipsis;
21 | white-space: nowrap;
22 | max-width: 100%;
23 | display: block;
24 | overflow: hidden;
25 | }
26 |
27 | .custom_mobile_date {
28 | float: right;
29 | vertical-align: top;
30 | font-size: 10px;
31 | }
32 |
33 | .mobile_date {
34 | display: inline-block;
35 | vertical-align: top;
36 | }
37 |
38 | .mobile_date_bt {
39 | display: inline-block;
40 | vertical-align: bottom;
41 | }
42 |
43 | .custom_date_td {
44 | width: 200px;
45 | }
46 |
47 | .custom_event_type_td {
48 | width: 50px;
49 | }
50 |
51 | .custom_mobile_message {
52 | display: block;
53 | overflow: hidden;
54 | }
55 |
56 | .mobile_message {
57 | text-overflow: ellipsis;
58 | white-space: nowrap;
59 | max-width: 100%;
60 | display: block;
61 | overflow: hidden;
62 | }
63 |
64 | .no_result {
65 | text-align: center;
66 | font-weight: bold;
67 | }
68 |
69 | .custom_devicename_td {
70 | display: block;
71 | width: calc(100% - 130px);
72 | overflow: hidden;
73 | text-overflow: ellipsis;
74 | white-space: nowrap;
75 | }
76 |
77 | .custom_btn {
78 | display: block;
79 | width: calc(100% - 100px);
80 | overflow: hidden;
81 | text-overflow: ellipsis;
82 | white-space: nowrap;
83 | }
84 |
85 | .mobile_tabs {
86 | font-size: 12px;
87 | }
88 |
89 | .custom_registration {
90 | font-size: 12px;
91 | }
92 |
93 | .custom_well {
94 | word-break: break-all;
95 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_dropdown.scss:
--------------------------------------------------------------------------------
1 | .dropdown-menu{
2 | visibility: hidden;
3 | margin: 0;
4 | padding: 0;
5 | border-radius: $border-radius-extreme;
6 | display: block;
7 | z-index: 9000;
8 | position: absolute;
9 |
10 | @include opacity(0);
11 | @include box-shadow($dropdown-shadow);
12 |
13 | .open &{
14 | @include opacity(1);
15 | visibility: visible;
16 | }
17 | .select &{
18 | border-radius: $border-radius-bottom;
19 | @include box-shadow(none);
20 | @include transform-origin($select-coordinates);
21 | @include transform-scale(1);
22 | @include transition($fast-transition-time, $transition-linear);
23 | margin-top: -20px;
24 | }
25 | .select.open &{
26 | margin-top: -1px;
27 | }
28 |
29 | > li > a {
30 | padding: $padding-base-vertical $padding-base-horizontal;
31 | color: #333333;
32 |
33 | img{
34 | margin-top: -3px;
35 | }
36 | }
37 | > li > a:focus{
38 | outline: 0 !important;
39 | }
40 |
41 | .btn-group.select &{
42 | min-width: 100%;
43 | }
44 |
45 | > li:first-child > a{
46 | border-top-left-radius: $border-radius-extreme;
47 | border-top-right-radius: $border-radius-extreme;
48 | }
49 |
50 | > li:last-child > a{
51 | border-bottom-left-radius: $border-radius-extreme;
52 | border-bottom-right-radius: $border-radius-extreme;
53 | }
54 |
55 | .select & > li:first-child > a{
56 | border-radius: 0;
57 | border-bottom: 0 none;
58 | }
59 |
60 | > li > a:hover,
61 | > li > a:focus {
62 | background-color: $smoke-bg;
63 | color: #333333;
64 | opacity: 1;
65 | text-decoration: none;
66 | }
67 |
68 | &.dropdown-blue > li > a:hover,
69 | &.dropdown-blue > li > a:focus{
70 | background-color: $light-blue;
71 | }
72 | &.dropdown-azure > li > a:hover,
73 | &.dropdown-azure > li > a:focus{
74 | background-color: $light-azure;
75 | }
76 | &.ct-green > li > a:hover,
77 | &.ct-green > li > a:focus{
78 | background-color: $light-green;
79 | }
80 | &.dropdown-orange > li > a:hover,
81 | &.dropdown-orange > li > a:focus{
82 | background-color: $light-orange;
83 | }
84 | &.dropdown-red > li > a:hover,
85 | &.dropdown-red > li > a:focus{
86 | background-color: $light-red;
87 | }
88 |
89 | }
90 |
91 | .dropdown-with-icons{
92 | > li > a{
93 | padding-left: 0px;
94 | line-height: 28px;
95 | }
96 | i{
97 | text-align: center;
98 | line-height: 28px;
99 | float: left;
100 |
101 | &[class^="pe-"]{
102 | font-size: 24px;
103 | width: 46px;
104 | }
105 | &[class^="fa"]{
106 | font-size: 14px;
107 | width: 38px;
108 | }
109 | }
110 | }
111 |
112 | //fix bug for the select items in btn-group
113 | .btn-group.select{
114 | overflow: hidden;
115 | }
116 | .btn-group.select.open{
117 | overflow: visible;
118 | }
119 |
120 |
121 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_footers.scss:
--------------------------------------------------------------------------------
1 | .footer{
2 | background-color: $white-color;
3 | line-height: $line-height;
4 |
5 | nav > ul{
6 | list-style: none;
7 | margin: 0;
8 | padding: 0;
9 | font-weight: normal;
10 |
11 | a:not(.btn){
12 | color: $dark-gray;
13 | display: block;
14 | margin-bottom: 3px;
15 | &:hover,
16 | &:focus{
17 | color: $default-states-color;
18 | }
19 | }
20 | }
21 | .social-area{
22 | padding: 15px 0;
23 | h5{
24 | padding-bottom: 15px;
25 | }
26 | }
27 | .social-area > a:not(.btn){
28 | color: $dark-gray;
29 | display: inline-block;
30 | vertical-align: top;
31 | padding: $padding-social-a;
32 | font-size: $font-size-large-navbar;
33 | font-weight: normal;
34 | line-height: $line-height;
35 | text-align: center;
36 | &:hover,
37 | &:focus{
38 | color: $default-states-color;
39 | }
40 | }
41 | .copyright{
42 | color: $default-states-color;
43 | padding: 10px 15px;
44 | margin: 10px 3px;
45 | line-height: 20px;
46 | font-size: $font-size-base;
47 | }
48 | hr{
49 | border-color: $medium-gray;
50 | }
51 | .title{
52 | color: $default-states-color;
53 | }
54 | }
55 |
56 | .footer-default{
57 | background-color: $smoke-bg;
58 | }
59 |
60 | .footer:not(.footer-big){
61 | nav > ul{
62 | font-size: $font-size-base;
63 | li{
64 | margin-left: 20px;
65 | float: left;
66 | }
67 | a{
68 | padding: 10px 0px;
69 | margin: 10px 10px 10px 0px;
70 | }
71 | }
72 | }
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_inputs.scss:
--------------------------------------------------------------------------------
1 | .form-control::-moz-placeholder{
2 | @include placeholder($medium-gray,1);
3 | }
4 | .form-control:-moz-placeholder{
5 | @include placeholder($medium-gray,1);
6 | }
7 | .form-control::-webkit-input-placeholder{
8 | @include placeholder($medium-gray,1);
9 | }
10 | .form-control:-ms-input-placeholder{
11 | @include placeholder($medium-gray,1);
12 | }
13 |
14 | .form-control {
15 | background-color: $white-bg;
16 | border: 1px solid $light-gray;
17 | border-radius: $border-radius-base;
18 | color: #565656;
19 | @include input-size($padding-base-vertical, $padding-base-horizontal - 4, $height-base);
20 | @include box-shadow(none);
21 |
22 | &:focus{
23 | background-color: $white-bg;
24 | border: 1px solid $medium-dark-gray;
25 | @include box-shadow(none);
26 | outline: 0 !important;
27 | color: #333333;
28 | }
29 |
30 | .has-success &,
31 | .has-error &,
32 | .has-success &:focus,
33 | .has-error &:focus{
34 | border-color: $light-gray;
35 | @include box-shadow(none);
36 | }
37 |
38 | .has-success &{
39 | color: $success-color;
40 | }
41 | .has-success &:focus{
42 | border-color: $success-color;
43 | }
44 | .has-error &{
45 | color: $danger-color;
46 | }
47 | .has-error &:focus{
48 | border-color: $danger-color;
49 | }
50 |
51 | & + .form-control-feedback{
52 | border-radius: $border-radius-large;
53 | font-size: $font-size-base;
54 | margin-top: -7px;
55 | position: absolute;
56 | right: 10px;
57 | top: 50%;
58 | vertical-align: middle;
59 | }
60 |
61 | .open &{
62 | border-radius: $border-radius-base $border-radius-base 0 0;
63 | border-bottom-color: transparent;
64 | }
65 | }
66 |
67 | .input-lg{
68 | height: 55px;
69 | padding: $padding-large-vertical $padding-large-horizontal;
70 | }
71 |
72 | .has-error{
73 | .form-control-feedback{
74 | color: $danger-color;
75 | }
76 | }
77 | .has-success{
78 | .form-control-feedback{
79 | color: $success-color
80 | }
81 | }
82 |
83 |
84 | .input-group-addon {
85 | background-color: $white-color;
86 | border: 1px solid $light-gray;
87 | border-radius: $border-radius-base;
88 |
89 | .has-success &,
90 | .has-error &{
91 | background-color: $white-color;
92 | border: 1px solid $light-gray;
93 | }
94 | .has-error .form-control:focus + &{
95 | border-color: $danger-color;
96 | color: $danger-color;
97 | }
98 | .has-success .form-control:focus + &{
99 | border-color: $success-color;
100 | color: $success-color;
101 | }
102 | .form-control:focus + &,
103 | .form-control:focus ~ &{
104 | background-color: $white-color;
105 | border-color: $dark-gray;
106 | }
107 | }
108 |
109 | .input-group .form-control:first-child,
110 | .input-group-addon:first-child,
111 | .input-group-btn:first-child > .dropdown-toggle,
112 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) {
113 | border-right: 0 none;
114 | }
115 | .input-group .form-control:last-child,
116 | .input-group-addon:last-child,
117 | .input-group-btn:last-child > .dropdown-toggle,
118 | .input-group-btn:first-child > .btn:not(:first-child) {
119 | border-left: 0 none;
120 | }
121 | .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control {
122 | background-color: $smoke-bg;
123 | color: $default-color;
124 | cursor: not-allowed;
125 | }
126 |
127 | .input-group-btn .btn{
128 | border-width: $border-thin;
129 | padding: $padding-round-vertical $padding-base-horizontal;
130 | }
131 | .input-group-btn .btn-default:not(.btn-fill){
132 | border-color: $medium-gray;
133 | }
134 |
135 | .input-group-btn:last-child > .btn{
136 | margin-left: 0;
137 | }
138 |
139 | .input-group-focus .input-group-addon{
140 | border-color: $dark-gray;
141 | }
142 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_misc.scss:
--------------------------------------------------------------------------------
1 | /* General overwrite */
2 | body,
3 | .wrapper{
4 | min-height: 100vh;
5 | position: relative;
6 | background-color: white;
7 | }
8 | a{
9 | color: $info-color;
10 |
11 | &:hover, &:focus{
12 | color: $info-states-color;
13 | text-decoration: none;
14 | }
15 | }
16 |
17 | a:focus, a:active,
18 | button::-moz-focus-inner,
19 | input::-moz-focus-inner,
20 | input[type="reset"]::-moz-focus-inner,
21 | input[type="button"]::-moz-focus-inner,
22 | input[type="submit"]::-moz-focus-inner,
23 | select::-moz-focus-inner,
24 | input[type="file"] > input[type="button"]::-moz-focus-inner{
25 | outline:0;
26 | }
27 | .ui-slider-handle:focus,
28 | .navbar-toggle,
29 | input:focus {
30 | outline : 0 !important;
31 | }
32 |
33 | /* Animations */
34 | .form-control,
35 | .input-group-addon,
36 | .tagsinput,
37 | .navbar,
38 | .navbar .alert{
39 | @include transition($general-transition-time, $transition-linear);
40 | }
41 |
42 | .sidebar .nav a,
43 | .table > tbody > tr .td-actions .btn{
44 | @include transition($fast-transition-time, $transition-ease-in);
45 | }
46 |
47 | .btn{
48 | @include transition($ultra-fast-transition-time, $transition-ease-in);
49 | }
50 | .fa{
51 | width: 18px;
52 | text-align: center;
53 | }
54 | .margin-top{
55 | margin-top: 50px;
56 | }
57 |
58 | .wrapper{
59 | position: relative;
60 | top: 0;
61 | height: 100vh;
62 | }
63 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_mixins.scss:
--------------------------------------------------------------------------------
1 | //Utilities
2 |
3 | @import "mixins/transparency";
4 | @import "mixins/vendor-prefixes";
5 |
6 |
7 | //Components
8 |
9 | @import "mixins/buttons";
10 | @import "mixins/inputs";
11 | @import "mixins/labels";
12 | @import "mixins/tabs";
13 |
14 | @import "mixins/navbars";
15 | @import "mixins/icons";
16 | @import "mixins/social-buttons";
17 |
18 | @import "mixins/morphing-buttons";
19 |
20 | @import "mixins/cards";
21 |
22 | @import "mixins/chartist";
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_sidebar-and-main-panel.scss:
--------------------------------------------------------------------------------
1 | .sidebar{
2 | position: fixed;
3 | top: 0;
4 | bottom: 0;
5 | left: 0;
6 | width: 260px;
7 | display: block;
8 | z-index: 1;
9 | color: #fff;
10 | font-weight: 200;
11 | background-size: cover;
12 | background-position: center center;
13 |
14 | .sidebar-wrapper{
15 | position: relative;
16 | height: calc(100vh - 75px);
17 | overflow: auto;
18 | width: 260px;
19 | z-index: 4;
20 | padding-bottom: 30px;
21 | }
22 |
23 | .logo{
24 | position: relative;
25 | z-index: 4;
26 |
27 | p{
28 | float: left;
29 | font-size: 20px;
30 | margin: 10px 10px;
31 | color: $white-color;
32 | line-height: 20px;
33 | font-family: "Amazon Ember", "Helvetica Neue", Helvetica, Arial, sans-serif;
34 | }
35 |
36 | div.logo-mini{
37 | float: left;
38 | text-align: center;
39 | width: 30px;
40 | margin-right: 15px;
41 |
42 | img{
43 | width: 40px;
44 | margin-left: 15px;
45 | display: block;
46 | margin-top: 10px;
47 | }
48 | }
49 |
50 | .logo-img{
51 | width: 34px;
52 | display: inline-block;
53 | height: 34px;
54 | margin-left: -2px;
55 | margin-top: -2px;
56 | margin-right: 10px;
57 | border-radius: 30px;
58 | text-align: center;
59 | }
60 | }
61 |
62 | .nav{
63 | margin-top: 20px;
64 |
65 | li{
66 | > a{
67 | color: #FFFFFF;
68 | margin: 5px 15px;
69 | opacity: .86;
70 | border-radius: 4px;
71 | }
72 |
73 | &:hover > a,
74 | &.open > a,
75 | &.open > a:focus,
76 | &.open > a:hover{
77 | background: rgba(255,255,255,0.13);
78 | opacity: 1;
79 | }
80 |
81 | &.active > a{
82 | color: #FFFFFF;
83 | opacity: 1;
84 | background: rgba(255,255,255,0.23);
85 |
86 | }
87 | }
88 |
89 | p{
90 | margin: 0;
91 | line-height: 30px;
92 | font-size: 12px;
93 | font-weight: 600;
94 | text-transform: uppercase;
95 | }
96 |
97 | .caret{
98 | top: 24px;
99 | position: absolute;
100 | right: 15px;
101 | }
102 |
103 | i{
104 | font-size: 28px;
105 | float: left;
106 | margin-right: 15px;
107 | line-height: 30px;
108 | width: 30px;
109 | text-align: center;
110 | }
111 | }
112 | }
113 |
114 |
115 | .sidebar,
116 | body > .navbar-collapse{
117 | .logo{
118 | padding: 10px 30px;
119 | border-bottom: 1px solid rgba(255, 255, 255, 0.2);
120 |
121 | p{
122 | float: left;
123 | font-size: 20px;
124 | margin: 10px 10px;
125 | color: $white-color;
126 | line-height: 20px;
127 | font-family: "Amazon Ember", "Helvetica Neue", Helvetica, Arial, sans-serif;
128 | }
129 |
130 | .simple-text{
131 | padding: $padding-small-vertical 15px;
132 | text-align: right;
133 | display: block;
134 | font-size: $font-size-large;
135 | color: $white-color;
136 | font-weight: $font-weight-normal;
137 | line-height: 30px;
138 | }
139 | }
140 |
141 | &:after,
142 | &:before{
143 | display: block;
144 | content: "";
145 | position: absolute;
146 | width: 100%;
147 | height: 100%;
148 | top: 0;
149 | left: 0;
150 | z-index: 2;
151 | }
152 |
153 | &:before{
154 | opacity: .33;
155 | background: #000000;
156 | }
157 |
158 | &:after{
159 | @include icon-gradient($black-color-top, $black-color-bottom);
160 | z-index: 3;
161 | opacity: 1;
162 | }
163 | }
164 |
165 |
166 | .main-panel{
167 | background: rgba(203,203,210,.15);
168 | position: relative;
169 | float: right;
170 | width: $sidebar-width;
171 |
172 | > .content{
173 | padding: 30px 15px;
174 | min-height: calc(100% - 123px);
175 | }
176 |
177 | > .footer{
178 | border-top: 1px solid #e7e7e7;
179 | }
180 |
181 | .navbar{
182 | margin-bottom: 0;
183 | }
184 | }
185 |
186 | .sidebar,
187 | .main-panel{
188 | -webkit-transition-property: top,bottom;
189 | transition-property: top,bottom;
190 | -webkit-transition-duration: .2s,.2s;
191 | transition-duration: .2s,.2s;
192 | -webkit-transition-timing-function: linear,linear;
193 | transition-timing-function: linear,linear;
194 | -webkit-overflow-scrolling: touch;
195 | }
196 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_tables.scss:
--------------------------------------------------------------------------------
1 | .table{
2 |
3 | .radio,
4 | .checkbox{
5 | position: relative;
6 | height: 20px;
7 | display: block;
8 | width: 20px;
9 | padding: 0px 0px;
10 | margin: 0px 5px;
11 | text-align: center;
12 |
13 | .icons{
14 | left: 5px;
15 | }
16 | }
17 | > thead > tr > th,
18 | > tbody > tr > th,
19 | > tfoot > tr > th,
20 | > thead > tr > td,
21 | > tbody > tr > td,
22 | > tfoot > tr > td{
23 | padding: 12px 8px;
24 | vertical-align: middle;
25 | }
26 | // MANU : aici este ceva schimbat!!!
27 | > tbody > tr > td{
28 | font-size: 14px;
29 | }
30 | > thead > tr > th{
31 | border-bottom-width: 1px;
32 | font-size: $font-size-small;
33 | text-transform: uppercase;
34 | color: $dark-gray;
35 | font-weight: $font-weight-normal;
36 | padding-bottom: 5px;
37 | }
38 |
39 | .td-actions .btn{
40 | @include opacity(0.36);
41 |
42 | &.btn-xs{
43 | padding-left: 3px;
44 | padding-right: 3px;
45 | }
46 | }
47 | .td-actions{
48 | min-width: 90px;
49 | }
50 |
51 | > tbody > tr{
52 | position: relative;
53 |
54 | &:hover{
55 | .td-actions .btn{
56 | @include opacity(1);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/_typography.scss:
--------------------------------------------------------------------------------
1 | /* Font Smoothing */
2 | body,
3 | h1, .h1,
4 | h2, .h2,
5 | h3, .h3,
6 | h4, .h4,
7 | h5, .h5,
8 | h6, .h6,
9 | p,
10 | .navbar,
11 | .brand,
12 | .btn-simple,
13 | .alert,
14 | a,
15 | .td-name,
16 | td,
17 | button.close{
18 | -moz-osx-font-smoothing: grayscale;
19 | -webkit-font-smoothing: antialiased;
20 | font-family: "Amazon Ember", "Roboto", "Helvetica Neue", Arial, sans-serif;
21 | font-weight: $font-weight-normal;
22 | }
23 |
24 | h1, .h1, h2, .h2, h3, .h3, h4, .h4{
25 | font-weight: $font-weight-light;
26 | margin: $margin-large-vertical 0 $margin-base-vertical;
27 | }
28 |
29 | h1, .h1 {
30 | font-size: $font-size-h1;
31 | }
32 | h2, .h2{
33 | font-size: $font-size-h2;
34 | }
35 | h3, .h3{
36 | font-size: $font-size-h3;
37 | margin: 20px 0 10px;
38 | }
39 | h4, .h4{
40 | font-size: $font-size-h4;
41 | line-height: 30px;
42 | }
43 | h5, .h5 {
44 | font-size: $font-size-h5;
45 | margin-bottom: 15px;
46 | }
47 | h6, .h6{
48 | font-size: $font-size-h6;
49 | font-weight: $font-weight-bold;
50 | text-transform: uppercase;
51 | }
52 | p{
53 | font-size: $font-paragraph;
54 | line-height: $line-height-general;
55 | }
56 |
57 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small {
58 | color: $dark-gray;
59 | font-weight: $font-weight-light;
60 | line-height: $line-height-general;
61 | }
62 |
63 | h1 small, h2 small, h3 small, h1 .small, h2 .small, h3 .small {
64 | font-size: 60%;
65 | }
66 |
67 | h1 .subtitle{
68 | display: block;
69 | margin: 0 0 $margin-large-vertical;
70 | }
71 |
72 | .text-muted{
73 | color: #9A9A9A;
74 | }
75 | .text-primary, .text-primary:hover{
76 | color: #1D62F0 !important;
77 | }
78 | .text-info, .text-info:hover{
79 | color: $info-color !important;
80 | }
81 | .text-success, .text-success:hover{
82 | color: $success-color !important;
83 | }
84 | .text-warning, .text-warning:hover{
85 | color: $warning-color !important;
86 | }
87 | .text-danger, .text-danger:hover{
88 | color: $danger-color !important;
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_buttons.scss:
--------------------------------------------------------------------------------
1 | // Mixin for generating new styles
2 | @mixin btn-styles($btn-color, $btn-states-color) {
3 | border-color: $btn-color;
4 | color: $btn-color;
5 |
6 | &:hover,
7 | &:focus,
8 | &:active,
9 | &.active,
10 | .open > &.dropdown-toggle {
11 | background-color: $transparent-bg;
12 | color: $btn-states-color;
13 | border-color: $btn-states-color;
14 | }
15 |
16 | &.disabled,
17 | &:disabled,
18 | &[disabled],
19 | fieldset[disabled] & {
20 | &,
21 | &:hover,
22 | &:focus,
23 | &.focus,
24 | &:active,
25 | &.active {
26 | background-color: $transparent-bg;
27 | border-color: $btn-color;
28 | }
29 | }
30 |
31 |
32 | &.btn-fill {
33 | color: $white-color;
34 | background-color: $btn-color;
35 | @include opacity(1);
36 |
37 | // &:hover,
38 | &:focus,
39 | &:active,
40 | &.active,
41 | .open > &.dropdown-toggle{
42 | background-color: $btn-states-color;
43 | color: $white-color;
44 | }
45 |
46 | .caret{
47 | border-top-color: $white-color;
48 | }
49 | }
50 |
51 | .caret{
52 | border-top-color: $btn-color;
53 | }
54 | }
55 |
56 |
57 | @mixin btn-size($padding-vertical, $padding-horizontal, $font-size, $border){
58 | font-size: $font-size;
59 | border-radius: $border;
60 | padding: $padding-vertical $padding-horizontal;
61 |
62 | &.btn-round{
63 | padding: $padding-vertical + 1 $padding-horizontal;
64 | }
65 |
66 | &.btn-simple{
67 | padding: $padding-vertical + 2 $padding-horizontal;
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_cards.scss:
--------------------------------------------------------------------------------
1 | @mixin filter($color){
2 | @if $color == #FFFFFF{
3 | background-color: rgba($color,.91);
4 | } @else {
5 | background-color: rgba($color,.69);
6 | }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_chartist.scss:
--------------------------------------------------------------------------------
1 | // Scales for responsive SVG containers
2 | $ct-scales: ((1), (15/16), (8/9), (5/6), (4/5), (3/4), (2/3), (5/8), (1/1.618), (3/5), (9/16), (8/15), (1/2), (2/5), (3/8), (1/3), (1/4)) !default;
3 | $ct-scales-names: (ct-square, ct-minor-second, ct-major-second, ct-minor-third, ct-major-third, ct-perfect-fourth, ct-perfect-fifth, ct-minor-sixth, ct-golden-section, ct-major-sixth, ct-minor-seventh, ct-major-seventh, ct-octave, ct-major-tenth, ct-major-eleventh, ct-major-twelfth, ct-double-octave) !default;
4 |
5 | // Class names to be used when generating CSS
6 | $ct-class-chart: ct-chart !default;
7 | $ct-class-chart-line: ct-chart-line !default;
8 | $ct-class-chart-bar: ct-chart-bar !default;
9 | $ct-class-horizontal-bars: ct-horizontal-bars !default;
10 | $ct-class-chart-pie: ct-chart-pie !default;
11 | $ct-class-chart-donut: ct-chart-donut !default;
12 | $ct-class-label: ct-label !default;
13 | $ct-class-series: ct-series !default;
14 | $ct-class-line: ct-line !default;
15 | $ct-class-point: ct-point !default;
16 | $ct-class-area: ct-area !default;
17 | $ct-class-bar: ct-bar !default;
18 | $ct-class-slice-pie: ct-slice-pie !default;
19 | $ct-class-slice-donut: ct-slice-donut !default;
20 | $ct-class-grid: ct-grid !default;
21 | $ct-class-vertical: ct-vertical !default;
22 | $ct-class-horizontal: ct-horizontal !default;
23 | $ct-class-start: ct-start !default;
24 | $ct-class-end: ct-end !default;
25 |
26 | // Container ratio
27 | $ct-container-ratio: (1/1.618) !default;
28 |
29 | // Text styles for labels
30 | $ct-text-color: rgba(0, 0, 0, 0.4) !default;
31 | $ct-text-size: 1.3rem !default;
32 | $ct-text-align: flex-start !default;
33 | $ct-text-justify: flex-start !default;
34 | $ct-text-line-height: 1;
35 |
36 | // Grid styles
37 | $ct-grid-color: rgba(0, 0, 0, 0.2) !default;
38 | $ct-grid-dasharray: 2px !default;
39 | $ct-grid-width: 1px !default;
40 |
41 | // Line chart properties
42 | $ct-line-width: 3px !default;
43 | $ct-line-dasharray: false !default;
44 | $ct-point-size: 8px !default;
45 | // Line chart point, can be either round or square
46 | $ct-point-shape: round !default;
47 | // Area fill transparency between 0 and 1
48 | $ct-area-opacity: 0.8 !default;
49 |
50 | // Bar chart bar width
51 | $ct-bar-width: 10px !default;
52 |
53 | // Donut width (If donut width is to big it can cause issues where the shape gets distorted)
54 | $ct-donut-width: 60px !default;
55 |
56 | // If set to true it will include the default classes and generate CSS output. If you're planning to use the mixins you
57 | // should set this property to false
58 | $ct-include-classes: true !default;
59 |
60 | // If this is set to true the CSS will contain colored series. You can extend or change the color with the
61 | // properties below
62 | $ct-include-colored-series: $ct-include-classes !default;
63 |
64 | // If set to true this will include all responsive container variations using the scales defined at the top of the script
65 | $ct-include-alternative-responsive-containers: $ct-include-classes !default;
66 |
67 | // Series names and colors. This can be extended or customized as desired. Just add more series and colors.
68 | $ct-series-names: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) !default;
69 | $ct-series-colors: (
70 | $new-blue,
71 | $new-red,
72 | $new-orange,
73 | $new-purple,
74 | $new-green,
75 | $new-dark-blue,
76 | $new-black,
77 | $social-google,
78 | $social-tumblr,
79 | $social-youtube,
80 | $social-twitter,
81 | $social-pinterest,
82 | $social-behance,
83 | #6188e2,
84 | #a748ca
85 | ) !default;
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_icons.scss:
--------------------------------------------------------------------------------
1 | @mixin icon-background ($icon-url){
2 | background-image : url($icon-url);
3 |
4 | }
5 |
6 | @mixin icon-shape ($size, $padding, $border-radius) {
7 | height: $size;
8 | width: $size;
9 | padding: $padding;
10 | border-radius: $border-radius;
11 | display: inline-table;
12 |
13 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_inputs.scss:
--------------------------------------------------------------------------------
1 | @mixin input-size($padding-vertical, $padding-horizontal, $height){
2 | padding: $padding-vertical $padding-horizontal;
3 | height: $height;
4 | }
5 |
6 | @mixin placeholder($color, $opacity){
7 | color: $color;
8 | @include opacity(1);
9 | }
10 |
11 | @mixin light-form(){
12 | border-radius: 0;
13 | border:0;
14 | padding: 0;
15 | background-color: transparent;
16 |
17 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_labels.scss:
--------------------------------------------------------------------------------
1 | @mixin label-style(){
2 | padding: $padding-label-vertical $padding-label-horizontal;
3 | border: 1px solid $default-color;
4 | border-radius: $border-radius-small;
5 | color: $default-color;
6 | font-weight: $font-weight-semi;
7 | font-size: $font-size-small;
8 | text-transform: uppercase;
9 | display: inline-block;
10 | vertical-align: middle;
11 | }
12 |
13 | @mixin label-color($color){
14 | border-color: $color;
15 | color: $color;
16 | }
17 | @mixin label-color-fill($color){
18 | border-color: $color;
19 | color: $white-color;
20 | background-color: $color;
21 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_morphing-buttons.scss:
--------------------------------------------------------------------------------
1 | $prefixes: ('', '-moz-', '-webkit-', '-ms-') !default;
2 |
3 | @mixin circle-animation(){
4 | @for $i from 0 to length($prefixes) {
5 | @include circle-animation-details(nth($prefixes, $i + 1));
6 | }
7 | }
8 |
9 | @mixin circle-animation-details($name){
10 | #{$name}animation-name: spin;
11 | #{$name}animation-duration: 1250ms;
12 | #{$name}animation-iteration-count: infinite;
13 | #{$name}animation-timing-function: linear;
14 |
15 | }
16 | @keyframes spin {
17 | from { transform:rotate(0deg); }
18 | to { transform:rotate(360deg); }
19 | }
20 |
21 | @-webkit-keyframes spin {
22 | from { -webkit-transform: rotate(0deg); }
23 | to { -webkit-transform: rotate(360deg); }
24 | }
25 |
26 | @-moz-keyframes spin {
27 | from { -moz-transform: rotate(0deg); }
28 | to { -moz-transform: rotate(360deg); }
29 | }
30 |
31 | @-ms-keyframes spin {
32 | from { -ms-transform: rotate(0deg); }
33 | to { -ms-transform: rotate(360deg); }
34 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_navbars.scss:
--------------------------------------------------------------------------------
1 | @mixin navbar-color($color){
2 | background-color: $color;
3 | }
4 |
5 | @mixin center-item(){
6 | left: 0;
7 | right: 0;
8 | margin-right: auto;
9 | margin-left: auto;
10 | position: absolute;
11 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_social-buttons.scss:
--------------------------------------------------------------------------------
1 | @mixin social-buttons-color ($color){
2 |
3 | border-color: $color;
4 | color: $color;
5 |
6 | &:hover,
7 | &:focus,
8 | &:active,
9 | &.active,
10 | .open > &.dropdown-toggle {
11 | background-color: $transparent-bg;
12 | color: $color;
13 | border-color: $color;
14 | opacity: 1;
15 | }
16 |
17 | &:disabled,
18 | &[disabled],
19 | &.disabled {
20 | background-color: $transparent-bg;
21 | border-color: $color;
22 | }
23 |
24 | &.btn-fill {
25 | color: $white-color;
26 | background-color: $color;
27 | opacity: 0.9;
28 |
29 | &:hover,
30 | &:focus,
31 | &:active,
32 | &.active,
33 | .open > &.dropdown-toggle{
34 | background-color: $color;
35 | color: $white-color;
36 | opacity: 1;
37 | }
38 |
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_tabs.scss:
--------------------------------------------------------------------------------
1 | @mixin pill-style($color){
2 | border: 1px solid $color;
3 | color: $color;
4 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/lbd/mixins/_transparency.scss:
--------------------------------------------------------------------------------
1 | // Opacity
2 |
3 | @mixin opacity($opacity) {
4 | opacity: $opacity;
5 | // IE8 filter
6 | $opacity-ie: ($opacity * 100);
7 | filter: #{alpha(opacity=$opacity-ie)};
8 | }
9 |
10 | @mixin black-filter($opacity){
11 | top: 0;
12 | left: 0;
13 | height: 100%;
14 | width: 100%;
15 | position: absolute;
16 | background-color: rgba(17,17,17,$opacity);
17 | display: block;
18 | content: "";
19 | z-index: 1;
20 | }
--------------------------------------------------------------------------------
/source/console/src/assets/sass/light-bootstrap-dashboard-react.scss:
--------------------------------------------------------------------------------
1 | /*!
2 |
3 | =========================================================
4 | * Light Bootstrap Dashboard React - v1.3.0
5 | * Based on Light Bootstrap Dashboard - v1.3.0
6 | =========================================================
7 |
8 | * Product Page: http://www.creative-tim.com/product/light-bootstrap-dashboard-react
9 | * Copyright 2017 Creative Tim (http://www.creative-tim.com)
10 | * Licensed under MIT (https://github.com/creativetimofficial/light-bootstrap-dashboard-react/blob/master/LICENSE.md)
11 |
12 | =========================================================
13 |
14 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 |
16 | */
17 |
18 | @import "lbd/variables";
19 | @import "lbd/mixins";
20 |
21 | // Core CSS
22 | @import "lbd/typography";
23 | @import "lbd/misc";
24 | @import "lbd/sidebar-and-main-panel";
25 | @import "lbd/buttons";
26 | @import "lbd/inputs";
27 |
28 | @import "lbd/alerts";
29 | @import "lbd/tables";
30 |
31 | @import "lbd/checkbox-radio-switch";
32 | @import "lbd/navbars";
33 | @import "lbd/footers";
34 |
35 | // Fancy Stuff
36 | @import "lbd/dropdown";
37 | @import "lbd/cards";
38 | @import "lbd/chartist";
39 | @import "lbd/responsive";
40 |
41 | @import "lbd/custom";
--------------------------------------------------------------------------------
/source/console/src/components/Card/Card.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | export class Card extends Component {
4 | render() {
5 | return (
6 |
7 |
8 |
{this.props.title}
9 |
{this.props.category}
10 |
11 |
20 | {this.props.content}
21 |
22 |
23 | {this.props.legend}
24 | {this.props.stats != null ?
: ""}
25 |
26 | {this.props.stats}
27 |
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default Card;
36 |
--------------------------------------------------------------------------------
/source/console/src/components/CustomCheckbox/CustomCheckbox.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | class CustomCheckbox extends Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = {
7 | is_checked: props.isChecked ? true : false
8 | };
9 | this.handleClick = this.handleClick.bind(this);
10 | }
11 | handleClick() {
12 | this.setState({ is_checked: !this.state.is_checked });
13 | }
14 | render() {
15 | const { isChecked, number, label, inline, ...rest } = this.props;
16 | const classes =
17 | inline !== undefined ? "checkbox checkbox-inline" : "checkbox";
18 | return (
19 |
20 |
27 |
28 |
29 | );
30 | }
31 | }
32 |
33 | export default CustomCheckbox;
34 |
--------------------------------------------------------------------------------
/source/console/src/components/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Grid } from "react-bootstrap";
3 |
4 | class Footer extends Component {
5 | render() {
6 | return (
7 |
19 | );
20 | }
21 | }
22 |
23 | export default Footer;
24 |
--------------------------------------------------------------------------------
/source/console/src/components/Navbars/AdminNavbar.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Navbar } from "react-bootstrap";
3 |
4 | import AdminNavbarLinks from "./AdminNavbarLinks.jsx";
5 |
6 | class Header extends Component {
7 | constructor(props) {
8 | super(props);
9 | this.mobileSidebarToggle = this.mobileSidebarToggle.bind(this);
10 | this.state = {
11 | sidebarExists: false
12 | };
13 | }
14 | mobileSidebarToggle(e) {
15 | if (this.state.sidebarExists === false) {
16 | this.setState({
17 | sidebarExists: true
18 | });
19 | }
20 | e.preventDefault();
21 | document.documentElement.classList.toggle("nav-open");
22 | var node = document.createElement("div");
23 | node.id = "bodyClick";
24 | node.onclick = function () {
25 | this.parentElement.removeChild(this);
26 | document.documentElement.classList.toggle("nav-open");
27 | };
28 | document.body.appendChild(node);
29 | }
30 | render() {
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | export default Header;
49 |
--------------------------------------------------------------------------------
/source/console/src/components/Navbars/AdminNavbarLinks.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import {
3 | NavItem,
4 | Nav
5 | } from "react-bootstrap";
6 | import { Auth, Logger, API } from 'aws-amplify';
7 |
8 | import configurations from "variables/configurations";
9 |
10 | class AdminNavbarLinks extends Component {
11 | constructor(props) {
12 | super(props);
13 |
14 | this.handleSelect = this.handleSelect.bind(this);
15 |
16 | this.logger = new Logger(configurations.logger.name, configurations.logger.level);
17 |
18 | // Sets up initial state
19 | this.state = {
20 | alertsCount: '-',
21 | side: this.props.side
22 | }
23 | }
24 |
25 | componentDidMount() {
26 | // In case the class is called from the side bar, it will get the value from the main container.
27 | // The main container gets the value through the API.
28 | if (this.state.side) {
29 | this.setState({
30 | alertsCount: document.getElementById('notification-count').innerText
31 | });
32 | this.timer = setInterval(() => {
33 | this.setState({
34 | alertsCount: document.getElementById('notification-count').innerText
35 | });
36 | }, 10000); // Gets alerts every 10 seconds from the main container
37 | } else {
38 | this.getAlertsCount();
39 | this.timer = setInterval(async() => {
40 | await this.getAlertsCount();
41 | }, 300000); // Gets alerts every 5 minute
42 | }
43 | }
44 |
45 | componentWillUnmount() {
46 | clearInterval(this.timer);
47 | }
48 |
49 | // Handles menu select
50 | handleSelect(eventKey) {
51 | if (eventKey === 'logout') {
52 | Auth.signOut()
53 | .then(data => this.logger.debug("Logged out"))
54 | .catch(err => this.logger.error(err));
55 | } else if (eventKey === 'alerts') {
56 | this.props.history.push('/alerts');
57 | }
58 | }
59 |
60 | // Gets alerts count
61 | getAlertsCount = async () => {
62 | let token = await this.props.getToken();
63 | let apiName = 'smart-product-api';
64 | let path = 'devices/alerts/count';
65 | let params = {
66 | headers: {
67 | 'Authorization': token,
68 | },
69 | response: false,
70 | };
71 |
72 | API.get(apiName, path, params)
73 | .then(response => {
74 | this.setState({ alertsCount: response.alertsCount });
75 | if (response.alertsCount > 0) {
76 | let message = `You have ${this.state.alertsCount} new alerts.`;
77 | this.props.handleNotification(message, 'warning', 'pe-7s-bell', 10);
78 | }
79 | })
80 | .catch(error => {
81 | this.props.handleNotification('An error occurred while getting the count of alerts', 'error', 'pe-7s-close-circle', 10);
82 | });
83 | }
84 |
85 | render() {
86 | return (
87 |
88 |
100 |
101 | );
102 | }
103 | }
104 |
105 | export default AdminNavbarLinks;
106 |
--------------------------------------------------------------------------------
/source/console/src/components/Sidebar/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { NavLink } from "react-router-dom";
3 |
4 | import AdminNavbarLinks from "../Navbars/AdminNavbarLinks.jsx";
5 | import logo from "assets/img/logo-aws.png";
6 |
7 | class Sidebar extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | width: window.innerWidth
12 | };
13 | }
14 | activeRoute(routeName) {
15 | return this.props.location.pathname.indexOf(routeName) > -1 ? "active" : "";
16 | }
17 | updateDimensions() {
18 | this.setState({ width: window.innerWidth });
19 | }
20 | componentDidMount() {
21 | this.updateDimensions();
22 | window.addEventListener("resize", this.updateDimensions.bind(this));
23 | }
24 | render() {
25 | return (
26 |
57 | );
58 | }
59 | }
60 |
61 | export default Sidebar;
62 |
--------------------------------------------------------------------------------
/source/console/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import { BrowserRouter, Route, Switch } from "react-router-dom";
5 |
6 | import "bootstrap/dist/css/bootstrap.min.css";
7 | import "./assets/css/animate.min.css";
8 | import "./assets/sass/light-bootstrap-dashboard-react.scss?v=1.3.0";
9 | import "./assets/css/demo.css";
10 | import "./assets/css/pe-icon-7-stroke.css";
11 |
12 | import AdminLayout from "layouts/Admin.jsx";
13 |
14 | ReactDOM.render(
15 |
16 |
17 | } />
18 |
19 | ,
20 | document.getElementById("root")
21 | );
22 |
--------------------------------------------------------------------------------
/source/console/src/layouts/Admin.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Route, Switch, Redirect } from "react-router-dom";
3 | import NotificationSystem from "react-notification-system";
4 | import { withAuthenticator } from "aws-amplify-react";
5 | import Amplify from '@aws-amplify/core';
6 | import Auth from "@aws-amplify/auth";
7 |
8 | import routes from "routes.js";
9 |
10 | import AdminNavbar from "components/Navbars/AdminNavbar";
11 | import Footer from "components/Footer/Footer";
12 | import Sidebar from "components/Sidebar/Sidebar";
13 | import { style } from "variables/Variables.jsx";
14 |
15 | declare var smart_product_config;
16 | Amplify.configure(smart_product_config);
17 |
18 | class Admin extends Component {
19 | constructor(props) {
20 | super(props);
21 | this.state = {
22 | notificationSystem: null,
23 | color: "black",
24 | fixedClasses: "dropdown show-dropdown open"
25 | };
26 | }
27 |
28 | // Handles Notification
29 | handleNotification = (message, level, iconClassName, autoDismissSecond) => {
30 | this.state.notificationSystem.addNotification({
31 | title: (),
32 | message: (
33 | {message}
34 | ),
35 | level: level,
36 | position: 'tr',
37 | autoDismiss: autoDismissSecond,
38 | });
39 | };
40 |
41 | // Gets API token
42 | getToken = async () => {
43 | let user = await Auth.currentAuthenticatedUser();
44 | let token = user.signInUserSession.idToken.jwtToken;
45 |
46 | return token;
47 | };
48 |
49 | // Checks scroll
50 | isScrollBottom = () => {
51 | let scrollTop = document.scrollingElement.scrollTop;
52 | let offsetHeight = document.documentElement.offsetHeight;
53 | let innerHeight = window.innerHeight;
54 |
55 | return innerHeight + scrollTop === offsetHeight;
56 | };
57 |
58 | // Goes to the top of the page
59 | goTop = () => {
60 | document.scrollingElement.scrollTop = 0;
61 | };
62 |
63 | handleDateSize = (date) => {
64 | return date.substring(0, 10);
65 | };
66 |
67 | getRoutes = routes => {
68 | return routes.map((prop, key) => {
69 | if (prop.layout === "/admin") {
70 | return (
71 | (
74 |
82 | )}
83 | exact
84 | key={key}
85 | />
86 | );
87 | } else {
88 | return null;
89 | }
90 | });
91 | };
92 | componentDidMount() {
93 | this.setState({ notificationSystem: this.refs.notificationSystem });
94 | }
95 | componentDidUpdate(e) {
96 | if (
97 | window.innerWidth < 993 &&
98 | e.history.location.pathname !== e.location.pathname &&
99 | document.documentElement.className.indexOf("nav-open") !== -1
100 | ) {
101 | document.documentElement.classList.toggle("nav-open");
102 | }
103 | if (e.history.action === "PUSH") {
104 | document.documentElement.scrollTop = 0;
105 | document.scrollingElement.scrollTop = 0;
106 | this.refs.mainPanel.scrollTop = 0;
107 | }
108 | }
109 | render() {
110 | return (
111 |
112 |
113 |
114 |
115 |
120 |
121 | {this.getRoutes(routes)}
122 |
123 |
124 |
125 |
126 |
127 | );
128 | }
129 | }
130 |
131 | export default withAuthenticator(Admin);
132 |
--------------------------------------------------------------------------------
/source/console/src/routes.js:
--------------------------------------------------------------------------------
1 | import Devices from "views/Devices.jsx";
2 | import DeviceRegistration from "views/DeviceRegistration.jsx";
3 | import DeviceDetail from "views/DeviceDetail.jsx";
4 | import Alerts from "views/Alerts.jsx";
5 | import History from "views/History.jsx";
6 | import UserSetting from "views/UserSetting.jsx";
7 |
8 | const dashboardRoutes = [
9 | {
10 | path: "/devices",
11 | name: "My Devices",
12 | icon: "pe-7s-home",
13 | component: Devices,
14 | layout: "/admin",
15 | visible: true
16 | },
17 | {
18 | path: "/devices/registration",
19 | name: "New Device Registration",
20 | icon: "pe-7s-home",
21 | component: DeviceRegistration,
22 | layout: "/admin",
23 | visible: false
24 | },
25 | {
26 | path: "/devices/:deviceId",
27 | name: "Device Detail",
28 | icon: "pe-7s-home",
29 | component: DeviceDetail,
30 | layout: "/admin",
31 | visible: false
32 | },
33 | {
34 | path: "/alerts",
35 | name: "Event Alerts",
36 | icon: "pe-7s-attention",
37 | component: Alerts,
38 | layout: "/admin",
39 | visible: true
40 | },
41 | {
42 | path: "/history",
43 | name: "Event History",
44 | icon: "pe-7s-note2",
45 | component: History,
46 | layout: "/admin",
47 | visible: true
48 | },
49 | {
50 | path: "/user",
51 | name: "User Setting",
52 | icon: "pe-7s-user",
53 | component: UserSetting,
54 | layout: "/admin",
55 | visible: true
56 | }
57 | ];
58 |
59 | export default dashboardRoutes;
60 |
--------------------------------------------------------------------------------
/source/console/src/variables/configurations.js:
--------------------------------------------------------------------------------
1 | const configurations = {
2 | "logger": {
3 | "name": "awsSmartProduct",
4 | "level": "INFO"
5 | }
6 | }
7 | export default configurations;
--------------------------------------------------------------------------------
/source/resources/authorizer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authorizer",
3 | "version": "0.0.1",
4 | "description": "Authorizer is an auxiliary class to create authentication and authorization claim ticket for solution services",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "private": true,
9 | "main": "./auth.common.js",
10 | "dependencies": {
11 | "aws-sdk": "*",
12 | "moment": "*",
13 | "underscore": "*",
14 | "jsonwebtoken": "*",
15 | "jwk-to-pem": "*",
16 | "request": "*",
17 | "js-base64": "*",
18 | "logger": "file:../logger"
19 | },
20 | "devDependencies": {
21 | "chai": "*",
22 | "sinon": "*",
23 | "sinon-chai": "*",
24 | "mocha": "*",
25 | "aws-sdk-mock": "*",
26 | "npm-run-all": "*",
27 | "proxyquire": "*"
28 | },
29 | "scripts": {
30 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
31 | "test": "mocha lib/*.spec.js",
32 | "prestart": "rm -rf package-lock.json && npm install",
33 | "start": "node index.js",
34 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
35 | "build": "npm run build:init && npm install --production"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/source/resources/authorizer/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.sandbox.create();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/resources/cicd/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cicd-helper",
3 | "version": "0.0.1",
4 | "description": "creates commit in users account with smart product code artifact",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "dependencies": {
9 | "aws-sdk": "*",
10 | "adm-zip": "^0.4.13",
11 | "logger": "file:../logger"
12 | },
13 | "main": "index.js",
14 | "scripts": {
15 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
16 | "build:zip": "zip -rq smart-product-cicd.zip . -x template.yml",
17 | "build:dist": "mkdir dist && mv smart-product-cicd.zip dist/",
18 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/source/resources/cognito/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const AWS = require('aws-sdk');
21 | const moment = require('moment');
22 | const Logger = require('logger');
23 |
24 | const creds = new AWS.EnvironmentCredentials('AWS');
25 | const dynamoConfig = {
26 | credentials: creds,
27 | region: process.env.AWS_REGION,
28 | };
29 | const docClient = new AWS.DynamoDB.DocumentClient(dynamoConfig);
30 |
31 | /**
32 | * Request handler.
33 | */
34 | const handler = async (event) => {
35 | try {
36 | let triggerSource = event.triggerSource;
37 | if (triggerSource === 'PostConfirmation_ConfirmSignUp') {
38 | let settingId = event.request.userAttributes.sub;
39 | let _setting = {
40 | settingId: settingId,
41 | setting: {
42 | alertLevel: [
43 | 'error',
44 | 'warning',
45 | ],
46 | sendNotification: false
47 | },
48 | createdAt: moment().utc().format(),
49 | updatedAt: moment().utc().format(),
50 | };
51 |
52 | let params = {
53 | TableName: process.env.SETTINGS_TBL,
54 | Item: _setting,
55 | };
56 | await docClient.put(params).promise();
57 |
58 | Logger.log(
59 | Logger.levels.INFO,
60 | `Success to process the setting: ${settingId}.`
61 | );
62 | }
63 | return Promise.resolve(event);
64 | } catch (err) {
65 | Logger.error(
66 | Logger.levels.INFO,
67 | `Error occurred while processing the Cognito trigger: ${err}`
68 | );
69 | return Promise.reject(err);
70 | }
71 | };
72 |
73 | module.exports = {
74 | handler,
75 | };
--------------------------------------------------------------------------------
/source/resources/cognito/index.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const expect = require('chai').expect;
4 | const AWS = require('aws-sdk-mock');
5 |
6 | let event = {
7 | version: '1',
8 | region: 'REGION',
9 | userPoolId: 'USER_POOL_ID',
10 | userName: 'USER_NAME',
11 | callerContext: {
12 | awsSdkVersion: 'aws-sdk-unknown-unknown',
13 | clientId: 'CLIENT_ID'
14 | },
15 | triggerSource: 'PostConfirmation_ConfirmSignUp',
16 | request: {
17 | userAttributes: {
18 | sub: 'SAMPLE_COGNITO_USER_ID',
19 | 'cognito:user_status': 'CONFIRMED',
20 | email_verified: 'true',
21 | 'cognito:email_alias': 'E_MAIL_ADDRESS',
22 | phone_number_verified: 'false',
23 | phone_number: 'PHONE_NUMBER',
24 | email: 'E_MAIL_ADDRESS'
25 | }
26 | },
27 | response: {}
28 | };
29 |
30 | let _module = require('./index.js');
31 |
32 | describe('Index', () => {
33 | /**
34 | * The test throws error due to the restriction of aws-sdk-mock.
35 | * Please refer to https://www.npmjs.com/package/aws-sdk-mock.
36 | * To test below, you need to put resource declarations in the handler function.
37 | */
38 | // afterEach(() => {
39 | // AWS.restore('DynamoDB.DocumentClient');
40 | // });
41 |
42 | // it('should return success when ddb put successful', function(done) {
43 | // AWS.mock('DynamoDB.DocumentClient', 'put', Promise.resolve('success'));
44 |
45 | // _module.handler(event)
46 | // .then(data => {
47 | // expect(data).to.deep.equal(event);
48 | // done();
49 | // })
50 | // .catch(err => {
51 | // done(err);
52 | // });
53 | // });
54 |
55 | // it('should return error information when ddb put fails', function(done) {
56 | // AWS.mock('DynamoDB.DocumentClient', 'put', Promise.reject('error'));
57 |
58 | // _module.handler(event)
59 | // .then(_data => {
60 | // done('invalid failure for negative test');
61 | // })
62 | // .catch(err => {
63 | // expect(err).to.equal('error');
64 | // done();
65 | // });
66 | // });
67 | });
68 |
--------------------------------------------------------------------------------
/source/resources/cognito/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cognito-trigger",
3 | "description": "Smart Product solution Cognito trigger Lambda function",
4 | "main": "index.js",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "version": "0.0.1",
9 | "private": true,
10 | "dependencies": {
11 | "moment": "*",
12 | "logger": "file:../logger"
13 | },
14 | "devDependencies": {
15 | "chai": "*",
16 | "mocha": "*",
17 | "aws-sdk-mock": "*"
18 | },
19 | "scripts": {
20 | "pretest": "rm -rf package-lock.json && rm -rf dist && npm install",
21 | "test": "mocha *.spec.js",
22 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
23 | "build:zip": "zip -rq smart-product-cognito.zip . -x template.yml",
24 | "build:dist": "mkdir dist && mv smart-product-cognito.zip dist/",
25 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist"
26 | },
27 | "bundledDependencies": [
28 | "moment"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/source/resources/helper/lib/dynamodb-helper.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const moment = require('moment');
21 | let AWS = require('aws-sdk');
22 | const _ = require('underscore');
23 | const Logger = require('logger');
24 |
25 | /**
26 | * Helper function to interact with dynamodb for cfn custom resource.
27 | *
28 | * @class dynamoDBHelper
29 | */
30 | class dynamoDBHelper {
31 | /**
32 | * @class dynamoDBHelper
33 | * @constructor
34 | */
35 | constructor() {
36 | this.creds = new AWS.EnvironmentCredentials('AWS'); // Lambda provided credentials
37 | this.dynamoConfig = {
38 | credentials: this.creds,
39 | region: process.env.AWS_REGION,
40 | };
41 | }
42 |
43 | saveItem(item, ddbTable) {
44 | // Handling Promise Rejection
45 | process.on('unhandledRejection', error => {
46 | throw error;
47 | });
48 |
49 | return new Promise((resolve, reject) => {
50 | for (var i = 0; i < _.keys(item).length; i++) {
51 | item[_.keys(item)[i]] = this._checkAssignedDataType(
52 | item[_.keys(item)[i]]
53 | );
54 | }
55 |
56 | item.createdAt = moment.utc().format();
57 | item.updatedAt = moment.utc().format();
58 |
59 | let params = {
60 | TableName: ddbTable,
61 | Item: item,
62 | };
63 |
64 | const docClient = new AWS.DynamoDB.DocumentClient(this.dynamoConfig);
65 | docClient.put(params, function (err, resp) {
66 | if (err) {
67 | Logger.error(
68 | Logger.levels.INFO,
69 | `Error occurred while attempting to save item ${JSON.stringify(params)}.`
70 | );
71 | reject(err);
72 | } else {
73 | Logger.log(
74 | Logger.levels.INFO,
75 | `${JSON.stringify(item)} saved.`
76 | );
77 | resolve(item);
78 | }
79 | });
80 | });
81 | }
82 |
83 | _checkAssignedDataType(attr) {
84 | if (_.isObject(attr)) {
85 | if (_.has(attr, 'N')) {
86 | return parseInt(attr['N']);
87 | } else if (_.has(attr, 'B')) {
88 | return attr['B'] === 'true';
89 | } else {
90 | for (var i = 0; i < _.keys(attr).length; i++) {
91 | attr[_.keys(attr)[i]] = this._checkAssignedDataType(
92 | attr[_.keys(attr)[i]]
93 | );
94 | }
95 | return attr;
96 | }
97 | } else {
98 | return attr;
99 | }
100 | }
101 | }
102 |
103 | module.exports = dynamoDBHelper;
104 |
--------------------------------------------------------------------------------
/source/resources/helper/lib/dynamodb-helper.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let assert = require('chai').assert;
4 | let expect = require('chai').expect;
5 | var path = require('path');
6 | let AWS = require('aws-sdk-mock');
7 | AWS.setSDK(path.resolve('./node_modules/aws-sdk'));
8 |
9 | let DynamoDBHelper = require('./dynamodb-helper.js');
10 |
11 | describe('dynamoDBHelper', function() {
12 | const _item = {
13 | id: 'abcxyz',
14 | val: {
15 | N: '12',
16 | },
17 | setting: {
18 | limit: {
19 | N: '45',
20 | },
21 | test: 'good',
22 | isABool: {
23 | B: 'true',
24 | },
25 | },
26 | };
27 |
28 | describe('#saveItem', function() {
29 | beforeEach(function() {});
30 |
31 | afterEach(function() {
32 | AWS.restore('DynamoDB.DocumentClient');
33 | });
34 |
35 | it('should return item when ddb put successful', function(done) {
36 | AWS.mock('DynamoDB.DocumentClient', 'put', function(params, callback) {
37 | callback(null, {});
38 | });
39 |
40 | let _helper = new DynamoDBHelper();
41 | _helper
42 | .saveItem(_item, 'testtbl')
43 | .then(data => {
44 | expect(data).to.have.property('id');
45 | expect(data).to.have.property('createdAt');
46 | expect(data).to.have.property('updatedAt');
47 | done();
48 | })
49 | .catch(err => {
50 | done(err);
51 | });
52 | });
53 |
54 | it('should return error information when ddb put fails', function(done) {
55 | AWS.mock('DynamoDB.DocumentClient', 'put', function(params, callback) {
56 | callback(
57 | {
58 | error: 'failed',
59 | },
60 | {}
61 | );
62 | });
63 |
64 | let _helper = new DynamoDBHelper();
65 | _helper
66 | .saveItem(_item, 'testtbl')
67 | .then(data => {
68 | done('invalid failure for negative test');
69 | })
70 | .catch(err => {
71 | expect(err).to.deep.equal({
72 | error: 'failed',
73 | });
74 | done();
75 | });
76 | });
77 | });
78 |
79 | describe('#_checkAssignedDataType', function() {
80 | beforeEach(function() {});
81 |
82 | afterEach(function() {});
83 |
84 | it('should return number when numeric value specifically assigned', function(done) {
85 | let _attr = {
86 | N: '12',
87 | };
88 |
89 | let _helper = new DynamoDBHelper();
90 | let val = _helper._checkAssignedDataType(_attr);
91 | expect(val).to.equal(12);
92 | done();
93 | });
94 |
95 | it('should return true/false when bool value specifically assigned', function(done) {
96 | let _attr = {
97 | B: 'false',
98 | };
99 |
100 | let _helper = new DynamoDBHelper();
101 | let val = _helper._checkAssignedDataType(_attr);
102 | expect(val).to.equal(false);
103 | done();
104 | });
105 |
106 | it('should return NaN when numeric value specifically assigned but not a number', function(done) {
107 | let _attr = {
108 | N: 'test',
109 | };
110 |
111 | let _helper = new DynamoDBHelper();
112 | let val = _helper._checkAssignedDataType(_attr);
113 | expect(val).to.be.NaN;
114 | done();
115 | });
116 |
117 | it('should return valid number when numeric value specifically assigned in nested object', function(done) {
118 | let _attr = {
119 | test: 'tesval',
120 | isNumber: {
121 | N: '88',
122 | },
123 | };
124 |
125 | let _helper = new DynamoDBHelper();
126 | let val = _helper._checkAssignedDataType(_attr);
127 | expect(val).to.deep.equal({
128 | test: 'tesval',
129 | isNumber: 88,
130 | });
131 | done();
132 | });
133 | });
134 | });
135 |
--------------------------------------------------------------------------------
/source/resources/helper/lib/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.createSandbox();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/resources/helper/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui-framework-helper",
3 | "description": "Smart Product solution custom resource helper Lambda function",
4 | "main": "index.js",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "version": "0.0.1",
9 | "private": true,
10 | "dependencies": {
11 | "aws-sdk": "*",
12 | "moment": "*",
13 | "underscore": "*",
14 | "password-generator": "*",
15 | "uuid": "^3.3.2",
16 | "usage-metrics": "file:../usage-metrics/",
17 | "logger": "file:../logger",
18 | "request-promise": "*",
19 | "request": "*"
20 | },
21 | "devDependencies": {
22 | "aws-sdk": "*",
23 | "chai": "*",
24 | "sinon": "*",
25 | "sinon-chai": "*",
26 | "mocha": "*",
27 | "aws-sdk-mock": "*",
28 | "npm-run-all": "*",
29 | "proxyquire": "*"
30 | },
31 | "scripts": {
32 | "pretest": "rm -rf package-lock.json && rm -rf dist && npm install",
33 | "test": "mocha lib/*.spec.js",
34 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
35 | "build:zip": "zip -rq smart-product-helper.zip . -x template.yml",
36 | "build:dist": "mkdir dist && mv smart-product-helper.zip dist/",
37 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist"
38 | },
39 | "bundledDependencies": [
40 | "aws-sdk",
41 | "moment",
42 | "underscore",
43 | "password-generator",
44 | "uuid",
45 | "request-promise",
46 | "request"
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/source/resources/logger/logger.common.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | // Logging class for sending messages to console log
21 | class Logger {
22 | constructor() {}
23 |
24 | static log(level, message) {
25 | if (level <= process.env.LOGGING_LEVEL) {
26 | console.log('[info] ', message);
27 | }
28 | }
29 |
30 | static warn(level, message) {
31 | if (level <= process.env.LOGGING_LEVEL) {
32 | console.log('[warn] ', message);
33 | }
34 | }
35 |
36 | static error(level, message) {
37 | if (level <= process.env.LOGGING_LEVEL) {
38 | console.log('[error] ', message);
39 | }
40 | }
41 |
42 | static get levels() {
43 | return {
44 | INFO: 1,
45 | ROBUST: 2,
46 | };
47 | }
48 | }
49 |
50 | module.exports = Object.freeze(Logger);
51 |
--------------------------------------------------------------------------------
/source/resources/logger/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "logger",
3 | "version": "0.0.1",
4 | "description": "Logger is an auxiliary logging class for solution services",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "private": true,
9 | "main": "./logger.common.js",
10 | "dependencies": {},
11 | "devDependencies": {
12 | "chai": "*",
13 | "sinon": "*",
14 | "sinon-chai": "*",
15 | "mocha": "*",
16 | "aws-sdk-mock": "*",
17 | "npm-run-all": "*",
18 | "proxyquire": "*"
19 | },
20 | "scripts": {
21 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
22 | "test": "mocha lib/*.spec.js",
23 | "prestart": "rm -rf package-lock.json && npm install",
24 | "start": "node index.js",
25 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
26 | "build": "npm run build:init && npm install --production"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/resources/logger/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.sandbox.create();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/resources/usage-metrics/metrics.common.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 | let https = require('https');
20 |
21 | // Metrics class for sending usage metrics to sb endpoints
22 | class Metrics {
23 | constructor() {
24 | this.endpoint = 'metrics.awssolutionsbuilder.com';
25 | }
26 |
27 | sendAnonymousMetric(metric) {
28 | return new Promise((resolve, reject) => {
29 | let _options = {
30 | hostname: this.endpoint,
31 | port: 443,
32 | path: '/generic',
33 | method: 'POST',
34 | headers: {
35 | 'Content-Type': 'application/json',
36 | },
37 | };
38 |
39 | let request = https.request(_options, function(response) {
40 | // data is streamed in chunks from the server
41 | // so we have to handle the "data" event
42 | let buffer;
43 | let data;
44 | let route;
45 |
46 | response.on('data', function(chunk) {
47 | buffer += chunk;
48 | });
49 |
50 | response.on('end', function(err) {
51 | resolve('metric sent');
52 | });
53 | });
54 |
55 | if (metric) {
56 | request.write(JSON.stringify(metric));
57 | }
58 |
59 | request.end();
60 |
61 | request.on('error', e => {
62 | console.error(e);
63 | reject(`Error occurred when sending metric request. ${JSON.stringify(_payload)}`);
64 | });
65 | });
66 | }
67 | }
68 |
69 | module.exports = Metrics;
70 |
--------------------------------------------------------------------------------
/source/resources/usage-metrics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "usage-metrics",
3 | "version": "0.0.1",
4 | "description": "Usage metrics is an auxiliary class to capture metrics pertinent for feedback on the solution",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "private": true,
9 | "main": "metrics.common.js",
10 | "dependencies": {
11 | "moment": "*"
12 | },
13 | "devDependencies": {
14 | "aws-sdk": "*",
15 | "chai": "*",
16 | "sinon": "*",
17 | "sinon-chai": "*",
18 | "mocha": "*",
19 | "aws-sdk-mock": "*",
20 | "npm-run-all": "*",
21 | "proxyquire": "*"
22 | },
23 | "scripts": {
24 | "test": "mocha *.spec.js"
25 | }
26 | }
--------------------------------------------------------------------------------
/source/resources/usage-metrics/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.sandbox.create();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/resources/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "utils",
3 | "version": "0.0.1",
4 | "description": "Utils is a common util class for solution services",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "private": true,
9 | "main": "./utils.common.js",
10 | "dependencies": {},
11 | "devDependencies": {
12 | "chai": "*",
13 | "sinon": "*",
14 | "sinon-chai": "*",
15 | "mocha": "*",
16 | "aws-sdk-mock": "*",
17 | "npm-run-all": "*",
18 | "proxyquire": "*"
19 | },
20 | "scripts": {
21 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
22 | "test": "mocha lib/*.spec.js",
23 | "prestart": "rm -rf package-lock.json && npm install",
24 | "start": "node index.js",
25 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
26 | "build": "npm run build:init && npm install --production"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/resources/utils/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.sandbox.create();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/resources/utils/utils.common.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | // Utils class for reducing redundant code
21 | class CommonUtils {
22 | constructor() {
23 |
24 | }
25 |
26 | /**
27 | * Generates DynamoDB query parameter.
28 | * @param {string} tableName - table name
29 | * @param {string} keyConditionExpression - key condition expression
30 | * @param {JSON} expressionAttributeValues - expression attribute values
31 | * @param {JSON} expressionAttributeNames - expression attribute names
32 | * @param {string} filterExpression - filter expression
33 | * @param {string} indexName - index name
34 | * @param {boolean} scanIndexForward - scan index forward
35 | * @param {JSON|string} lastevalkey - last evaluated key
36 | * @param {string} projectionExpression - projection expression
37 | * @param {Number} limit - limit
38 | */
39 | generateDynamoDBQueryParams(tableName, keyConditionExpression, expressionAttributeValues,
40 | expressionAttributeNames, filterExpression, indexName, scanIndexForward, lastevalkey,
41 | projectionExpression, limit) {
42 | let params = {
43 | TableName: tableName,
44 | KeyConditionExpression: keyConditionExpression,
45 | ExpressionAttributeValues: expressionAttributeValues,
46 | Limit: 50,
47 | };
48 |
49 | if (indexName) {
50 | params.IndexName = indexName;
51 | }
52 |
53 | if (scanIndexForward !== undefined) {
54 | params.ScanIndexForward = scanIndexForward
55 | }
56 |
57 | if (expressionAttributeNames) {
58 | params.ExpressionAttributeNames = expressionAttributeNames;
59 | }
60 |
61 | if (filterExpression) {
62 | params.FilterExpression = filterExpression;
63 | }
64 |
65 | if (projectionExpression) {
66 | params.ProjectionExpression = projectionExpression;
67 | }
68 |
69 | if (limit) {
70 | params.Limit = limit;
71 | }
72 |
73 | if (lastevalkey) {
74 | // If the LastEvaluatedKey is from the UI, parse the string to JSON object.
75 | if (typeof lastevalkey === 'string') {
76 | if (lastevalkey !== 'null') {
77 | params.ExclusiveStartKey = JSON.parse(decodeURIComponent(lastevalkey));
78 | }
79 | } else {
80 | if (lastevalkey !== null) {
81 | params.ExclusiveStartKey = lastevalkey;
82 | }
83 | }
84 | }
85 |
86 | return params;
87 | }
88 | }
89 |
90 | module.exports = CommonUtils;
91 |
--------------------------------------------------------------------------------
/source/services/api/admin/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const awsServerlessExpress = require('aws-serverless-express');
21 | const Logger = require('logger');
22 | let app = require('./lib/app.js');
23 |
24 | const server = awsServerlessExpress.createServer(app);
25 |
26 | exports.handler = function(event, context) {
27 | Logger.log(Logger.levels.INFO, 'Admin service recieved event:');
28 | Logger.log(Logger.levels.INFO, event);
29 |
30 | awsServerlessExpress.proxy(server, event, context);
31 | };
32 |
--------------------------------------------------------------------------------
/source/services/api/admin/lib/app.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 | const Auth = require('authorizer');
25 | const Admin = require('./admin.js');
26 | const express = require('express');
27 | const bodyParser = require('body-parser');
28 | const cors = require('cors');
29 | const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware');
30 | const app = express();
31 | const router = express.Router();
32 |
33 | // declare a new express app
34 | router.use(cors());
35 | router.use((req, res, next) => {
36 | bodyParser.json()(req, res, err => {
37 | if (err) {
38 | return res.status(400).json({
39 | code: 400,
40 | error: 'BadRequest',
41 | message: err.message
42 | });
43 | }
44 | next();
45 | });
46 | });
47 | router.use(bodyParser.urlencoded({extended: true}));
48 | router.use(awsServerlessExpressMiddleware.eventContext());
49 |
50 | const claimTicketHandler = async (req, res, next) => {
51 | try {
52 | const ticket = await Auth.getUserClaimTicket(req.header('Authorization'));
53 | req.ticket = ticket;
54 | next();
55 | } catch (err) {
56 | return res
57 | .status(401)
58 | .json({error: 'AccessDeniedException', message: err.message});
59 | }
60 | };
61 |
62 | // Gets a setting
63 | const getSettings = async (req, res) => {
64 | const {settingId} = req.params;
65 | let _admin = new Admin();
66 | Logger.log(
67 | Logger.levels.INFO,
68 | `Attempting to get setting for a setting ${settingId}`
69 | );
70 |
71 | try {
72 | const result = await _admin.getSettings(settingId);
73 | res.json(result);
74 | } catch (err) {
75 | Logger.log(Logger.levels.INFO, err);
76 |
77 | let status = err.code;
78 | return res.status(status).json(err);
79 | }
80 | };
81 |
82 | // Updates a setting
83 | const updateSettings = async (req, res) => {
84 | const {settingId} = req.params;
85 | const {body} = req;
86 | let _admin = new Admin();
87 | Logger.log(
88 | Logger.levels.INFO,
89 | `Attempting to update a setting ${settingId}`
90 | );
91 |
92 | try {
93 | const result = await _admin.updateSetting(settingId, body);
94 | res.json(result);
95 | } catch (err) {
96 | Logger.log(Logger.levels.INFO, err);
97 |
98 | let status = err.code;
99 | return res.status(status).json(err);
100 | }
101 | };
102 |
103 | /****************************
104 | * Event methods *
105 | ****************************/
106 |
107 | router.get('/admin/settings/config/:settingId', claimTicketHandler, getSettings);
108 |
109 | router.put('/admin/settings/config/:settingId', claimTicketHandler, updateSettings);
110 |
111 | app.use('/', router);
112 |
113 | // Export the app object. When executing the application local this does nothing. However,
114 | // to port it to AWS Lambda we will create a wrapper around that will load the app from
115 | // this file
116 | module.exports = app;
--------------------------------------------------------------------------------
/source/services/api/admin/lib/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.createSandbox();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/services/api/admin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-admin-service",
3 | "description": "The administration microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "aws-serverless-express": "^3.3.6",
9 | "body-parser": "^1.17.1",
10 | "express": "^4.15.2",
11 | "cors": "^2.8.3",
12 | "authorizer": "file:../../../resources/authorizer",
13 | "logger": "file:../../../resources/logger",
14 | "moment": "*",
15 | "underscore": "*"
16 | },
17 | "devDependencies": {
18 | "aws-sdk": "*",
19 | "chai": "*",
20 | "sinon": "*",
21 | "sinon-chai": "*",
22 | "mocha": "*",
23 | "aws-sdk-mock": "*",
24 | "proxyquire": "*"
25 | },
26 | "scripts": {
27 | "pretest": "rm -rf package-lock.json && npm install",
28 | "test": "mocha lib/*.spec.js",
29 | "prestart": "rm -rf package-lock.json && npm install",
30 | "start": "node index.js",
31 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
32 | "build:zip": "zip -rq smart-product-admin-service.zip . -x template.yml",
33 | "build:dist": "mkdir dist && mv smart-product-admin-service.zip dist/",
34 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
35 | "local:depfix": "rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && cp -R ../../resources/authorizer ./node_modules/authorizer && cp -R ../../resources/logger ./node_modules/logger",
36 | "local:test": "sam local invoke 'AdminService' -e ../tests/admin/get-user-event.json --env-vars ../tests/env-vars.json",
37 | "local:api": "sam local start-api --env-vars ../tests/env-vars.json"
38 | },
39 | "bundledDependencies": [
40 | "aws-serverless-express",
41 | "body-parser",
42 | "express",
43 | "cors",
44 | "moment",
45 | "underscore"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/source/services/api/command/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const awsServerlessExpress = require('aws-serverless-express');
21 | const Logger = require('logger');
22 | let app = require('./lib/app.js');
23 |
24 | const server = awsServerlessExpress.createServer(app);
25 |
26 | exports.handler = (event, context) => {
27 | Logger.log(Logger.levels.INFO, 'Command service recieved event:');
28 | Logger.log(Logger.levels.INFO, event);
29 |
30 | awsServerlessExpress.proxy(server, event, context);
31 | };
32 |
--------------------------------------------------------------------------------
/source/services/api/command/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-command-service",
3 | "description": "The command microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "aws-serverless-express": "^3.3.6",
9 | "body-parser": "^1.17.1",
10 | "express": "^4.15.2",
11 | "cors": "^2.8.3",
12 | "moment": "*",
13 | "underscore": "*",
14 | "authorizer": "file:../../../resources/authorizer/",
15 | "logger": "file:../../../resources/logger/",
16 | "uuid": "^3.3.2",
17 | "usage-metrics": "file:../../../resources/usage-metrics",
18 | "utils": "file:../../../resources/utils/"
19 | },
20 | "devDependencies": {
21 | "aws-sdk": "*",
22 | "aws-sdk-mock": "*",
23 | "chai": "*",
24 | "mocha": "*",
25 | "npm-run-all": "*",
26 | "proxyquire": "*",
27 | "sinon": "*",
28 | "sinon-chai": "*",
29 | "sinon-test": "^2.4.0"
30 | },
31 | "scripts": {
32 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
33 | "test": "env AWS_REGION='test' mocha lib/*.spec.js",
34 | "prestart": "rm -rf package-lock.json && npm install",
35 | "start": "node index.js",
36 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
37 | "build:zip": "zip -rq smart-product-command-service.zip . -x template.yml",
38 | "build:dist": "mkdir dist && mv smart-product-command-service.zip dist/",
39 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
40 | "local:depfix": "rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && rm -rf ./node_modules/utils && rm -rf ./node_modules/usage-metrics && cp -R ../../../resources/authorizer ./node_modules/authorizer && cp -R ../../../resources/logger ./node_modules/logger && cp -R ../../../resources/utils ./node_modules/utils && cp -R ../../../resources/usage-metrics ./node_modules/usage-metrics",
41 | "local:api": "npm run test && npm run local:depfix && sam local start-api --env-vars ../../tests/env-vars.json"
42 | },
43 | "bundledDependencies": [
44 | "aws-serverless-express",
45 | "body-parser",
46 | "express",
47 | "cors",
48 | "moment",
49 | "underscore",
50 | "uuid",
51 | "usage-metrics",
52 | "utils"
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------
/source/services/api/device/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const awsServerlessExpress = require('aws-serverless-express');
21 | const Logger = require('logger');
22 | let app = require('./lib/app.js');
23 |
24 | const server = awsServerlessExpress.createServer(app);
25 |
26 | exports.handler = (event, context) => {
27 | Logger.log(Logger.levels.INFO, 'Device service recieved event:');
28 | Logger.log(Logger.levels.INFO, event);
29 |
30 | awsServerlessExpress.proxy(server, event, context);
31 | };
32 |
--------------------------------------------------------------------------------
/source/services/api/device/lib/app.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 | const Auth = require('authorizer');
25 | const Device = require('./device.js');
26 | const express = require('express');
27 | const bodyParser = require('body-parser');
28 | const cors = require('cors');
29 | const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware');
30 | const app = express();
31 | const router = express.Router();
32 |
33 | // declare a new express app
34 | router.use(cors());
35 | router.use((req, res, next) => {
36 | bodyParser.json()(req, res, err => {
37 | if (err) {
38 | return res.status(400).json({
39 | code: 400,
40 | error: 'BadRequest',
41 | message: err.message
42 | });
43 | }
44 | next();
45 | });
46 | });
47 | router.use(bodyParser.urlencoded({extended: true}));
48 | router.use(awsServerlessExpressMiddleware.eventContext());
49 |
50 | const claimTicketHandler = async (req, res, next) => {
51 | try {
52 | const ticket = await Auth.getUserClaimTicket(req.header('Authorization'));
53 | req.ticket = ticket;
54 | next();
55 | } catch (err) {
56 | return res
57 | .status(401)
58 | .json({error: 'AccessDeniedException', message: err.message});
59 | }
60 | };
61 |
62 | // Gets the list of devices
63 | const getDevices = async (req, res) => {
64 | const {ticket} = req;
65 | let _device = new Device();
66 | Logger.log(
67 | Logger.levels.INFO,
68 | `Attempting to retrieve the list of devices`
69 | );
70 |
71 | try {
72 | const result = await _device.getDevices(ticket);
73 | res.json(result);
74 | } catch (err) {
75 | Logger.log(Logger.levels.INFO, err);
76 |
77 | let status = err.code;
78 | return res.status(status).json(err);
79 | }
80 | };
81 |
82 | // Gets device information
83 | const getDevice = async (req, res) => {
84 | const {ticket} = req;
85 | const {deviceId} = req.params;
86 | let _device = new Device();
87 | Logger.log(
88 | Logger.levels.INFO,
89 | `Attempting to retrieve device information for a device ${deviceId}`
90 | );
91 |
92 | try {
93 | const result = await _device.getDevice(ticket, deviceId);
94 | res.json(result);
95 | } catch (err) {
96 | Logger.log(Logger.levels.INFO, err);
97 |
98 | let status = err.code;
99 | return res.status(status).json(err);
100 | }
101 | };
102 |
103 | // Deletes a device
104 | const deleteDevice = async (req, res) => {
105 | const {ticket} = req;
106 | const {deviceId} = req.params;
107 | let _device = new Device();
108 | Logger.log(
109 | Logger.levels.INFO,
110 | `Attempting to delete a device ${deviceId}`
111 | );
112 |
113 | try {
114 | const result = await _device.deleteDevice(ticket, deviceId);
115 | res.json(result);
116 | } catch (err) {
117 | Logger.log(Logger.levels.INFO, err);
118 |
119 | let status = err.code;
120 | return res.status(status).json(err);
121 | }
122 | }
123 |
124 | /****************************
125 | * Event methods *
126 | ****************************/
127 |
128 | router.get('/devices', claimTicketHandler, getDevices);
129 |
130 | router.get('/devices/:deviceId', claimTicketHandler, getDevice);
131 |
132 | router.delete('/devices/:deviceId', claimTicketHandler, deleteDevice);
133 |
134 | app.use('/', router);
135 |
136 | // Export the app object. When executing the application local this does nothing. However,
137 | // to port it to AWS Lambda we will create a wrapper around that will load the app from
138 | // this file
139 | module.exports = app;
140 |
--------------------------------------------------------------------------------
/source/services/api/device/lib/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.createSandbox();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/services/api/device/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-device-service",
3 | "description": "The device microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "aws-sdk": "*",
9 | "aws-serverless-express": "^3.3.6",
10 | "body-parser": "^1.17.1",
11 | "express": "^4.15.2",
12 | "cors": "^2.8.3",
13 | "underscore": "*",
14 | "authorizer": "file:../../../resources/authorizer/",
15 | "logger": "file:../../../resources/logger/",
16 | "moment": "^2.24.0"
17 | },
18 | "devDependencies": {
19 | "aws-sdk": "*",
20 | "aws-sdk-mock": "*",
21 | "chai": "*",
22 | "mocha": "*",
23 | "npm-run-all": "*",
24 | "proxyquire": "*",
25 | "sinon": "*",
26 | "sinon-chai": "*"
27 | },
28 | "scripts": {
29 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
30 | "test": "mocha lib/*.spec.js",
31 | "prestart": "rm -rf package-lock.json && npm install",
32 | "start": "node index.js",
33 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
34 | "build:zip": "zip -rq smart-product-device-service.zip . -x template.yml",
35 | "build:dist": "mkdir dist && mv smart-product-device-service.zip dist/",
36 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
37 | "local:depfix": "npm run build:init && npm install && rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && cp -R ../../../resources/authorizer ./node_modules/authorizer && cp -R ../../../resources/logger ./node_modules/logger",
38 | "local:api": "npm run test && npm run local:depfix && sam local start-api --env-vars ../../tests/env-vars.json"
39 | },
40 | "bundledDependencies": [
41 | "aws-sdk",
42 | "aws-serverless-express",
43 | "body-parser",
44 | "express",
45 | "cors",
46 | "underscore",
47 | "moment"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/source/services/api/event/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const awsServerlessExpress = require('aws-serverless-express');
21 | const Logger = require('logger');
22 | let app = require('./lib/app.js');
23 |
24 | const server = awsServerlessExpress.createServer(app);
25 |
26 | exports.handler = (event, context) => {
27 | Logger.log(Logger.levels.INFO, 'Event service recieved event:');
28 | Logger.log(Logger.levels.INFO, event);
29 |
30 | awsServerlessExpress.proxy(server, event, context);
31 | };
32 |
--------------------------------------------------------------------------------
/source/services/api/event/lib/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.createSandbox();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/services/api/event/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-event-service",
3 | "description": "The event microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "aws-serverless-express": "^3.3.6",
9 | "body-parser": "^1.17.1",
10 | "express": "^4.15.2",
11 | "cors": "^2.8.3",
12 | "moment": "*",
13 | "underscore": "*",
14 | "authorizer": "file:../../../resources/authorizer/",
15 | "logger": "file:../../../resources/logger/",
16 | "utils": "file:../../../resources/utils/"
17 | },
18 | "devDependencies": {
19 | "aws-sdk": "*",
20 | "aws-sdk-mock": "*",
21 | "chai": "*",
22 | "mocha": "*",
23 | "npm-run-all": "*",
24 | "proxyquire": "*",
25 | "sinon": "*",
26 | "sinon-chai": "*"
27 | },
28 | "scripts": {
29 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
30 | "test": "mocha lib/*.spec.js",
31 | "prestart": "rm -rf package-lock.json && npm install",
32 | "start": "node index.js",
33 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
34 | "build:zip": "zip -rq smart-product-event-service.zip . -x template.yml",
35 | "build:dist": "mkdir dist && mv smart-product-event-service.zip dist/",
36 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
37 | "local:depfix": "npm run build:init && npm install && rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && rm -rf ./node_modules/utils && cp -R ../../../resources/authorizer ./node_modules/authorizer && cp -R ../../../resources/logger ./node_modules/logger && cp -R ../../../resources/utils ./node_modules/utils",
38 | "local:api": "npm run test && npm run local:depfix && sam local start-api --env-vars ../../tests/env-vars.json"
39 | },
40 | "bundledDependencies": [
41 | "aws-serverless-express",
42 | "body-parser",
43 | "express",
44 | "cors",
45 | "moment",
46 | "underscore",
47 | "colors",
48 | "utils"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/source/services/api/registration/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const awsServerlessExpress = require('aws-serverless-express');
21 | const Logger = require('logger');
22 | let app = require('./lib/app.js');
23 |
24 | const server = awsServerlessExpress.createServer(app);
25 |
26 | exports.handler = (event, context) => {
27 | Logger.log(Logger.levels.INFO, 'Registration service recieved event:');
28 | Logger.log(Logger.levels.INFO, event);
29 |
30 | awsServerlessExpress.proxy(server, event, context);
31 | };
32 |
--------------------------------------------------------------------------------
/source/services/api/registration/lib/app.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 | const Auth = require('authorizer');
25 | const Registration = require('./registration.js');
26 | const express = require('express');
27 | const bodyParser = require('body-parser');
28 | const cors = require('cors');
29 | const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware');
30 | const app = express();
31 | const router = express.Router();
32 |
33 | // declare a new express app
34 | router.use(cors());
35 | router.use((req, res, next) => {
36 | bodyParser.json()(req, res, err => {
37 | if (err) {
38 | return res.status(400).json({
39 | code: 400,
40 | error: 'BadRequest',
41 | message: err.message
42 | });
43 | }
44 | next();
45 | });
46 | });
47 | router.use(bodyParser.urlencoded({extended: true}));
48 | router.use(awsServerlessExpressMiddleware.eventContext());
49 |
50 | const claimTicketHandler = async (req, res, next) => {
51 | try {
52 | const ticket = await Auth.getUserClaimTicket(req.header('Authorization'));
53 | req.ticket = ticket;
54 | next();
55 | } catch (err) {
56 | return res
57 | .status(401)
58 | .json({error: 'AccessDeniedException', message: err.message});
59 | }
60 | };
61 |
62 | const createRegistration = async (req, res) => {
63 | const {body, ticket} = req;
64 | let _registration = new Registration();
65 | Logger.log(
66 | Logger.levels.INFO,
67 | `Attempting create device registration for device`
68 | );
69 |
70 | try {
71 | const result = await _registration.createRegistration(ticket, body);
72 | res.json(result);
73 | } catch (err) {
74 | Logger.log(Logger.levels.INFO, err);
75 |
76 | let status = err.code;
77 | return res.status(status).json(err);
78 | }
79 | };
80 |
81 | const getRegistrations = async (req, res) => {
82 | const {ticket} = req;
83 | let _registration = new Registration();
84 | Logger.log(
85 | Logger.levels.INFO,
86 | `Attempting retrieve device registrations`
87 | );
88 |
89 | try {
90 | const result = await _registration.listRegistrations(ticket);
91 | res.json(result);
92 | } catch (err) {
93 | Logger.log(Logger.levels.INFO, err);
94 |
95 | let status = err.code;
96 | return res.status(status).json(err);
97 | }
98 | };
99 |
100 | /****************************
101 | * Event methods *
102 | ****************************/
103 |
104 | router.post('/registration', claimTicketHandler, createRegistration);
105 |
106 | router.get('/registration', claimTicketHandler, getRegistrations);
107 |
108 | app.use('/', router);
109 |
110 | // Export the app object. When executing the application local this does nothing. However,
111 | // to port it to AWS Lambda we will create a wrapper around that will load the app from
112 | // this file
113 | module.exports = app;
114 |
--------------------------------------------------------------------------------
/source/services/api/registration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-registration-service",
3 | "description": "The registration microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "aws-serverless-express": "^3.3.6",
9 | "body-parser": "^1.17.1",
10 | "express": "^4.15.2",
11 | "cors": "^2.8.3",
12 | "moment": "*",
13 | "underscore": "*",
14 | "authorizer": "file:../../../resources/authorizer/",
15 | "logger": "file:../../../resources/logger/",
16 | "usage-metrics": "file:../../../resources/usage-metrics"
17 | },
18 | "devDependencies": {
19 | "aws-sdk": "*",
20 | "aws-sdk-mock": "*",
21 | "chai": "*",
22 | "mocha": "*",
23 | "npm-run-all": "*",
24 | "proxyquire": "*",
25 | "sinon": "*",
26 | "sinon-chai": "*",
27 | "sinon-test": "^2.4.0"
28 | },
29 | "scripts": {
30 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
31 | "test": "env AWS_REGION='testRegion', THING_TYPE='testType' mocha lib/*.spec.js",
32 | "prestart": "rm -rf package-lock.json && npm install",
33 | "start": "node index.js",
34 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
35 | "build:zip": "zip -rq smart-product-registration-service.zip . -x template.yml",
36 | "build:dist": "mkdir dist && mv smart-product-registration-service.zip dist/",
37 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
38 | "local:depfix": "npm run build:init && npm install && rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && cp -R ../../../resources/authorizer ./node_modules/authorizer && cp -R ../../../resources/logger ./node_modules/logger",
39 | "local:api": "npm run local:depfix && sam local start-api --env-vars ../../tests/env-vars.json"
40 | },
41 | "bundledDependencies": [
42 | "aws-serverless-express",
43 | "body-parser",
44 | "express",
45 | "cors",
46 | "moment",
47 | "underscore",
48 | "usage-metrics"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/source/services/api/status/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const awsServerlessExpress = require('aws-serverless-express');
21 | const Logger = require('logger');
22 | let app = require('./lib/app.js');
23 |
24 | const server = awsServerlessExpress.createServer(app);
25 |
26 | exports.handler = (event, context) => {
27 | Logger.log(Logger.levels.INFO, 'Registration service recieved event:');
28 | Logger.log(Logger.levels.INFO, event);
29 |
30 | awsServerlessExpress.proxy(server, event, context);
31 | };
32 |
--------------------------------------------------------------------------------
/source/services/api/status/lib/app.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 | const Auth = require('authorizer');
25 | const Status = require('./status.js');
26 | const express = require('express');
27 | const bodyParser = require('body-parser');
28 | const cors = require('cors');
29 | const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware');
30 | const app = express();
31 | const router = express.Router();
32 |
33 | // declare a new express app
34 | router.use(cors());
35 | router.use((req, res, next) => {
36 | bodyParser.json()(req, res, err => {
37 | if (err) {
38 | return res.status(400).json({
39 | code: 400,
40 | error: 'BadRequest',
41 | message: err.message
42 | });
43 | }
44 | next();
45 | });
46 | });
47 | router.use(bodyParser.urlencoded({extended: true}));
48 | router.use(awsServerlessExpressMiddleware.eventContext());
49 |
50 | const claimTicketHandler = async (req, res, next) => {
51 | try {
52 | const ticket = await Auth.getUserClaimTicket(req.header('Authorization'));
53 | req.ticket = ticket;
54 | next();
55 | } catch (err) {
56 | return res
57 | .status(401)
58 | .json({error: 'AccessDeniedException', message: err.message});
59 | }
60 | };
61 |
62 | const getStatus = async (req, res) => {
63 | const {ticket} = req;
64 | const {deviceId} = req.params;
65 | let _status = new Status();
66 | Logger.log(
67 | Logger.levels.INFO,
68 | `Attempting retrieve device status for device ${deviceId}`
69 | );
70 |
71 | try {
72 | const result = await _status.getDeviceStatus(ticket, deviceId);
73 | res.json(result);
74 | } catch (err) {
75 | Logger.log(Logger.levels.INFO, err);
76 |
77 | let status = err.code;
78 | return res.status(status).json(err);
79 | }
80 | };
81 |
82 | /****************************
83 | * Status methods *
84 | ****************************/
85 |
86 | router.get('/devices/:deviceId/status', claimTicketHandler, getStatus);
87 |
88 | app.use('/', router);
89 |
90 | // Export the app object. When executing the application local this does nothing. However,
91 | // to port it to AWS Lambda we will create a wrapper around that will load the app from
92 | // this file
93 | module.exports = app;
94 |
--------------------------------------------------------------------------------
/source/services/api/status/lib/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.createSandbox();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/services/api/status/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-status-service",
3 | "description": "The status microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "aws-sdk": "*",
9 | "aws-serverless-express": "^3.3.6",
10 | "body-parser": "^1.17.1",
11 | "express": "^4.15.2",
12 | "cors": "^2.8.3",
13 | "underscore": "*",
14 | "authorizer": "file:../../../resources/authorizer/",
15 | "logger": "file:../../../resources/logger/"
16 | },
17 | "devDependencies": {
18 | "aws-sdk": "*",
19 | "aws-sdk-mock": "*",
20 | "chai": "*",
21 | "mocha": "*",
22 | "npm-run-all": "*",
23 | "proxyquire": "*",
24 | "sinon": "*",
25 | "sinon-chai": "*"
26 | },
27 | "scripts": {
28 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
29 | "test": "mocha lib/*.spec.js",
30 | "prestart": "rm -rf package-lock.json && npm install",
31 | "start": "node index.js",
32 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
33 | "build:zip": "zip -rq smart-product-status-service.zip . -x template.yml",
34 | "build:dist": "mkdir dist && mv smart-product-status-service.zip dist/",
35 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
36 | "local:depfix": "npm run build:init && npm install && rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && cp -R ../../../resources/authorizer ./node_modules/authorizer && cp -R ../../../resources/logger ./node_modules/logger",
37 | "local:api": "npm run local:depfix && sam local start-api --env-vars ../../tests/env-vars.json"
38 | },
39 | "bundledDependencies": [
40 | "aws-sdk",
41 | "aws-serverless-express",
42 | "body-parser",
43 | "express",
44 | "cors",
45 | "underscore"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/source/services/command-status/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | console.log('Loading function');
21 | const Logger = require('logger');
22 | let lib = require('./lib');
23 |
24 | exports.handler = function(event, context, callback) {
25 | Logger.log(Logger.levels.INFO, 'recieved event:');
26 | Logger.log(Logger.levels.INFO, event);
27 |
28 | lib
29 | .response(event)
30 | .then(data => {
31 | return callback(null, data);
32 | })
33 | .catch(err => {
34 | return callback(err, null);
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/source/services/command-status/lib/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 | const Message = require('./message.js');
25 |
26 | const response = async event => {
27 | let _m = new Message();
28 | let _message = {};
29 |
30 | if (typeof event === 'object') {
31 | _message = event;
32 | } else {
33 | _message = JSON.parse(event);
34 | }
35 |
36 | try {
37 | const result = await _m.statusUpdate(_message);
38 | return Promise.resolve(result);
39 | } catch (err) {
40 | Logger.error(Logger.levels.INFO, err);
41 | Logger.error(
42 | Logger.levels.INFO,
43 | `Error occurred while updating status for device ${
44 | _message.deviceId
45 | } command ${_message.commandId}.`
46 | );
47 | return Promise.reject(err);
48 | }
49 | };
50 |
51 | module.exports = {
52 | response,
53 | };
54 |
--------------------------------------------------------------------------------
/source/services/command-status/lib/index.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AWS = require('aws-sdk-mock');
4 | const moment = require('moment');
5 | const sinon = require('sinon');
6 | const sinonTest = require('sinon-test');
7 | const test = sinonTest(sinon);
8 | const expect = require('chai').expect;
9 |
10 | const lib = require('./index.js');
11 | const Message = require('./message.js');
12 |
13 | describe('Index', function() {
14 | let _event = {
15 | deviceId: '42adad4d-fdd1-4db0-a501-61cffd0fa3e4',
16 | status: 'ACK',
17 | commandId: '11advd4d-ccd1-4zw0-a459-6trfpq0fa454',
18 | };
19 |
20 | it(
21 | 'should return event message with successful library Message updateStatus response',
22 | test(async function() {
23 | let _resp = {
24 | ..._event,
25 | };
26 | _resp.updatedAt = moment()
27 | .utc()
28 | .format();
29 |
30 | const stub = this.stub(Message.prototype, 'statusUpdate');
31 | stub.resolves(_resp);
32 | try {
33 | const data = await lib.response(_event);
34 | expect(data.deviceId).to.equal(_resp.deviceId);
35 | expect(data.commandId).to.equal(_resp.commandId);
36 | expect(data.status).to.equal(_resp.status);
37 | expect(data).to.have.property('updatedAt');
38 | } catch (err) {
39 | console.log('negative test: ', err);
40 | }
41 | })
42 | );
43 |
44 | it(
45 | 'should return error with failed library Message updateStatus response',
46 | test(async function() {
47 | const _resp = {
48 | code: 500,
49 | error: 'StatusUpdateFailure',
50 | message: `Error occurred while updating command status for device ${
51 | _event.deviceId
52 | } command ${_event.commandId}.`,
53 | };
54 | const stub = this.stub(Message.prototype, 'statusUpdate');
55 | stub.rejects(_resp);
56 | try {
57 | await lib.response(_event);
58 | } catch (err) {
59 | expect(err).to.be.deep.equal(_resp);
60 | }
61 | })
62 | );
63 | });
64 |
--------------------------------------------------------------------------------
/source/services/command-status/lib/message.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const Logger = require('logger');
21 | const moment = require('moment');
22 | const AWS = require('aws-sdk');
23 |
24 | /**
25 | * Performs proxy actions for messages from smart products to IoT.
26 | *
27 | * @class Message
28 | */
29 | class Message {
30 | /**
31 | * @class Message
32 | * @constructor
33 | */
34 | constructor() {
35 | this.creds = new AWS.EnvironmentCredentials('AWS'); // Lambda provided credentials
36 | this.dynamoConfig = {
37 | credentials: this.creds,
38 | region: process.env.AWS_REGION,
39 | };
40 | }
41 |
42 | /**
43 | * Updates command status for device
44 | * @param {JSON} message - message object
45 | */
46 | async statusUpdate(message) {
47 | // const _self = this;
48 | let _event = {
49 | ...message,
50 | };
51 | _event.updatedAt = moment()
52 | .utc()
53 | .format();
54 |
55 | const params = {
56 | TableName: process.env.COMMANDS_TBL,
57 | Key: {
58 | deviceId: message.deviceId,
59 | commandId: message.commandId,
60 | },
61 | ExpressionAttributeNames: {
62 | '#S': 'status',
63 | '#U': 'updatedAt',
64 | '#R': 'reason',
65 | },
66 | ExpressionAttributeValues: {
67 | ':s': message.status,
68 | ':u': moment()
69 | .utc()
70 | .format(),
71 | ':r': message.reason,
72 | },
73 | UpdateExpression: 'SET #S = :s, #U = :u, #R = :r',
74 | };
75 |
76 | let docClient = new AWS.DynamoDB.DocumentClient(this.dynamoConfig);
77 | let ddbPromise = docClient.update(params).promise();
78 |
79 | try {
80 | await ddbPromise;
81 | return Promise.resolve(_event);
82 | } catch (err) {
83 | Logger.error(Logger.levels.INFO, err);
84 | return Promise.reject({
85 | code: 500,
86 | error: 'StatusUpdateFailure',
87 | message: `Error occurred while updating command status for device ${
88 | message.deviceId
89 | } command ${message.commandId}.`,
90 | });
91 | }
92 | }
93 | }
94 |
95 | module.exports = Message;
96 |
--------------------------------------------------------------------------------
/source/services/command-status/lib/message.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let expect = require('chai').expect;
4 | let AWS = require('aws-sdk-mock');
5 | const sinon = require('sinon');
6 | const sinonTest = require('sinon-test');
7 | const test = sinonTest(sinon);
8 |
9 | let Message = require('./message.js');
10 |
11 | let message = {
12 | deviceId: '42adad4d-fdd1-4db0-a501-61cffd0fa3e4',
13 | status: 'ACK',
14 | commandId: '11advd4d-ccd1-4zw0-a459-6trfpq0fa454',
15 | };
16 |
17 | describe('Message', function() {
18 | beforeEach(function() {});
19 |
20 | describe('#statusUpdate', function() {
21 | afterEach(function() {
22 | AWS.restore('DynamoDB.DocumentClient');
23 | });
24 |
25 | it(
26 | 'should return event with successful update',
27 | test(async function() {
28 | AWS.mock('DynamoDB.DocumentClient', 'update', Promise.resolve());
29 |
30 | let _m = new Message();
31 | try {
32 | const data = await _m.statusUpdate(message);
33 | expect(data.deviceId).to.equal(message.deviceId);
34 | expect(data.commandId).to.equal(message.commandId);
35 | expect(data.status).to.equal(message.status);
36 | expect(data).to.have.property('updatedAt');
37 | } catch (e) {
38 | console.log(e);
39 | }
40 | })
41 | );
42 |
43 | it(
44 | 'should return error information when ddb update fails',
45 | test(async function() {
46 | AWS.mock('DynamoDB.DocumentClient', 'update', Promise.reject());
47 |
48 | let _m = new Message();
49 | try {
50 | await _m.statusUpdate(message);
51 | } catch (e) {
52 | expect(e).to.be.deep.equal({
53 | code: 500,
54 | error: 'StatusUpdateFailure',
55 | message: `Error occurred while updating command status for device ${
56 | message.deviceId
57 | } command ${message.commandId}.`,
58 | });
59 | }
60 | })
61 | );
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/source/services/command-status/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-command-status",
3 | "description": "The command status microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "logger": "file:../../resources/logger/"
9 | },
10 | "devDependencies": {
11 | "aws-sdk-mock": "*",
12 | "chai": "*",
13 | "mocha": "*",
14 | "moment": "^2.24.0",
15 | "npm-run-all": "*",
16 | "sinon": "*",
17 | "sinon-test": "^2.4.0"
18 | },
19 | "scripts": {
20 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
21 | "test": "env COMMANDS_TBL='cmdTable', AWS_REGION='test' mocha lib/*.spec.js",
22 | "prestart": "rm -rf package-lock.json && npm install",
23 | "start": "node index.js",
24 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
25 | "build:zip": "zip -rq smart-product-command-status.zip . -x template.yml",
26 | "build:dist": "mkdir dist && mv smart-product-command-status.zip dist/",
27 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
28 | "local:depfix": "rm -rf ./node_modules/logger && cp -R ../../resources/logger ./node_modules/logger",
29 | "local:test": "npm run local:depfix && sam local invoke 'CommandStatus' -e ../tests/event-message.json --env-vars ../tests/env-vars.json"
30 | },
31 | "bundledDependencies": [
32 | "moment"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/source/services/event-proxy/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | console.log('Loading function');
21 | const Logger = require('logger');
22 | let lib = require('./lib');
23 |
24 | exports.handler = function(event, context, callback) {
25 | Logger.log(Logger.levels.INFO, 'Message proxy recieved event:');
26 | Logger.log(Logger.levels.INFO, event);
27 |
28 | lib
29 | .response(event)
30 | .then(data => {
31 | return callback(null, data);
32 | })
33 | .catch(err => {
34 | return callback(err, null);
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/source/services/event-proxy/lib/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const _ = require('underscore');
24 | const Logger = require('logger');
25 | const Message = require('./message.js');
26 | const AWS = require('aws-sdk');
27 |
28 | const response = async event => {
29 | let _m = new Message();
30 | let _message = {};
31 |
32 | if (typeof event === 'object') {
33 | _message = event;
34 | } else {
35 | _message = JSON.parse(event);
36 | }
37 |
38 | try {
39 | const result = await _m.createEvent(_message);
40 | const lambda = new AWS.Lambda({region: process.env.AWS_REGION});
41 | await lambda
42 | .invoke({
43 | FunctionName: process.env.NOTIFICATION_LAMBDA,
44 | InvocationType: 'Event',
45 | Payload: JSON.stringify(result),
46 | })
47 | .promise();
48 | return Promise.resolve(result);
49 | } catch (err) {
50 | Logger.error(Logger.levels.INFO, err);
51 | Logger.error(
52 | Logger.levels.INFO,
53 | `Error occurred while attempting to create event and invoke notification for device.`
54 | );
55 | return Promise.reject(err);
56 | }
57 | };
58 |
59 | module.exports = {
60 | response,
61 | };
62 |
--------------------------------------------------------------------------------
/source/services/event-proxy/lib/index.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const sinon = require('sinon');
4 | const assert = require('chai').assert;
5 | const expect = require('chai').expect;
6 | const path = require('path');
7 | const AWS = require('aws-sdk-mock');
8 | //AWS.setSDK(path.resolve('./node_modules/aws-sdk'));
9 |
10 | const lib = require('./index.js');
11 | const Message = require('./message.js');
12 |
13 | let sandbox;
14 |
15 | describe('Index', function() {
16 | beforeEach(function() {
17 | sandbox = sinon.createSandbox();
18 | });
19 |
20 | afterEach(function() {
21 | sandbox.restore();
22 | });
23 |
24 | it('should return event message with successful library Message createEvent response', function(done) {
25 | const _event = {
26 | createdAt: '2018-02-06T20:57:48Z',
27 | deviceId: '42adad4d-fdd1-4db0-a501-61cffd0fa3e4',
28 | messageId: '085e4e22-bd06-4ca6-b913-8b0b6bf154c1',
29 | message: 'R-410A refrigerant pressure exceeding upper threshold',
30 | details: {
31 | type: 'warning',
32 | eventId: 'R-410A-UT',
33 | sensorId: 'cps-1234',
34 | sensor: 'coolant pressure switch',
35 | value: 612,
36 | },
37 | type: 'warning',
38 | sentAt: '2018-02-06T20:57:48Z',
39 | };
40 |
41 | let _resp = {
42 | ..._event,
43 | };
44 | _resp.id = 123;
45 |
46 | sandbox.stub(Message.prototype, 'createEvent').resolves(_resp);
47 | AWS.mock('Lambda', 'invoke', Promise.resolve());
48 |
49 | lib
50 | .response(_event)
51 | .then(data => {
52 | expect(data.deviceId).to.equal(_event.deviceId);
53 | expect(data.messageId).to.equal(_event.messageId);
54 | expect(data).to.have.property('id');
55 | done();
56 | AWS.restore('Lambda');
57 | })
58 | .catch(err => {
59 | done(err);
60 | });
61 | });
62 |
63 | it('should return error with failed library Message createEvent response', function(done) {
64 | const _event = {
65 | createdAt: '2018-02-06T20:57:48Z',
66 | deviceId: '42adad4d-fdd1-4db0-a501-61cffd0fa3e4',
67 | messageId: '085e4e22-bd06-4ca6-b913-8b0b6bf154c1',
68 | message: 'R-410A refrigerant pressure exceeding upper threshold',
69 | details: {
70 | type: 'warning',
71 | eventId: 'R-410A-UT',
72 | sensorId: 'cps-1234',
73 | sensor: 'coolant pressure switch',
74 | value: 612,
75 | },
76 | type: 'warning',
77 | sentAt: '2018-02-06T20:57:48Z',
78 | };
79 |
80 | let _resp = {
81 | code: 500,
82 | error: 'EventCreateFailure',
83 | message: `Error occurred while attempting to create event message for device ${
84 | _event.deviceId
85 | }.`,
86 | };
87 |
88 | sandbox.stub(Message.prototype, 'createEvent').rejects(_resp);
89 | AWS.mock('Lambda', 'invoke', Promise.resolve());
90 |
91 | lib
92 | .response(_event)
93 | .then(data => {
94 | done('invalid failure for negative test');
95 | })
96 | .catch(err => {
97 | expect(err).to.deep.equal(_resp);
98 | AWS.restore('Lambda');
99 | done();
100 | });
101 | });
102 |
103 | it('should return error for failed lambda invocation', function(done) {
104 | const _event = {
105 | createdAt: '2018-02-06T20:57:48Z',
106 | deviceId: '42adad4d-fdd1-4db0-a501-61cffd0fa3e4',
107 | messageId: '085e4e22-bd06-4ca6-b913-8b0b6bf154c1',
108 | message: 'R-410A refrigerant pressure exceeding upper threshold',
109 | details: {
110 | type: 'warning',
111 | eventId: 'R-410A-UT',
112 | sensorId: 'cps-1234',
113 | sensor: 'coolant pressure switch',
114 | value: 612,
115 | },
116 | type: 'warning',
117 | sentAt: '2018-02-06T20:57:48Z',
118 | };
119 |
120 | let _resp = {
121 | code: 500,
122 | error: 'LambdaInvocationFailed',
123 | message: `Error occurred while invoking lambda function.`,
124 | };
125 |
126 | sandbox.stub(Message.prototype, 'createEvent').resolves();
127 | AWS.mock('Lambda', 'invoke', Promise.reject(_resp));
128 |
129 | lib
130 | .response(_event)
131 | .then(data => {
132 | done('invalid failure for negative test');
133 | })
134 | .catch(err => {
135 | expect(err).to.deep.equal(_resp);
136 | AWS.restore('Lambda');
137 | done();
138 | });
139 | });
140 | });
141 |
--------------------------------------------------------------------------------
/source/services/event-proxy/lib/message.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let assert = require('chai').assert;
4 | let expect = require('chai').expect;
5 | var path = require('path');
6 | let AWS = require('aws-sdk-mock');
7 | // AWS.setSDK(path.resolve('./node_modules/aws-sdk'));
8 |
9 | let Message = require('./message.js');
10 |
11 | let message = {
12 | createdAt: '2018-02-06T20:57:48Z',
13 | deviceId: '42adad4d-fdd1-4db0-a501-61cffd0fa3e4',
14 | messageId: '085e4e22-bd06-4ca6-b913-8b0b6bf154c1',
15 | message: 'R-410A refrigerant pressure exceeding upper threshold',
16 | details: {
17 | type: 'warning',
18 | eventId: 'R-410A-UT',
19 | sensorId: 'cps-1234',
20 | sensor: 'coolant pressure switch',
21 | value: 612,
22 | },
23 | type: 'warning',
24 | sentAt: '2018-02-06T20:57:48Z',
25 | };
26 |
27 | let successUserId = {
28 | Items: [
29 | {
30 | deviceId: '21c131f9-81a1-4c4f-8d3d-919803ca3234',
31 | userId: '6aeec314-405d-4371-b2f9-42bab52c2ea1',
32 | updatedAt: '2019-05-17T15:45:55.687Z',
33 | status: 'complete',
34 | activedAt: '2019-05-17T15:45:55.687Z',
35 | createdAt: '2019-05-17T15:45:55.687Z',
36 | }
37 | ],
38 | Count: 1,
39 | ScannedCount: 1
40 | };
41 |
42 | describe('Message', function() {
43 | beforeEach(function() {});
44 |
45 | describe('#createEvent', function() {
46 | afterEach(function() {
47 | AWS.restore('DynamoDB.DocumentClient');
48 | });
49 |
50 | it('should return event with successful create', function(done) {
51 | AWS.mock('DynamoDB.DocumentClient', 'query', function(params, callback) {
52 | callback(null, successUserId);
53 | });
54 | AWS.mock('DynamoDB.DocumentClient', 'put', function(params, callback) {
55 | callback(null, message);
56 | });
57 |
58 | let _mm = new Message();
59 | _mm
60 | .createEvent(message)
61 | .then(data => {
62 | expect(data.deviceId).to.equal(message.deviceId);
63 | expect(data.messageId).to.equal(message.messageId);
64 | expect(data).to.have.property('id');
65 | done();
66 | })
67 | .catch(err => {
68 | done(err);
69 | });
70 | });
71 |
72 | it('should return error information when ddb put fails', function(done) {
73 | AWS.mock('DynamoDB.DocumentClient', 'query', function(params, callback) {
74 | callback(null, successUserId);
75 | });
76 | AWS.mock('DynamoDB.DocumentClient', 'put', function(params, callback) {
77 | callback('ddb error', null);
78 | });
79 |
80 | let _mm = new Message();
81 | _mm
82 | .createEvent(message)
83 | .then(data => {
84 | done('invalid failure for negative test');
85 | })
86 | .catch(err => {
87 | expect(err).to.deep.equal({
88 | code: 500,
89 | error: 'EventCreateFailure',
90 | message: `Error occurred while attempting to create event message for device ${
91 | message.deviceId
92 | }.`,
93 | });
94 | done();
95 | });
96 | });
97 | });
98 | });
99 |
--------------------------------------------------------------------------------
/source/services/event-proxy/lib/test-setup.spec.js:
--------------------------------------------------------------------------------
1 | const sinon = require('sinon');
2 | const chai = require('chai');
3 | const sinonChai = require('sinon-chai');
4 |
5 | before(function() {
6 | chai.use(sinonChai);
7 | });
8 |
9 | beforeEach(function() {
10 | this.sandbox = sinon.createSandbox();
11 | });
12 |
13 | afterEach(function() {
14 | this.sandbox.restore();
15 | });
16 |
--------------------------------------------------------------------------------
/source/services/event-proxy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-event-proxy",
3 | "description": "The event proxy microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "moment": "*",
9 | "underscore": "*",
10 | "logger": "file:../../resources/logger/",
11 | "uuid": "*"
12 | },
13 | "devDependencies": {
14 | "aws-sdk": "*",
15 | "aws-sdk-mock": "*",
16 | "chai": "*",
17 | "mocha": "*",
18 | "npm-run-all": "*",
19 | "proxyquire": "*",
20 | "sinon": "*",
21 | "sinon-chai": "*"
22 | },
23 | "scripts": {
24 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
25 | "test": "env NOTIFICATION_LAMBDA='lambda_arn', AWS_REGION='test' mocha lib/*.spec.js",
26 | "prestart": "rm -rf package-lock.json && npm install",
27 | "start": "node index.js",
28 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
29 | "build:zip": "zip -rq smart-product-event-proxy.zip . -x template.yml",
30 | "build:dist": "mkdir dist && mv smart-product-event-proxy.zip dist/",
31 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
32 | "local:depfix": "rm -rf ./node_modules/logger && cp -R ../../resources/logger ./node_modules/logger",
33 | "local:test": "npm run local:depfix && sam local invoke 'EventProxy' -e ../tests/event-message.json --env-vars ../tests/env-vars.json"
34 | },
35 | "bundledDependencies": [
36 | "moment",
37 | "underscore",
38 | "colors",
39 | "uuid",
40 | "logger"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/source/services/jitr/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | console.log('Loading function');
21 | const Logger = require('logger');
22 | let lib = require('./lib');
23 |
24 | exports.handler = async (event, context) => {
25 | Logger.log(Logger.levels.INFO, 'JITR proxy recieved event:');
26 | Logger.log(Logger.levels.INFO, event);
27 |
28 | try {
29 | await lib.respond(event);
30 | } catch (err) {
31 | Logger.error(Logger.levels.INFO, err);
32 | throw new Error(err);
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/source/services/jitr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-jitr",
3 | "description": "A Lambda function for the smart product device just-in-time-registration microservice",
4 | "main": "index.js",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "version": "0.0.1",
9 | "private": true,
10 | "dependencies": {
11 | "authorizer": "file:../../resources/authorizer/",
12 | "logger": "file:../../resources/logger/",
13 | "moment": "*",
14 | "usage-metrics": "file:../../resources/usage-metrics",
15 | "@fidm/x509": "^1.2.1"
16 | },
17 | "devDependencies": {
18 | "aws-sdk-mock": "*",
19 | "chai": "^4.2.0",
20 | "eslint": "^5.16.0",
21 | "eslint-config-strongloop": "^2.1.0",
22 | "mocha": "^7.0.0",
23 | "sinon": "^8.0.4",
24 | "sinon-chai": "^3.4.0",
25 | "sinon-test": "^2.4.1"
26 | },
27 | "scripts": {
28 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
29 | "test": "env LOGGING_LEVEL='2' AWS_REGION='us-east-1' mocha lib/*.spec.js",
30 | "prestart": "rm -rf package-lock.json && npm install",
31 | "start": "node index.js",
32 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
33 | "build:zip": "zip -rq smart-product-jitr-service.zip . -x template.yml",
34 | "build:dist": "mkdir dist && mv smart-product-jitr-service.zip dist/",
35 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist"
36 | },
37 | "bundledDependencies": [],
38 | "eslintConfig": {
39 | "extends": "strongloop",
40 | "env": {
41 | "node": true,
42 | "mocha": true
43 | },
44 | "parserOptions": {
45 | "ecmaVersion": 8
46 | },
47 | "rules": {
48 | "max-len": [
49 | 2,
50 | 120,
51 | 8
52 | ]
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/source/services/notification/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | console.log('Loading function');
21 | const Logger = require('logger');
22 | let lib = require('./lib/index.js');
23 |
24 | exports.handler = function(event, context, callback) {
25 | Logger.log(Logger.levels.INFO, 'Notification service recieved event:');
26 | Logger.log(Logger.levels.INFO, event);
27 |
28 | lib
29 | .respond(event)
30 | .then(data => {
31 | return callback(null, data);
32 | })
33 | .catch(err => {
34 | return callback(err, null);
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/source/services/notification/lib/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 | const Alert = require('./alert.js');
25 |
26 | const respond = async event => {
27 | const _a = new Alert();
28 | try {
29 | const result = await _a.sendAlert(event);
30 | return Promise.resolve(result);
31 | } catch (err) {
32 | Logger.error(Logger.levels.INFO, err);
33 | Logger.error(
34 | Logger.levels.INFO,
35 | `Error occurred while attempting to send alert notification`
36 | );
37 | return Promise.reject(err);
38 | }
39 | };
40 |
41 | module.exports = {
42 | respond,
43 | };
44 |
--------------------------------------------------------------------------------
/source/services/notification/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-notification-service",
3 | "description": "The notification microservice for the smart product solution",
4 | "main": "index.js",
5 | "version": "0.0.1",
6 | "private": true,
7 | "dependencies": {
8 | "authorizer": "file:../../resources/authorizer/",
9 | "logger": "file:../../resources/logger/",
10 | "moment": "*",
11 | "underscore": "^1.9.1"
12 | },
13 | "devDependencies": {
14 | "aws-sdk": "*",
15 | "aws-sdk-mock": "*",
16 | "chai": "*",
17 | "mocha": "*",
18 | "npm-run-all": "*",
19 | "proxyquire": "*",
20 | "sinon": "*",
21 | "sinon-chai": "*",
22 | "sinon-test": "^2.4.0"
23 | },
24 | "scripts": {
25 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
26 | "test": "env AWS_REGION='test', REGISTRATION_TBL='table1', SETTINGS_TBL='table2', IDP='userPoolID' mocha lib/alert.spec.js",
27 | "prestart": "rm -rf package-lock.json && npm install",
28 | "start": "node index.js",
29 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
30 | "build:zip": "zip -rq smart-product-notification-service.zip . -x template.yml",
31 | "build:dist": "mkdir dist && mv smart-product-notification-service.zip dist/",
32 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist",
33 | "local:depfix": "rm -rf ./node_modules/authorizer && rm -rf ./node_modules/logger && cp -R ../../resources/authorizer ./node_modules/authorizer && cp -R ../../resources/logger ./node_modules/logger",
34 | "local:api": "sam local start-api --env-vars ../tests/env-vars.json"
35 | },
36 | "bundledDependencies": [
37 | "moment",
38 | "underscore",
39 | "colors"
40 | ],
41 | "eslintConfig": {
42 | "extends": "strongloop",
43 | "env": {
44 | "node": true,
45 | "mocha": true
46 | },
47 | "parserOptions": {
48 | "ecmaVersion": 8
49 | },
50 | "rules": {
51 | "max-len": [
52 | 2,
53 | 120,
54 | 8
55 | ]
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/source/services/telemetry/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | const Logger = require('logger');
21 | let lib = require('./lib');
22 |
23 | exports.handler = function(event, context, callback) {
24 | Logger.log(Logger.levels.INFO, 'Telemetry recieved event:');
25 | Logger.log(Logger.levels.INFO, event);
26 |
27 | lib
28 | .process(event)
29 | .then(data => {
30 | return callback(null, data);
31 | })
32 | .catch(err => {
33 | return callback(err, null);
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/source/services/telemetry/lib/index.js:
--------------------------------------------------------------------------------
1 | /*********************************************************************************************************************
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3 | * *
4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5 | * with the License. A copy of the License is located at *
6 | * *
7 | * http://www.apache.org/licenses/LICENSE-2.0 *
8 | * *
9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11 | * and limitations under the License. *
12 | *********************************************************************************************************************/
13 |
14 | /**
15 | * @author Solution Builders
16 | */
17 |
18 | 'use strict';
19 |
20 | /**
21 | * Lib
22 | */
23 | const Logger = require('logger');
24 |
25 | const has = Object.prototype.hasOwnProperty;
26 |
27 | /**
28 | * Convert Fahrenheit of degree to Celsius of degree
29 | * @param {number} temperature
30 | * @returns {number}
31 | */
32 | const fahrenheitToCelsius = temperature => {
33 | return parseFloat((((temperature - 32) * 5) / 9).toFixed(2));
34 | };
35 |
36 | const process = async function(event) {
37 | try {
38 | event.forEach(e => {
39 | // Converting temperature from Fahrenheit to Celsius
40 | if (has.call(e, 'actualTemperature')) {
41 | e.actualTemperatureC = fahrenheitToCelsius(e.actualTemperature);
42 | }
43 | if (has.call(e, 'targetTemperature')) {
44 | e.targetTemperatureC = fahrenheitToCelsius(e.targetTemperature);
45 | }
46 |
47 | // Adding UTC time
48 | if (has.call(e, 'timestamp')) {
49 | let utcTime = new Date(e.timestamp).toISOString();
50 | e.sentAtUtc = utcTime;
51 | e.createdAtUtc = utcTime;
52 | }
53 | });
54 |
55 | return Promise.resolve(event);
56 | } catch (err) {
57 | Logger.error(Logger.levels.INFO, err);
58 | Logger.error(
59 | Logger.levels.INFO,
60 | `Error occurred while transforming the telemetry event.`
61 | );
62 | return Promise.reject(err);
63 | }
64 | };
65 |
66 | module.exports = {
67 | process,
68 | };
69 |
--------------------------------------------------------------------------------
/source/services/telemetry/lib/index.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const expect = require('chai').expect;
4 |
5 | const lib = require('./index.js');
6 | let event = {};
7 |
8 | const successData = [
9 | {
10 | createdAt: '2019-05-01T12:43:07-07:00',
11 | deviceId: '9fd6fd62-0b10-4b5c-a7a1-5995c81db07e',
12 | actualTemperature: 45.75,
13 | targetTemperature: 70,
14 | sentAt: '2019-05-01T12:43:07-07:00',
15 | timestamp: 1556739787113,
16 | actualTemperatureC: 7.64,
17 | targetTemperatureC: 21.11,
18 | sentAtUtc: '2019-05-01T19:43:07.113Z',
19 | createdAtUtc: '2019-05-01T19:43:07.113Z',
20 | },
21 | {
22 | createdAt: '2019-05-01T12:43:17-07:00',
23 | deviceId: '9fd6fd62-0b10-4b5c-a7a1-5995c81db07e',
24 | actualTemperature: 46.5,
25 | targetTemperature: 70,
26 | sentAt: '2019-05-01T12:43:17-07:00',
27 | timestamp: 1556739797117,
28 | actualTemperatureC: 8.06,
29 | targetTemperatureC: 21.11,
30 | sentAtUtc: '2019-05-01T19:43:17.117Z',
31 | createdAtUtc: '2019-05-01T19:43:17.117Z',
32 | },
33 | {
34 | createdAt: '2019-05-01T12:43:27-07:00',
35 | deviceId: '9fd6fd62-0b10-4b5c-a7a1-5995c81db07e',
36 | actualTemperature: 47.25,
37 | targetTemperature: 70,
38 | sentAt: '2019-05-01T12:43:27-07:00',
39 | timestamp: 1556739807117,
40 | actualTemperatureC: 8.47,
41 | targetTemperatureC: 21.11,
42 | sentAtUtc: '2019-05-01T19:43:27.117Z',
43 | createdAtUtc: '2019-05-01T19:43:27.117Z',
44 | },
45 | ];
46 |
47 | describe('Index', () => {
48 | beforeEach(() => {});
49 |
50 | it('should return event messages with Celsius temperature and UTC time if keys exist', (done) => {
51 | event = [
52 | {
53 | createdAt: '2019-05-01T12:43:07-07:00',
54 | deviceId: '9fd6fd62-0b10-4b5c-a7a1-5995c81db07e',
55 | actualTemperature: 45.75,
56 | targetTemperature: 70,
57 | sentAt: '2019-05-01T12:43:07-07:00',
58 | timestamp: 1556739787113,
59 | },
60 | {
61 | createdAt: '2019-05-01T12:43:17-07:00',
62 | deviceId: '9fd6fd62-0b10-4b5c-a7a1-5995c81db07e',
63 | actualTemperature: 46.5,
64 | targetTemperature: 70,
65 | sentAt: '2019-05-01T12:43:17-07:00',
66 | timestamp: 1556739797117,
67 | },
68 | {
69 | createdAt: '2019-05-01T12:43:27-07:00',
70 | deviceId: '9fd6fd62-0b10-4b5c-a7a1-5995c81db07e',
71 | actualTemperature: 47.25,
72 | targetTemperature: 70,
73 | sentAt: '2019-05-01T12:43:27-07:00',
74 | timestamp: 1556739807117,
75 | },
76 | ];
77 |
78 | lib
79 | .process(event)
80 | .then(data => {
81 | expect(data).to.deep.equal(successData);
82 | done();
83 | })
84 | .catch(err => {
85 | done(err);
86 | });
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/source/services/telemetry/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-product-telemetry",
3 | "description": "A Lambda function for the smart product telemetry transformation",
4 | "main": "index.js",
5 | "author": {
6 | "name": "aws-solutions-builder"
7 | },
8 | "version": "0.0.1",
9 | "private": true,
10 | "dependencies": {
11 | "moment": "*",
12 | "logger": "file:../../resources/logger/"
13 | },
14 | "devDependencies": {
15 | "chai": "*",
16 | "sinon": "*",
17 | "sinon-chai": "*",
18 | "mocha": "*",
19 | "npm-run-all": "*",
20 | "proxyquire": "*"
21 | },
22 | "scripts": {
23 | "pretest": "rm -rf package-lock.json && rm -rf node_modules && npm install",
24 | "test": "mocha lib/*.spec.js",
25 | "prestart": "rm -rf package-lock.json && npm install",
26 | "start": "node index.js",
27 | "build:init": "rm -rf package-lock.json && rm -rf dist && rm -rf node_modules",
28 | "build:zip": "zip -rq smart-product-telemetry-service.zip . -x template.yml",
29 | "build:dist": "mkdir dist && mv smart-product-telemetry-service.zip dist/",
30 | "build": "npm run build:init && npm install --production && npm run build:zip && npm run build:dist"
31 | },
32 | "bundledDependencies": [
33 | "moment"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------