├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── documentation.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── closed-issue-message.yml │ ├── handle-stale-discussions.yml │ ├── issue-regression-labeler.yml │ └── stale_issue.yml ├── .gitignore ├── .jscsrc ├── .jshintrc ├── .npmignore ├── CHANGELOG.md ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── browser ├── index.js └── package.json ├── common └── lib │ ├── exceptions.js │ ├── is-undefined.js │ └── tls-reader.js ├── continuous-delivery ├── publish.yml ├── test-version-exists.sh ├── test-version-exists.yml └── update-version.sh ├── device ├── index.js └── lib │ ├── exceptions.js │ ├── tls.js │ └── ws.js ├── examples ├── browser │ ├── lifecycle │ │ ├── aws-configuration.js │ │ ├── example.css │ │ ├── index.html │ │ └── index.js │ ├── mqtt-explorer │ │ ├── aws-configuration.js │ │ ├── example.css │ │ ├── index.html │ │ └── index.js │ ├── mqtt-webpack │ │ ├── aws-configuration.js │ │ ├── entry.js │ │ ├── example.css │ │ ├── index.html │ │ ├── package.json │ │ └── webpack.config.js │ └── temperature-monitor │ │ ├── aws-configuration.js │ │ ├── example.css │ │ ├── index.html │ │ └── index.js ├── device-example.js ├── echo-example.js ├── jobs-agent.js ├── jobs-example.js ├── lib │ ├── cmdline.js │ ├── copy-file.js │ └── download-file.js ├── temperature-control │ ├── package.json │ └── temperature-control.js ├── thing-example.js └── thing-passthrough-example.js ├── gulpfile.js ├── index.js ├── integration-testing ├── integration-tests │ ├── device-integration-test.js │ ├── jobs-integration-test.js │ ├── offline-publishing-test.js │ ├── run-device-integration-test.sh │ ├── run-jobs-integration-test.sh │ ├── run-offline-publishing-test.sh │ ├── run-thing-integration-test.sh │ └── thing-integration-test.js ├── run-integration-tests.sh └── run-tests.sh ├── jobs └── index.js ├── package.json ├── scripts ├── browserize.sh ├── exclude_list.txt └── windows-browserize.bat ├── test ├── data │ ├── README.txt │ ├── certificate.pem.crt │ ├── credentials │ ├── invalid_credentials │ ├── private.pem.key │ └── root-CA.crt ├── device-unit-tests.js ├── jobs-agent-unit-tests.js ├── jobs-unit-tests.js ├── mock │ ├── mockMQTTClient.js │ └── mockTls.js └── thing-unit-tests.js └── thing └── index.js /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | description: Report a bug 4 | title: "(short issue description)" 5 | labels: [bug, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the bug 12 | description: What is the problem? A clear and concise description of the bug. 13 | validations: 14 | required: true 15 | - type: checkboxes 16 | id: regression 17 | attributes: 18 | label: Regression Issue 19 | description: What is a regression? If it worked in a previous version but doesn't in the latest version, it's considered a regression. In this case, please provide specific version number in the report. 20 | options: 21 | - label: Select this option if this issue appears to be a regression. 22 | required: false 23 | - type: textarea 24 | id: expected 25 | attributes: 26 | label: Expected Behavior 27 | description: | 28 | What did you expect to happen? 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: current 33 | attributes: 34 | label: Current Behavior 35 | description: | 36 | What actually happened? 37 | 38 | Please include full errors, uncaught exceptions, stack traces, and relevant logs. 39 | If service responses are relevant, please include wire logs. 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: reproduction 44 | attributes: 45 | label: Reproduction Steps 46 | description: | 47 | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. 48 | For more complex issues provide a repo with the smallest sample that reproduces the bug. 49 | 50 | Avoid including business logic or unrelated code, it makes diagnosis more difficult. 51 | The code sample should be an SSCCE. See http://sscce.org/ for details. In short, please provide a code sample that we can copy/paste, run and reproduce. 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: solution 56 | attributes: 57 | label: Possible Solution 58 | description: | 59 | Suggest a fix/reason for the bug 60 | validations: 61 | required: false 62 | - type: textarea 63 | id: context 64 | attributes: 65 | label: Additional Information/Context 66 | description: | 67 | Anything else that might be relevant for troubleshooting this bug. Providing context helps us come up with a solution that is most useful in the real world. 68 | validations: 69 | required: false 70 | - type: input 71 | id: sdk-version 72 | attributes: 73 | label: SDK version used 74 | validations: 75 | required: true 76 | - type: input 77 | id: environment 78 | attributes: 79 | label: Environment details (OS name and version, etc.) 80 | validations: 81 | required: true 82 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: 💬 General Question 5 | url: https://github.com/aws/aws-iot-device-sdk-js/discussions/categories/q-a 6 | about: Please ask and answer questions as a discussion thread 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | name: "📕 Documentation Issue" 2 | description: Report an issue in the API Reference documentation or Developer Guide 3 | title: "(short issue description)" 4 | labels: [documentation, needs-triage] 5 | assignees: [] 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: Describe the issue 11 | description: A clear and concise description of the issue. 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: links 17 | attributes: 18 | label: Links 19 | description: | 20 | Include links to affected documentation page(s). 21 | validations: 22 | required: true 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | description: Suggest an idea for this project 4 | title: "(short issue description)" 5 | labels: [feature-request, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the feature 12 | description: A clear and concise description of the feature you are proposing. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: use-case 17 | attributes: 18 | label: Use Case 19 | description: | 20 | Why do you need this feature? For example: "I'm always frustrated when..." 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: solution 25 | attributes: 26 | label: Proposed Solution 27 | description: | 28 | Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. 29 | validations: 30 | required: false 31 | - type: textarea 32 | id: other 33 | attributes: 34 | label: Other Information 35 | description: | 36 | Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. 37 | validations: 38 | required: false 39 | - type: checkboxes 40 | id: ack 41 | attributes: 42 | label: Acknowledgements 43 | options: 44 | - label: I may be able to implement this feature request 45 | required: false 46 | - label: This feature might incur a breaking change 47 | required: false 48 | - type: input 49 | id: sdk-version 50 | attributes: 51 | label: SDK version used 52 | validations: 53 | required: true 54 | - type: input 55 | id: environment 56 | attributes: 57 | label: Environment details (OS name and version, etc.) 58 | validations: 59 | required: true 60 | -------------------------------------------------------------------------------- /.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 my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - '!main' 8 | 9 | env: 10 | RUN: ${{ github.run_id }}-${{ github.run_number }} 11 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 12 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 13 | PACKAGE_NAME: aws-iot-device-sdk-js 14 | AWS_EC2_METADATA_DISABLED: true 15 | 16 | jobs: 17 | unit-tests: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [10.x] 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Unit Tests - Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v2 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm install 31 | - run: npm test 32 | 33 | integration-tests: 34 | runs-on: ubuntu-latest 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | node-version: [10.x] 39 | test-type: [websocket, certificate, custom-auth] 40 | 41 | steps: 42 | - uses: actions/checkout@v2 43 | - name: ${{ matrix.test-type }} Integration Tests - Node.js ${{ matrix.node-version }} 44 | uses: actions/setup-node@v2 45 | with: 46 | node-version: ${{ matrix.node-version }} 47 | - run: cd integration-testing && ./run-tests.sh ${{ matrix.test-type }} 48 | -------------------------------------------------------------------------------- /.github/workflows/closed-issue-message.yml: -------------------------------------------------------------------------------- 1 | name: Closed Issue Message 2 | on: 3 | issues: 4 | types: [closed] 5 | jobs: 6 | auto_comment: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | steps: 11 | - uses: aws-actions/closed-issue-message@v1 12 | with: 13 | # These inputs are both required 14 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 15 | message: | 16 | This issue is now closed. Comments on closed issues are hard for our team to see. 17 | If you need more assistance, please open a new issue that references this one. 18 | -------------------------------------------------------------------------------- /.github/workflows/handle-stale-discussions.yml: -------------------------------------------------------------------------------- 1 | name: HandleStaleDiscussions 2 | on: 3 | schedule: 4 | - cron: '0 */4 * * *' 5 | discussion_comment: 6 | types: [created] 7 | 8 | jobs: 9 | handle-stale-discussions: 10 | name: Handle stale discussions 11 | runs-on: ubuntu-latest 12 | permissions: 13 | discussions: write 14 | steps: 15 | - name: Stale discussions action 16 | uses: aws-github-ops/handle-stale-discussions@v1 17 | env: 18 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /.github/workflows/issue-regression-labeler.yml: -------------------------------------------------------------------------------- 1 | # Apply potential regression label on issues 2 | name: issue-regression-label 3 | on: 4 | issues: 5 | types: [opened, edited] 6 | jobs: 7 | add-regression-label: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | steps: 12 | - name: Fetch template body 13 | id: check_regression 14 | uses: actions/github-script@v7 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | TEMPLATE_BODY: ${{ github.event.issue.body }} 18 | with: 19 | script: | 20 | const regressionPattern = /\[x\] Select this option if this issue appears to be a regression\./i; 21 | const template = `${process.env.TEMPLATE_BODY}` 22 | const match = regressionPattern.test(template); 23 | core.setOutput('is_regression', match); 24 | - name: Manage regression label 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: | 28 | if [ "${{ steps.check_regression.outputs.is_regression }}" == "true" ]; then 29 | gh issue edit ${{ github.event.issue.number }} --add-label "potential-regression" -R ${{ github.repository }} 30 | else 31 | gh issue edit ${{ github.event.issue.number }} --remove-label "potential-regression" -R ${{ github.repository }} 32 | fi 33 | -------------------------------------------------------------------------------- /.github/workflows/stale_issue.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | # Controls when the action will run. 4 | on: 5 | schedule: 6 | - cron: "*/60 * * * *" 7 | 8 | jobs: 9 | cleanup: 10 | runs-on: ubuntu-latest 11 | name: Stale issue job 12 | steps: 13 | - uses: aws-actions/stale-issue-cleanup@v3 14 | with: 15 | # Setting messages to an empty string will cause the automation to skip 16 | # that category 17 | ancient-issue-message: Greetings! Sorry to say but this is a very old issue that is probably not getting as much attention as it deserves. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to open a new one. 18 | stale-issue-message: Greetings! It looks like this issue hasn’t been active in a few days. We encourage you to check if this is still an issue in the latest release. Because it has been a few days since the last update on this, and in the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please feel free to provide a comment or add an upvote to prevent automatic closure, or if the issue is already closed, please feel free to open a new one. 19 | stale-pr-message: Greetings! It looks like this PR hasn’t been active in a few days, add a comment or an upvote to prevent automatic closure, or if the issue is already closed, please feel free to open a new one. 20 | 21 | # These labels are required 22 | stale-issue-label: closing-soon 23 | exempt-issue-label: automation-exempt 24 | stale-pr-label: closing-soon 25 | exempt-pr-label: pr/needs-review 26 | response-requested-label: response-requested 27 | 28 | # Don't set closed-for-staleness label to skip closing very old issues 29 | # regardless of label 30 | closed-for-staleness-label: closed-for-staleness 31 | 32 | # Issue timing 33 | days-before-stale: 10 34 | days-before-close: 4 35 | days-before-ancient: 36500 36 | 37 | # If you don't want to mark a issue as being ancient based on a 38 | # threshold of "upvotes", you can set this here. An "upvote" is 39 | # the total number of +1, heart, hooray, and rocket reactions 40 | # on an issue. 41 | minimum-upvotes-to-exempt: 1 42 | 43 | repo-token: ${{ secrets.GITHUB_TOKEN }} 44 | loglevel: DEBUG 45 | # Set dry-run to true to not perform label or close actions. 46 | dry-run: false 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # npm subdirectories # 2 | ###################### 3 | node_modules 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | .DS_Store? 9 | ._* 10 | .Spotlight-V100 11 | .Trashes 12 | 13 | # editor save files # 14 | ###################### 15 | .*.swp 16 | 17 | # unit test/reports # 18 | ###################### 19 | .coverdata 20 | .coverrun 21 | reports 22 | 23 | # debugging # 24 | ###################### 25 | debug 26 | 27 | # browser bundles # 28 | ###################### 29 | *bundle.js 30 | 31 | # misc # 32 | .npmrc 33 | *.tgz 34 | .idea 35 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowAnonymousFunctions": null, 3 | "disallowCapitalizedComments": null, 4 | "disallowCommaBeforeLineBreak": null, 5 | "disallowDanglingUnderscores": null, 6 | "disallowEmptyBlocks": true, 7 | "disallowFunctionDeclarations": null, 8 | "disallowImplicitTypeConversion": null, 9 | "disallowKeywordsOnNewLine": null, 10 | "disallowKeywords": ["with"], 11 | "disallowMixedSpacesAndTabs": null, 12 | "disallowMultipleLineBreaks": true, 13 | "disallowMultipleLineStrings": true, 14 | "disallowMultipleVarDecl": null, 15 | "disallowNewlineBeforeBlockStatements": null, 16 | "disallowOperatorBeforeLineBreak": ["."], 17 | "disallowPaddingNewlinesBeforeKeywords": null, 18 | "disallowPaddingNewlinesInBlocks": null, 19 | "disallowPaddingNewLinesInObjects": null, 20 | "disallowQuotedKeysInObjects": null, 21 | "disallowSemicolons": null, 22 | "disallowSpaceAfterBinaryOperators": null, 23 | "disallowSpaceAfterKeywords": null, 24 | "disallowSpaceAfterLineComment": null, 25 | "disallowSpaceAfterObjectKeys": null, 26 | "disallowSpaceAfterPrefixUnaryOperators": null, 27 | "disallowSpaceBeforeBinaryOperators": null, 28 | "disallowSpaceBeforeBlockStatements": null, 29 | "disallowSpaceBeforeKeywords": null, 30 | "disallowSpaceBeforeObjectValues": null, 31 | "disallowSpaceBeforePostfixUnaryOperators": null, 32 | "disallowSpaceBetweenArguments": null, 33 | "disallowSpacesInAnonymousFunctionExpression": { 34 | "beforeOpeningRoundBrace": true 35 | }, 36 | "disallowSpacesInCallExpression": null, 37 | "disallowSpacesInConditionalExpression": null, 38 | "disallowSpacesInForStatement": null, 39 | "disallowSpacesInFunctionDeclaration": null, 40 | "disallowSpacesInFunctionExpression": null, 41 | "disallowSpacesInFunction": null, 42 | "disallowSpacesInNamedFunctionExpression": null, 43 | "disallowSpacesInsideArrayBrackets": null, 44 | "disallowSpacesInsideObjectBrackets": null, 45 | "disallowSpacesInsideObjectBrackets": null, 46 | "disallowSpacesInsideParentheses": null, 47 | "disallowTrailingComma": true, 48 | "disallowTrailingWhitespace": null, 49 | "disallowYodaConditions": true, 50 | "maximumLineLength": { 51 | "value": 140, 52 | "allowRegex": true 53 | }, 54 | "requireAlignedObjectValues": null, 55 | "requireAnonymousFunctions": null, 56 | "requireBlocksOnNewline": 1, 57 | "requireCamelCaseOrUpperCaseIdentifiers": true, 58 | "requireCapitalizedComments": null, 59 | "requireCapitalizedConstructors": true, 60 | "requireCommaBeforeLineBreak": true, 61 | "requireCurlyBraces": [ 62 | "if", 63 | "else", 64 | "for", 65 | "while", 66 | "do", 67 | "try", 68 | "catch" 69 | ], 70 | "requireDotNotation": "except_snake_case", 71 | "requireFunctionDeclarations": null, 72 | "requireKeywordsOnNewLine": null, 73 | "requireLineBreakAfterVariableAssignment": null, 74 | "requireLineFeedAtFileEnd": true, 75 | "requireMultipleVarDecl": null, 76 | "requireNewlineBeforeBlockStatements": null, 77 | "requireOperatorBeforeLineBreak": true, 78 | "requirePaddingNewlinesBeforeKeywords": null, 79 | "requirePaddingNewlinesInBlocks": null, 80 | "requirePaddingNewLinesInObjects": null, 81 | "requireParenthesesAroundIIFE": true, 82 | "requireQuotedKeysInObjects": null, 83 | "requireSpaceAfterBinaryOperators": null, 84 | "requireSpaceAfterKeywords": [ 85 | "if", 86 | "for", 87 | "while", 88 | "do", 89 | "switch", 90 | "return", 91 | "try" 92 | ], 93 | "requireSpaceAfterLineComment": null, 94 | "requireSpaceAfterObjectKeys": null, 95 | "requireSpaceAfterPrefixUnaryOperators": null, 96 | "requireSpaceBeforeBinaryOperators": null, 97 | "requireSpaceBeforeBlockStatements": null, 98 | "requireSpaceBeforeKeywords": [ 99 | "else", "while", "catch" 100 | ], 101 | "requireSpaceBeforeObjectValues": null, 102 | "requireSpaceBeforePostfixUnaryOperators": null, 103 | "requireSpaceBetweenArguments": null, 104 | "requireSpacesInAnonymousFunctionExpression": null, 105 | "requireSpacesInCallExpression": null, 106 | "requireSpacesInConditionalExpression": null, 107 | "requireSpacesInForStatement": null, 108 | "requireSpacesInFunctionDeclaration": { 109 | "beforeOpeningCurlyBrace": true 110 | }, 111 | "requireSpacesInFunctionExpression": { 112 | "beforeOpeningCurlyBrace": true 113 | }, 114 | "requireSpacesInFunction": null, 115 | "requireSpacesInNamedFunctionExpression": null, 116 | "requireSpacesInsideArrayBrackets": null, 117 | "requireSpacesInsideObjectBrackets": null, 118 | "requireSpacesInsideParentheses": null, 119 | "requireTrailingComma": null, 120 | "requireYodaConditions": null, 121 | "safeContextKeyword": null, 122 | "validateJSDoc": { 123 | "checkParamNames": true, 124 | "requireParamTypes": true 125 | }, 126 | "validateLineBreaks": "LF", 127 | "validateParameterSeparator": ", ", 128 | "validateQuoteMarks": { "mark": "'", "escape": true } 129 | } 130 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 100, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : false, // true: Identifiers must be in camelCase 10 | "curly" : true, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : false, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "latedef" : false, // true: Require variables/functions to be defined before being used 16 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 17 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 18 | "noempty" : true, // true: Prohibit use of empty blocks 19 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 20 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 21 | "plusplus" : false, // true: Prohibit use of `++` & `--` 22 | "quotmark" : false, // Quotation mark consistency: 23 | // false : do nothing (default) 24 | // true : ensure whatever is used is consistent 25 | // "single" : require single quotes 26 | // "double" : require double quotes 27 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 28 | "unused" : "vars", // Unused variables: 29 | // true : all variables, last function parameter 30 | // "vars" : all variables only 31 | // "strict" : all variables, all function parameters 32 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode 33 | "maxparams" : false, // {int} Max number of formal params allowed per function 34 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 35 | "maxstatements" : false, // {int} Max number statements per function 36 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 37 | "maxlen" : false, // {int} Max number of characters per line 38 | 39 | // Relaxing 40 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 41 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 42 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 43 | "eqnull" : false, // true: Tolerate use of `== null` 44 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 45 | "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) 46 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 47 | // (ex: `for each`, multiple try/catch, function expression…) 48 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 49 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 50 | "funcscope" : false, // true: Tolerate defining variables inside control statements 51 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 52 | "iterator" : false, // true: Tolerate using the `__iterator__` property 53 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 54 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 55 | "laxcomma" : false, // true: Tolerate comma-first style coding 56 | "loopfunc" : false, // true: Tolerate functions being defined in loops 57 | "multistr" : false, // true: Tolerate multi-line strings 58 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 59 | "notypeof" : false, // true: Tolerate invalid typeof operator values 60 | "proto" : false, // true: Tolerate using the `__proto__` property 61 | "scripturl" : false, // true: Tolerate script-targeted URLs 62 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 63 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 64 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 65 | "validthis" : false, // true: Tolerate using this in a non-constructor function 66 | 67 | // Environments 68 | "browser" : true, // Web Browser (window, document, etc) 69 | "browserify" : false, // Browserify (node.js code in the browser) 70 | "couch" : false, // CouchDB 71 | "devel" : true, // Development/debugging (alert, confirm, etc) 72 | "dojo" : false, // Dojo Toolkit 73 | "jasmine" : false, // Jasmine 74 | "jquery" : false, // jQuery 75 | "mocha" : true, // Mocha 76 | "mootools" : false, // MooTools 77 | "node" : true, // Node.js 78 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 79 | "phantom" : false, // PhantomJS 80 | "prototypejs" : false, // Prototype and Scriptaculous 81 | "qunit" : false, // QUnit 82 | "rhino" : false, // Rhino 83 | "shelljs" : false, // ShellJS 84 | "typed" : false, // Globals for typed array constructions 85 | "worker" : false, // Web Workers 86 | "wsh" : false, // Windows Scripting Host 87 | "yui" : false, // Yahoo User Interface 88 | 89 | // Custom Globals 90 | "globals" : {} // additional predefined global variables 91 | } 92 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | debug 3 | .coverdata 4 | .coverrun 5 | .git 6 | .github 7 | continuous-delivery 8 | integration-testing 9 | test 10 | reports 11 | .npmrc 12 | .idea 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.2.12](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.12) (July 30 2021) 2 | 3 | Bugfixes/Improvements 4 | - Updated minimist version 5 | 6 | ## [2.2.11](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.11) (July 30 2021) 7 | 8 | Bugfixes/Improvements 9 | - Added examples back into npm package 10 | 11 | ## [2.2.10](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.10) (July 9 2021) 12 | 13 | Bugfixes/Improvements 14 | - Merged support for custom auth connections via query parameters 15 | - Bound/propagated the mqtt-js 'end' event 16 | - Unit test reliability and wording updates 17 | 18 | ## [2.2.9](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.9) (July 8, 2021) 19 | 20 | Bugfixes/Improvements 21 | - Updated crypto-js version 22 | - Updated mqtt-js version 23 | - Removed reserved topic checks from subscribe calls 24 | 25 | ## [2.2.8](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.8) (May 20, 2021) 26 | 27 | Bugfixes/Improvements 28 | - Updated mqtt dependency to latest version 29 | 30 | ## [2.2.7](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.7) (Sep 15, 2020) 31 | 32 | Bugfixes/Improvements 33 | - send SNI string while connecting to AWS IoT 34 | - README warns about MacOS storing certificate in keychain 35 | 36 | ## [2.2.6](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.6) (May 18, 2020) 37 | 38 | Bugfixes/Improvements 39 | - Require only the necessary modules from crypto-js to optimize bundle 40 | 41 | ## [2.2.5](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.5) (Apr 7, 2020) 42 | 43 | Bugfixes/Improvements 44 | - Re-adding github related templates and readme change. 45 | 46 | ## [2.2.4](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.4) (Mar 31, 2020) 47 | 48 | Bugfixes/Improvements 49 | - Bumping minimist version to 1.2.5 to address security issue 50 | 51 | ## [2.2.3](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.3) (Jan 20, 2020) 52 | 53 | Bugfixes/Improvements 54 | - Upgrade MQTT.js to 3.0.0 to incorporate important bug fixes 55 | 56 | ## [2.2.2](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.2) (Sep 24, 2019) 57 | 58 | Bugfixes/Improvements 59 | - Reorder timeout state cleanup in order to support invoking update from a timeout callback 60 | 61 | ## [2.2.1](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.1) (Jan 24, 2018) 62 | 63 | Bugfixes/Improvements 64 | - Upgrade MQTT.js to 2.15.1 to address security issue 65 | 66 | ## [2.2.0](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.2.0) (Nov 29, 2017) 67 | 68 | Features 69 | - Added AWS IoT Job API 70 | - Added options to enable AWS IoT Custom Auth 71 | - Added options to enalbe/disable AWS IoT metrics collection 72 | - Added new API to support packetsend and packetreceive events 73 | 74 | Bugfixes/Improvements 75 | - Modify Keepalive defaults to 300 secs to maintain consistency across SDKs 76 | - Expose shadow version from raw json object 77 | - Added samples to demonstrate AWS IoT Job API 78 | - Disabled MQTT.js default resubscribe. 79 | 80 | ## [2.1.0](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.0.1) (Sep 28, 2017) 81 | 82 | Features 83 | - Update MQTT.js to 2.13.0. [MQTT.js](https://github.com/mqttjs/MQTT.js/releases/tag/v2.13.0) 84 | 85 | Bugfixes/Improvements 86 | - Propagated 'error' from 'close' event. [#131](https://github.com/aws/aws-iot-device-sdk-js/pull/131) 87 | - Fixed method of handleMessage to be overridden rather than pass-through. [#129](https://github.com/aws/aws-iot-device-sdk-js/pull/129) 88 | - Pass 'connack' parameter in 'connect' event [#99](https://github.com/aws/aws-iot-device-sdk-js/pull/99) 89 | - Update iot service name to 'iotdevicegateway' 90 | 91 | ## [2.0.1](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.0.1) (Jul 2, 2017) 92 | 93 | Bugfixes/Improvements 94 | - Removed validation against .com in websocket connection. 95 | 96 | ## [2.0.0](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v2.0.0) (Mar 21, 2017) 97 | 98 | API Changes 99 | - Deprecated region option(-g) in device configuration. 100 | - Added host endpoint option(-H) to connect to custom host endpoint 101 | 102 | Features 103 | - Added support for browserify on Windows CMD. [#74](https://github.com/aws/aws-iot-device-sdk-js/issues/74) 104 | - Added support for loading IAM credentials from aws credential files. 105 | - Added sample for using Node.js SDK with webpack. 106 | 107 | Bugfixes/Improvements 108 | - Fixed README.md typo [#101](https://github.com/aws/aws-iot-device-sdk-js/issues/101) 109 | - Fixed thing.register() API to have independent optional parameters.[#106](https://github.com/aws/aws-iot-device-sdk-js/issues/106) 110 | - Upgrade MQTT.js to v2.2.1 and gulp dependencies. 111 | - Fixed npm test failure in node version above 4. 112 | 113 | ## [1.0.14](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.14) (Dec 7, 2016) 114 | 115 | Bugfixes/Improvements 116 | - Fixes for GitHub issues [#67]( https://github.com/aws/aws-iot-device-sdk-js/issues/67), [#95](https://github.com/aws/aws-iot-device-sdk-js/issues/95), [#96](https://github.com/aws/aws-iot-device-sdk-js/issues/96). 117 | 118 | ## [1.0.13](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.13) (July 11, 2016) 119 | 120 | Bugfixes/Improvements 121 | - Addressed pull request[#83](https://github.com/aws/aws-iot-device-sdk-js/pull/83) Credits given to [Torsph](https://github.com/Torsph) 122 | - Updated lifecycle events browser demo to read from DynamoDB table of connected clients if available 123 | - Addresses pull request [#60](https://github.com/aws/aws-iot-device-sdk-js/pull/60) 124 | - Fixes for GitHub issues [#66](https://github.com/aws/aws-iot-device-sdk-js/issues/66), [#61](https://github.com/aws/aws-iot-device-sdk-js/issues/61), [#53](https://github.com/aws/aws-iot-device-sdk-js/issues/53), [#48](https://github.com/aws/aws-iot-device-sdk-js/issues/48), and [#44](https://github.com/aws/aws-iot-device-sdk-js/issues/44). 125 | 126 | ## [1.0.12](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.12) (April 19, 2016) 127 | 128 | Features 129 | - Added support for use in browser applications 130 | 131 | Bugfixes/Improvements 132 | - Incorporated GitHub pull request [#49](https://github.com/aws/aws-iot-device-sdk-js/pull/49) 133 | - Fixes for GitHub issues [#41](https://github.com/aws/aws-iot-device-sdk-js/issues/41), [#47](https://github.com/aws/aws-iot-device-sdk-js/issues/47), and [#50](https://github.com/aws/aws-iot-device-sdk-js/issues/50). 134 | 135 | ## [1.0.11](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.11) (March 4, 2016) 136 | 137 | Features: 138 | - Configurable exponential backoff retries after connection loss 139 | - Configurable offline publish message queueing 140 | - Added option for automatic re-subscription after reconnect 141 | - Added shadow option for versioning disable 142 | - Added session token support 143 | 144 | Bugfixes/Improvements 145 | - Incorporated github pull requests [#33](https://github.com/aws/aws-iot-device-sdk-js/pull/33), [#34](https://github.com/aws/aws-iot-device-sdk-js/pull/34), and [#39](https://github.com/aws/aws-iot-device-sdk-js/pull/39) 146 | - Fixes for github issue [#36](https://github.com/aws/aws-iot-device-sdk-js/issues/36) 147 | - Updated unit tests 148 | - Updated documentation 149 | 150 | ## [1.0.10](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.10) (January 28, 2016) 151 | 152 | Features: 153 | - Added support for WebSocket connections to AWS IoT 154 | 155 | Bugfixes/Improvements 156 | - Incorporated github pull requests [#28](https://github.com/aws/aws-iot-device-sdk-js/pull/28) and [#29](https://github.com/aws/aws-iot-device-sdk-js/pull/29) 157 | - Fixes for github issues [#30](https://github.com/aws/aws-iot-device-sdk-js/issues/30) 158 | - Added unit tests to release 159 | - Updated documentation 160 | 161 | ## [1.0.7](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.7) (October 30, 2015) 162 | 163 | Bugfixes/Improvements: 164 | - Incorporated github pull requests [#7](https://github.com/aws/aws-iot-device-sdk-js/pull/7), [#9](https://github.com/aws/aws-iot-device-sdk-js/pull/9), and [#14.](https://github.com/aws/aws-iot-device-sdk-js/pull/14) 165 | - Fixes for github issues [#8](https://github.com/aws/aws-iot-device-sdk-js/issues/8) and [#16.](https://github.com/aws/aws-iot-device-sdk-js/issues/16) 166 | - Updated documentation 167 | - JSHint cleanup 168 | 169 | ## [1.0.6](https://github.com/aws/aws-iot-device-sdk-js/releases/tag/v1.0.6) (October 14, 2015) 170 | 171 | Features: 172 | - Added support for AWS Console JSON configuration in example programs 173 | - Added thing-passthrough-example.js example program 174 | 175 | Bugfixes/Improvements: 176 | - Fixes for github issues [#4](https://github.com/aws/aws-iot-device-sdk-js/issues/4), [#5](https://github.com/aws/aws-iot-device-sdk-js/issues/5), and [#6.](https://github.com/aws/aws-iot-device-sdk-js/issues/4) 177 | - Updated documentation 178 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | AWS IoT JavaScript SDK for Embedded Devices 2 | Copyright 2012-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /browser/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | var AWS = require('aws-sdk'); 16 | var AWSIoTData = require('aws-iot-device-sdk'); 17 | 18 | module.exports.AWS = AWS; 19 | module.exports.AWSIoTData = AWSIoTData; 20 | -------------------------------------------------------------------------------- /browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-iot-sdk-browser-bundle", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"bundle exists\" && exit 0" 8 | }, 9 | "author": "", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "aws-iot-device-sdk": "^1.0.11", 13 | "aws-sdk": "^2.3.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/lib/exceptions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | 22 | //begin module 23 | module.exports = { 24 | NO_KEY_OPTION: 'No "keyPath" or "privateKey" option supplied.', 25 | NO_CERT_OPTION: 'No "certPath" or "clientCert" option supplied.', 26 | NO_CA_OPTION: 'No "caPath" or "caCert" option supplied.', 27 | INVALID_KEY_PATH_OPTION: 'Invalid "keyPath" option supplied.', 28 | INVALID_CERT_PATH_OPTION: 'Invalid "certPath" option supplied.', 29 | INVALID_CA_PATH_OPTION: 'Invalid "caPath" option supplied.', 30 | INVALID_CLIENT_CERT_OPTION: 'Invalid "clientCert" option supplied.', 31 | INVALID_PRIVATE_KEY_OPTION: 'Invalid "privateKey" option supplied.', 32 | INVALID_CA_CERT_OPTION: 'Invalid "caCert" option supplied.' 33 | }; 34 | -------------------------------------------------------------------------------- /common/lib/is-undefined.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | 22 | //begin module 23 | /** 24 | * This is the exposed module. 25 | * This method determines if an object is undefined. 26 | * 27 | * @param {Object} value 28 | * @access public 29 | */ 30 | module.exports = function(value) { 31 | return typeof value === 'undefined' || value === null; 32 | }; 33 | -------------------------------------------------------------------------------- /common/lib/tls-reader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | var filesys = require('fs'); 18 | 19 | //npm deps 20 | 21 | //app deps 22 | var isUndefined = require('./is-undefined'); 23 | var exceptions = require('./exceptions'); 24 | 25 | //begin module 26 | /** 27 | * This method is the exposed module; it validates and prepares the tls 28 | * options as required for connection to the AWS IoT service. 29 | * 30 | * @param {Object} options 31 | * @access public 32 | */ 33 | module.exports = function(options) { 34 | 35 | // verify certificate paths 36 | if (isUndefined(options.keyPath) && isUndefined(options.privateKey)) { 37 | throw new Error(exceptions.NO_KEY_OPTION); 38 | } 39 | if (isUndefined(options.certPath) && isUndefined(options.clientCert)) { 40 | throw new Error(exceptions.NO_CERT_OPTION); 41 | } 42 | if (isUndefined(options.caPath) && isUndefined(options.caCert)) { 43 | throw new Error(exceptions.NO_CA_OPTION); 44 | } 45 | // 46 | // Certificates and private keys may be passed in files using options 47 | // ending in 'Path', e.g. 'keyPath', 'certPath', and 'caPath'. In addition, 48 | // they can also be passed in as buffers or files using the options 49 | // 'privateKey', 'clientCert', and 'caCert'. This second set is the one 50 | // that the AWS Console generates a JSON configuration document for. 51 | // 52 | if (!isUndefined(options.caCert)) { 53 | if (Buffer.isBuffer(options.caCert)) { 54 | options.ca = options.caCert; 55 | } else { 56 | if (filesys.existsSync(options.caCert)) { 57 | options.ca = filesys.readFileSync(options.caCert); 58 | } else { 59 | throw new Error(exceptions.INVALID_CA_CERT_OPTION); 60 | } 61 | } 62 | } 63 | if (!isUndefined(options.privateKey)) { 64 | if (Buffer.isBuffer(options.privateKey)) { 65 | options.key = options.privateKey; 66 | } else { 67 | if (filesys.existsSync(options.privateKey)) { 68 | options.key = filesys.readFileSync(options.privateKey); 69 | } else { 70 | throw new Error(exceptions.INVALID_PRIVATE_KEY_OPTION); 71 | } 72 | } 73 | } 74 | if (!isUndefined(options.clientCert)) { 75 | if (Buffer.isBuffer(options.clientCert)) { 76 | options.cert = options.clientCert; 77 | } else { 78 | if (filesys.existsSync(options.clientCert)) { 79 | options.cert = filesys.readFileSync(options.clientCert); 80 | } else { 81 | throw new Error(exceptions.INVALID_CLIENT_CERT_OPTION); 82 | } 83 | } 84 | } 85 | 86 | // Parse PEM files. Options ending in 'Path' must be files 87 | // and will override options which do not end in 'Path'. 88 | 89 | if (filesys.existsSync(options.keyPath)) { 90 | options.key = filesys.readFileSync(options.keyPath); 91 | } else if (!isUndefined(options.keyPath)) { 92 | throw new Error(exceptions.INVALID_KEY_PATH_OPTION); 93 | } 94 | if (filesys.existsSync(options.certPath)) { 95 | options.cert = filesys.readFileSync(options.certPath); 96 | } else if (!isUndefined(options.certPath)) { 97 | throw new Error(exceptions.INVALID_CERT_PATH_OPTION); 98 | } 99 | if (filesys.existsSync(options.caPath)) { 100 | options.ca = filesys.readFileSync(options.caPath); 101 | } else if (!isUndefined(options.caPath)) { 102 | throw new Error(exceptions.INVALID_CA_PATH_OPTION); 103 | } 104 | 105 | // request certificate from partner 106 | options.requestCert = true; 107 | 108 | // require certificate authentication 109 | options.rejectUnauthorized = true; 110 | 111 | }; 112 | -------------------------------------------------------------------------------- /continuous-delivery/publish.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | phases: 3 | install: 4 | runtime-versions: 5 | nodejs: 10 6 | pre_build: 7 | commands: 8 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-js 9 | - bash ./continuous-delivery/update-version.sh 10 | build: 11 | commands: 12 | - aws secretsmanager get-secret-value --secret-id V1JavascriptNpmAuthToken --region us-east-1 | jq -r .SecretString > .npmrc 13 | - npm install 14 | - npm pack 15 | - npm --userconfig ./.npmrc publish aws-iot-device-sdk-*.tgz 16 | -------------------------------------------------------------------------------- /continuous-delivery/test-version-exists.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # force a failure if there's no tag 5 | git describe --tags 6 | # now get the tag 7 | CURRENT_TAG=$(git describe --tags | cut -f2 -dv) 8 | # convert v0.2.12-2-g50254a9 to 0.2.12 9 | CURRENT_TAG_VERSION=$(git describe --tags | cut -f1 -d'-' | cut -f2 -dv) 10 | # if there's a hash on the tag, then this is not a release tagged commit 11 | if [ "$CURRENT_TAG" != "$CURRENT_TAG_VERSION" ]; then 12 | echo "Current tag version is not a release tag, cut a new release if you want to publish." 13 | exit 1 14 | fi 15 | 16 | PUBLISHED_TAG_VERSION=`npm show aws-iot-device-sdk version` 17 | if [ "$PUBLISHED_TAG_VERSION" == "$CURRENT_TAG_VERSION" ]; then 18 | echo "$CURRENT_TAG_VERSION is already in npm, cut a new tag if you want to upload another version." 19 | exit 1 20 | fi 21 | 22 | echo "$CURRENT_TAG_VERSION currently does not exist in npm, allowing pipeline to continue." 23 | exit 0 24 | -------------------------------------------------------------------------------- /continuous-delivery/test-version-exists.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | phases: 3 | install: 4 | runtime-versions: 5 | nodejs: 10 6 | build: 7 | commands: 8 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-js 9 | - bash ./continuous-delivery/test-version-exists.sh 10 | 11 | -------------------------------------------------------------------------------- /continuous-delivery/update-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # force a failure if there's no tag 5 | git describe --tags 6 | # now get the tag 7 | CURRENT_TAG=$(git describe --tags | cut -f2 -dv) 8 | # convert v0.2.12-2-g50254a9 to 0.2.12 9 | CURRENT_TAG_VERSION=$(git describe --tags | cut -f1 -d'-' | cut -f2 -dv) 10 | # if there's a hash on the tag, then this is not a release tagged commit 11 | if [ "$CURRENT_TAG" != "$CURRENT_TAG_VERSION" ]; then 12 | echo "Current tag version is not a release tag, cut a new release if you want to publish." 13 | exit 1 14 | fi 15 | 16 | sed --in-place -E "s/\"version\": \".+\"/\"version\": \"${CURRENT_TAG_VERSION}\"/" package.json 17 | 18 | exit 0 19 | -------------------------------------------------------------------------------- /device/lib/exceptions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | 22 | //begin module 23 | module.exports = { 24 | INVALID_CONNECT_OPTIONS: 'Invalid connect options supplied.', 25 | INVALID_CLIENT_ID_OPTION: 'Invalid "clientId" (mqtt client id) option supplied.', 26 | INVALID_RECONNECT_TIMING: 'Invalid reconnect timing options supplied.', 27 | INVALID_OFFLINE_QUEUEING_PARAMETERS: 'Invalid offline queueing options supplied.' 28 | }; 29 | -------------------------------------------------------------------------------- /device/lib/tls.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | var tls = require('tls'); 18 | 19 | //npm deps 20 | 21 | //app deps 22 | 23 | function buildBuilder(mqttClient, opts) { 24 | var connection; 25 | 26 | connection = tls.connect(opts); 27 | 28 | function handleTLSerrors(err) { 29 | mqttClient.emit('error', err); 30 | connection.end(); 31 | } 32 | 33 | connection.on('secureConnect', function() { 34 | if (!connection.authorized) { 35 | connection.emit('error', new Error('TLS not authorized')); 36 | } else { 37 | connection.removeListener('error', handleTLSerrors); 38 | } 39 | }); 40 | 41 | connection.on('error', handleTLSerrors); 42 | return connection; 43 | } 44 | 45 | module.exports = buildBuilder; 46 | -------------------------------------------------------------------------------- /device/lib/ws.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | var websocket = require('@httptoolkit/websocket-stream'); 20 | 21 | //app deps 22 | 23 | function buildBuilder(client, opts) { 24 | return websocket(opts.url, ['mqttv3.1'], opts.websocketOptions); 25 | } 26 | 27 | module.exports = buildBuilder; 28 | -------------------------------------------------------------------------------- /examples/browser/lifecycle/aws-configuration.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | /* 17 | * NOTE: You must set the following string constants prior to running this 18 | * example application. 19 | */ 20 | var awsConfiguration = { 21 | poolId: YOUR_COGNITO_IDENTITY_POOL_ID_GOES_HERE, // 'YourCognitoIdentityPoolId' 22 | host: YOUR_AWS_IOT_ENDPOINT_GOES_HERE, // 'YourAWSIoTEndpoint', e.g. 'prefix.iot.us-east-1.amazonaws.com' 23 | region: YOUR_AWS_REGION_GOES_HERE // 'YourAwsRegion', e.g. 'us-east-1' 24 | }; 25 | module.exports = awsConfiguration; 26 | 27 | -------------------------------------------------------------------------------- /examples/browser/lifecycle/example.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | .spacer { 17 | height: 125px ; 18 | } 19 | 20 | .block { 21 | position: relative ; 22 | width:450px ; 23 | margin-left: auto ; 24 | margin-right: auto ; 25 | text-align: center ; 26 | } 27 | 28 | .stack { 29 | background-color:#32CD32 ; 30 | width: 400px ; 31 | margin-left: auto ; 32 | margin-right: auto ; 33 | margin-bottom: 2px ; 34 | padding-top: 2px ; 35 | padding-bottom: 2px ; 36 | text-align: center ; 37 | color: #ffffff ; 38 | display: block ; 39 | clear: both ; 40 | } 41 | -------------------------------------------------------------------------------- /examples/browser/lifecycle/index.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/browser/mqtt-explorer/aws-configuration.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | /* 17 | * NOTE: You must set the following string constants prior to running this 18 | * example application. 19 | */ 20 | var awsConfiguration = { 21 | poolId: YOUR_COGNITO_IDENTITY_POOL_ID_GOES_HERE, // 'YourCognitoIdentityPoolId' 22 | host: YOUR_AWS_IOT_ENDPOINT_GOES_HERE, // 'YourAwsIoTEndpoint', e.g. 'prefix.iot.us-east-1.amazonaws.com' 23 | region: YOUR_AWS_REGION_GOES_HERE // 'YourAwsRegion', e.g. 'us-east-1' 24 | }; 25 | module.exports = awsConfiguration; 26 | 27 | -------------------------------------------------------------------------------- /examples/browser/mqtt-explorer/example.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | .spacer { 17 | height: 125px ; 18 | } 19 | 20 | .block { 21 | position: relative ; 22 | width:900px ; 23 | margin-left: auto ; 24 | margin-right: auto ; 25 | text-align: center ; 26 | } 27 | 28 | .subscribe { 29 | background-color:#303030 ; 30 | width: 850px ; 31 | text-align: left ; 32 | margin-left: auto ; 33 | margin-right: auto ; 34 | margin-bottom: 2px ; 35 | padding-top: 2px ; 36 | padding-bottom: 2px ; 37 | display: block ; 38 | color: #ffffff ; 39 | } 40 | 41 | .settings { 42 | background-color:#32CD32 ; 43 | width: 500px ; 44 | text-align: left ; 45 | margin-left: auto ; 46 | margin-right: auto ; 47 | margin-bottom: 2px ; 48 | padding-top: 2px ; 49 | padding-bottom: 2px ; 50 | display: block ; 51 | color: #ffffff ; 52 | } 53 | 54 | p { 55 | margin-left: 10px ; 56 | margin-right: 10px ; 57 | } 58 | -------------------------------------------------------------------------------- /examples/browser/mqtt-explorer/index.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/browser/mqtt-explorer/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | // 17 | // Instantiate the AWS SDK and configuration objects. The AWS SDK for 18 | // JavaScript (aws-sdk) is used for Cognito Identity/Authentication, and 19 | // the AWS IoT SDK for JavaScript (aws-iot-device-sdk) is used for the 20 | // WebSocket connection to AWS IoT and device shadow APIs. 21 | // 22 | var AWS = require('aws-sdk'); 23 | var AWSIoTData = require('aws-iot-device-sdk'); 24 | var AWSConfiguration = require('./aws-configuration.js'); 25 | 26 | console.log('Loaded AWS SDK for JavaScript and AWS IoT SDK for Node.js'); 27 | 28 | // 29 | // Remember our current subscription topic here. 30 | // 31 | var currentlySubscribedTopic = 'subscribe-topic'; 32 | 33 | // 34 | // Remember our message history here. 35 | // 36 | var messageHistory = ''; 37 | 38 | // 39 | // Create a client id to use when connecting to AWS IoT. 40 | // 41 | var clientId = 'mqtt-explorer-' + (Math.floor((Math.random() * 100000) + 1)); 42 | 43 | // 44 | // Initialize our configuration. 45 | // 46 | AWS.config.region = AWSConfiguration.region; 47 | 48 | AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 49 | IdentityPoolId: AWSConfiguration.poolId 50 | }); 51 | 52 | // 53 | // Create the AWS IoT device object. Note that the credentials must be 54 | // initialized with empty strings; when we successfully authenticate to 55 | // the Cognito Identity Pool, the credentials will be dynamically updated. 56 | // 57 | const mqttClient = AWSIoTData.device({ 58 | // 59 | // Set the AWS region we will operate in. 60 | // 61 | region: AWS.config.region, 62 | // 63 | ////Set the AWS IoT Host Endpoint 64 | host:AWSConfiguration.host, 65 | // 66 | // Use the clientId created earlier. 67 | // 68 | clientId: clientId, 69 | // 70 | // Connect via secure WebSocket 71 | // 72 | protocol: 'wss', 73 | // 74 | // Set the maximum reconnect time to 8 seconds; this is a browser application 75 | // so we don't want to leave the user waiting too long for reconnection after 76 | // re-connecting to the network/re-opening their laptop/etc... 77 | // 78 | maximumReconnectTimeMs: 8000, 79 | // 80 | // Enable console debugging information (optional) 81 | // 82 | debug: true, 83 | // 84 | // IMPORTANT: the AWS access key ID, secret key, and sesion token must be 85 | // initialized with empty strings. 86 | // 87 | accessKeyId: '', 88 | secretKey: '', 89 | sessionToken: '' 90 | }); 91 | 92 | // 93 | // Attempt to authenticate to the Cognito Identity Pool. Note that this 94 | // example only supports use of a pool which allows unauthenticated 95 | // identities. 96 | // 97 | var cognitoIdentity = new AWS.CognitoIdentity(); 98 | AWS.config.credentials.get(function(err, data) { 99 | if (!err) { 100 | console.log('retrieved identity: ' + AWS.config.credentials.identityId); 101 | var params = { 102 | IdentityId: AWS.config.credentials.identityId 103 | }; 104 | cognitoIdentity.getCredentialsForIdentity(params, function(err, data) { 105 | if (!err) { 106 | // 107 | // Update our latest AWS credentials; the MQTT client will use these 108 | // during its next reconnect attempt. 109 | // 110 | mqttClient.updateWebSocketCredentials(data.Credentials.AccessKeyId, 111 | data.Credentials.SecretKey, 112 | data.Credentials.SessionToken); 113 | } else { 114 | console.log('error retrieving credentials: ' + err); 115 | alert('error retrieving credentials: ' + err); 116 | } 117 | }); 118 | } else { 119 | console.log('error retrieving identity:' + err); 120 | alert('error retrieving identity: ' + err); 121 | } 122 | }); 123 | 124 | // 125 | // Connect handler; update div visibility and fetch latest shadow documents. 126 | // Subscribe to lifecycle events on the first connect event. 127 | // 128 | window.mqttClientConnectHandler = function() { 129 | console.log('connect'); 130 | document.getElementById("connecting-div").style.visibility = 'hidden'; 131 | document.getElementById("explorer-div").style.visibility = 'visible'; 132 | document.getElementById('subscribe-div').innerHTML = '


'; 133 | messageHistory = ''; 134 | 135 | // 136 | // Subscribe to our current topic. 137 | // 138 | mqttClient.subscribe(currentlySubscribedTopic); 139 | }; 140 | 141 | // 142 | // Reconnect handler; update div visibility. 143 | // 144 | window.mqttClientReconnectHandler = function() { 145 | console.log('reconnect'); 146 | document.getElementById("connecting-div").style.visibility = 'visible'; 147 | document.getElementById("explorer-div").style.visibility = 'hidden'; 148 | }; 149 | 150 | // 151 | // Utility function to determine if a value has been defined. 152 | // 153 | window.isUndefined = function(value) { 154 | return typeof value === 'undefined' || typeof value === null; 155 | }; 156 | 157 | // 158 | // Message handler for lifecycle events; create/destroy divs as clients 159 | // connect/disconnect. 160 | // 161 | window.mqttClientMessageHandler = function(topic, payload) { 162 | console.log('message: ' + topic + ':' + payload.toString()); 163 | messageHistory = messageHistory + topic + ':' + payload.toString() + '
'; 164 | document.getElementById('subscribe-div').innerHTML = '

' + messageHistory + '

'; 165 | }; 166 | 167 | // 168 | // Handle the UI for the current topic subscription 169 | // 170 | window.updateSubscriptionTopic = function() { 171 | var subscribeTopic = document.getElementById('subscribe-topic').value; 172 | document.getElementById('subscribe-div').innerHTML = ''; 173 | mqttClient.unsubscribe(currentlySubscribedTopic); 174 | currentlySubscribedTopic = subscribeTopic; 175 | mqttClient.subscribe(currentlySubscribedTopic); 176 | }; 177 | 178 | // 179 | // Handle the UI to clear the history window 180 | // 181 | window.clearHistory = function() { 182 | if (confirm('Delete message history?') === true) { 183 | document.getElementById('subscribe-div').innerHTML = '


'; 184 | messageHistory = ''; 185 | } 186 | }; 187 | 188 | // 189 | // Handle the UI to update the topic we're publishing on 190 | // 191 | window.updatePublishTopic = function() {}; 192 | 193 | // 194 | // Handle the UI to update the data we're publishing 195 | // 196 | window.updatePublishData = function() { 197 | var publishText = document.getElementById('publish-data').value; 198 | var publishTopic = document.getElementById('publish-topic').value; 199 | 200 | mqttClient.publish(publishTopic, publishText); 201 | document.getElementById('publish-data').value = ''; 202 | }; 203 | 204 | // 205 | // Install connect/reconnect event handlers. 206 | // 207 | mqttClient.on('connect', window.mqttClientConnectHandler); 208 | mqttClient.on('reconnect', window.mqttClientReconnectHandler); 209 | mqttClient.on('message', window.mqttClientMessageHandler); 210 | 211 | // 212 | // Initialize divs. 213 | // 214 | document.getElementById('connecting-div').style.visibility = 'visible'; 215 | document.getElementById('explorer-div').style.visibility = 'hidden'; 216 | document.getElementById('connecting-div').innerHTML = '

attempting to connect to aws iot...

'; 217 | -------------------------------------------------------------------------------- /examples/browser/mqtt-webpack/aws-configuration.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | /* 17 | * NOTE: You must set the following string constants prior to running this 18 | * example application. 19 | */ 20 | var awsConfiguration = { 21 | poolId: YOUR_COGNITO_IDENTITY_POOL_ID_GOES_HERE, // 'YourCognitoIdentityPoolId' 22 | host: YOUR_AWS_IOT_ENDPOINT_GOES_HERE, // 'YourAwsIoTEndpoint', e.g. 'prefix.iot.us-east-1.amazonaws.com' 23 | region: YOUR_AWS_REGION_GOES_HERE // 'YourAwsRegion', e.g. 'us-east-1' 24 | }; 25 | module.exports = awsConfiguration; 26 | 27 | -------------------------------------------------------------------------------- /examples/browser/mqtt-webpack/entry.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | var AWSIoTData = require('aws-iot-device-sdk'); 3 | var AWSConfiguration = require('./aws-configuration.js'); 4 | 5 | console.log('Loaded AWS SDK for JavaScript and AWS IoT SDK for Node.js'); 6 | 7 | // 8 | // Remember our current subscription topic here. 9 | // 10 | var currentlySubscribedTopic = 'subscribe-topic'; 11 | 12 | // 13 | // Remember our message history here. 14 | // 15 | var messageHistory = ''; 16 | 17 | // 18 | // Create a client id to use when connecting to AWS IoT. 19 | // 20 | var clientId = 'mqtt-explorer-' + (Math.floor((Math.random() * 100000) + 1)); 21 | 22 | // 23 | // Initialize our configuration. 24 | // 25 | AWS.config.region = AWSConfiguration.region; 26 | 27 | AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 28 | IdentityPoolId: AWSConfiguration.poolId 29 | }); 30 | 31 | // 32 | // Create the AWS IoT device object. Note that the credentials must be 33 | // initialized with empty strings; when we successfully authenticate to 34 | // the Cognito Identity Pool, the credentials will be dynamically updated. 35 | // 36 | const mqttClient = AWSIoTData.device({ 37 | // 38 | // Set the AWS region we will operate in. 39 | // 40 | region: AWS.config.region, 41 | host:AWSConfiguration.host, 42 | // 43 | // Use the clientId created earlier. 44 | // 45 | clientId: clientId, 46 | // 47 | // Connect via secure WebSocket 48 | // 49 | protocol: 'wss', 50 | // 51 | // Set the maximum reconnect time to 8 seconds; this is a browser application 52 | // so we don't want to leave the user waiting too long for reconnection after 53 | // re-connecting to the network/re-opening their laptop/etc... 54 | // 55 | maximumReconnectTimeMs: 8000, 56 | // 57 | // Enable console debugging information (optional) 58 | // 59 | debug: true, 60 | // 61 | // IMPORTANT: the AWS access key ID, secret key, and sesion token must be 62 | // initialized with empty strings. 63 | // 64 | accessKeyId: '', 65 | secretKey: '', 66 | sessionToken: '' 67 | }); 68 | 69 | // 70 | // Attempt to authenticate to the Cognito Identity Pool. Note that this 71 | // example only supports use of a pool which allows unauthenticated 72 | // identities. 73 | // 74 | var cognitoIdentity = new AWS.CognitoIdentity(); 75 | AWS.config.credentials.get(function(err, data) { 76 | if (!err) { 77 | console.log('retrieved identity: ' + AWS.config.credentials.identityId); 78 | var params = { 79 | IdentityId: AWS.config.credentials.identityId 80 | }; 81 | cognitoIdentity.getCredentialsForIdentity(params, function(err, data) { 82 | if (!err) { 83 | // 84 | // Update our latest AWS credentials; the MQTT client will use these 85 | // during its next reconnect attempt. 86 | // 87 | mqttClient.updateWebSocketCredentials(data.Credentials.AccessKeyId, 88 | data.Credentials.SecretKey, 89 | data.Credentials.SessionToken); 90 | } else { 91 | console.log('error retrieving credentials: ' + err); 92 | alert('error retrieving credentials: ' + err); 93 | } 94 | }); 95 | } else { 96 | console.log('error retrieving identity:' + err); 97 | alert('error retrieving identity: ' + err); 98 | } 99 | }); 100 | 101 | // 102 | // Connect handler; update div visibility and fetch latest shadow documents. 103 | // Subscribe to lifecycle events on the first connect event. 104 | // 105 | window.mqttClientConnectHandler = function() { 106 | console.log('connect'); 107 | document.getElementById("connecting-div").style.visibility = 'hidden'; 108 | document.getElementById("explorer-div").style.visibility = 'visible'; 109 | document.getElementById('subscribe-div').innerHTML = '


'; 110 | messageHistory = ''; 111 | 112 | // 113 | // Subscribe to our current topic. 114 | // 115 | mqttClient.subscribe(currentlySubscribedTopic); 116 | }; 117 | 118 | // 119 | // Reconnect handler; update div visibility. 120 | // 121 | window.mqttClientReconnectHandler = function() { 122 | console.log('reconnect'); 123 | document.getElementById("connecting-div").style.visibility = 'visible'; 124 | document.getElementById("explorer-div").style.visibility = 'hidden'; 125 | }; 126 | 127 | // 128 | // Utility function to determine if a value has been defined. 129 | // 130 | window.isUndefined = function(value) { 131 | return typeof value === 'undefined' || value === null; 132 | }; 133 | 134 | // 135 | // Message handler for lifecycle events; create/destroy divs as clients 136 | // connect/disconnect. 137 | // 138 | window.mqttClientMessageHandler = function(topic, payload) { 139 | console.log('message: ' + topic + ':' + payload.toString()); 140 | messageHistory = messageHistory + topic + ':' + payload.toString() + '
'; 141 | document.getElementById('subscribe-div').innerHTML = '

' + messageHistory + '

'; 142 | }; 143 | 144 | // 145 | // Handle the UI for the current topic subscription 146 | // 147 | window.updateSubscriptionTopic = function() { 148 | var subscribeTopic = document.getElementById('subscribe-topic').value; 149 | document.getElementById('subscribe-div').innerHTML = ''; 150 | mqttClient.unsubscribe(currentlySubscribedTopic); 151 | currentlySubscribedTopic = subscribeTopic; 152 | mqttClient.subscribe(currentlySubscribedTopic); 153 | }; 154 | 155 | // 156 | // Handle the UI to clear the history window 157 | // 158 | window.clearHistory = function() { 159 | if (confirm('Delete message history?') === true) { 160 | document.getElementById('subscribe-div').innerHTML = '


'; 161 | messageHistory = ''; 162 | } 163 | }; 164 | 165 | // 166 | // Handle the UI to update the topic we're publishing on 167 | // 168 | window.updatePublishTopic = function() {}; 169 | 170 | // 171 | // Handle the UI to update the data we're publishing 172 | // 173 | window.updatePublishData = function() { 174 | var publishText = document.getElementById('publish-data').value; 175 | var publishTopic = document.getElementById('publish-topic').value; 176 | 177 | mqttClient.publish(publishTopic, publishText); 178 | document.getElementById('publish-data').value = ''; 179 | }; 180 | 181 | // 182 | // Install connect/reconnect event handlers. 183 | // 184 | mqttClient.on('connect', window.mqttClientConnectHandler); 185 | mqttClient.on('reconnect', window.mqttClientReconnectHandler); 186 | mqttClient.on('message', window.mqttClientMessageHandler); 187 | 188 | // 189 | // Initialize divs. 190 | // 191 | document.getElementById('connecting-div').style.visibility = 'visible'; 192 | document.getElementById('explorer-div').style.visibility = 'hidden'; 193 | document.getElementById('connecting-div').innerHTML = '

attempting to connect to aws iot...

'; 194 | -------------------------------------------------------------------------------- /examples/browser/mqtt-webpack/example.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | .spacer { 17 | height: 125px ; 18 | } 19 | 20 | .block { 21 | position: relative ; 22 | width:900px ; 23 | margin-left: auto ; 24 | margin-right: auto ; 25 | text-align: center ; 26 | } 27 | 28 | .subscribe { 29 | background-color:#303030 ; 30 | width: 850px ; 31 | text-align: left ; 32 | margin-left: auto ; 33 | margin-right: auto ; 34 | margin-bottom: 2px ; 35 | padding-top: 2px ; 36 | padding-bottom: 2px ; 37 | display: block ; 38 | color: #ffffff ; 39 | } 40 | 41 | .settings { 42 | background-color:#32CD32 ; 43 | width: 500px ; 44 | text-align: left ; 45 | margin-left: auto ; 46 | margin-right: auto ; 47 | margin-bottom: 2px ; 48 | padding-top: 2px ; 49 | padding-bottom: 2px ; 50 | display: block ; 51 | color: #ffffff ; 52 | } 53 | 54 | p { 55 | margin-left: 10px ; 56 | margin-right: 10px ; 57 | } 58 | -------------------------------------------------------------------------------- /examples/browser/mqtt-webpack/index.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /examples/browser/mqtt-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mqtt-webpack", 3 | "version": "1.0.0", 4 | "description": "Amazon AWS IoT Javascript Device SDK with WebPack", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": { 9 | "name":"Amazon Web Services", 10 | "email":"", 11 | "url":"http://aws.amazon.com" 12 | }, 13 | "dependencies": { 14 | "aws-iot-device-sdk": "^2.0.0", 15 | "aws-sdk": "^2.10.0", 16 | "webpack":"^2.2.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/browser/mqtt-webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: "./entry.js", 3 | output: { 4 | path: __dirname, 5 | filename: "bundle.js" 6 | }, 7 | node: { 8 | fs: 'empty', 9 | tls: 'empty' 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /examples/browser/temperature-monitor/aws-configuration.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | /* 17 | * NOTE: You must set the following string constants prior to running this 18 | * example application. 19 | */ 20 | var awsConfiguration = { 21 | poolId: YOUR_COGNITO_IDENTITY_POOL_ID_GOES_HERE, // 'YourCognitoIdentityPoolId' 22 | host: YOUR_AWS_IOT_ENDPOINT_GOES_HERE, // 'YourAWSIoTEndpoint', e.g. 'prefix.iot.us-east-1.amazonaws.com' 23 | region: YOUR_AWS_REGION_GOES_HERE // 'YourAwsRegion', e.g. 'us-east-1' 24 | }; 25 | module.exports = awsConfiguration; 26 | 27 | -------------------------------------------------------------------------------- /examples/browser/temperature-monitor/example.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | .spacer { 17 | height: 125px ; 18 | } 19 | 20 | .block { 21 | width:250px ; 22 | margin-left: auto ; 23 | margin-right: auto ; 24 | } 25 | -------------------------------------------------------------------------------- /examples/browser/temperature-monitor/index.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/browser/temperature-monitor/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | // 17 | // Instantiate the AWS SDK and configuration objects. The AWS SDK for 18 | // JavaScript (aws-sdk) is used for Cognito Identity/Authentication, and 19 | // the AWS IoT SDK for JavaScript (aws-iot-device-sdk) is used for the 20 | // WebSocket connection to AWS IoT and device shadow APIs. 21 | // 22 | var AWS = require('aws-sdk'); 23 | var AWSIoTData = require('aws-iot-device-sdk'); 24 | var AWSConfiguration = require('./aws-configuration.js'); 25 | 26 | console.log('Loaded AWS SDK for JavaScript and AWS IoT SDK for Node.js'); 27 | 28 | // 29 | // Initialize our configuration. 30 | // 31 | AWS.config.region = AWSConfiguration.region; 32 | 33 | AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 34 | IdentityPoolId: AWSConfiguration.poolId 35 | }); 36 | 37 | // 38 | // Keep track of whether or not we've registered the shadows used by this 39 | // example. 40 | // 41 | var shadowsRegistered = false; 42 | 43 | // 44 | // Create the AWS IoT shadows object. Note that the credentials must be 45 | // initialized with empty strings; when we successfully authenticate to 46 | // the Cognito Identity Pool, the credentials will be dynamically updated. 47 | // 48 | const shadows = AWSIoTData.thingShadow({ 49 | // 50 | // Set the AWS region we will operate in. 51 | // 52 | region: AWS.config.region, 53 | // 54 | //Set the AWS IoT Host Endpoint 55 | // 56 | host:AWSConfiguration.host, 57 | // 58 | // Use a random client ID. 59 | // 60 | clientId: 'temperature-control-browser-' + (Math.floor((Math.random() * 100000) + 1)), 61 | // 62 | // Connect via secure WebSocket 63 | // 64 | protocol: 'wss', 65 | // 66 | // Set the maximum reconnect time to 8 seconds; this is a browser application 67 | // so we don't want to leave the user waiting too long for reconnection after 68 | // re-connecting to the network/re-opening their laptop/etc... 69 | // 70 | maximumReconnectTimeMs: 8000, 71 | // 72 | // Enable console debugging information (optional) 73 | // 74 | debug: true, 75 | // 76 | // IMPORTANT: the AWS access key ID, secret key, and sesion token must be 77 | // initialized with empty strings. 78 | // 79 | accessKeyId: '', 80 | secretKey: '', 81 | sessionToken: '' 82 | }); 83 | 84 | // 85 | // Update divs whenever we receive delta events from the shadows. 86 | // 87 | shadows.on('delta', function(name, stateObject) { 88 | if (name === 'TemperatureStatus') { 89 | document.getElementById('temperature-monitor-div').innerHTML = '

interior: ' + stateObject.state.intTemp + '

' + 90 | '

exterior: ' + stateObject.state.extTemp + '

' + 91 | '

state: ' + stateObject.state.curState + '

'; 92 | } else { // name === 'TemperatureControl' 93 | var enabled = stateObject.state.enabled ? 'enabled' : 'disabled'; 94 | document.getElementById('temperature-control-div').innerHTML = '

setpoint: ' + stateObject.state.setPoint + '

' + 95 | '

mode: ' + enabled + '

'; 96 | } 97 | }); 98 | 99 | // 100 | // Update divs whenever we receive status events from the shadows. 101 | // 102 | shadows.on('status', function(name, statusType, clientToken, stateObject) { 103 | if (statusType === 'rejected') { 104 | // 105 | // If an operation is rejected it is likely due to a version conflict; 106 | // request the latest version so that we synchronize with the shadow 107 | // The most notable exception to this is if the thing shadow has not 108 | // yet been created or has been deleted. 109 | // 110 | if (stateObject.code !== 404) { 111 | console.log('resync with thing shadow'); 112 | var opClientToken = shadows.get(name); 113 | if (opClientToken === null) { 114 | console.log('operation in progress'); 115 | } 116 | } 117 | } else { // statusType === 'accepted' 118 | if (name === 'TemperatureStatus') { 119 | document.getElementById('temperature-monitor-div').innerHTML = '

interior: ' + stateObject.state.desired.intTemp + '

' + 120 | '

exterior: ' + stateObject.state.desired.extTemp + '

' + 121 | '

state: ' + stateObject.state.desired.curState + '

'; 122 | } else { // name === 'TemperatureControl' 123 | var enabled = stateObject.state.desired.enabled ? 'enabled' : 'disabled'; 124 | document.getElementById('temperature-control-div').innerHTML = '

setpoint: ' + stateObject.state.desired.setPoint + '

' + 125 | '

mode: ' + enabled + '

'; 126 | } 127 | } 128 | }); 129 | 130 | // 131 | // Attempt to authenticate to the Cognito Identity Pool. Note that this 132 | // example only supports use of a pool which allows unauthenticated 133 | // identities. 134 | // 135 | var cognitoIdentity = new AWS.CognitoIdentity(); 136 | AWS.config.credentials.get(function(err, data) { 137 | if (!err) { 138 | console.log('retrieved identity: ' + AWS.config.credentials.identityId); 139 | var params = { 140 | IdentityId: AWS.config.credentials.identityId 141 | }; 142 | cognitoIdentity.getCredentialsForIdentity(params, function(err, data) { 143 | if (!err) { 144 | // 145 | // Update our latest AWS credentials; the MQTT client will use these 146 | // during its next reconnect attempt. 147 | // 148 | shadows.updateWebSocketCredentials(data.Credentials.AccessKeyId, 149 | data.Credentials.SecretKey, 150 | data.Credentials.SessionToken); 151 | } else { 152 | console.log('error retrieving credentials: ' + err); 153 | alert('error retrieving credentials: ' + err); 154 | } 155 | }); 156 | } else { 157 | console.log('error retrieving identity:' + err); 158 | alert('error retrieving identity: ' + err); 159 | } 160 | }); 161 | 162 | // 163 | // Connect handler; update div visibility and fetch latest shadow documents. 164 | // Register shadows on the first connect event. 165 | // 166 | window.shadowConnectHandler = function() { 167 | console.log('connect'); 168 | document.getElementById("connecting-div").style.visibility = 'hidden'; 169 | document.getElementById("temperature-monitor-div").style.visibility = 'visible'; 170 | document.getElementById("temperature-control-div").style.visibility = 'visible'; 171 | 172 | // 173 | // We only register our shadows once. 174 | // 175 | if (!shadowsRegistered) { 176 | shadows.register('TemperatureStatus', { 177 | persistentSubscribe: true 178 | }); 179 | shadows.register('TemperatureControl', { 180 | persistentSubscribe: true 181 | }); 182 | shadowsRegistered = true; 183 | } 184 | // 185 | // After connecting, wait for a few seconds and then ask for the 186 | // current state of the shadows. 187 | // 188 | setTimeout(function() { 189 | var opClientToken = shadows.get('TemperatureControl'); 190 | if (opClientToken === null) { 191 | console.log('operation in progress'); 192 | } 193 | opClientToken = shadows.get('TemperatureStatus'); 194 | if (opClientToken === null) { 195 | console.log('operation in progress'); 196 | } 197 | }, 3000); 198 | }; 199 | 200 | // 201 | // Reconnect handler; update div visibility. 202 | // 203 | window.shadowReconnectHandler = function() { 204 | console.log('reconnect'); 205 | document.getElementById("connecting-div").style.visibility = 'visible'; 206 | document.getElementById("temperature-monitor-div").style.visibility = 'hidden'; 207 | document.getElementById("temperature-control-div").style.visibility = 'hidden'; 208 | }; 209 | 210 | // 211 | // Install connect/reconnect event handlers. 212 | // 213 | shadows.on('connect', window.shadowConnectHandler); 214 | shadows.on('reconnect', window.shadowReconnectHandler); 215 | 216 | // 217 | // Initialize divs. 218 | // 219 | document.getElementById('connecting-div').style.visibility = 'visible'; 220 | document.getElementById('temperature-control-div').style.visibility = 'hidden'; 221 | document.getElementById('temperature-monitor-div').style.visibility = 'hidden'; 222 | document.getElementById('connecting-div').innerHTML = '

attempting to connect to aws iot...

'; 223 | document.getElementById('temperature-control-div').innerHTML = '

getting latest status...

'; 224 | document.getElementById('temperature-monitor-div').innerHTML = ''; 225 | -------------------------------------------------------------------------------- /examples/device-example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const deviceModule = require('..').device; 22 | const cmdLineProcess = require('./lib/cmdline'); 23 | 24 | //begin module 25 | 26 | function processTest(args) { 27 | // 28 | // The device module exports an MQTT instance, which will attempt 29 | // to connect to the AWS IoT endpoint configured in the arguments. 30 | // Once connected, it will emit events which our application can 31 | // handle. 32 | // 33 | const device = deviceModule({ 34 | keyPath: args.privateKey, 35 | certPath: args.clientCert, 36 | caPath: args.caCert, 37 | clientId: args.clientId, 38 | region: args.region, 39 | baseReconnectTimeMs: args.baseReconnectTimeMs, 40 | keepalive: args.keepAlive, 41 | protocol: args.Protocol, 42 | port: args.Port, 43 | host: args.Host, 44 | debug: args.Debug 45 | }); 46 | 47 | var timeout; 48 | var count = 0; 49 | const minimumDelay = 250; 50 | 51 | if (args.testMode === 1) { 52 | device.subscribe('topic_1'); 53 | } else { 54 | device.subscribe('topic_2'); 55 | } 56 | if ((Math.max(args.delay, minimumDelay)) !== args.delay) { 57 | console.log('substituting ' + minimumDelay + 'ms delay for ' + args.delay + 'ms...'); 58 | } 59 | timeout = setInterval(function() { 60 | count++; 61 | 62 | if (args.testMode === 1) { 63 | device.publish('topic_2', JSON.stringify({ 64 | mode1Process: count 65 | })); 66 | } else { 67 | device.publish('topic_1', JSON.stringify({ 68 | mode2Process: count 69 | })); 70 | } 71 | }, Math.max(args.delay, minimumDelay)); // clip to minimum 72 | 73 | // 74 | // Do a simple publish/subscribe demo based on the test-mode passed 75 | // in the command line arguments. If test-mode is 1, subscribe to 76 | // 'topic_1' and publish to 'topic_2'; otherwise vice versa. Publish 77 | // a message every four seconds. 78 | // 79 | device 80 | .on('connect', function() { 81 | console.log('connect'); 82 | }); 83 | device 84 | .on('close', function() { 85 | console.log('close'); 86 | }); 87 | device 88 | .on('reconnect', function() { 89 | console.log('reconnect'); 90 | }); 91 | device 92 | .on('offline', function() { 93 | console.log('offline'); 94 | }); 95 | device 96 | .on('error', function(error) { 97 | console.log('error', error); 98 | }); 99 | device 100 | .on('message', function(topic, payload) { 101 | console.log('message', topic, payload.toString()); 102 | }); 103 | 104 | } 105 | 106 | module.exports = cmdLineProcess; 107 | 108 | if (require.main === module) { 109 | cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2', 110 | process.argv.slice(2), processTest); 111 | } 112 | -------------------------------------------------------------------------------- /examples/echo-example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const thingShadow = require('..').thingShadow; 22 | const isUndefined = require('../common/lib/is-undefined'); 23 | const cmdLineProcess = require('./lib/cmdline'); 24 | 25 | //begin module 26 | 27 | function processTest(args) { 28 | 29 | if (isUndefined(args.thingName)) { 30 | console.log('thing name must be specified with --thing-name'); 31 | process.exit(1); 32 | } 33 | // 34 | // The thing module exports the thing class through which we 35 | // can register and unregister interest in thing shadows, perform 36 | // update/get/delete operations on them, and receive delta updates 37 | // when the cloud state differs from the device state. 38 | // 39 | const thingShadows = thingShadow({ 40 | keyPath: args.privateKey, 41 | certPath: args.clientCert, 42 | caPath: args.caCert, 43 | clientId: args.clientId, 44 | region: args.region, 45 | baseReconnectTimeMs: args.baseReconnectTimeMs, 46 | keepalive: args.keepAlive, 47 | protocol: args.Protocol, 48 | port: args.Port, 49 | host: args.Host, 50 | debug: args.Debug 51 | }); 52 | // 53 | // Register a thing name and listen for deltas. Whatever we receive on delta 54 | // is echoed via thing shadow updates. 55 | // 56 | thingShadows.register(args.thingName, { 57 | persistentSubscribe: true 58 | }); 59 | 60 | thingShadows 61 | .on('error', function(error) { 62 | console.log('error', error); 63 | }); 64 | 65 | thingShadows 66 | .on('delta', function(thingName, stateObject) { 67 | console.log('received delta on ' + thingName + ': ' + 68 | JSON.stringify(stateObject)); 69 | thingShadows.update(thingName, { 70 | state: { 71 | reported: stateObject.state 72 | } 73 | }); 74 | }); 75 | 76 | thingShadows 77 | .on('timeout', function(thingName, clientToken) { 78 | console.warn('timeout: ' + thingName + ', clientToken=' + clientToken); 79 | }); 80 | } 81 | 82 | module.exports = cmdLineProcess; 83 | 84 | if (require.main === module) { 85 | cmdLineProcess('connect to the AWS IoT service and perform thing shadow echo', 86 | process.argv.slice(2), processTest, ' ', true); 87 | } 88 | -------------------------------------------------------------------------------- /examples/jobs-example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const jobsModule = require('..').jobs; 22 | const cmdLineProcess = require('./lib/cmdline'); 23 | const isUndefined = require('../common/lib/is-undefined'); 24 | 25 | //begin module 26 | 27 | function processTest(args) { 28 | // 29 | // The jobs module exports an MQTT instance, which will attempt 30 | // to connect to the AWS IoT endpoint configured in the arguments. 31 | // Once connected, it will emit events which our application can 32 | // handle. 33 | // 34 | const jobs = jobsModule({ 35 | keyPath: args.privateKey, 36 | certPath: args.clientCert, 37 | caPath: args.caCert, 38 | clientId: args.clientId, 39 | region: args.region, 40 | baseReconnectTimeMs: args.baseReconnectTimeMs, 41 | keepalive: args.keepAlive, 42 | protocol: args.Protocol, 43 | port: args.Port, 44 | host: args.Host, 45 | debug: args.Debug 46 | }); 47 | 48 | jobs 49 | .on('connect', function() { 50 | console.log('connect'); 51 | }); 52 | jobs 53 | .on('close', function() { 54 | console.log('close'); 55 | }); 56 | jobs 57 | .on('reconnect', function() { 58 | console.log('reconnect'); 59 | }); 60 | jobs 61 | .on('offline', function() { 62 | console.log('offline'); 63 | }); 64 | jobs 65 | .on('error', function(error) { 66 | console.log('error', error); 67 | }); 68 | jobs 69 | .on('message', function(topic, payload) { 70 | console.log('message', topic, payload.toString()); 71 | }); 72 | 73 | jobs.subscribe('testTopic'); 74 | 75 | if (args.thingName) { 76 | jobs.subscribeToJobs(args.thingName, 'customJob', function(err, job) { 77 | if (isUndefined(err)) { 78 | console.log('customJob operation handler invoked, jobId: ' + job.id.toString()); 79 | 80 | // 81 | // Indicate to AWS IoT Jobs manager that the job execution is in progress of being processed 82 | // 83 | job.inProgress({ operation: 'customJob', step: 'step 1 of customJob' }, function(err) { 84 | // 85 | // Do some work... 86 | // 87 | 88 | // 89 | // Indicate to AWS IoT Jobs manager that the job execution is successfully completed 90 | // 91 | job.succeeded({ operation: 'customJob', step: 'finished all steps' }, function(err) { }); 92 | }); 93 | } 94 | else { 95 | console.error(err); 96 | } 97 | }); 98 | 99 | jobs.subscribeToJobs(args.thingName, function(err, job) { 100 | if (isUndefined(err)) { 101 | console.log('default job handler invoked, jobId: ' + job.id.toString()); 102 | 103 | // 104 | // Indicate to AWS IoT Jobs manager that the job execution failed 105 | // 106 | job.failed({ operation: job.operation, errorCondition: 'not yet implemented' }, function(err) { }); 107 | } 108 | else { 109 | console.error(err); 110 | } 111 | }); 112 | 113 | jobs.startJobNotifications(args.thingName, function(err) { 114 | if (isUndefined(err)) { 115 | console.log('startJobNotifications completed for thing: ' + args.thingName); 116 | } 117 | else { 118 | console.error(err); 119 | } 120 | }); 121 | } 122 | 123 | } 124 | 125 | module.exports = cmdLineProcess; 126 | 127 | if (require.main === module) { 128 | cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2', 129 | process.argv.slice(2), processTest); 130 | } 131 | 132 | -------------------------------------------------------------------------------- /examples/lib/cmdline.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | const fs = require('fs'); 18 | 19 | //npm deps 20 | const minimist = require('minimist'); 21 | 22 | //app deps 23 | const isUndefined = require('../../common/lib/is-undefined'); 24 | 25 | //begin module 26 | var clientIdDefault; 27 | if (!isUndefined(process.env.USER)) { 28 | clientIdDefault = process.env.USER.concat(Math.floor((Math.random() * 100000) + 1)); 29 | } else { 30 | clientIdDefault = 'nouser' + (Math.floor((Math.random() * 100000) + 1)); 31 | } 32 | 33 | module.exports = function(description, args, processFunction, argumentHelp) { 34 | var doHelp = function() { 35 | var progName = process.argv[1]; 36 | var lastSlash = progName.lastIndexOf('/'); 37 | if (lastSlash !== -1) { 38 | progName = progName.substring(lastSlash + 1, progName.length); 39 | } 40 | if (isUndefined(argumentHelp)) { 41 | console.log('Usage: ' + progName + ' [OPTION...]'); 42 | } else { 43 | console.log('Usage: ' + progName + ' [OPTION...] ARGUMENTS...'); 44 | } 45 | console.log('\n' + progName + ': ' + description + '\n\n' + 46 | ' Options\n\n' + 47 | ' -i, --client-id=ID use ID as client ID\n' + 48 | ' -H, --host-name=HOST connect to HOST (overrides --aws-region)\n' + 49 | ' -p, --port=PORT connect to PORT (overrides defaults)\n' + 50 | ' -P, --protocol=PROTOCOL connect using PROTOCOL (mqtts|wss)\n' + 51 | ' -k, --private-key=FILE use FILE as private key\n' + 52 | ' -c, --client-certificate=FILE use FILE as client certificate\n' + 53 | ' -a, --ca-certificate=FILE use FILE as CA certificate\n' + 54 | ' -f, --certificate-dir=DIR look in DIR for certificates\n' + 55 | ' -F, --configuration-file=FILE use FILE (JSON format) for configuration\n' + 56 | ' -r, --reconnect-period-ms=VALUE use VALUE as the reconnect period (ms)\n' + 57 | ' -K, --keepalive=VALUE use VALUE as the keepalive time (seconds)\n' + 58 | ' -t, --test-mode=[1-n] set test mode for multi-process tests\n' + 59 | ' -T, --thing-name=THINGNAME access thing shadow named THINGNAME\n' + 60 | ' -d, --delay-ms=VALUE delay in milliseconds before publishing\n' + 61 | ' -D, --debug print additional debugging information\n\n' + 62 | ' Default values\n\n' + 63 | ' client-id $USER\n' + 64 | ' protocol mqtts\n' + 65 | ' private-key private.pem.key\n' + 66 | ' client-certificate certificate.pem.crt\n' + 67 | ' ca-certificate root-CA.crt\n' + 68 | ' reconnect-period-ms 3000ms\n' + 69 | ' delay-ms 4000ms\n' + 70 | ' test-mode 1\n'); 71 | if (!isUndefined(argumentHelp)) { 72 | console.log(argumentHelp); 73 | } 74 | }; 75 | args = minimist(args, { 76 | string: ['certificate-dir', 'private-key', 'client-certificate', 77 | 'ca-certificate', 'client-id', 'thing-name', 'configuration-file', 78 | 'host-name', 'protocol' 79 | ], 80 | integer: ['reconnect-period-ms', 'test-mode', 'port', 'delay-ms', 81 | 'keepalive' 82 | ], 83 | boolean: ['help', 'debug'], 84 | alias: { 85 | clientId: ['i', 'client-id'], 86 | privateKey: ['k', 'private-key'], 87 | clientCert: ['c', 'client-certificate'], 88 | caCert: ['a', 'ca-certificate'], 89 | certDir: ['f', 'certificate-dir'], 90 | configFile: ['F', 'configuration-file'], 91 | baseReconnectTimeMs: ['r', 'reconnect-period-ms'], 92 | keepAlive: ['K', 'keepalive'], 93 | testMode: ['t', 'test-mode'], 94 | thingName: ['T', 'thing-name'], 95 | delay: ['d', 'delay-ms'], 96 | Port: ['p', 'port'], 97 | Protocol: ['P', 'protocol'], 98 | Host: ['H', 'host-name'], 99 | Debug: ['D', 'debug'], 100 | help: 'h' 101 | }, 102 | default: { 103 | protocol: 'mqtts', 104 | clientId: clientIdDefault, 105 | privateKey: 'private.pem.key', 106 | clientCert: 'certificate.pem.crt', 107 | caCert: 'root-CA.crt', 108 | testMode: 1, 109 | /* milliseconds */ 110 | baseReconnectTimeMs: 4000, 111 | /* seconds */ 112 | keepAlive: 300, 113 | /* milliseconds */ 114 | delay: 4000, 115 | Debug: false 116 | }, 117 | unknown: function() { 118 | console.error('***unrecognized options***'); 119 | doHelp(); 120 | process.exit(1); 121 | } 122 | }); 123 | if (args.help) { 124 | doHelp(); 125 | return; 126 | } 127 | // 128 | // If the user has specified a directory where certificates are located, 129 | // prepend it to all of the certificate filenames. 130 | // 131 | if (!isUndefined(args.certDir)) { 132 | args.privateKey = args.certDir + '/' + args.privateKey; 133 | args.clientCert = args.certDir + '/' + args.clientCert; 134 | args.caCert = args.certDir + '/' + args.caCert; 135 | } 136 | // 137 | // If the configuration file is defined, read it in and set the parameters based 138 | // on the values inside; these will override any other arguments specified on 139 | // the command line. 140 | // 141 | if (!isUndefined(args.configFile)) { 142 | if (!fs.existsSync(args.configFile)) { 143 | console.error('\n' + args.configFile + ' doesn\'t exist (--help for usage)\n'); 144 | return; 145 | } 146 | var config = JSON.parse(fs.readFileSync(args.configFile, 'utf8')); 147 | 148 | if (!isUndefined(config.privateKey)) { 149 | if (!isUndefined(args.certDir)) { 150 | args.privateKey = args.certDir + '/' + config.privateKey; 151 | } else { 152 | args.privateKey = config.privateKey; 153 | } 154 | } 155 | if (!isUndefined(config.clientCert)) { 156 | if (!isUndefined(args.certDir)) { 157 | args.clientCert = args.certDir + '/' + config.clientCert; 158 | } else { 159 | args.clientCert = config.clientCert; 160 | } 161 | } 162 | if (!isUndefined(config.caCert)) { 163 | if (!isUndefined(args.certDir)) { 164 | args.caCert = args.certDir + '/' + config.caCert; 165 | } else { 166 | args.caCert = config.caCert; 167 | } 168 | } 169 | if (!isUndefined(config.host)) { 170 | args.Host = config.host; 171 | } 172 | if (!isUndefined(config.port)) { 173 | args.Port = config.port; 174 | } 175 | // 176 | // When using a JSON configuration document from the AWS Console, allow 177 | // the client ID to be overriden by the command line option for client ID. 178 | // This is required to run the example programs from a JSON configuration 179 | // document, since both instances must use different client IDs. 180 | // 181 | if (!isUndefined(config.clientId) && isUndefined(args.clientId)) { 182 | args.clientId = config.clientId; 183 | } 184 | if (!isUndefined(config.thingName)) { 185 | args.thingName = config.thingName; 186 | } 187 | } 188 | 189 | if (args.Protocol === 'mqtts') { 190 | // 191 | // Client certificate, private key, and CA certificate must all exist if 192 | // connecting via mqtts. 193 | // 194 | if (!fs.existsSync(args.privateKey)) { 195 | console.error('\n' + args.privateKey + ' doesn\'t exist (--help for usage)\n'); 196 | return; 197 | } 198 | if (!fs.existsSync(args.clientCert)) { 199 | console.error('\n' + args.clientCert + ' doesn\'t exist (--help for usage)\n'); 200 | return; 201 | } 202 | if (!fs.existsSync(args.caCert)) { 203 | console.error('\n' + args.caCert + ' doesn\'t exist (--help for usage)\n'); 204 | return; 205 | } 206 | } 207 | 208 | processFunction(args); 209 | }; 210 | -------------------------------------------------------------------------------- /examples/lib/copy-file.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | var fs = require('fs'); 18 | 19 | //npm deps 20 | 21 | //app deps 22 | 23 | //begin module 24 | 25 | /** 26 | * This is the exposed module. 27 | * This method facilitates copying a file. 28 | * 29 | * @param {String} fileSrc 30 | * @param {String} fileDest 31 | * @param {Function} cb 32 | * @access public 33 | */ 34 | module.exports = function(fileSrc, fileDest, cb) { 35 | var cbCalled = false; 36 | 37 | var rd = fs.createReadStream(fileSrc); 38 | rd.on("error", function(err) { 39 | err.fileName = fileSrc; 40 | done(err); 41 | }); 42 | 43 | var wr = fs.createWriteStream(fileDest); 44 | wr.on("error", function(err) { 45 | err.fileName = fileDest; 46 | done(err); 47 | }); 48 | 49 | wr.on("close", function(ex) { 50 | done(); 51 | }); 52 | rd.pipe(wr); 53 | 54 | function done(err) { 55 | if (!cbCalled) { 56 | cb(err); 57 | cbCalled = true; 58 | } 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /examples/lib/download-file.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | var fs = require('fs'); 18 | var https = require('https'); 19 | var url = require('url'); 20 | 21 | //npm deps 22 | 23 | //app deps 24 | var isUndefined = require('../../common/lib/is-undefined'); 25 | var copyFile = require('./copy-file'); 26 | 27 | //begin module 28 | 29 | var supportedProtocols = { 30 | "https:": https 31 | }; 32 | 33 | /** 34 | * This is the exposed module. 35 | * This method facilitates downloading a file. 36 | * 37 | * @param {String} fileUrlStr 38 | * @param {String} fileDest 39 | * @param {Function} cb 40 | * @access public 41 | */ 42 | module.exports = function(fileUrlStr, fileDest, cb) { 43 | var fileUrl = url.parse(fileUrlStr); 44 | var protocolLib = supportedProtocols[ fileUrl.protocol ]; 45 | 46 | if (!isUndefined(protocolLib)) { 47 | var file = fs.createWriteStream(fileDest); 48 | protocolLib.get(fileUrlStr, function(res) { 49 | if (res.statusCode !== 200) { 50 | var err = new Error('file download failed'); 51 | err.fileName = fileDest; 52 | err.statusCode = res.statusCode; 53 | return cb(err); 54 | } 55 | 56 | res.on('data', function(data) { 57 | file.write(data); 58 | }).on('end', function() { 59 | file.end(); 60 | cb(); 61 | }); 62 | }).on('error', function (err) { 63 | console.log('downloadFile error'); 64 | fs.unlink(fileDest); 65 | err.fileName = fileDest; 66 | cb(err); 67 | }); 68 | } 69 | else { 70 | copyFile(fileUrl.pathname, fileDest, cb); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /examples/temperature-control/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "temperature-control", 3 | "description": "AWS IoT Temperature Control Simulation", 4 | "version": "1.0.0", 5 | "author": { 6 | "name":"Amazon Web Services", 7 | "email":"", 8 | "url":"http://aws.amazon.com" 9 | }, 10 | "main": "temperature-control.js", 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "blessed": "0.1.81", 14 | "blessed-contrib": "2.3.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/thing-example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const thingShadow = require('..').thingShadow; 22 | const cmdLineProcess = require('./lib/cmdline'); 23 | const isUndefined = require('../common/lib/is-undefined'); 24 | 25 | //begin module 26 | 27 | // 28 | // Simulate the interaction of a mobile device and a remote thing via the 29 | // AWS IoT service. The remote thing will be a dimmable color lamp, where 30 | // the individual RGB channels can be set to an intensity between 0 and 255. 31 | // One process will simulate each side, with testMode being used to distinguish 32 | // between the mobile app (1) and the remote thing (2). The remote thing 33 | // will update its state periodically using an 'update thing shadow' operation, 34 | // and the mobile device will listen to delta events to receive the updated 35 | // state information. 36 | // 37 | 38 | function processTest(args) { 39 | // 40 | // Instantiate the thing shadow class. 41 | // 42 | const thingShadows = thingShadow({ 43 | keyPath: args.privateKey, 44 | certPath: args.clientCert, 45 | caPath: args.caCert, 46 | clientId: args.clientId, 47 | region: args.region, 48 | baseReconnectTimeMs: args.baseReconnectTimeMs, 49 | keepalive: args.keepAlive, 50 | protocol: args.Protocol, 51 | port: args.Port, 52 | host: args.Host, 53 | debug: args.Debug 54 | }); 55 | 56 | // 57 | // Operation timeout in milliseconds 58 | // 59 | const operationTimeout = 10000; 60 | 61 | const thingName = 'RGBLedLamp'; 62 | 63 | var currentTimeout = null; 64 | 65 | // 66 | // For convenience, use a stack to keep track of the current client 67 | // token; in this example app, this should never reach a depth of more 68 | // than a single element, but if your application uses multiple thing 69 | // shadows simultaneously, you'll need some data structure to correlate 70 | // client tokens with their respective thing shadows. 71 | // 72 | var stack = []; 73 | 74 | function genericOperation(operation, state) { 75 | var clientToken = thingShadows[operation](thingName, state); 76 | 77 | if (clientToken === null) { 78 | // 79 | // The thing shadow operation can't be performed because another one 80 | // is pending; if no other operation is pending, reschedule it after an 81 | // interval which is greater than the thing shadow operation timeout. 82 | // 83 | if (currentTimeout !== null) { 84 | console.log('operation in progress, scheduling retry...'); 85 | currentTimeout = setTimeout( 86 | function() { 87 | genericOperation(operation, state); 88 | }, 89 | operationTimeout * 2); 90 | } 91 | } else { 92 | // 93 | // Save the client token so that we know when the operation completes. 94 | // 95 | stack.push(clientToken); 96 | } 97 | } 98 | 99 | function generateRandomState() { 100 | var rgbValues = { 101 | red: 0, 102 | green: 0, 103 | blue: 0 104 | }; 105 | 106 | rgbValues.red = Math.floor(Math.random() * 255); 107 | rgbValues.green = Math.floor(Math.random() * 255); 108 | rgbValues.blue = Math.floor(Math.random() * 255); 109 | 110 | return { 111 | state: { 112 | desired: rgbValues 113 | } 114 | }; 115 | } 116 | 117 | function mobileAppConnect() { 118 | thingShadows.register(thingName, { 119 | ignoreDeltas: false 120 | }, 121 | function(err, failedTopics) { 122 | if (isUndefined(err) && isUndefined(failedTopics)) { 123 | console.log('Mobile thing registered.'); 124 | } 125 | }); 126 | } 127 | 128 | function deviceConnect() { 129 | thingShadows.register(thingName, { 130 | ignoreDeltas: true 131 | }, 132 | function(err, failedTopics) { 133 | if (isUndefined(err) && isUndefined(failedTopics)) { 134 | console.log('Device thing registered.'); 135 | genericOperation('update', generateRandomState()); 136 | } 137 | }); 138 | } 139 | 140 | if (args.testMode === 1) { 141 | mobileAppConnect(); 142 | } else { 143 | deviceConnect(); 144 | } 145 | 146 | function handleStatus(thingName, stat, clientToken, stateObject) { 147 | var expectedClientToken = stack.pop(); 148 | 149 | if (expectedClientToken === clientToken) { 150 | console.log('got \'' + stat + '\' status on: ' + thingName); 151 | } else { 152 | console.log('(status) client token mismtach on: ' + thingName); 153 | } 154 | 155 | if (args.testMode === 2) { 156 | console.log('updated state to thing shadow'); 157 | // 158 | // If no other operation is pending, restart it after 10 seconds. 159 | // 160 | if (currentTimeout === null) { 161 | currentTimeout = setTimeout(function() { 162 | currentTimeout = null; 163 | genericOperation('update', generateRandomState()); 164 | }, 10000); 165 | } 166 | } 167 | } 168 | 169 | function handleDelta(thingName, stateObject) { 170 | if (args.testMode === 2) { 171 | console.log('unexpected delta in device mode: ' + thingName); 172 | } else { 173 | console.log('delta on: ' + thingName + JSON.stringify(stateObject)); 174 | } 175 | } 176 | 177 | function handleTimeout(thingName, clientToken) { 178 | var expectedClientToken = stack.pop(); 179 | 180 | if (expectedClientToken === clientToken) { 181 | console.log('timeout on: ' + thingName); 182 | } else { 183 | console.log('(timeout) client token mismtach on: ' + thingName); 184 | } 185 | 186 | if (args.testMode === 2) { 187 | genericOperation('update', generateRandomState()); 188 | } 189 | } 190 | 191 | thingShadows.on('connect', function() { 192 | console.log('connected to AWS IoT'); 193 | }); 194 | 195 | thingShadows.on('close', function() { 196 | console.log('close'); 197 | thingShadows.unregister(thingName); 198 | }); 199 | 200 | thingShadows.on('reconnect', function() { 201 | console.log('reconnect'); 202 | }); 203 | 204 | thingShadows.on('offline', function() { 205 | // 206 | // If any timeout is currently pending, cancel it. 207 | // 208 | if (currentTimeout !== null) { 209 | clearTimeout(currentTimeout); 210 | currentTimeout = null; 211 | } 212 | // 213 | // If any operation is currently underway, cancel it. 214 | // 215 | while (stack.length) { 216 | stack.pop(); 217 | } 218 | console.log('offline'); 219 | }); 220 | 221 | thingShadows.on('error', function(error) { 222 | console.log('error', error); 223 | }); 224 | 225 | thingShadows.on('message', function(topic, payload) { 226 | console.log('message', topic, payload.toString()); 227 | }); 228 | 229 | thingShadows.on('status', function(thingName, stat, clientToken, stateObject) { 230 | handleStatus(thingName, stat, clientToken, stateObject); 231 | }); 232 | 233 | thingShadows.on('delta', function(thingName, stateObject) { 234 | handleDelta(thingName, stateObject); 235 | }); 236 | 237 | thingShadows.on('timeout', function(thingName, clientToken) { 238 | handleTimeout(thingName, clientToken); 239 | }); 240 | } 241 | 242 | module.exports = cmdLineProcess; 243 | 244 | if (require.main === module) { 245 | cmdLineProcess('connect to the AWS IoT service and demonstrate thing shadow APIs, test modes 1-2', 246 | process.argv.slice(2), processTest); 247 | } 248 | -------------------------------------------------------------------------------- /examples/thing-passthrough-example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const thingShadow = require('..').thingShadow; 22 | const cmdLineProcess = require('./lib/cmdline'); 23 | const isUndefined = require('../common/lib/is-undefined'); 24 | 25 | //begin module 26 | 27 | // 28 | // This test demonstrates the use of thing shadows along with 29 | // non-thing topics. One process updates a thing shadow and 30 | // subscribes to a non-thing topic; the other receives delta 31 | // updates on the thing shadow on publishes to the non-thing 32 | // topic. 33 | // 34 | function processTest(args) { 35 | // 36 | // Instantiate the thing shadow class. 37 | // 38 | const thingShadows = thingShadow({ 39 | keyPath: args.privateKey, 40 | certPath: args.clientCert, 41 | caPath: args.caCert, 42 | clientId: args.clientId, 43 | region: args.region, 44 | baseReconnectTimeMs: args.baseReconnectTimeMs, 45 | protocol: args.Protocol, 46 | port: args.Port, 47 | host: args.Host, 48 | debug: args.Debug 49 | }); 50 | 51 | // 52 | // Operation timeout in milliseconds 53 | // 54 | const operationTimeout = 10000; 55 | 56 | const thingName = 'thingShadow1'; 57 | const nonThingName = 'nonThingTopic1'; 58 | 59 | // 60 | // The current operation count 61 | // 62 | var count = 1; 63 | 64 | var currentTimeout = null; 65 | 66 | // 67 | // For convenience, use a stack to keep track of the current client 68 | // token; in this example app, this should never reach a depth of more 69 | // than a single element, but if your application uses multiple thing 70 | // shadows simultaneously, you'll need some data structure to correlate 71 | // client tokens with their respective thing shadows. 72 | // 73 | var stack = []; 74 | 75 | function genericOperation(operation, state) { 76 | var clientToken = thingShadows[operation](thingName, state); 77 | if (clientToken === null) { 78 | // 79 | // The thing shadow operation can't be performed because another one 80 | // is pending; if no other operation is pending, reschedule it after an 81 | // interval which is greater than the thing shadow operation timeout. 82 | // 83 | if (currentTimeout !== null) { 84 | console.log('operation in progress, scheduling retry...'); 85 | currentTimeout = setTimeout( 86 | function() { 87 | genericOperation(operation, state); 88 | }, 89 | operationTimeout * 2); 90 | } 91 | } else { 92 | // 93 | // Save the client token so that we know when the operation completes. 94 | // 95 | stack.push(clientToken); 96 | } 97 | } 98 | 99 | function generateState() { 100 | return { 101 | state: { 102 | desired: { 103 | value: count++ 104 | } 105 | } 106 | }; 107 | } 108 | 109 | function thingUpdateeConnect() { 110 | // 111 | // This process receives deltas from the thing shadow and publishes the 112 | // data to the non-thing topic. 113 | // 114 | thingShadows.register(thingName, { 115 | ignoreDeltas: false 116 | }); 117 | } 118 | 119 | function thingUpdaterConnect() { 120 | // 121 | // This process updates the thing shadow and subscribes to the non-thing 122 | // topic. 123 | // 124 | thingShadows.register(thingName, { 125 | ignoreDeltas: true 126 | }, function(err, failedTopics){ 127 | if (isUndefined(err) && isUndefined(failedTopics)) { 128 | genericOperation('update', generateState()); 129 | thingShadows.subscribe(nonThingName); 130 | } 131 | }); 132 | } 133 | 134 | if (args.testMode === 1) { 135 | thingUpdateeConnect(); 136 | } else { 137 | thingUpdaterConnect(); 138 | } 139 | 140 | function handleStatus(thingName, stat, clientToken, stateObject) { 141 | var expectedClientToken = stack.pop(); 142 | 143 | if (expectedClientToken === clientToken) { 144 | console.log('got \'' + stat + '\' status on: ' + thingName); 145 | } else { 146 | console.log('(status) client token mismtach on: ' + thingName); 147 | } 148 | 149 | if (args.testMode === 2) { 150 | console.log('updated state to thing shadow'); 151 | // 152 | // If no other operation is pending, restart it after 10 seconds. 153 | // 154 | if (currentTimeout === null) { 155 | currentTimeout = setTimeout(function() { 156 | currentTimeout = null; 157 | genericOperation('update', generateState()); 158 | }, 10000); 159 | } 160 | } 161 | } 162 | 163 | function handleDelta(thingName, stateObject) { 164 | if (args.testMode === 2) { 165 | console.log('unexpected delta in device mode: ' + thingName); 166 | } else { 167 | console.log('received delta on ' + thingName + 168 | ', publishing on non-thing topic...'); 169 | thingShadows.publish(nonThingName, 170 | JSON.stringify({ 171 | message: 'received ' + 172 | JSON.stringify(stateObject.state) 173 | })); 174 | } 175 | } 176 | 177 | function handleTimeout(thingName, clientToken) { 178 | var expectedClientToken = stack.pop(); 179 | 180 | if (expectedClientToken === clientToken) { 181 | console.log('timeout on: ' + thingName); 182 | } else { 183 | console.log('(timeout) client token mismtach on: ' + thingName); 184 | } 185 | 186 | if (args.testMode === 2) { 187 | genericOperation('update', generateState()); 188 | } 189 | } 190 | 191 | function handleMessage(topic, payload) { 192 | console.log('received on \'' + topic + '\': ' + payload.toString()); 193 | } 194 | 195 | thingShadows.on('connect', function() { 196 | console.log('connected to AWS IoT'); 197 | }); 198 | 199 | thingShadows.on('close', function() { 200 | console.log('close'); 201 | thingShadows.unregister(thingName); 202 | }); 203 | 204 | thingShadows.on('reconnect', function() { 205 | console.log('reconnect'); 206 | }); 207 | 208 | thingShadows.on('offline', function() { 209 | // 210 | // If any timeout is currently pending, cancel it. 211 | // 212 | if (currentTimeout !== null) { 213 | clearTimeout(currentTimeout); 214 | currentTimeout = null; 215 | } 216 | // 217 | // If any operation is currently underway, cancel it. 218 | // 219 | while (stack.length) { 220 | stack.pop(); 221 | } 222 | console.log('offline'); 223 | }); 224 | 225 | thingShadows.on('error', function(error) { 226 | console.log('error', error); 227 | }); 228 | 229 | thingShadows.on('message', function(topic, payload) { 230 | console.log('message', topic, payload.toString()); 231 | }); 232 | 233 | thingShadows.on('status', function(thingName, stat, clientToken, stateObject) { 234 | handleStatus(thingName, stat, clientToken, stateObject); 235 | }); 236 | 237 | thingShadows.on('delta', function(thingName, stateObject) { 238 | handleDelta(thingName, stateObject); 239 | }); 240 | 241 | thingShadows.on('timeout', function(thingName, clientToken) { 242 | handleTimeout(thingName, clientToken); 243 | }); 244 | 245 | thingShadows.on('message', function(topic, payload) { 246 | handleMessage(topic, payload); 247 | }); 248 | } 249 | 250 | module.exports = cmdLineProcess; 251 | 252 | if (require.main === module) { 253 | cmdLineProcess('connect to the AWS IoT service and demonstrate thing shadow APIs, test modes 1-2', 254 | process.argv.slice(2), processTest); 255 | } 256 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | /*jshint node:true*/ 16 | 'use strict'; 17 | 18 | var gulp = require('gulp'), 19 | jshint = require('gulp-jshint'), 20 | concat = require('gulp-concat'), 21 | mocha = require('gulp-mocha'), 22 | cover = require('gulp-coverage'), 23 | jscs = require('gulp-jscs'), 24 | beautify = require('gulp-beautify'); 25 | 26 | gulp.task('default', ['test']); 27 | 28 | gulp.task('test', ['jshint'], function() { 29 | console.log('Running unit tests'); 30 | return gulp.src(['test/*unit-tests.js'], {read: false}) 31 | .pipe(cover.instrument({ 32 | pattern: ['common/lib/*.js','device/**/*.js','thing/*.js','index.js'], 33 | debugDirectory: 'debug' 34 | })) 35 | .pipe(mocha({ 36 | reporter: 'spec', 37 | globals: {} 38 | })) 39 | .pipe(cover.gather()) 40 | .pipe(cover.format()) 41 | .pipe(gulp.dest('reports')) 42 | .once('end', function() { 43 | process.exit(); 44 | }); 45 | }); 46 | 47 | gulp.task('jshint', function() { 48 | console.log('Analyzing source with JSHint and JSCS'); 49 | return gulp 50 | .src(['common/lib/*.js','examples/**/*.js', 'device/**/*.js','thing/*.js','index.js', '!node_modules/**/*.js', '!examples/**/node_modules/**/*.js', '!examples/**/aws-configuration.js', '!browser/**/*bundle.js', '!examples/browser/**/*bundle.js']) 51 | .pipe(jshint()) 52 | .pipe(jshint.reporter('jshint-stylish', {verbose: true})) 53 | .pipe(jshint.reporter('fail')) 54 | .pipe(jscs()); 55 | }); 56 | 57 | gulp.task('beautify', function() { 58 | console.log('Beautifying source with indent level 3'); 59 | return gulp 60 | .src(['!browser/**/*bundle.js', '!examples/**/*bundle.js', 'browser/**/*.js','common/**/*.js','examples/**/*.js', 'device/**/*.js','thing/*.js','index.js', '!node_modules/**/*.js', '!examples/**/node_modules/**/*.js', '!browser/**/*bundle.js', '!examples/browser/**/*bundle.js']) 61 | .pipe(beautify({'indent_size':3, 'indent_char': ' ', 'end_with_newline': true})) 62 | // 63 | // Replace the files in-place with the beautified versions. 64 | // 65 | .pipe(gulp.dest( function(vinylFile) { console.log('Beautifying \''+vinylFile.path+'\'...'); return vinylFile.base; })); 66 | }); 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | /* 17 | * Expose AWS IoT Embedded Javascript SDK modules 18 | */ 19 | module.exports.device = require('./device'); 20 | module.exports.thingShadow = require('./thing'); 21 | module.exports.jobs = require('./jobs'); 22 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/device-integration-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const deviceModule = require('..').device; 22 | const cmdLineProcess = require('../examples/lib/cmdline'); 23 | const isUndefined = require('../common/lib/is-undefined.js'); 24 | 25 | //begin module 26 | 27 | function processTest( args, argsRemaining ) { 28 | // 29 | // Use the command line flag '--thing-name | -T' to pass in a topic 30 | // prefix; this allows us to run multiple copies of this test simultaneously 31 | // within the same AWS account 32 | // 33 | var topicPrefix = args.thingName; 34 | var integrationTestTopic = 'deviceIntegrationTestTopic'; 35 | 36 | if (!isUndefined(topicPrefix)) { 37 | integrationTestTopic=topicPrefix+'/'+integrationTestTopic; 38 | } 39 | 40 | var customAuthHeaders; 41 | var region = args.region; 42 | var customAuthUsername; 43 | var customAuthPassword; 44 | 45 | var enableMetrics = true; 46 | 47 | if(args.Protocol === 'wss-custom-auth') { 48 | customAuthHeaders = JSON.parse(process.env.CUSTOM_AUTH_HEADERS); 49 | region = 'us-east-1'; 50 | customAuthUsername = "username?x-amz-customauthorizer-name=" + process.env.CUSTOM_AUTH_NAME; 51 | customAuthPassword = process.env.CUSTOM_AUTH_PASSWORD; 52 | enableMetrics = false; 53 | } 54 | 55 | // 56 | // The device module exports an MQTT instance, which will attempt 57 | // to connect to the AWS IoT endpoint configured in the arguments. 58 | // Once connected, it will emit events which our application can 59 | // handle. 60 | // 61 | const device = deviceModule({ 62 | keyPath: args.privateKey, 63 | certPath: args.clientCert, 64 | caPath: args.caCert, 65 | clientId: args.clientId, 66 | region: region, 67 | reconnectPeriod: args.reconnectPeriod, 68 | protocol: args.Protocol, 69 | port: args.Port, 70 | host: args.Host, 71 | debug: args.Debug, 72 | customAuthHeaders: customAuthHeaders, 73 | username: customAuthUsername, 74 | password: customAuthPassword, 75 | enableMetrics: enableMetrics, 76 | }); 77 | 78 | var timeout; 79 | var value=1; 80 | var count=1; 81 | var accumulator=0; 82 | 83 | function checkAccumulator() 84 | { 85 | // 86 | // Check the accumulator to see how many messages were received from the 87 | // partner process via the MQTT passthrough. 88 | // 89 | var i, messages = 0, accSave=accumulator; 90 | for (i = 0; i < 48; i++) 91 | { 92 | if (accumulator & 1) 93 | { 94 | messages++; 95 | } 96 | accumulator = accumulator>>1; 97 | } 98 | console.log(messages+' messages received, accumulator='+accSave.toString(16)); 99 | } 100 | 101 | // 102 | // Do a simple publish/subscribe demo based on the test-mode passed 103 | // in the command line arguments. 104 | // 105 | device 106 | .on('connect', function() { 107 | const minimumDelay=250; 108 | console.log('connect'); 109 | if (args.testMode === 2) 110 | { 111 | device.subscribe(integrationTestTopic); 112 | } 113 | else 114 | { 115 | if ((Math.max(args.delay,minimumDelay) ) !== args.delay) 116 | { 117 | console.log( 'substituting '+ minimumDelay + 'ms delay for ' + args.delay + 'ms...' ); 118 | } 119 | timeout = setInterval( function() { 120 | console.log('publishing value='+value+' on \''+integrationTestTopic+'\''); 121 | device.publish(integrationTestTopic, JSON.stringify({ 122 | value: value }), { qos: 1 }); 123 | 124 | // 125 | // After 48 publishes, exit the process. This number is chosen as it's the last power of 2 less 126 | // than 53 (the mantissa size for Node's native floating point numbers). 127 | // 128 | value=value*2; 129 | count++; 130 | if (count >= 48) 131 | { 132 | device.publish(integrationTestTopic, JSON.stringify({ 133 | quit: 1 })); 134 | setTimeout( function() { 135 | process.exit(0); }, 500); 136 | } 137 | }, Math.max(args.delay,minimumDelay) ); // clip to minimum 138 | } 139 | }); 140 | device 141 | .on('close', function() { 142 | console.log('close'); 143 | process.exit(1); 144 | }); 145 | device 146 | .on('reconnect', function() { 147 | console.log('reconnect'); 148 | process.exit(1); 149 | }); 150 | device 151 | .on('offline', function() { 152 | console.log('offline'); 153 | process.exit(1); 154 | }); 155 | device 156 | .on('error', function(error) { 157 | console.log('error', error); 158 | process.exit(1); 159 | }); 160 | device 161 | .on('message', function(topic, payload) { 162 | 163 | var stateObject = JSON.parse( payload.toString() ); 164 | console.log('received on \''+topic+'\': '+payload.toString()); 165 | if (!isUndefined( stateObject.value )) 166 | { 167 | accumulator+=stateObject.value; 168 | } 169 | if (!isUndefined( stateObject.quit)) 170 | { 171 | checkAccumulator(); 172 | setTimeout( function() { process.exit(0); }, 500 ); 173 | } 174 | }); 175 | 176 | } 177 | 178 | module.exports = cmdLineProcess; 179 | 180 | if (require.main === module) { 181 | cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2', 182 | process.argv.slice(2), processTest ); 183 | } 184 | 185 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/jobs-integration-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const jobsModule = require('..').jobs; 22 | const cmdLineProcess = require('../examples/lib/cmdline'); 23 | const isUndefined = require('../common/lib/is-undefined.js'); 24 | const awsSDK = require('aws-sdk'); 25 | awsSDK.config.update({ "accessKeyId": process.env.JOBS_AWS_ACCESS_KEY_ID, "secretAccessKey": process.env.JOBS_AWS_SECRET_ACCESS_KEY, "region": "us-east-1" }); 26 | 27 | var iot = new awsSDK.Iot(); 28 | 29 | //begin module 30 | 31 | function processTest( args, argsRemaining ) { 32 | // 33 | // Use the command line flag '--thing-name | -T' to pass in a topic 34 | // prefix; this allows us to run multiple copies of this test simultaneously 35 | // within the same AWS account 36 | // 37 | 38 | var topicPrefix = args.thingName; 39 | var thingName = args.thingName; 40 | var integrationTestTopic = 'jobsIntegrationTestTopic'; 41 | var preRegisteredThingName = 'testThing1'; 42 | 43 | if (!isUndefined(topicPrefix)) { 44 | integrationTestTopic=topicPrefix+'/'+integrationTestTopic; 45 | } 46 | 47 | // 48 | // The jobs module exports an MQTT instance, which will attempt 49 | // to connect to the AWS IoT endpoint configured in the arguments. 50 | // Once connected, it will emit events which our application can 51 | // handle. 52 | // 53 | const jobs = jobsModule({ 54 | keyPath: args.privateKey, 55 | certPath: args.clientCert, 56 | caPath: args.caCert, 57 | clientId: args.clientId, 58 | region: args.region, 59 | reconnectPeriod: args.reconnectPeriod, 60 | protocol: args.Protocol, 61 | port: args.Port, 62 | host: args.Host, 63 | debug: args.Debug 64 | }); 65 | 66 | var timeout; 67 | var value=1; 68 | var count=1; 69 | var accumulator=0; 70 | var jobCount=5; 71 | var jobCompletedCount=0; 72 | 73 | function checkAccumulator() 74 | { 75 | console.log('checkAccumulator'); 76 | // 77 | // Check the accumulator to see how many messages were received from the 78 | // partner process via the MQTT passthrough. 79 | // 80 | var i, messages = 0, accSave=accumulator; 81 | for (i = 0; i < 48; i++) 82 | { 83 | if (accumulator & 1) 84 | { 85 | messages++; 86 | } 87 | accumulator = accumulator>>1; 88 | } 89 | console.log(messages+' messages received, accumulator='+accSave.toString(16)); 90 | console.log(jobCompletedCount+' jobs completed'); 91 | } 92 | 93 | function handleJob(err, job) 94 | { 95 | if (isUndefined(err)) { 96 | console.log('received job: ' + JSON.stringify(job.document)); 97 | job.inProgress(function(err) { 98 | if (isUndefined(err)) { 99 | var jobFunction = job.succeeded; 100 | if (job.document.jobNum & 1) { 101 | jobFunction = job.failed; 102 | } 103 | 104 | jobFunction(function(err) { 105 | if (!isUndefined(err)) { 106 | console.log(err); 107 | } 108 | }); 109 | } else { 110 | console.log(err); 111 | } 112 | }); 113 | } else { 114 | console.log(err); 115 | } 116 | } 117 | 118 | // 119 | // Do a simple publish/subscribe demo based on the test-mode passed 120 | // in the command line arguments. 121 | // 122 | jobs 123 | .on('connect', function() { 124 | const minimumDelay=250; 125 | console.log('connect'); 126 | if (args.testMode === 2) 127 | { 128 | jobs.subscribe(integrationTestTopic); 129 | for (var i = 0; i < jobCount - 1; i++) { 130 | jobs.subscribeToJobs(preRegisteredThingName, 'test' + i.toString(), handleJob); 131 | } 132 | 133 | jobs.subscribeToJobs(preRegisteredThingName, handleJob); 134 | 135 | jobs.startJobNotifications(preRegisteredThingName); 136 | } 137 | else 138 | { 139 | var jobIdPrefix = 'test-job-id-' + (Math.floor(Math.random() * 99999999)).toString(); 140 | 141 | for (var i = 0; i < jobCount; i++) { 142 | iot.createJob({ jobId: jobIdPrefix + '-' + i.toString(), targets: [ 'arn:aws:iot:us-east-1:809478692717:thing/' + preRegisteredThingName ], document: '{ "operation":"test' + i.toString() + '", "jobNum": ' + i.toString() + ' }' }, function(err, data) { 143 | console.log('createJob:'); 144 | if (isUndefined(err)) { 145 | console.log(data); 146 | } else { 147 | console.log(err); 148 | } 149 | }); 150 | } 151 | 152 | // 153 | // Test device messaging through jobs module 154 | // 155 | if ((Math.max(args.delay,minimumDelay) ) !== args.delay) 156 | { 157 | console.log( 'substituting '+ minimumDelay + 'ms delay for ' + args.delay + 'ms...' ); 158 | } 159 | 160 | setTimeout( function() { 161 | function checkJobExecutions(jobNum) { 162 | if (jobNum < jobCount) { 163 | console.log('checking execution status on ' + preRegisteredThingName + ' for job: ' + jobIdPrefix + '-' + jobNum.toString()); 164 | iot.describeJobExecution({ thingName: preRegisteredThingName, jobId: jobIdPrefix + '-' + jobNum.toString() }, function(err, data) { 165 | if (!isUndefined(data) && !isUndefined(data.execution) && 166 | ((jobNum & 1) ? data.execution.status === 'FAILED' : (data.execution.status === 'SUCCESS' || data.execution.status === 'SUCCEEDED'))) { 167 | jobCompletedCount++; 168 | } 169 | 170 | console.log('cancelling job ' + jobIdPrefix + '-' + jobNum.toString() + ' to prevent leaving orphan jobs'); 171 | iot.cancelJob({ jobId: jobIdPrefix + '-' + jobNum.toString() }); 172 | 173 | checkJobExecutions(jobNum + 1); 174 | }); 175 | } 176 | } 177 | 178 | console.log( 'tally completed job executions' ); 179 | checkJobExecutions(0); 180 | }, 15000); 181 | 182 | timeout = setInterval( function() { 183 | console.log('publishing value='+value+' on \''+integrationTestTopic+'\''); 184 | jobs.publish(integrationTestTopic, JSON.stringify({ 185 | value: value }), { qos: 1 }); 186 | 187 | // 188 | // After 48 publishes, exit the process. This number is chosen as it's the last power of 2 less 189 | // than 53 (the mantissa size for Node's native floating point numbers). 190 | // 191 | value=value*2; 192 | count++; 193 | if (count >= 48) 194 | { 195 | jobs.publish(integrationTestTopic, JSON.stringify({ quit: 1, jobCompletedCount: jobCompletedCount }), function() { 196 | setTimeout( function() { process.exit(0); }, 500); 197 | }); 198 | } 199 | }, Math.max(args.delay,minimumDelay) ); // clip to minimum 200 | } 201 | }); 202 | jobs 203 | .on('close', function() { 204 | console.log('close'); 205 | process.exit(1); 206 | }); 207 | jobs 208 | .on('reconnect', function() { 209 | console.log('reconnect'); 210 | process.exit(1); 211 | }); 212 | jobs 213 | .on('offline', function() { 214 | console.log('offline'); 215 | process.exit(1); 216 | }); 217 | jobs 218 | .on('error', function(error) { 219 | console.log('error', error); 220 | process.exit(1); 221 | }); 222 | jobs 223 | .on('message', function(topic, payload) { 224 | 225 | var stateObject = JSON.parse( payload.toString() ); 226 | console.log('received on \''+topic+'\': '+payload.toString()); 227 | if (!isUndefined( stateObject.value )) 228 | { 229 | accumulator+=stateObject.value; 230 | } 231 | if (!isUndefined(stateObject.quit)) 232 | { 233 | jobCompletedCount = (!isUndefined(stateObject.jobCompletedCount) ? stateObject.jobCompletedCount : 0); 234 | checkAccumulator(); 235 | setTimeout( function() { process.exit(0); }, 500 ); 236 | } 237 | }); 238 | 239 | } 240 | 241 | module.exports = cmdLineProcess; 242 | 243 | if (require.main === module) { 244 | cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2', 245 | process.argv.slice(2), processTest ); 246 | } 247 | 248 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/offline-publishing-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | const deviceModule = require('..').device; 22 | const cmdLineProcess = require('../examples/lib/cmdline'); 23 | const isUndefined = require('../common/lib/is-undefined.js'); 24 | 25 | const RECEIVER_TIMEOUT_DELAY = 60000; 26 | const TOTAL_MESSAGES_TO_SEND = 1000; 27 | const RECONNECTS_BEFORE_FAILURE = 3; 28 | 29 | //begin module 30 | 31 | function processTest(args) { 32 | // 33 | // Use the command line flag '--thing-name | -T' to pass in a topic 34 | // prefix; this allows us to run multiple copies of this test simultaneously 35 | // within the same AWS account 36 | // 37 | var topicPrefix = args.thingName; 38 | var offlinePublishTestTopic = 'offlinePublishTestTopic'; 39 | 40 | if (!isUndefined(topicPrefix)) { 41 | offlinePublishTestTopic=topicPrefix+'/'+offlinePublishTestTopic; 42 | } 43 | 44 | var customAuthHeaders; 45 | var region = args.region; 46 | var customAuthUsername; 47 | var customAuthPassword; 48 | 49 | var enableMetrics = true; 50 | 51 | if(args.Protocol === 'wss-custom-auth') { 52 | customAuthHeaders = JSON.parse(process.env.CUSTOM_AUTH_HEADERS); 53 | region = 'us-east-1'; 54 | customAuthUsername = "username?x-amz-customauthorizer-name=" + process.env.CUSTOM_AUTH_NAME; 55 | customAuthPassword = process.env.CUSTOM_AUTH_PASSWORD; 56 | enableMetrics = false; 57 | } 58 | 59 | // 60 | // The device module exports an MQTT instance, which will attempt 61 | // to connect to the AWS IoT endpoint configured in the arguments. 62 | // Once connected, it will emit events which our application can 63 | // handle. 64 | // 65 | const device = deviceModule({ 66 | keyPath: args.privateKey, 67 | certPath: args.clientCert, 68 | caPath: args.caCert, 69 | clientId: args.clientId, 70 | region: region, 71 | baseReconnectTimeMs: args.baseReconnectTimeMs, 72 | drainTimeMs: 35, 73 | offlineQueueing: true, 74 | keepalive: args.keepAlive, 75 | protocol: args.Protocol, 76 | port: args.Port, 77 | host: args.Host, 78 | debug: args.Debug, 79 | customAuthHeaders: customAuthHeaders, 80 | username: customAuthUsername, 81 | password: customAuthPassword, 82 | enableMetrics: enableMetrics, 83 | }); 84 | var receiveCount = 0; 85 | var outOfOrderCount = 0; 86 | var connectCount = 0; 87 | var quitTimeout; 88 | var expectedSum = 0; 89 | var actualSum = 0; 90 | 91 | var reconnectsSinceLastSuccessfulConnect = 0; 92 | 93 | function receiverExitWithError() { 94 | console.log('No messages received in the past ' + RECEIVER_TIMEOUT_DELAY + ' ms, exiting test'); 95 | process.exit(1); 96 | } 97 | 98 | if (args.testMode === 1) { 99 | // 100 | // This process is the receiver 101 | // 102 | var noMessageReceivedTimeout; 103 | noMessageReceivedTimeout = setTimeout(receiverExitWithError, RECEIVER_TIMEOUT_DELAY); 104 | device.subscribe(offlinePublishTestTopic); 105 | } else { 106 | var publishTimeout; 107 | var disconnectTimeout; 108 | var transmitCount = 0; 109 | const minimumDelay = 100; 110 | 111 | if ((Math.max(args.delay, minimumDelay)) !== args.delay) { 112 | console.log('substituting ' + minimumDelay + 'ms delay for ' + args.delay + 'ms...'); 113 | } 114 | publishTimeout = setInterval(function() { 115 | transmitCount++; 116 | device.publish(offlinePublishTestTopic, JSON.stringify({ 117 | value: transmitCount 118 | })); 119 | if(transmitCount > TOTAL_MESSAGES_TO_SEND) { 120 | clearInterval(publishTimeout); 121 | setTimeout( function() { 122 | process.exit(0); }, 500); 123 | } 124 | }, Math.max(args.delay, minimumDelay)); // clip to minimum 125 | } 126 | 127 | device 128 | .on('connect', function() { 129 | connectCount++; 130 | reconnectsSinceLastSuccessfulConnect = 0; 131 | if (args.testMode === 2) { 132 | if (connectCount < 10) { 133 | disconnectTimeout = setTimeout(function() { 134 | console.log("Connect count: " + connectCount); 135 | device.simulateNetworkFailure(); 136 | }, 20000); // disconnect us every 20 seconds 137 | } else { 138 | quitTimeout = setTimeout(function() { 139 | device.publish(offlinePublishTestTopic, JSON.stringify({ 140 | value: 'quit' 141 | }), { 142 | qos: 0 143 | }); 144 | setTimeout(function() { 145 | process.exit(0); 146 | }, 1500); 147 | }, 10000); /* run the test for just under 10 additional seconds */ 148 | } 149 | } 150 | console.log('connect'); 151 | }); 152 | device 153 | .on('close', function() { 154 | console.log('close'); 155 | }); 156 | device 157 | .on('reconnect', function() { 158 | console.log('reconnect'); 159 | reconnectsSinceLastSuccessfulConnect++; 160 | if(reconnectsSinceLastSuccessfulConnect > RECONNECTS_BEFORE_FAILURE) { 161 | console.log('attempted to reconnect too many times'); 162 | process.exit(2); 163 | } 164 | }); 165 | device 166 | .on('offline', function() { 167 | console.log('offline'); 168 | }); 169 | device 170 | .on('error', function(error) { 171 | console.log('error', error); 172 | }); 173 | device 174 | .on('message', function(topic, payload) { 175 | if (args.testMode === 1) { 176 | var obj = JSON.parse(payload.toString()); 177 | if (obj.value === 'quit') { 178 | var errorRate = (expectedSum / actualSum); 179 | 180 | console.log('quality (closer to 1.0 = fewer drops): ' + errorRate.toFixed(6)); 181 | setTimeout(function() { 182 | process.exit(0); 183 | }, 500); 184 | } else { 185 | receiveCount++; 186 | expectedSum += receiveCount; 187 | actualSum += obj.value; 188 | if (obj.value !== receiveCount) { 189 | outOfOrderCount++; 190 | } 191 | clearTimeout(noMessageReceivedTimeout); 192 | noMessageReceivedTimeout = setTimeout(receiverExitWithError, RECEIVER_TIMEOUT_DELAY); 193 | } 194 | } 195 | console.log('message', topic, payload.toString()); 196 | }); 197 | } 198 | 199 | module.exports = cmdLineProcess; 200 | 201 | if (require.main === module) { 202 | cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2', 203 | process.argv.slice(2), processTest); 204 | } 205 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/run-device-integration-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run the device-mode integration test. 4 | # 5 | # 6 | # Check to make sure the top-level test directory is defined. 7 | # 8 | if [ $NPMTEST_DIR"" = "" ] 9 | then 10 | echo ${0##*/}": NPMTEST_DIR must be defined!" 11 | exit 1 12 | fi 13 | # 14 | # Set the name of the node executable (it should be in our path) 15 | # 16 | NODE=node 17 | # 18 | # Set a randomized test tag to isolate this test run from others within 19 | # the AWS account associated with our Odin material set. This ensures 20 | # that all topic names used are unique to this test run. 21 | # 22 | TEST_TAG="test-"$RANDOM 23 | # 24 | # Capture the exit code of the first command which fails in a pipeline. 25 | # 26 | set -o pipefail 27 | # 28 | # The integration tests pass a number of updates between two partner 29 | # processes. Because these are QoS 0, delivery isn't guaranteed, but 30 | # we should expect to receive most of them. All tests use the same number 31 | # of updates (48). 32 | # 33 | RECEIVES_REQUIRED=46 34 | TRANSMITS_TOTAL=48 35 | export HOSTNAME="ajje7lpljulm4-ats.iot.us-east-1.amazonaws.com" 36 | export CUSTOM_AUTH_HOST=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 37 | # 38 | # Process output will be captured in these files. 39 | # 40 | PROC1_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-1.out 41 | PROC2_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-2.out 42 | # 43 | # Move the test javascript program to the installed SDK 44 | # directory 45 | # 46 | INT_TEST_DIR=$NPMTEST_DIR/aws-iot-device-sdk-js/integration-test 47 | mkdir -p $INT_TEST_DIR 48 | cp ${0%/*}/device-integration-test.js $INT_TEST_DIR 49 | # 50 | # Start the two partner processes for the device-mode integration test and 51 | # save their PIDs. 52 | # 53 | if [ $AUTHENTICATION_TYPE"" == "certificate" ] 54 | then 55 | echo "###################################################################" 56 | echo ${0##*/}": running device integration test (certificate auth)" 57 | echo "###################################################################" 58 | $NODE $INT_TEST_DIR/device-integration-test.js -H $HOSTNAME -f $CERT_DIR -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 59 | PROC2_PID=$! 60 | $NODE $INT_TEST_DIR/device-integration-test.js -H $HOSTNAME -f $CERT_DIR -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 61 | PROC1_PID=$! 62 | elif [ $AUTHENTICATION_TYPE"" == "custom-auth" ] 63 | then 64 | echo "###################################################################" 65 | echo ${0##*/}": running device integration test (websocket/custom auth)" 66 | echo "###################################################################" 67 | $NODE $INT_TEST_DIR/device-integration-test.js -H $CUSTOM_AUTH_HOST -P=wss-custom-auth -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 68 | PROC2_PID=$! 69 | $NODE $INT_TEST_DIR/device-integration-test.js -H $CUSTOM_AUTH_HOST -P=wss-custom-auth -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 70 | PROC1_PID=$! 71 | else 72 | echo "###################################################################" 73 | echo ${0##*/}": running device integration test (websocket/sigv4)" 74 | echo "###################################################################" 75 | $NODE $INT_TEST_DIR/device-integration-test.js -H $HOSTNAME -P=wss -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 76 | PROC2_PID=$! 77 | $NODE $INT_TEST_DIR/device-integration-test.js -H $HOSTNAME -P=wss -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 78 | PROC1_PID=$! 79 | fi 80 | # 81 | # Wait on the two partner processes and record their exit codes. 82 | # 83 | wait $PROC1_PID 84 | PROC1_EXIT_CODE=$? 85 | wait $PROC2_PID 86 | PROC2_EXIT_CODE=$? 87 | # 88 | # Combine the two exit codes; if either process exited abnormally, this 89 | # test is a failure. 90 | # 91 | COMBINED_EXIT_CODE=$((PROC1_EXIT_CODE | PROC_2_EXIT_CODE)) 92 | if [ $COMBINED_EXIT_CODE"" = "0" ] 93 | then 94 | numReceived=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $1}'` 95 | receiveMask=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $4}'|sed -e 's/.*=//'` 96 | 97 | echo $numReceived" messages received, receive mask ["$receiveMask"]" 98 | 99 | if [ $numReceived"" -gt $RECEIVES_REQUIRED"" ] 100 | then 101 | echo "########################################################" 102 | echo " TEST SUCCEEDED: RECEIVED "$numReceived"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 103 | echo "########################################################" 104 | else 105 | echo "########################################################" 106 | echo " TEST FAILED: RECEIVED "$numReceived"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 107 | echo "########################################################" 108 | exit 2 109 | fi 110 | else 111 | echo ${0##*/}": FAILED ("$PROC1_EXIT_CODE":"$PROC2_EXIT_CODE")" 112 | exit $COMBINED_EXIT_CODE 113 | fi 114 | rm $PROC1_OUTFILE $PROC2_OUTFILE 115 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/run-jobs-integration-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run the jobs-mode integration test. 4 | # 5 | # 6 | # Check to make sure the top-level test directory is defined. 7 | # 8 | if [ $NPMTEST_DIR"" = "" ] 9 | then 10 | echo ${0##*/}": NPMTEST_DIR must be defined!" 11 | exit 1 12 | fi 13 | # 14 | # Set the name of the node executable (it should be in our path) 15 | # 16 | NODE=node 17 | # 18 | # Set a randomized test tag to isolate this test run from others within 19 | # the AWS account associated with our Odin material set. This ensures 20 | # that all topic names used are unique to this test run. 21 | # 22 | TEST_TAG="test-"$RANDOM 23 | # 24 | # Capture the exit code of the first command which fails in a pipeline. 25 | # 26 | set -o pipefail 27 | # 28 | # The integration tests pass a number of updates between two partner 29 | # processes. Because these are QoS 0, delivery isn't guaranteed, but 30 | # we should expect to receive most of them. All tests use the same number 31 | # of updates (48). 32 | # 33 | RECEIVES_REQUIRED=46 34 | TRANSMITS_TOTAL=48 35 | COMPLETED_JOBS_REQUIRED=4 36 | CREATED_JOBS_TOTAL=5 37 | export HOSTNAME="ajje7lpljulm4-ats.iot.us-east-1.amazonaws.com" 38 | # 39 | # Process output will be captured in these files. 40 | # 41 | PROC1_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-1.out 42 | PROC2_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-2.out 43 | # 44 | # Move the test javascript program to the installed SDK 45 | # directory 46 | # 47 | INT_TEST_DIR=$NPMTEST_DIR/aws-iot-device-sdk-js/integration-test 48 | mkdir -p $INT_TEST_DIR 49 | cp ${0%/*}/jobs-integration-test.js $INT_TEST_DIR 50 | # 51 | # Start the two partner processes for the jobs-mode integration test and 52 | # save their PIDs. 53 | # 54 | if [ $CERT_DIR"" != "" ] 55 | then 56 | echo "###################################################################" 57 | echo ${0##*/}": running jobs integration test (certificate auth)" 58 | echo "###################################################################" 59 | $NODE $INT_TEST_DIR/jobs-integration-test.js -H $HOSTNAME -f $CERT_DIR -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 60 | PROC2_PID=$! 61 | $NODE $INT_TEST_DIR/jobs-integration-test.js -H $HOSTNAME -f $CERT_DIR -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 62 | PROC1_PID=$! 63 | elif [ $AUTHENTICATION_TYPE"" == "websocket" ] 64 | then 65 | echo "###################################################################" 66 | echo ${0##*/}": running jobs integration test (websocket/sigv4)" 67 | echo "###################################################################" 68 | $NODE $INT_TEST_DIR/jobs-integration-test.js -H $HOSTNAME -P=wss -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 69 | PROC2_PID=$! 70 | $NODE $INT_TEST_DIR/jobs-integration-test.js -H $HOSTNAME -P=wss -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 71 | PROC1_PID=$! 72 | else 73 | echo "###################################################################" 74 | echo ${0##*/}": skipping jobs integration test (custom_auth)" 75 | echo "###################################################################" 76 | exit 0 77 | fi 78 | # 79 | # Wait on the two partner processes and record their exit codes. 80 | # 81 | wait $PROC1_PID 82 | PROC1_EXIT_CODE=$? 83 | wait $PROC2_PID 84 | PROC2_EXIT_CODE=$? 85 | # 86 | # Combine the two exit codes; if either process exited abnormally, this 87 | # test is a failure. 88 | # 89 | COMBINED_EXIT_CODE=$((PROC1_EXIT_CODE | PROC_2_EXIT_CODE)) 90 | if [ $COMBINED_EXIT_CODE"" = "0" ] 91 | then 92 | jobsCompleted=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ jobs completed' |awk '{print $1}'` 93 | numReceived=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $1}'` 94 | receiveMask=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $4}'|sed -e 's/.*=//'` 95 | 96 | echo $numReceived" messages received, receive mask ["$receiveMask"]" 97 | echo $jobsCompleted" jobs completed" 98 | 99 | if !([ $numReceived"" -lt $RECEIVES_REQUIRED"" ] || [ $jobsCompleted -lt $COMPLETED_JOBS_REQUIRED ]) 100 | then 101 | echo "########################################################" 102 | echo " TEST SUCCEEDED: RECEIVED "$numReceived"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 103 | echo " JOBS COMPLETED "$jobsCompleted"/"$CREATED_JOBS_TOTAL", "$COMPLETED_JOBS_REQUIRED" required" 104 | echo "########################################################" 105 | else 106 | echo "########################################################" 107 | echo " TEST FAILED: RECEIVED "$numReceived"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 108 | echo " JOBS COMPLETED "$jobsCompleted"/"$CREATED_JOBS_TOTAL", "$COMPLETED_JOBS_REQUIRED" required" 109 | echo "########################################################" 110 | exit 2 111 | fi 112 | else 113 | echo ${0##*/}": FAILED ("$PROC1_EXIT_CODE":"$PROC2_EXIT_CODE")" 114 | exit $COMBINED_EXIT_CODE 115 | fi 116 | rm $PROC1_OUTFILE $PROC2_OUTFILE 117 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/run-offline-publishing-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run the offline publishing integration test. 4 | # 5 | # 6 | # Check to make sure the top-level test directory is defined. 7 | # 8 | if [ $NPMTEST_DIR"" = "" ] 9 | then 10 | echo ${0##*/}": NPMTEST_DIR must be defined!" 11 | exit 1 12 | fi 13 | # 14 | # Set the name of the node executable (it should be in our path) 15 | # 16 | NODE=node 17 | # 18 | # Set a randomized test tag to isolate this test run from others within 19 | # the AWS account associated with our Odin material set. This ensures 20 | # that all topic names used are unique to this test run. 21 | # 22 | TEST_TAG="test-"$RANDOM 23 | export HOSTNAME="ajje7lpljulm4-ats.iot.us-east-1.amazonaws.com" 24 | export CUSTOM_AUTH_HOST=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 25 | # 26 | # Capture the exit code of the first command which fails in a pipeline. 27 | # 28 | set -o pipefail 29 | # 30 | # The integration tests pass a number of updates between two partner 31 | # processes. Because these are QoS 0, delivery isn't guaranteed, but 32 | # we should expect to receive most of them. In this test, the transmitting 33 | # process is subjected to simulated network failures; since offline 34 | # publish queueing is enabled, published messages are stored until it 35 | # reconnects and are then drained out. The receiving process should 36 | # receive all of these if everything is working correctly. 37 | # 38 | # The transmitting process sends messages with a monotonically increasing 39 | # value, starting at 1. The receiving process maintains a running sum of 40 | # these values, as well as a running sum of the message numbers received. 41 | # At the end of the test, the ratio of these sums is calculated; if no 42 | # messages are missing at the receiver, this ratio will be exactly 1.0. 43 | # Any messages which weren't received will cause this ratio to be less than 44 | # 1.0. Since QoS 0 is used for the test, this is possible so a very small 45 | # error difference is allowed. 46 | # 47 | RECEIVES_REQUIRED=46 48 | TRANSMITS_TOTAL=48 49 | # 50 | # Process output will be captured in these files. 51 | # 52 | PROC1_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-1.out 53 | PROC2_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-2.out 54 | # 55 | # Move the test javascript programs to the installed SDK 56 | # directory 57 | # 58 | INT_TEST_DIR=$NPMTEST_DIR/aws-iot-device-sdk-js/integration-test 59 | mkdir -p $INT_TEST_DIR 60 | cp ${0%/*}/offline-publishing-test.js $INT_TEST_DIR 61 | # 62 | # Start the two partner processes for the offline publishing integration test and 63 | # save their PIDs. 64 | # 65 | if [ $AUTHENTICATION_TYPE"" == "certificate" ] 66 | then 67 | echo "###################################################################" 68 | echo ${0##*/}": running thing integration test (certificate auth)" 69 | echo "###################################################################" 70 | $NODE $INT_TEST_DIR/offline-publishing-test.js -H $HOSTNAME -f $CERT_DIR -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 71 | PROC1_PID=$! 72 | sleep 3 # wait 3 seconds prior to starting transmitting process 73 | # 74 | # transmit 4x/second 75 | # 76 | $NODE $INT_TEST_DIR/offline-publishing-test.js -H $HOSTNAME -f $CERT_DIR -t2 --debug=true --delay-ms=250 -T $TEST_TAG | tee $PROC2_OUTFILE & 77 | PROC2_PID=$! 78 | elif [ $AUTHENTICATION_TYPE"" == "custom-auth" ] 79 | then 80 | echo "###################################################################" 81 | echo ${0##*/}": running device integration test (websocket/custom auth)" 82 | echo "###################################################################" 83 | $NODE $INT_TEST_DIR/offline-publishing-test.js -H $CUSTOM_AUTH_HOST -P=wss-custom-auth -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 84 | PROC1_PID=$! 85 | sleep 3 # wait 3 seconds prior to starting transmitting process 86 | # 87 | # transmit 4x/second 88 | # 89 | $NODE $INT_TEST_DIR/offline-publishing-test.js -H $CUSTOM_AUTH_HOST -P=wss-custom-auth -t2 --debug=true --delay-ms=250 -T $TEST_TAG | tee $PROC2_OUTFILE & 90 | PROC2_PID=$! 91 | else 92 | echo "###################################################################" 93 | echo ${0##*/}": running thing integration test (websocket/sigv4)" 94 | echo "###################################################################" 95 | $NODE $INT_TEST_DIR/offline-publishing-test.js -H $HOSTNAME -P=wss -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 96 | PROC1_PID=$! 97 | sleep 3 # wait 3 seconds prior to starting transmitting process 98 | # 99 | # transmit 4x/second 100 | # 101 | $NODE $INT_TEST_DIR/offline-publishing-test.js -H $HOSTNAME -P=wss -t2 --debug=true --delay-ms=250 -T $TEST_TAG | tee $PROC2_OUTFILE & 102 | PROC2_PID=$! 103 | fi 104 | # 105 | # Wait on the two partner processes and record their exit codes. 106 | # 107 | wait $PROC1_PID 108 | PROC1_EXIT_CODE=$? 109 | wait $PROC2_PID 110 | PROC2_EXIT_CODE=$? 111 | # 112 | # Combine the two exit codes; if either process exited abnormally, this 113 | # test is a failure. 114 | # 115 | COMBINED_EXIT_CODE=$((PROC1_EXIT_CODE | PROC_2_EXIT_CODE)) 116 | if [ $COMBINED_EXIT_CODE"" = "0" ] 117 | then 118 | # 119 | # Print out the received quality for debugging. 120 | # 121 | cat $PROC1_OUTFILE | grep -E '^quality' |awk '{print $NF}' 122 | receiveQuality=`cat $PROC1_OUTFILE | grep -E '^quality' |awk '{print $NF}'` 123 | 124 | # 125 | # We should receive all of these; allow only a very small error margin. 126 | # 127 | minAcceptableQuality=0.99 128 | qualityGtMinAcceptable=`echo $receiveQuality"" \> $minAcceptableQuality"" | bc -l` 129 | # 130 | # The quality ratio should never exceed 1.0. 131 | # 132 | maxAcceptableQuality=1.00001 133 | qualityLtMaxAcceptable=`echo $maxAcceptableQuality"" \> $receiveQuality"" | bc -l` 134 | 135 | if [ $qualityGtMinAcceptable"" -eq 1 ] && [ $qualityLtMaxAcceptable"" -eq 1 ] 136 | then 137 | echo "########################################################" 138 | echo " TEST SUCCEEDED: PROC 1 QUALITY "$receiveQuality", "$minAcceptableQuality" required" 139 | echo "########################################################" 140 | else 141 | echo "########################################################" 142 | echo " TEST FAILED: PROC 1 QUALITY "$receiveQuality", "$minAcceptableQuality" required" 143 | echo "########################################################" 144 | exit 2 145 | fi 146 | else 147 | echo ${0##*/}": FAILED ("$PROC1_EXIT_CODE":"$PROC2_EXIT_CODE")" 148 | exit $COMBINED_EXIT_CODE 149 | fi 150 | rm $PROC1_OUTFILE $PROC2_OUTFILE 151 | -------------------------------------------------------------------------------- /integration-testing/integration-tests/run-thing-integration-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run the thing-mode integration test. 4 | # 5 | # 6 | # Check to make sure the top-level test directory is defined. 7 | # 8 | if [ $NPMTEST_DIR"" = "" ] 9 | then 10 | echo ${0##*/}": NPMTEST_DIR must be defined!" 11 | exit 1 12 | fi 13 | # 14 | # Set the name of the node executable (it should be in our path) 15 | # 16 | NODE=node 17 | # 18 | # Set a randomized test tag to isolate this test run from others within 19 | # the AWS account associated with our Odin material set. This ensures 20 | # that all topic names used are unique to this test run. 21 | # 22 | TEST_TAG="test-"$RANDOM 23 | export HOSTNAME="ajje7lpljulm4-ats.iot.us-east-1.amazonaws.com" 24 | export CUSTOM_AUTH_HOST=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 25 | # 26 | # Capture the exit code of the first command which fails in a pipeline. 27 | # 28 | set -o pipefail 29 | # 30 | # The integration tests pass a number of updates between two partner 31 | # processes. Because these are QoS 0, delivery isn't guaranteed, but 32 | # we should expect to receive most of them. All tests use the same number 33 | # of updates (48). 34 | # 35 | RECEIVES_REQUIRED=46 36 | TRANSMITS_TOTAL=48 37 | # 38 | # Process output will be captured in these files. 39 | # 40 | PROC1_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-1.out 41 | PROC2_OUTFILE=$NPMTEST_DIR/${0##*/}-$RANDOM-$RANDOM-proc-2.out 42 | # 43 | # Move the test javascript programs to the installed SDK 44 | # directory 45 | # 46 | INT_TEST_DIR=$NPMTEST_DIR/aws-iot-device-sdk-js/integration-test 47 | mkdir -p $INT_TEST_DIR 48 | cp ${0%/*}/thing-integration-test.js $INT_TEST_DIR 49 | # 50 | # Start the two partner processes for the thing-mode integration test and 51 | # save their PIDs. 52 | # 53 | if [ $AUTHENTICATION_TYPE"" == "certificate" ] 54 | then 55 | echo "###################################################################" 56 | echo ${0##*/}": running thing integration test (certificate auth)" 57 | echo "###################################################################" 58 | $NODE $INT_TEST_DIR/thing-integration-test.js -H $HOSTNAME -f $CERT_DIR -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 59 | PROC1_PID=$! 60 | $NODE $INT_TEST_DIR/thing-integration-test.js -H $HOSTNAME -f $CERT_DIR -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 61 | PROC2_PID=$! 62 | elif [ $AUTHENTICATION_TYPE"" == "custom-auth" ] 63 | then 64 | echo "###################################################################" 65 | echo ${0##*/}": running device integration test (websocket/custom auth)" 66 | echo "###################################################################" 67 | $NODE $INT_TEST_DIR/thing-integration-test.js -H $CUSTOM_AUTH_HOST -P=wss-custom-auth -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 68 | PROC1_PID=$! 69 | $NODE $INT_TEST_DIR/thing-integration-test.js -H $CUSTOM_AUTH_HOST -P=wss-custom-auth -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 70 | PROC2_PID=$! 71 | else 72 | echo "###################################################################" 73 | echo ${0##*/}": running thing integration test (websocket/sigv4)" 74 | echo "###################################################################" 75 | $NODE $INT_TEST_DIR/thing-integration-test.js -H $HOSTNAME -P=wss -t1 --debug=true -T $TEST_TAG | tee $PROC1_OUTFILE & 76 | PROC1_PID=$! 77 | $NODE $INT_TEST_DIR/thing-integration-test.js -H $HOSTNAME -P=wss -t2 --debug=true -T $TEST_TAG | tee $PROC2_OUTFILE & 78 | PROC2_PID=$! 79 | fi 80 | # 81 | # Wait on the two partner processes and record their exit codes. 82 | # 83 | wait $PROC1_PID 84 | PROC1_EXIT_CODE=$? 85 | wait $PROC2_PID 86 | PROC2_EXIT_CODE=$? 87 | # 88 | # Combine the two exit codes; if either process exited abnormally, this 89 | # test is a failure. 90 | # 91 | COMBINED_EXIT_CODE=$((PROC1_EXIT_CODE | PROC_2_EXIT_CODE)) 92 | if [ $COMBINED_EXIT_CODE"" = "0" ] 93 | then 94 | numReceived1=`cat $PROC1_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $1}'` 95 | receiveMask1=`cat $PROC1_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $4}'|sed -e 's/.*=//'` 96 | numReceived2=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $1}'` 97 | receiveMask2=`cat $PROC2_OUTFILE | grep -E '^[0-9]+\ messages received, accumulator=.*' |awk '{print $4}'|sed -e 's/.*=//'` 98 | 99 | echo "proc 1: "$numReceived1" messages received, receive mask ["$receiveMask1"]" 100 | echo "proc 2: "$numReceived2" messages received, receive mask ["$receiveMask2"]" 101 | 102 | if [ $numReceived1"" -gt $RECEIVES_REQUIRED ] && [ $numReceived2"" -gt $RECEIVES_REQUIRED ] 103 | then 104 | echo "########################################################" 105 | echo " TEST SUCCEEDED: PROC 1 RECEIVED "$numReceived1"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 106 | echo " PROC 2 RECEIVED "$numReceived2"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 107 | echo "########################################################" 108 | else 109 | echo "########################################################" 110 | echo " TEST FAILED: PROC 1 RECEIVED "$numReceived1"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 111 | echo " PROC 2 RECEIVED "$numReceived2"/"$TRANSMITS_TOTAL", "$RECEIVES_REQUIRED" required" 112 | echo "########################################################" 113 | exit 2 114 | fi 115 | else 116 | echo ${0##*/}": FAILED ("$PROC1_EXIT_CODE":"$PROC2_EXIT_CODE")" 117 | exit $COMBINED_EXIT_CODE 118 | fi 119 | rm $PROC1_OUTFILE $PROC2_OUTFILE 120 | -------------------------------------------------------------------------------- /integration-testing/run-integration-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | INTEGRATION_TEST_PATH=./integration-tests 5 | 6 | TEST_COUNT=0 7 | # 8 | # Run each integration test; if it exits with a non-zero status, 9 | # stop testing and return that status to the parent process. 10 | # 11 | for file in `ls $INTEGRATION_TEST_PATH` 12 | do 13 | # 14 | # Only run executable files. 15 | # 16 | if [ -x $INTEGRATION_TEST_PATH/$file ] 17 | then 18 | # 19 | # If not running in parallel mode, execute each test runner in sequence. 20 | # 21 | if [ $PARALLEL_EXECUTION"" = "" ] 22 | then 23 | echo "###################################################################" 24 | echo ${0##*/}": running "${file##*/}"..." 25 | echo "###################################################################" 26 | $INTEGRATION_TEST_PATH/$file 27 | statusValue=$? 28 | echo "###################################################################" 29 | echo ${0##*/}": complete, status="$statusValue 30 | echo "###################################################################" 31 | if [ $statusValue"" != 0 ] 32 | then 33 | exit $statusValue"" 34 | fi 35 | else 36 | # 37 | # If running in parallel mode, execute all test runners simultaneously. 38 | # 39 | echo "###################################################################" 40 | echo ${0##*/}": running "${file##*/}" (parallel mode)..." 41 | echo "###################################################################" 42 | $INTEGRATION_TEST_PATH/$file & 43 | PIDS[$TEST_COUNT]=$! 44 | NAMES[$TEST_COUNT]=${file##*/} 45 | TEST_COUNT=$((TEST_COUNT+1)) 46 | fi 47 | fi 48 | done 49 | 50 | if [ $PARALLEL_EXECUTION"" != "" ] 51 | then 52 | COMBINED_EXIT_CODE=0 53 | # 54 | # Wait on all test runner processes. 55 | # 56 | TEST_COUNT=0 57 | for PID in "${PIDS[@]}" 58 | do 59 | wait "$PID" 60 | EXIT_CODE=$? 61 | echo "###################################################################" 62 | echo ${NAMES[$TEST_COUNT]}" complete, status="$EXIT_CODE 63 | echo "###################################################################" 64 | TEST_COUNT=$((TEST_COUNT+1)) 65 | COMBINED_EXIT_CODE=$((EXIT_CODE | COMBINED_EXIT_CODE)) 66 | done 67 | echo "###################################################################" 68 | echo ${0##*/}": all test runners complete, status="$COMBINED_EXIT_CODE 69 | echo "###################################################################" 70 | exit $COMBINED_EXIT_CODE 71 | else 72 | # 73 | # All test runners have completed successfully, exit with status 0 74 | # 75 | exit 0 76 | fi 77 | -------------------------------------------------------------------------------- /integration-testing/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Master integration test runner for the AWS IoT Node.js device SDK. 4 | # 5 | # USAGE 6 | # 7 | # run-tests.sh 8 | # 9 | # PARAMETERS 10 | # 11 | # : [websocket|certificate|custom-auth] 12 | # 13 | # This program will first validate the given parameters, then attempt to 14 | # retrieve required secrets from AWS secrets manager; if both are successful, it 15 | # will execute all of the scripts under the 'integration-tests' directory 16 | # until either one of them exits with a non-zero status, or they have all 17 | # been executed. If this program exits with a zero status, that indicates 18 | # that both the test setup and the test execution was successful. 19 | # 20 | # RETURNS 21 | # 22 | # 0 if successful, non-zero otherwise 23 | # 24 | # SECRET HANDLING 25 | # 26 | # This script handles retrieving secrets from AWS secrets manager; for 27 | # websocket authentication, it will place the appropriate values in the AWS_ACCESS_KEY_ID 28 | # and AWS_SECRET_ACCESS_KEY environment variables used by the SDK. For 29 | # certificate authentication, the certificate and private key will be stored 30 | # in PEM-format temporary files, along with the root CA certificate; 31 | # the files are named according to the default naming convention 32 | # specified in the README.md, and the following environment variable points 33 | # to their location: 34 | # 35 | # CERT_DIR 36 | # 37 | # Temporary certificate and private key files are deleted after running the 38 | # individual test runner scripts. 39 | # 40 | # 41 | 42 | # 43 | # Set a randomized directory name to isolate this test run from others 44 | # in the same environment and make it available to sub-scripts via export. 45 | # 46 | export NPMTEST_DIR="/tmp/npmtest-"$RANDOM 47 | 48 | # 49 | # Validate arguments 50 | # 51 | if [ $# -eq 1 ] 52 | then 53 | export AUTHENTICATION_TYPE=$1 54 | 55 | if [ $AUTHENTICATION_TYPE"" != "websocket" ] && \ 56 | [ $AUTHENTICATION_TYPE"" != "certificate" ] && \ 57 | [ $AUTHENTICATION_TYPE"" != "custom-auth" ] 58 | then 59 | echo ${0##*/}": authentication-type must be one of [websocket|certificate|custom-auth]" 60 | exit 2 61 | fi 62 | export LONG_RUNNING_TEST="" 63 | else 64 | echo ${0##*/}" " 65 | exit 1 66 | fi 67 | 68 | # 69 | # Set node/npm paths 70 | NODE=`which node` 71 | if [ ! -x $NODE"" ] 72 | then 73 | echo ${0##*/}": can't find node, exiting..." 74 | exit 1 75 | fi 76 | 77 | NPM=`which npm` 78 | if [ ! -x $NPM"" ] 79 | then 80 | echo ${0##*/}": can't find npm, exiting..." 81 | exit 1 82 | fi 83 | 84 | # 85 | # node-gyp requires that $HOME be defined 86 | # 87 | #export HOME=$PWD 88 | 89 | # 90 | # This script will run all of the programs under the integration-tests directory until 91 | # either one executes with a non-zero status or they have all been run. 92 | # 93 | RUN_INTEGRATION_TESTS='./run-integration-tests.sh' 94 | 95 | # 96 | # Install the Node.js SDK under a new temporary directory. 97 | # 98 | echo "Running integration tests in ${NPMTEST_DIR}" 99 | mkdir -p $NPMTEST_DIR 100 | (cp -r ../../aws-iot-device-sdk-js $NPMTEST_DIR; cd $NPMTEST_DIR) 101 | if [ $? != "0" ] 102 | then 103 | echo "###################################################################" 104 | echo ${0##*/}": unable to copy iot sdk to test directory!" 105 | echo "###################################################################" 106 | exit 4 107 | fi 108 | 109 | # 110 | # Attempt an npm install of the AWS Node.js SDK for control plane access for creating test jobs 111 | # 112 | (cd $NPMTEST_DIR/aws-iot-device-sdk-js; $NPM install aws-sdk) 113 | if [ $? != "0" ] 114 | then 115 | echo "###################################################################" 116 | echo ${0##*/}": unable to npm install aws-sdk!" 117 | echo "###################################################################" 118 | exit 4 119 | fi 120 | 121 | 122 | # 123 | # Attempt an npm install of the Node.js SDK 124 | # 125 | (cd $NPMTEST_DIR/aws-iot-device-sdk-js; $NPM install) 126 | if [ $? != "0" ] 127 | then 128 | echo "###################################################################" 129 | echo ${0##*/}": unable to npm install aws iot device sdk!" 130 | echo "###################################################################" 131 | exit 4 132 | fi 133 | 134 | # 135 | # The SDK installed without errors; now, retrieve credentials 136 | # 137 | echo "###################################################################" 138 | echo ${0##*/}": retrieving AWS credentials from AWS SecretsManager" 139 | echo "###################################################################" 140 | # fetch secret value and strip quotes with sed 141 | principal=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id V1IotSdkIntegrationTestWebsocketAccessKeyId --query SecretString | sed -n 's/^"\(.*\)"/\1/p') 142 | if [ $? == "0" ] 143 | then 144 | echo ${0##*/}": retrieved ws testing access key id" 145 | else 146 | echo ${0##*/}": couldn't retrieve ws testing access key id!" 147 | exit 5 148 | fi 149 | 150 | # fetch secret value and strip quotes with sed 151 | credential=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id V1IotSdkIntegrationTestWebsocketSecretAccessKey --query SecretString | sed -n 's/^"\(.*\)"/\1/p') 152 | if [ $? == "0" ] 153 | then 154 | echo ${0##*/}": retrieved ws testing secret access key" 155 | else 156 | echo ${0##*/}": couldn't retrieve ws testing secret access key!" 157 | exit 6 158 | fi 159 | 160 | case $AUTHENTICATION_TYPE"" in 161 | 162 | websocket) 163 | export AWS_ACCESS_KEY_ID=$principal 164 | export AWS_SECRET_ACCESS_KEY=$credential 165 | 166 | $RUN_INTEGRATION_TESTS 167 | exit $? 168 | ;; 169 | 170 | custom-auth) 171 | echo "###################################################################" 172 | echo ${0##*/}": setting custom-auth credentials" 173 | echo "###################################################################" 174 | 175 | # We need the custom authorization headers to use custom authorization, but we will verify via username and password 176 | export CUSTOM_AUTH_HEADERS="{}" 177 | export CUSTOM_AUTH_NAME=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id "unit-test/authorizer-name" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 178 | export CUSTOM_AUTH_PASSWORD=$(aws --region us-east-1 secretsmanager get-secret-value --secret-id "unit-test/authorizer-password" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 179 | 180 | $RUN_INTEGRATION_TESTS 181 | exit $? 182 | ;; 183 | 184 | certificate) 185 | export JOBS_AWS_ACCESS_KEY_ID=$principal 186 | export JOBS_AWS_SECRET_ACCESS_KEY=$credential 187 | 188 | export CERT_DIR=$NPMTEST_DIR/certs 189 | mkdir -p $CERT_DIR 190 | echo "###################################################################" 191 | echo ${0##*/}": retrieving certificate credentials from AWS Secrets Manager" 192 | echo "###################################################################" 193 | 194 | # fetch secret value, strip quotes and replace "\n" with an actual newline 195 | aws --region us-east-1 secretsmanager get-secret-value --secret-id V1IotSdkIntegrationTestCertificate --query SecretString | sed -n 's/^"\(.*\)"/\1/p' | sed 's/\\n/\ 196 | /g' > $CERT_DIR/certificate.pem.crt 197 | if [ $? == "0" ] 198 | then 199 | echo ${0##*/}": retrieved Certificate" 200 | else 201 | echo ${0##*/}": couldn't retrieve Certificate!" 202 | exit 5 203 | fi 204 | 205 | # fetch secret value, strip quotes and replace "\n" with an actual newline 206 | aws --region us-east-1 secretsmanager get-secret-value --secret-id V1IotSdkIntegrationTestPrivateKey --query SecretString | sed -n 's/^"\(.*\)"/\1/p' | sed 's/\\n/\ 207 | /g' > $CERT_DIR/private.pem.key 208 | if [ $? == "0" ] 209 | then 210 | echo ${0##*/}": retrieved Private Key" 211 | else 212 | echo ${0##*/}": couldn't retrieve Private Key!" 213 | exit 6 214 | fi 215 | 216 | # 217 | # Retrieve the root CA certificate 218 | # 219 | curl -s 'https://www.amazontrust.com/repository/AmazonRootCA1.pem' > $CERT_DIR/root-CA.crt 220 | if [ $? == "0" ] 221 | then 222 | echo ${0##*/}": retrieved root CA certificate" 223 | else 224 | echo ${0##*/}": couldn't retrieve root CA certificate!" 225 | exit 7 226 | fi 227 | 228 | $RUN_INTEGRATION_TESTS 229 | exit $? 230 | ;; 231 | 232 | *) 233 | echo ${0##*/}": unsupported authentication type ("$AUTHENTICATION_TYPE")" 234 | ;; 235 | esac 236 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-iot-device-sdk", 3 | "description": "AWS IoT Node.js SDK for Embedded Devices", 4 | "version": "2.0.0-dev", 5 | "author": { 6 | "name": "Amazon Web Services", 7 | "email": "", 8 | "url": "http://aws.amazon.com" 9 | }, 10 | "homepage": "https://github.com/aws/aws-iot-device-sdk-js", 11 | "main": "index.js", 12 | "engines": { 13 | "node": ">=8.17.0" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/aws/aws-iot-device-sdk-js" 18 | }, 19 | "bugs": { 20 | "url": "http://github.com/aws/aws-iot-device-sdk-js/issues" 21 | }, 22 | "license": "Apache-2.0", 23 | "keywords": [ 24 | "api", 25 | "amazon", 26 | "aws", 27 | "iot", 28 | "mqtt" 29 | ], 30 | "dependencies": { 31 | "crypto-js": "4.2.0", 32 | "minimist": "1.2.6", 33 | "mqtt": "4.2.8", 34 | "@httptoolkit/websocket-stream": "^6.0.1" 35 | }, 36 | "devDependencies": { 37 | "gulp": "^3.9.0", 38 | "gulp-beautify": "^2.0.0", 39 | "gulp-concat": "^2.6.0", 40 | "gulp-coverage": "^0.3.38", 41 | "gulp-jscs": "^4.0.0", 42 | "gulp-jshint": "^2.0.0", 43 | "gulp-mocha": "^3.0.1", 44 | "jshint": "^2.9.1", 45 | "jshint-stylish": "^2.2.1", 46 | "rewire": "^2.5.1", 47 | "sinon": "^1.17.3" 48 | }, 49 | "scripts": { 50 | "test": "node ./node_modules/gulp/bin/gulp.js test --verbose", 51 | "browserize": "./scripts/browserize.sh", 52 | "beautify": "node ./node_modules/gulp/bin/gulp.js beautify" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /scripts/browserize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # A copy of the License is located at 8 | # 9 | # http://aws.amazon.com/apache2.0 10 | # 11 | # or in the "license" file accompanying this file. This file is distributed 12 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | # express or implied. See the License for the specific language governing 14 | # permissions and limitations under the License. 15 | # 16 | set -e 17 | 18 | BROWSER_BUNDLE_DIR=browser 19 | 20 | # 21 | # Make sure that browserify is available. 22 | # 23 | BROWSERIFY=`which browserify` 24 | if [ $? -ne 0 ]; then 25 | echo ${0##*/}": browserify is not available, please install, e.g. npm install -g browserify" 26 | exit 1 27 | fi 28 | 29 | # 30 | # Make sure that npm is available. 31 | # 32 | NPM=`which npm` 33 | if [ $? -ne 0 ]; then 34 | echo ${0##*/}": npm is not available, please install" 35 | exit 2 36 | fi 37 | 38 | # 39 | # Verify that npm is at least version 3 (this script won't work with previous 40 | # versions). 41 | # 42 | NPM_MAJOR_VERSION=`npm -v | cut -d'.' -f1` 43 | if [ $NPM_MAJOR_VERSION"" -lt 3 ]; then 44 | echo ${0##*/}": npm must be at least version 3 (installed npm version:"$NPM_MAJOR_VERSION")" 45 | exit 3 46 | fi 47 | 48 | # 49 | # Make sure we are in the right directory by verifying the presence of a couple of files 50 | # 51 | if [ ! -f "README.md" ] || [ ! -f "thing/index.js" ] || [ ! -d $BROWSER_BUNDLE_DIR ]; then 52 | echo ${0##*/}": this script must be run in the package top-level directory" 53 | exit 4 54 | fi 55 | 56 | # 57 | # Check to see if the SDK browser bundle exists; if it does not, we'll create the browser 58 | # bundle to be used by other applications. 59 | # 60 | if [ ! -f $BROWSER_BUNDLE_DIR/aws-iot-sdk-browser-bundle.js ]; then 61 | # 62 | # Prepare the bundle by doing an npm install in the browser bundle directory. Note 63 | # that we use this copy of the SDK files rather than pulling them from npm, so that 64 | # we can easily work with local changes, if necessary. 65 | # 66 | (cd $BROWSER_BUNDLE_DIR; tar cvzf aws-iot-device-sdk.tgz --exclude ${PWD##*/} --exclude node_modules --exclude .git --exclude .github --exclude .coverdata --exclude debug --exclude examples --exclude reports --exclude test -C ../ .; mkdir -p node_modules/aws-iot-device-sdk; (cd node_modules/aws-iot-device-sdk; tar xvzf ../../aws-iot-device-sdk.tgz); npm install) 67 | # 68 | # Create the browser bundle and delete all working files/directories. Allow 69 | # aws-iot-device-sdk and aws-sdk to be required by other browserify bundles. 70 | # 71 | (cd $BROWSER_BUNDLE_DIR; rm -f bundle.js; $BROWSERIFY -r aws-iot-device-sdk -r aws-sdk -o aws-iot-sdk-browser-bundle.js; rm -rf node_modules; rm -f aws-iot-device-sdk.tgz) 72 | echo ${0##*/}": prepared browser bundle" 73 | fi 74 | 75 | # 76 | # Check to see how many arguments are available; if one argument is available, we'll 77 | # browserify that file using external references to aws-sdk and aws-iot-device-sdk and 78 | # place the result in that directory under 'bundle.js'. If two arguments are available 79 | # we'll use the second argument as the bundle output file. Finally, we'll copy the browser 80 | # bundle into the application directory so that it's available for use. 81 | # 82 | if [ $# -eq 0 ]; then 83 | exit 0 84 | elif [ $# -eq 1 ] || [ $# -eq 2 ]; then 85 | # 86 | # Browserify another app using external references to aws-sdk and aws-iot-device-sdk. 87 | # 88 | if [ ! -f $1"" ]; then 89 | echo ${0##*/}": can't browserify ("$1") because it's not a file or does not exist" 90 | exit 5 91 | fi 92 | 93 | APP_PATH=${1%/*}"" 94 | APP_NAME=${1##*/}"" 95 | OUTPUT_FILE=bundle.js 96 | 97 | if [ $# -eq 2 ] && [ $2"" != "" ]; then 98 | OUTPUT_FILE=${2##*/} 99 | fi 100 | 101 | echo "browserifying "$1" and placing result in "$APP_PATH/$OUTPUT_FILE"..." 102 | (cd $APP_PATH""; $BROWSERIFY -x aws-sdk -x aws-iot-device-sdk $APP_NAME -o $OUTPUT_FILE) 103 | cp $BROWSER_BUNDLE_DIR/aws-iot-sdk-browser-bundle.js $APP_PATH 104 | else 105 | echo "Usage: "${0##*/}" [javascript application] [output file]" 106 | exit 6 107 | fi 108 | 109 | exit 0 110 | -------------------------------------------------------------------------------- /scripts/exclude_list.txt: -------------------------------------------------------------------------------- 1 | .git 2 | .coverdata 3 | 4 | -------------------------------------------------------------------------------- /scripts/windows-browserize.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | SET BROWSER_BUNDLE_DIR=browser 4 | 5 | rem Make sure npm is available 6 | where npm 7 | IF %ERRORLEVEL% NEQ 0 ECHO "npm is not available, please install" && EXIT /B 2 8 | 9 | rem Make sure browserify is available 10 | 11 | where browserify 12 | IF %ERRORLEVEL% NEQ 0 ECHO "browserify is not available, please install, e.g. npm install -g browserify" && EXIT /B 1 13 | for /f "delims=" %%a in ('where browserify') do @set BROWSERIFY=%%a 14 | 15 | rem Make sure in the top top-level directory by verifying a few files 16 | IF NOT EXIST %BROWSER_BUNDLE_DIR% goto :EX3 17 | IF NOT EXIST thing/index.js goto :EX3 18 | IF NOT EXIST README.md goto :EX3 19 | goto CONT 20 | :EX3 21 | echo "this script must be run in the package top-level directory" 22 | 23 | rem Check to see if the SDK browser bundle exists; if it does not, we'll create the browser 24 | rem bundle to be used by other applications. 25 | :CONT 26 | SET ROOTDIR="%cd%" 27 | IF NOT EXIST %BROWSER_BUNDLE_DIR%\aws-iot-sdk-browser-bundle.js ( 28 | echo %BROWSER_BUNDLE_DIR%\aws-iot-sdk-browser-bundle.js 29 | 30 | rem Prepare the bundle by doing an npm install in the browser bundle directory. Note 31 | rem that we use this copy of the SDK files rather than pulling them from npm, so that 32 | rem we can easily work with local changes, if necessary. 33 | cd %BROWSER_BUNDLE_DIR% 34 | mkdir node_modules\aws-iot-device-sdk 35 | xcopy /y /EXCLUDE:..\scripts\exclude_list.txt ..\* .\node_modules\aws-iot-device-sdk 36 | xcopy /e /i /y ..\device .\node_modules\aws-iot-device-sdk\device 37 | xcopy /e /i /y ..\common .\node_modules\aws-iot-device-sdk\common 38 | xcopy /e /i /y ..\scripts .\node_modules\aws-iot-device-sdk\scripts 39 | xcopy /e /i /y ..\thing .\node_modules\aws-iot-device-sdk\thing 40 | call npm install 41 | 42 | rem Create the browser bundle and delete all working files/directories. Allow 43 | rem aws-iot-device-sdk and aws-sdk to be required by other browserify bundles. 44 | 45 | call %BROWSERIFY% -r aws-iot-device-sdk -r aws-sdk -o aws-iot-sdk-browser-bundle.js 46 | rmdir /s /q node_modules 47 | cd %ROOTDIR% 48 | ) 49 | 50 | rem Check to see how many arguments are available; if one argument is available, we'll 51 | rem browserify that file using external references to aws-sdk and aws-iot-device-sdk and 52 | rem place the result in that directory under 'bundle.js'. If two arguments are available 53 | rem we'll use the second argument as the bundle output file. Finally, we'll copy the browser 54 | rem bundle into the application directory so that it's available for use. 55 | 56 | 57 | IF NOT "%1"=="" ( 58 | SET APP_PATH=%~p1 59 | SET APP_NAME=%~n1 60 | SET OUTPUT_FILE=bundle.js 61 | IF NOT "%2"=="" SET OUTPUT_FILE=%~nx2 62 | echo browserifying %APP_NAME% and placing result in "%APP_PATH%%OUTPUT_FILE%..." 63 | copy %BROWSER_BUNDLE_DIR%\aws-iot-sdk-browser-bundle.js %APP_PATH% 64 | cd %APP_PATH% 65 | call %BROWSERIFY% -x aws-sdk -x aws-iot-device-sdk %APP_NAME% -o %OUTPUT_FILE% 66 | cd %ROOTDIR% 67 | ) 68 | 69 | EXIT /B 0 70 | 71 | -------------------------------------------------------------------------------- /test/data/README.txt: -------------------------------------------------------------------------------- 1 | This directory contains data files used during unit testing. 2 | 3 | The client certificate (certificate.pem.crt), private key (private.pem.key), 4 | and root CA certificate (root-CA.crt) in this directory are not valid; they 5 | are for testing purposes only and cannot be used to connect with the AWS IoT 6 | platform. 7 | -------------------------------------------------------------------------------- /test/data/certificate.pem.crt: -------------------------------------------------------------------------------- 1 | this is not a valid certificate 2 | -------------------------------------------------------------------------------- /test/data/credentials: -------------------------------------------------------------------------------- 1 | [default] 2 | aws_access_key_id = not_a_valid_key_id 3 | aws_secret_access_key = not_a_valid_secret_key 4 | [user2] 5 | aws_access_key_id = not_a_valid_key_id 6 | aws_secret_access_key = not_a_valid_secret_key 7 | -------------------------------------------------------------------------------- /test/data/invalid_credentials: -------------------------------------------------------------------------------- 1 | this is an invalid credential files for unit testing 2 | -------------------------------------------------------------------------------- /test/data/private.pem.key: -------------------------------------------------------------------------------- 1 | this is not a valid private key 2 | -------------------------------------------------------------------------------- /test/data/root-CA.crt: -------------------------------------------------------------------------------- 1 | this is not a valid root certificate 2 | -------------------------------------------------------------------------------- /test/jobs-unit-tests.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2015 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 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | //node.js deps 17 | 18 | //npm deps 19 | 20 | //app deps 21 | var rewire = require('rewire'); 22 | var sinon = require('sinon'); 23 | var assert = require('assert'); 24 | var mqtt = require('mqtt'); 25 | var myTls = rewire('../device/lib/tls'); 26 | var mockTls = require('./mock/mockTls'); 27 | var mockMQTTClient = require('./mock/mockMQTTClient'); 28 | 29 | describe( "jobs class unit tests", function() { 30 | var jobsModule = require('../').jobs; 31 | 32 | var mockMQTTClientObject; 33 | var fakeConnect; 34 | var mqttSave; 35 | var mockTlsRevert; 36 | var mockMqttRevert; 37 | 38 | var mockTlsObject = new mockTls(); 39 | var mockMqttObject = new mockTls.mqtt(); 40 | 41 | beforeEach( function () { 42 | // Mock the connect API for mqtt.js 43 | fakeConnect = function(wrapper,options) { 44 | mockMQTTClientObject = new mockMQTTClient(); // return the mocking object 45 | mockMQTTClientObject.reInitCommandCalled(); 46 | mockMQTTClientObject.resetPublishedMessage(); 47 | return mockMQTTClientObject; 48 | }; 49 | 50 | mqttSave = sinon.stub(mqtt, 'MqttClient', fakeConnect); 51 | 52 | mockTlsRevert = myTls.__set__("tls", mockTlsObject); 53 | mockMqttRevert = myTls.__set__("mqtt", mockMqttObject); 54 | }); 55 | afterEach( function () { 56 | mqttSave.restore(); 57 | mockTlsRevert(); 58 | mockMqttRevert(); 59 | }); 60 | 61 | describe("TLS handler calls the correct functions", function() { 62 | it("calls the correct functions", function() { 63 | mockTlsObject.reInitCommandCalled(); 64 | myTls(mockMqttObject, { 'testOption': true } ); 65 | assert.equal(mockTlsObject.commandCalled['connect'], 1); 66 | assert.equal(mockTlsObject.commandCalled['on'], 2); 67 | assert.equal(mockTlsObject.commandCalled['emit'], 1); 68 | assert.equal(mockMqttObject.commandCalled['emit'], 1); 69 | }) 70 | }); 71 | 72 | // Test cases begin 73 | describe( "subscribeToJobs / unsubscribeFromJobs", function() { 74 | var jobsConfig = { 75 | keyPath:'test/data/private.pem.key', 76 | certPath:'test/data/certificate.pem.crt', 77 | caPath:'test/data/root-CA.crt', 78 | clientId:'dummy-client-1', 79 | host:'XXXX.iot.us-east-1.amazonaws.com' 80 | }; 81 | 82 | it("matching thing, omitted operation returns no errors", function() { 83 | var jobs = jobsModule(jobsConfig); 84 | 85 | jobs.subscribeToJobs('testThingName', function(err) { assert.equal(err, undefined); }); 86 | jobs.unsubscribeFromJobs('testThingName', function(err) { assert.equal(err, undefined); }); 87 | }); 88 | 89 | it("matching thing, matching operation returns no errors", function() { 90 | var jobs = jobsModule(jobsConfig); 91 | 92 | jobs.subscribeToJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 93 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 94 | }); 95 | 96 | it("no matching thing, no matching operation returns error", function() { 97 | var jobs = jobsModule(jobsConfig); 98 | 99 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.notEqual(err, undefined); }); 100 | }); 101 | 102 | it("matching thing, no matching operation returns error", function() { 103 | var jobs = jobsModule(jobsConfig); 104 | 105 | jobs.subscribeToJobs('testThingName', 'testOperationName1', function(err) { assert.equal(err, undefined); }); 106 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName2', function(err) { assert.notEqual(err, undefined); }); 107 | }); 108 | 109 | it("no matching thing, matching operation returns error", function() { 110 | var jobs = jobsModule(jobsConfig); 111 | 112 | jobs.subscribeToJobs('testThingName1', 'testOperationName', function(err) { assert.equal(err, undefined); }); 113 | jobs.unsubscribeFromJobs('testThingName2', 'testOperationName', function(err) { assert.notEqual(err, undefined); }); 114 | }); 115 | 116 | it("duplicate returns error, omitted operation", function() { 117 | var jobs = jobsModule(jobsConfig); 118 | 119 | jobs.subscribeToJobs('testThingName', function(err) { assert.equal(err, undefined); }); 120 | jobs.unsubscribeFromJobs('testThingName', function(err) { assert.equal(err, undefined); }); 121 | jobs.unsubscribeFromJobs('testThingName', function(err) { assert.notEqual(err, undefined); }); 122 | }); 123 | 124 | it("duplicate returns error", function() { 125 | var jobs = jobsModule(jobsConfig); 126 | 127 | jobs.subscribeToJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 128 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 129 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.notEqual(err, undefined); }); 130 | }); 131 | 132 | it("duplicate subscribe, single unsubscribe returns no errors", function() { 133 | var jobs = jobsModule(jobsConfig); 134 | 135 | jobs.subscribeToJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 136 | jobs.subscribeToJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 137 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 138 | }); 139 | 140 | it("duplicate subscribe, duplicate unsubscribe returns error", function() { 141 | var jobs = jobsModule(jobsConfig); 142 | 143 | jobs.subscribeToJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 144 | jobs.subscribeToJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 145 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.equal(err, undefined); }); 146 | jobs.unsubscribeFromJobs('testThingName', 'testOperationName', function(err) { assert.notEqual(err, undefined); }); 147 | }); 148 | }); 149 | 150 | describe( "job execution callbacks", function() { 151 | var jobsConfig = { 152 | keyPath:'test/data/private.pem.key', 153 | certPath:'test/data/certificate.pem.crt', 154 | caPath:'test/data/root-CA.crt', 155 | clientId:'dummy-client-1', 156 | host:'XXXX.iot.us-east-1.amazonaws.com' 157 | }; 158 | 159 | var jobExecutionData = 160 | { 161 | clientToken : "client-1", 162 | execution : { 163 | jobId : "022", 164 | status : "QUEUED", 165 | jobDocument : {operation: "testOperationName", data: "value"} 166 | } 167 | }; 168 | 169 | it("job subscription valid callback", function() { 170 | // Faking callback 171 | fakeCallback = sinon.spy(); 172 | // Reinit mockMQTTClientObject 173 | mockMQTTClientObject.reInitCommandCalled(); 174 | mockMQTTClientObject.resetPublishedMessage(); 175 | 176 | var jobs = jobsModule(jobsConfig); 177 | 178 | jobs.subscribeToJobs('testThingName', 'testOperationName', fakeCallback); 179 | mockMQTTClientObject.emit('message', '$aws/things/testThingName/jobs/$next/get/accepted', JSON.stringify(jobExecutionData) ); 180 | mockMQTTClientObject.emit('message', '$aws/things/testThingName/jobs/notify-next', JSON.stringify(jobExecutionData) ); 181 | // Check spy 182 | assert(fakeCallback.calledTwice); 183 | 184 | }); 185 | }); 186 | }); 187 | 188 | -------------------------------------------------------------------------------- /test/mock/mockMQTTClient.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const EventEmitter = require('events'); 3 | const isUndefined = require('../../common/lib/is-undefined.js'); 4 | 5 | function mockMQTTClient( wrapper, options ) { 6 | 7 | var that = this; 8 | this.options = { 'reconnectPeriod':0 }; 9 | 10 | // Record list indicating whether the corresponding method is called 11 | this.commandCalled = {'publish':0, 'subscribe':0, 'unsubscribe':0, 'end':0, 'handleMessage':0}; 12 | this.lastPublishedMessage = 'Empty'; // Keep track of the last published message 13 | this.subscriptions = new Array; 14 | this.subscriptions.length = 0; 15 | this.unsubscriptions = new Array; 16 | this.unsubscriptions.length = 0; 17 | this.publishes = new Array; // for all topics 18 | this.publishes.length = 0; 19 | this.subscribeQosValues = new Array; 20 | this.subscribeQosValues.length = 0; 21 | this.publishQosValues = new Array; 22 | this.publishQosValues.length = 0; 23 | 24 | // Reinit the record list 25 | this.reInitCommandCalled = function() { 26 | this.commandCalled['publish'] = 0; 27 | this.commandCalled['subscribe'] = 0; 28 | this.commandCalled['unsubscribe'] = 0; 29 | this.commandCalled['end'] = 0; 30 | this.commandCalled['handleMessage'] = 0; 31 | 32 | var topic = this.subscriptions.shift(); 33 | while (!isUndefined( topic )) { 34 | topic = this.subscriptions.shift(); 35 | } 36 | var message = this.publishes.shift(); 37 | while (!isUndefined( message )) { 38 | message = this.publishes.shift(); 39 | } 40 | var qos = this.subscribeQosValues.shift(); 41 | while (!isUndefined( qos )) { 42 | qos = this.subscribeQosValues.shift(); 43 | } 44 | var qos = this.publishQosValues.shift(); 45 | while (!isUndefined( qos )) { 46 | qos = this.publishQosValues.shift(); 47 | } 48 | }; 49 | 50 | // Reset publishedMessage 51 | this.resetPublishedMessage = function() { 52 | this.lastPublishedMessage = 'Empty'; 53 | } 54 | 55 | // This is the module mocking the client returned by mqtt.connect, APIs are: 56 | this.publish = function(topic, message, options, callback) { 57 | options = options || ''; 58 | callback = callback || ''; 59 | this.lastPublishedMessage = message; 60 | this.commandCalled['publish'] += 1; 61 | this.publishes.push( message ); 62 | 63 | if (!isUndefined( options.qos )) { 64 | this.publishQosValues.push( options.qos ); 65 | } 66 | }; 67 | 68 | this.subscribe = function(topic, options, callback) { 69 | options = options || ''; 70 | callback = callback || ''; 71 | this.commandCalled['subscribe'] += 1; 72 | 73 | var granted = []; 74 | if ( Object.prototype.toString.call(topic) === '[object Array]' ) { 75 | topic.forEach( function( item, index, array ) { 76 | var grantedTopic = {topic: item, qos: 0} 77 | that.subscriptions.push( item ); 78 | if (!isUndefined( options.qos )) { 79 | that.subscribeQosValues.push( options.qos ); 80 | grantedTopic.qos = options.qos; 81 | } 82 | 83 | if (mockMQTTClient.triggerError()) { 84 | grantedTopic.qos = 128; 85 | } 86 | 87 | granted.push(grantedTopic); 88 | }); 89 | } 90 | else { 91 | var grantedTopic = {topic: topic, qos: 0} 92 | this.subscriptions.push( topic ); 93 | if (!isUndefined( options.qos )) { 94 | that.subscribeQosValues.push( options.qos ); 95 | grantedTopic.qos = options.qos; 96 | } 97 | if (mockMQTTClient.triggerError()) { 98 | grantedTopic.qos = 128; 99 | } 100 | granted.push(grantedTopic); 101 | } 102 | if(callback !== '') { 103 | callback(null, granted); // call callback 104 | } 105 | }; 106 | 107 | this.unsubscribe = function(topic, callback) { 108 | callback = callback || ''; 109 | this.commandCalled['unsubscribe'] += 1; 110 | if ( Object.prototype.toString.call(topic) === '[object Array]' ) { 111 | topic.forEach( function( item, index, array ) { 112 | that.unsubscriptions.push( item ); 113 | }); 114 | } 115 | else { 116 | this.unsubscriptions.push( topic ); 117 | } 118 | if (callback !== '') { 119 | callback(null); // call callback 120 | } 121 | }; 122 | 123 | this.end = function(force, cb) { 124 | force = force || false; 125 | cb = cb || ''; 126 | this.commandCalled['end'] += 1; 127 | }; 128 | 129 | this.handleMessage = function(packet, callback) { 130 | this.commandCalled['handleMessage'] += 1; 131 | }; 132 | 133 | EventEmitter.call(this); 134 | } 135 | 136 | util.inherits(mockMQTTClient, EventEmitter); 137 | 138 | mockMQTTClient.triggerError = function() { 139 | return false; 140 | }; 141 | 142 | module.exports = mockMQTTClient; 143 | -------------------------------------------------------------------------------- /test/mock/mockTls.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const EventEmitter = require('events'); 3 | 4 | function mockTls() { 5 | // Record list indicating whether the corresponding method is called 6 | this.commandCalled = {'connect':0, 'end':0, 'emit':0, 'removeListener':0, 'on':0}; 7 | 8 | // Reinit the record list 9 | this.reInitCommandCalled = function() { 10 | this.commandCalled['connect'] = 0; 11 | this.commandCalled['end'] = 0; 12 | this.commandCalled['emit'] = 0; 13 | this.commandCalled['removeListener'] = 0; 14 | this.commandCalled['on'] = 0; 15 | }; 16 | this.reInitError = function() { 17 | this.error = ''; 18 | }; 19 | 20 | // This is the module mocking a TLS connection, APIs are: 21 | this.connect = function(options) { 22 | options = options || ''; 23 | this.commandCalled['connect'] += 1; 24 | return this; 25 | }; 26 | 27 | this.end = function() { 28 | this.commandCalled['end'] += 1; 29 | }; 30 | 31 | this.emit = function(errorString, error) { 32 | this.commandCalled['emit'] += 1; 33 | this.error = error; 34 | }; 35 | this.on = function(message, callback) { 36 | callback = callback || ''; 37 | this.commandCalled['on'] += 1; 38 | if(callback !== '') { 39 | if (message === 'error') 40 | { 41 | callback('testing error'); // call callback 42 | } 43 | else 44 | { 45 | callback(null); // call callback 46 | } 47 | } 48 | }; 49 | 50 | this.removeListener = function(message, callback) { 51 | force = force || false; 52 | callback = callback || ''; 53 | if(callback !== '') { 54 | callback(null); // call callback 55 | } 56 | this.commandCalled['removeListener'] += 1; 57 | }; 58 | 59 | EventEmitter.call(this); 60 | } 61 | 62 | function mockMqtt() { 63 | // Record list indicating whether the corresponding method is called 64 | this.commandCalled = {'emit':0}; 65 | 66 | // Reinit the record list 67 | this.reInitCommandCalled = function() { 68 | this.commandCalled['emit'] = 0; 69 | }; 70 | this.emit = function(errorString, error) { 71 | this.commandCalled['emit'] += 1; 72 | this.error = error; 73 | }; 74 | EventEmitter.call(this); 75 | } 76 | 77 | util.inherits(mockTls, EventEmitter); 78 | module.exports = mockTls; 79 | 80 | util.inherits(mockMqtt, EventEmitter); 81 | module.exports.mqtt = mockMqtt; 82 | --------------------------------------------------------------------------------