├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── THIRD-PARTY-LICENSES.txt ├── deployment ├── build-s3-dist-env.sh ├── build-s3-dist.sh ├── cdk-solution-helper │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json ├── deploy.sh ├── reduce_contents.py ├── requirements.txt ├── run-pre-check.sh ├── run-unit-tests.sh ├── test-release.py └── venv_check.py ├── docs ├── ARCHITECTURE.md ├── DEV_PROCESS.md ├── EXTENDING_MSAM.md ├── MANAGED_INSTANCES.md ├── MEDIALIVE_WORKFLOW_WIZARD.md ├── RESOURCE_TAGS.md ├── REST_API.md ├── WORKSHOP.md ├── behav-frontend-update.jpg ├── behav-general-api-invoke.jpg ├── behav-general-backend-notify.jpg ├── behav-webtool-bootstrap.jpg ├── behavioral-views.drawio ├── deployment-view.drawio ├── deployment-view.jpg ├── github-workflows.drawio ├── github-workflows.jpg ├── images │ ├── S3-diagram-tag.png │ ├── ack-iam-resources.png │ ├── added-tile.png │ ├── advanced-settings.jpeg │ ├── advanced-settings.png │ ├── alarm-channel-tiles.png │ ├── api-gateway-documentation.png │ ├── api-key.jpeg │ ├── cfn-browser-url.jpeg │ ├── cfn-browser-url.png │ ├── cfn-core-endpoint.jpeg │ ├── cfn-core-endpoint.png │ ├── cfn-delete-stack.jpeg │ ├── cfn-dynamodb-tables.jpeg │ ├── cfn-dynamodb-tables.png │ ├── channel-tile-json.jpeg │ ├── channel-tiles.png │ ├── cloudfront-dns-error.png │ ├── cloudwatch-channel-tile.jpeg │ ├── cloudwatch-diagram.jpeg │ ├── cloudwatch-diagram.png │ ├── complex-workflow.jpeg │ ├── connection-settings.png │ ├── connections-content-db.png │ ├── create-channel-tile.jpeg │ ├── custom-nodes.jpeg │ ├── diagram-complex.png │ ├── diagram-locked.png │ ├── diagram-nodes-edges.png │ ├── diagram-unlocked.png │ ├── diagram-with-EMT.png │ ├── ec2-diagram-nodetype.png │ ├── ec2-nodetype.png │ ├── horizontal-alignment.png │ ├── iam-template-outputs.png │ ├── master-template-params.png │ ├── mediapackage-cloudfront-tag.png │ ├── monitor-tab.jpeg │ ├── pipeline-alerts-tab.png │ ├── recent-cloudwatch-events.png │ ├── remember-button.jpeg │ ├── removed-tile.png │ ├── root-template-outputs.png │ ├── selected-item-json.jpeg │ ├── service-content-db.png │ ├── simple-workflow.jpeg │ ├── ssm-custom-namespace.png │ ├── ssm-document-tag.png │ ├── ssm-documents.png │ ├── ssm-elemental-live-tag.png │ ├── ssm-live-encoder-metrics.png │ ├── ssm-live-status-metric.png │ ├── ssm-managed-instances.png │ ├── ssm-node-in-alarm-state.png │ ├── ssm-node-subscribed-to-alarm.png │ ├── ssm-node-update-interval.png │ ├── ssm-remove-alarm-notification.png │ ├── ssm-subscribe-alarm.png │ ├── subscribe-alarms.jpeg │ ├── subscribed-alarms-tab.png │ ├── tag-generated-tile.png │ ├── tile-tag-input-channel.png │ ├── tiles-overview.png │ ├── usage-plan-stage.jpeg │ ├── vertical-alignment.png │ ├── video-sources-diagram-tag.png │ ├── workflow-basic-details.png │ ├── workflow-complete.png │ ├── workflow-diagram-layout.png │ ├── workflow-diagram-tile.png │ ├── workflow-video-outputs-1.png │ ├── workflow-video-outputs-2.png │ └── workflow-video-source.png ├── logical-view.drawio ├── logical-view.jpg ├── physical-view.drawio ├── physical-view.jpg ├── use-cases.drawio └── use-cases.jpg ├── solution-manifest.yaml └── source ├── .coveragerc ├── cdk ├── README.md ├── bin │ └── media-services-application-mapper.ts ├── cdk.json ├── lib │ ├── media-services-application-mapper.ts │ ├── msam-browser-app.ts │ ├── msam-core.ts │ ├── msam-dynamodb.ts │ ├── msam-events.ts │ ├── msam-iam-roles.ts │ └── utils.ts ├── package-lock.json ├── package.json ├── pretest.sh ├── test │ ├── __snapshots__ │ │ ├── media-services-application-mapper.test.ts.snap │ │ ├── msam-browser-app.test.ts.snap │ │ ├── msam-core.test.ts.snap │ │ ├── msam-dynamodb.test.ts.snap │ │ ├── msam-events.test.ts.snap │ │ └── msam-iam-roles.test.ts.snap │ ├── media-services-application-mapper.test.ts │ ├── msam-browser-app.test.ts │ ├── msam-core.test.ts │ ├── msam-dynamodb.test.ts │ ├── msam-events.test.ts │ └── msam-iam-roles.test.ts └── tsconfig.json ├── events ├── .gitignore ├── __init__.py ├── cloudwatch_alarm.py ├── media_events.py ├── requirements.txt └── test │ ├── __init__.py │ ├── run_unit_tests.py │ ├── test_cloudwatch_alarm.py │ └── test_media_events.py ├── html ├── .eslintrc.json ├── .gitignore ├── .jshintrc ├── babel.config.json ├── favicon.ico ├── index.html ├── jest.config.js ├── js │ ├── app │ │ ├── alarms.js │ │ ├── api_check.js │ │ ├── build-tmp.js │ │ ├── build.js │ │ ├── channels.js │ │ ├── cloudwatch_events.js │ │ ├── connections.js │ │ ├── events.js │ │ ├── main.js │ │ ├── mappers │ │ │ ├── connections │ │ │ │ ├── cloudfront_medialive_input.js │ │ │ │ ├── helper.js │ │ │ │ ├── link_device_medialive_input.js │ │ │ │ ├── mediaconnect_flow_mediaconnect_flow.js │ │ │ │ ├── mediaconnect_flow_medialive_input.js │ │ │ │ ├── medialive_channel_input.js │ │ │ │ ├── medialive_channel_mediaconnect_flow.js │ │ │ │ ├── medialive_channel_mediapackage_channel.js │ │ │ │ ├── medialive_channel_mediastore_container.js │ │ │ │ ├── medialive_channel_multiplex.js │ │ │ │ ├── medialive_channel_s3_bucket.js │ │ │ │ ├── medialive_input_channel.js │ │ │ │ ├── mediapackage_channel_endpoint.js │ │ │ │ ├── mediapackage_endpoint_cloudfront.js │ │ │ │ ├── mediapackage_endpoint_mediatailor_configuration.js │ │ │ │ ├── mediastore_container_cloudfront.js │ │ │ │ ├── mediastore_container_medialive_input.js │ │ │ │ ├── mediastore_container_mediatailor_configuration.js │ │ │ │ ├── multiplex_mediaconnect_flow.js │ │ │ │ ├── s3_bucket_cloudfront.js │ │ │ │ ├── s3_bucket_medialive_input.js │ │ │ │ ├── s3_bucket_mediatailor_configuration.js │ │ │ │ ├── speke.js │ │ │ │ └── user_defined_connection.js │ │ │ ├── mappers.js │ │ │ └── nodes │ │ │ │ ├── cloudfront.js │ │ │ │ ├── ec2.js │ │ │ │ ├── mediaconnect.js │ │ │ │ ├── medialive.js │ │ │ │ ├── mediapackage.js │ │ │ │ ├── mediastore.js │ │ │ │ ├── mediatailor.js │ │ │ │ ├── s3.js │ │ │ │ ├── speke.js │ │ │ │ ├── ssm_managed_instance.js │ │ │ │ └── user_defined_node.js │ │ ├── model.js │ │ ├── notes.js │ │ ├── regions.js │ │ ├── search.js │ │ ├── server.js │ │ ├── settings.js │ │ ├── statemachine.js │ │ ├── tools │ │ │ ├── build_numbers.js │ │ │ ├── clear_http.js │ │ │ └── duplicate_names.js │ │ └── ui │ │ │ ├── alarm_indicators.js │ │ │ ├── alarms_menu.js │ │ │ ├── alert.js │ │ │ ├── channels_menu.js │ │ │ ├── confirmation.js │ │ │ ├── diagram_factory.js │ │ │ ├── diagram_menu.js │ │ │ ├── diagram_statemachine_factory.js │ │ │ ├── diagrams.js │ │ │ ├── dragdrop.js │ │ │ ├── event_alert_indicators.js │ │ │ ├── help_menu.js │ │ │ ├── information_compartment.js │ │ │ ├── informational_overlays.js │ │ │ ├── layout.js │ │ │ ├── monitor_view.js │ │ │ ├── nodes_menu.js │ │ │ ├── notes_menu.js │ │ │ ├── notes_view.js │ │ │ ├── overlays │ │ │ ├── alarms_only.js │ │ │ ├── ec2_instance.js │ │ │ ├── mediaconnect_flow.js │ │ │ ├── medialive_channel.js │ │ │ ├── medialive_multiplex.js │ │ │ ├── overlay_tools.js │ │ │ ├── overlays.js │ │ │ └── ssm_managed_instance.js │ │ │ ├── search_view.js │ │ │ ├── selected_item_view.js │ │ │ ├── settings_menu.js │ │ │ ├── status_view.js │ │ │ ├── svg_node.js │ │ │ ├── tile_view.js │ │ │ ├── user_defined.js │ │ │ ├── util.js │ │ │ └── vis_options.js │ └── test │ │ ├── alarms.test.js │ │ ├── always.test.js │ │ ├── api_check.test.js │ │ └── connections.test.js ├── package-lock.json └── package.json ├── msam ├── .chalice │ ├── config.json │ └── policy-dev.json ├── .coveragerc ├── .gitignore ├── __init__.py ├── app.py ├── chalicelib │ ├── __init__.py │ ├── cache.py │ ├── channels.py │ ├── cloudwatch.py │ ├── connections.py │ ├── content.py │ ├── layout.py │ ├── nodes.py │ ├── notes.py │ ├── periodic.py │ ├── settings.py │ └── tags.py ├── db │ ├── lambda_function.py │ └── makezip.sh ├── requirements.txt └── test │ ├── __init__.py │ ├── run_unit_tests.py │ ├── test_app.py │ ├── test_cache.py │ ├── test_channels.py │ ├── test_cloudwatch.py │ ├── test_connections.py │ ├── test_content.py │ ├── test_db.py │ ├── test_layout.py │ ├── test_nodes.py │ ├── test_notes.py │ ├── test_periodic.py │ ├── test_settings.py │ └── test_tags.py ├── tools ├── autopep8.sh ├── copy_table.py ├── create_test_alarms.py ├── create_test_alarms.sh ├── delete_disconnected.py ├── docker_msam_browser_app.sh ├── js-beautify.sh ├── js-check.sh ├── jshint.sh ├── jslint-report.sh ├── pa11y.json ├── pylint.sh └── yapf.sh └── web-cloudformation ├── .gitignore ├── cfn_bucket_loader.py ├── cfn_invalidate_resource.py └── makezip.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .DS_Store 3 | __pycache__/ 4 | *~ 5 | ~* 6 | jslint-report.txt 7 | 8 | deployment/*.template 9 | deployment/test_venv 10 | 11 | **/global-s3-assets 12 | **/regional-s3-assets 13 | **/assets 14 | **/open-source 15 | **/*.zip 16 | **/package/ 17 | 18 | **/error.txt 19 | 20 | node_modules/ 21 | 22 | coverage.lcov 23 | coverage.xml* 24 | .coverage 25 | 26 | source/cdk/*.js 27 | !source/cdk/jest.config.js 28 | source/cdk/*.d.ts 29 | source/cdk/node_modules 30 | source/cdk/coverage 31 | 32 | # CDK asset staging directory 33 | source/cdk/.cdk.staging 34 | source/cdk/cdk.out 35 | source/cdk/lib/*.d.ts 36 | source/cdk/test/*.d.ts 37 | source/cdk/bin/*.d.ts 38 | source/cdk/lib/*.js 39 | source/cdk/test/*.js 40 | source/cdk/bin/*.js 41 | cdk.context.json 42 | source/cdk/dist/* 43 | 44 | test_venv 45 | trufflehog 46 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at msam-github-team@amazon.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/awslabs/aws-media-services-application-mapper/issues), or [recently closed](https://github.com/awslabs/aws-media-services-application-mapper/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Please visit the [Development Process](docs/DEV_PROCESS.md). 25 | 26 | ## Finding contributions to work on 27 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-media-services-application-mapper/labels/help%20wanted) issues is a great place to start. 28 | 29 | 30 | ## Code of Conduct 31 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 32 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 33 | opensource-codeofconduct@amazon.com with any additional questions or comments. 34 | 35 | 36 | ## Security issue notifications 37 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 38 | 39 | 40 | ## Licensing 41 | 42 | See the [LICENSE](https://github.com/awslabs/aws-media-services-application-mapper/blob/main/LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 43 | 44 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 45 | 46 | Navigate to [README](README.md) | [Architecture](https://docs.aws.amazon.com/solutions/latest/media-services-application-mapper/architecture-overview.html) | [Workshop](docs/WORKSHOP.md) | [Install](https://docs.aws.amazon.com/solutions/latest/media-services-application-mapper/automated-deployment.html) | [Usage](https://docs.aws.amazon.com/solutions/latest/media-services-application-mapper/using-the-browser-application.html) | [Uninstall](https://docs.aws.amazon.com/solutions/latest/media-services-application-mapper/uninstall-the-solution.html) | [Rest API](docs/REST_API.md) -------------------------------------------------------------------------------- /deployment/build-s3-dist-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is used to configure the virtual build environment 4 | # required by the solution. 5 | 6 | set -euo pipefail 7 | 8 | # get reference for all important folders 9 | template_dir="$PWD" 10 | current_dir="$PWD" 11 | source_dir="$template_dir/../source" 12 | 13 | # configure the environment 14 | pip install --upgrade -r requirements.txt 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/README.md: -------------------------------------------------------------------------------- 1 | # cdk-solution-helper 2 | 3 | A lightweight helper function that cleans-up synthesized templates from the AWS Cloud Development Kit (CDK) and prepares 4 | them for use with the AWS Solutions publishing pipeline. This function performs the following task: 5 | 6 | ## Template cleanup 7 | 8 | Cleans out parameters and metadata created by AWS CDK that help with `cdk deploy`, but aren't used in the 9 | published CloudFormation templates. 10 | 11 | *** 12 | © Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 13 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-solution-helper", 3 | "version": "0.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cdk-solution-helper", 9 | "version": "0.1.0", 10 | "license": "Apache-2.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-solution-helper", 3 | "version": "0.1.0", 4 | "description": "Cleans-up synthesized templates from the AWS Cloud Development Kit (CDK) and prepares them for use with the AWS Solutions publishing pipeline.", 5 | "license": "Apache-2.0", 6 | "author": { 7 | "name": "Amazon Web Services", 8 | "url": "https://aws.amazon.com/solutions" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /deployment/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -euo pipefail 7 | 8 | # local folders 9 | template_dir="$PWD" 10 | template_dist_dir="$template_dir/global-s3-assets" 11 | build_dist_dir="$template_dir/regional-s3-assets" 12 | 13 | # AWS default settings 14 | BUCKET="rodeolabz" 15 | REGIONS="ap-south-1 eu-west-3 eu-north-1 eu-west-2 eu-west-1 ap-northeast-2 ap-northeast-1 sa-east-1 ca-central-1 ap-southeast-1 ap-southeast-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2" 16 | ACL="public-read" 17 | DEPLOY_TYPE="dev" 18 | SOLUTION_NAME="aws-media-services-application-mapper" 19 | VERSION="v1.0.0" 20 | 21 | while getopts 'a:b:p:r:s:t:v:h' OPTION; do 22 | case "$OPTION" in 23 | b) 24 | BUCKET="$OPTARG" 25 | ;; 26 | r) 27 | REGIONS="$OPTARG" 28 | ;; 29 | p) 30 | DEPLOY_PROFILE="$OPTARG" 31 | ;; 32 | a) 33 | ACL="$OPTARG" 34 | ;; 35 | t) 36 | DEPLOY_TYPE="$OPTARG" 37 | ;; 38 | s) 39 | SOLUTION_NAME="$OPTARG" 40 | ;; 41 | v) 42 | VERSION="$OPTARG" 43 | ;; 44 | h) 45 | echo 46 | echo "script usage: $(basename $0) [-b BucketBasename] [-s SolutionName] [-v VersionString] [-r RegionsForDeploy] [-a ACLSettings(public-read|none)] [-t DeployType(dev|release)]" >&2 47 | echo "example usage: ./$(basename $0) -b mybucket -s aws-media-services-application-mapper -v v1.8.0 -r \"us-west-2 us-east-1\" -a public-read -t dev" >&2 48 | echo 49 | exit 1 50 | ;; 51 | ?) 52 | echo "script usage: $(basename $0) [-b BucketBasename] [-s SolutionName] [-v VersionString] [-r RegionsForDeploy] [-a ACLSettings(public-read|none)] [-t DeployType(dev|release)]" >&2 53 | exit 1 54 | ;; 55 | esac 56 | done 57 | shift "$(($OPTIND -1))" 58 | 59 | echo Bucket Basename = $BUCKET 60 | echo Regions = $REGIONS 61 | echo ACL Setting = $ACL 62 | echo Deploy Type = $DEPLOY_TYPE 63 | 64 | # Get account id 65 | account_id=$(aws sts get-caller-identity --query Account --output text) 66 | if [ $? -ne 0 ]; then 67 | echo "ERROR: Failed to get AWS account ID" 68 | exit 1 69 | fi 70 | 71 | for R in $REGIONS; do 72 | # Validate ownership of $BUCKET-$R 73 | aws s3api head-bucket --bucket $BUCKET-$R --expected-bucket-owner $account_id 74 | if [ $? -ne 0 ]; then 75 | echo "ERROR: Your AWS account does not own s3://$BUCKET-$R/" 76 | exit 1 77 | fi 78 | if [ "$ACL" = "public-read" ]; then 79 | aws s3 sync $template_dist_dir/ s3://$BUCKET-$R/$SOLUTION_NAME/$VERSION --exclude "*release.template" --acl public-read --storage-class INTELLIGENT_TIERING 80 | aws s3 sync $build_dist_dir/ s3://$BUCKET-$R/$SOLUTION_NAME/$VERSION --acl public-read --storage-class INTELLIGENT_TIERING 81 | else 82 | aws s3 sync $template_dist_dir/ s3://$BUCKET-$R/$SOLUTION_NAME/$VERSION --exclude "*release.template" --storage-class INTELLIGENT_TIERING 83 | aws s3 sync $build_dist_dir/ s3://$BUCKET-$R/$SOLUTION_NAME/$VERSION --storage-class INTELLIGENT_TIERING 84 | fi 85 | 86 | if [ "$DEPLOY_TYPE" = "release" ]; then 87 | aws s3 sync $template_dist_dir/ s3://$BUCKET-$R/$SOLUTION_NAME/latest --exclude "*" --include "*release.template" --acl public-read --storage-class INTELLIGENT_TIERING 88 | echo 89 | echo "ROOT TEMPLATE WEB LOCATION: https://$BUCKET-$R.s3.amazonaws.com/$SOLUTION_NAME/latest/aws-media-services-application-mapper-release.template" 90 | else 91 | echo "ROOT TEMPLATE WEB LOCATION: https://$BUCKET-$R.s3.amazonaws.com/$SOLUTION_NAME/$VERSION/aws-media-services-application-mapper-release.template" 92 | fi 93 | done 94 | -------------------------------------------------------------------------------- /deployment/reduce_contents.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This program returns 0 if the current environment is a virtual environment. 4 | """ 5 | 6 | import argparse 7 | import os 8 | 9 | 10 | def main(): 11 | parser = argparse.ArgumentParser(description="""Reduce a folder's contents to only those in a file list""") 12 | parser.add_argument('--file', 13 | required=True, 14 | help='retain files and folders in this file') 15 | parser.add_argument('--folder', 16 | required=True, 17 | help='target folder to reduce') 18 | parser.add_argument('--execute', 19 | action='store_true', 20 | help='required, otherwise do a dryrun') 21 | parser.add_argument('--verbose', 22 | action='store_true', 23 | help='detailed output') 24 | 25 | args = parser.parse_args() 26 | 27 | if os.path.isfile(args.file) and os.path.isdir(args.folder): 28 | with open(args.file, 'rt') as file: 29 | retain_files = file.read().replace('\n', '') 30 | for dirpath, _, filenames in os.walk(args.folder): 31 | for name in filenames: 32 | absfilename = f"{dirpath}/{name}" 33 | if absfilename in retain_files: 34 | if args.verbose: 35 | print(f"keep file: {absfilename}") 36 | else: 37 | if args.verbose: 38 | print(f"remove file: {absfilename}") 39 | if args.execute: 40 | os.remove(absfilename) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /deployment/requirements.txt: -------------------------------------------------------------------------------- 1 | chalice 2 | networkx 3 | boto3 4 | botocore 5 | cfn-lint 6 | crhelper 7 | defusedxml 8 | jsonpath_ng 9 | pylint 10 | requests 11 | stringcase 12 | testresources 13 | aws-requests-auth 14 | coverage 15 | -------------------------------------------------------------------------------- /deployment/run-unit-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This assumes all of the OS-level configuration has been completed and git repo has already been cloned 4 | # 5 | # This script should be run from the repo's deployment directory 6 | # cd deployment 7 | # ./run-unit-tests.sh 8 | # 9 | 10 | # get reference for all important folders 11 | template_dir="$PWD" 12 | current_dir="$PWD" 13 | source_dir="$template_dir/../source" 14 | 15 | # PYTHON UNIT TESTS 16 | test_venv_name="test_venv" 17 | test_venv_dir="$current_dir/$test_venv_name" 18 | 19 | # clean up testing venv if present 20 | rm -rf $test_venv_dir 21 | 22 | # check if we need a new testing venv 23 | ./venv_check.py 24 | if [ $? == 1 ]; then 25 | echo 'creating temporary virtual environment for testing' 26 | python3 -m venv $test_venv_name 27 | source $test_venv_name/bin/activate 28 | # configure the environment 29 | pip install --upgrade -r requirements.txt 30 | pip install --upgrade -r $source_dir/msam/requirements.txt 31 | pip install --upgrade -r $source_dir/events/requirements.txt 32 | else 33 | echo 'using current virtual environment for tests' 34 | fi 35 | 36 | echo 37 | echo --------------------------------- 38 | echo BACK-END UNIT TESTS 39 | echo --------------------------------- 40 | 41 | # launch python unit tests 42 | cd $source_dir/msam 43 | coverage run -m test.run_unit_tests 44 | coverage xml 45 | # fix the source file paths 46 | sed -i -- 's/filename\=\"/filename\=\"source\/msam\//g' coverage.xml 47 | echo coverage report is at $source_dir/msam/coverage.xml 48 | 49 | cd $source_dir/events 50 | coverage run -m test.run_unit_tests 51 | coverage xml 52 | # fix the source file paths 53 | sed -i -- 's/filename\=\"/filename\=\"source\/events\//g' coverage.xml 54 | echo coverage report is at $source_dir/events/coverage.xml 55 | 56 | echo 57 | echo --------------------------------- 58 | echo FRONT-END UNIT TESTS 59 | echo --------------------------------- 60 | 61 | # launch javascript unit tests for UI 62 | cd $source_dir/html 63 | rm -rf node_modules 64 | npm install 65 | npm test 66 | # fix the source file paths 67 | sed -i -- 's/SF:/SF:source\/html\//g' lcov.info 68 | echo coverage report is at $source_dir/html/lcov.info 69 | 70 | echo 71 | echo --------------------------------- 72 | echo CDK UNIT TESTS 73 | echo --------------------------------- 74 | echo 75 | cd $source_dir/cdk 76 | rm -rf node_modules 77 | npm test 78 | echo coverage report is at $source_dir/cdk/coverage/lcov.info 79 | -------------------------------------------------------------------------------- /deployment/test-release.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """ 5 | This module invokes an endpoint to deploy the built template for testing. 6 | """ 7 | 8 | import os 9 | import requests 10 | from aws_requests_auth.boto_utils import BotoAWSRequestsAuth 11 | from urllib.parse import urlparse 12 | 13 | API_REGION = os.environ.get('AWS_DEFAULT_REGION') 14 | TEST_ENDPOINT = os.environ.get('TEST_ENDPOINT') 15 | 16 | def main(): 17 | """ 18 | Invokes endpoint for testing built template. 19 | """ 20 | 21 | parsed = urlparse(TEST_ENDPOINT) 22 | auth = BotoAWSRequestsAuth(aws_host=parsed.netloc, 23 | aws_region=API_REGION, 24 | aws_service='execute-api') 25 | response = requests.post(TEST_ENDPOINT, auth=auth, timeout=25) 26 | print(response.json()) 27 | if response.status_code != 200: 28 | return 1 29 | return 0 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /deployment/venv_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This program returns 0 if the current environment is a virtual environment. 4 | """ 5 | import sys 6 | 7 | # compare the python prefixes, same == not venv 8 | IN_VENV = (getattr(sys, "base_prefix", None) or getattr( 9 | sys, "real_prefix", None) or sys.prefix) != sys.prefix 10 | # return success (0) if in a venv 11 | sys.exit(IN_VENV is False) 12 | -------------------------------------------------------------------------------- /docs/behav-frontend-update.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/behav-frontend-update.jpg -------------------------------------------------------------------------------- /docs/behav-general-api-invoke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/behav-general-api-invoke.jpg -------------------------------------------------------------------------------- /docs/behav-general-backend-notify.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/behav-general-backend-notify.jpg -------------------------------------------------------------------------------- /docs/behav-webtool-bootstrap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/behav-webtool-bootstrap.jpg -------------------------------------------------------------------------------- /docs/deployment-view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/deployment-view.jpg -------------------------------------------------------------------------------- /docs/github-workflows.drawio: -------------------------------------------------------------------------------- 1 | 7V1bd6M2EP41Pid9SA4XA/Zj7I2z2zbbbdzd7falB4NsaABRLnbcX18JC1802ODYAQw+eQgIoYiZb0bfjC7pyEP39THQfesJm8jpSIL52pE/dCRJlEWZ/KIlS1YiCKxkFtgmK9sUjO3/UFqRlca2icKdihHGTmT7u4UG9jxkRDtlehDgxW61KXZ2/6qvzxAoGBu6A0u/22ZksVLyHZsHH5E9s9if7insgaunlVlBaOkmXmwVyQ8deRhgHK2u3Nchcqj0Urms3hvtebruWIC8qMgLv32Ov/2jeP5y9FkXFHk+eYmeb5ky5roTsw/+EocWKRliLwrsSRzhgHU/WqYymdqOM8QOeUJv5YfRSBjJHXkQRgF+QekTD3uk/sDUQwvRLgjkZo6CyCbCvXfsmUfKIuyT0in5W2PWvMDut9onPyNyIQ/gFzMh0GbR61YRk8Ajwi6KgiWpwp52mTKWu7eLLdX2WJm1pVWtzwp1BqfZuuWNxMkFE/oRChCBAobYde0owXlHUh3Sg8GE6ECd0atfMZEeefYNBaGNPXI1CHTPoBq7mf959+Pur5+AugIce2aiApG0sLDsCI193aBPF8RqSZkVuQ57rE9C7MQRug8MZoxJ6eauu1Z0ag/SeVTT41WToRshQzfrwrPrRoLGEaDbVD+8aoYWMl4aIvxMwyhX+AoU/jPvl1TiHUTqMW6mOHghUib98MyVrw2SW5eOSCG0iQt2YZJaNx+m7htEqAcTnpGLI9QKn6WolZuNlm0234l9TB1CPxpkCF0wYlRtCL19htBE8WtC3cTfP8ylhJQ8fUBz5GDfpd++cUaX6YFkoIXKXVAaurWBN/HirwFxEmFIkTkcN9kKajAQizC0ftKdBfnKNUsdIyNAVPhjQ/cuVfRat34W0IUDAXUyOI6aIuVe9UKGrP+TZ0e2njiX1NlfprR7tRM2pPWD2HY2Ae9X38G6mfr4scz798vUA4/6OpAbyPH/QK7vrGDP/DqOA2NlBWbyizqfsClKqMPYCpn+Bxoi8RKG8tiSaGjpPq3nvs7o3MUdjc8MSw+iuwgFru3pNMWUrZd3kKomFZSq+l5CTTuwJVQgUGTOUBpe4iCy8Ax7uvOwKd0WMPLMezoDQ+v6yKMlziS5TWPXBLJE4mk1FueSF0c27XwSwu6GwiSIVbrKQKVw3xiQcEgp4coc8/kC6coMRQU4HRXDQSVnKTFAxEnYc7TTtywtsua+YDth5gwwCh/oSRwQVp/J3tpgATSk5jW0EgNoKAHV+htPwBmMT9qCM7kozrQrzk7HmdRanGlFcda74ux0nMFAvy046xXFWf+Ks9NxBrMajUwoKdKunGuQUJLgZPSlJ5R4KVef45BgQonlOJoh4RokLySYRmp48oJXQg2SFxLMIH3Xk0nKKV3TIiTz9c9obqPFWilPiAwtQAc1Tm/wcq9BegPmjIBAm0nT0tErl6alDKMKmgaW/72Vpsl5Db0zTZNbm0aTiqbR0pH+irNTcNbaNFoKn3ycVZhGaw7OWptGk4qm0aQK02jNwRlMo1ESHELa21C0FU2myUp1aKs8mUaUpC+3qvm0QnigwxqX5mDJpH394jcIHVtflHfqk4tVj89rKlmZQC9O1raySJFfZLlZgCw8zLNWhLTcqMQKQ5+LMyqVGyo05bCR8PXLMRKYs23M+jR+uWv1yVsZJm9b4mLEwtFIiz3M2YwaJrBbAjOp6EB2RdnpKIMZ+pagTCyIsgoTeM1BGZyQaClLLzqCXkn6ESSdm4BTxZxAlmOU6uHqfZ6BHlm/lBAgjc23LGyk2w49GKAVdlWYM7TZnR+dUeJWJ/UPA1+pAvetnRqSi051p3uUWwn5s+FMaivOUvjk4uy68PUMMGvtAut+UW92jfrPADM4q9IqtljYp10nII8Jw7Tj+CJfvxzCCOdKWpqK6BbO5l+NoLgRdLkj6zTtsBHw9csxAjiFNQxQMl04+uqbq3nDZHF2csZCA0+z46cVCy/afr95xW5rJ3zEorn4bp2yNzwQ3uyJ+IbOR/7kv9Evy0+f/r3Vf54bi/sfTw/93zOO4X1GDtLp6rO6Ht4H7DoDZ3tNvQ82J0FLf7fD+zI1AIO8GmyxO03G3ChW7kbGTCHDECf3GKG1HTRDCeXuwstUQtbCJDLiJC+yI/qmiHxfsgvv3jCIoBtjA+Xuv8sUP6R5FZwddFahlrq5LlOmjWBpHIHKpW2HhrFt1rbfE5fC0TR+qH9rtCgK4p4BrSSS1ogdnGdBWcZOu/1DzWWhrJfX0HtHAtIVZbtEJRdlamkoAy5IeWvACVrSysYZ5OJf4oljJyc1P9rRx3iyzb6FGzfZ4lFlcumspKXUNevZGoBEvK2WnnUC4n56U5KpcwOBrLzZ1PkhRSxm6semuSFByll0l/fCsYlucrv5f2Kr6pt/yyY//A8= -------------------------------------------------------------------------------- /docs/github-workflows.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/github-workflows.jpg -------------------------------------------------------------------------------- /docs/images/S3-diagram-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/S3-diagram-tag.png -------------------------------------------------------------------------------- /docs/images/ack-iam-resources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ack-iam-resources.png -------------------------------------------------------------------------------- /docs/images/added-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/added-tile.png -------------------------------------------------------------------------------- /docs/images/advanced-settings.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/advanced-settings.jpeg -------------------------------------------------------------------------------- /docs/images/advanced-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/advanced-settings.png -------------------------------------------------------------------------------- /docs/images/alarm-channel-tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/alarm-channel-tiles.png -------------------------------------------------------------------------------- /docs/images/api-gateway-documentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/api-gateway-documentation.png -------------------------------------------------------------------------------- /docs/images/api-key.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/api-key.jpeg -------------------------------------------------------------------------------- /docs/images/cfn-browser-url.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-browser-url.jpeg -------------------------------------------------------------------------------- /docs/images/cfn-browser-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-browser-url.png -------------------------------------------------------------------------------- /docs/images/cfn-core-endpoint.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-core-endpoint.jpeg -------------------------------------------------------------------------------- /docs/images/cfn-core-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-core-endpoint.png -------------------------------------------------------------------------------- /docs/images/cfn-delete-stack.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-delete-stack.jpeg -------------------------------------------------------------------------------- /docs/images/cfn-dynamodb-tables.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-dynamodb-tables.jpeg -------------------------------------------------------------------------------- /docs/images/cfn-dynamodb-tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cfn-dynamodb-tables.png -------------------------------------------------------------------------------- /docs/images/channel-tile-json.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/channel-tile-json.jpeg -------------------------------------------------------------------------------- /docs/images/channel-tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/channel-tiles.png -------------------------------------------------------------------------------- /docs/images/cloudfront-dns-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cloudfront-dns-error.png -------------------------------------------------------------------------------- /docs/images/cloudwatch-channel-tile.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cloudwatch-channel-tile.jpeg -------------------------------------------------------------------------------- /docs/images/cloudwatch-diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cloudwatch-diagram.jpeg -------------------------------------------------------------------------------- /docs/images/cloudwatch-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/cloudwatch-diagram.png -------------------------------------------------------------------------------- /docs/images/complex-workflow.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/complex-workflow.jpeg -------------------------------------------------------------------------------- /docs/images/connection-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/connection-settings.png -------------------------------------------------------------------------------- /docs/images/connections-content-db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/connections-content-db.png -------------------------------------------------------------------------------- /docs/images/create-channel-tile.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/create-channel-tile.jpeg -------------------------------------------------------------------------------- /docs/images/custom-nodes.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/custom-nodes.jpeg -------------------------------------------------------------------------------- /docs/images/diagram-complex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/diagram-complex.png -------------------------------------------------------------------------------- /docs/images/diagram-locked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/diagram-locked.png -------------------------------------------------------------------------------- /docs/images/diagram-nodes-edges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/diagram-nodes-edges.png -------------------------------------------------------------------------------- /docs/images/diagram-unlocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/diagram-unlocked.png -------------------------------------------------------------------------------- /docs/images/diagram-with-EMT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/diagram-with-EMT.png -------------------------------------------------------------------------------- /docs/images/ec2-diagram-nodetype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ec2-diagram-nodetype.png -------------------------------------------------------------------------------- /docs/images/ec2-nodetype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ec2-nodetype.png -------------------------------------------------------------------------------- /docs/images/horizontal-alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/horizontal-alignment.png -------------------------------------------------------------------------------- /docs/images/iam-template-outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/iam-template-outputs.png -------------------------------------------------------------------------------- /docs/images/master-template-params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/master-template-params.png -------------------------------------------------------------------------------- /docs/images/mediapackage-cloudfront-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/mediapackage-cloudfront-tag.png -------------------------------------------------------------------------------- /docs/images/monitor-tab.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/monitor-tab.jpeg -------------------------------------------------------------------------------- /docs/images/pipeline-alerts-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/pipeline-alerts-tab.png -------------------------------------------------------------------------------- /docs/images/recent-cloudwatch-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/recent-cloudwatch-events.png -------------------------------------------------------------------------------- /docs/images/remember-button.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/remember-button.jpeg -------------------------------------------------------------------------------- /docs/images/removed-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/removed-tile.png -------------------------------------------------------------------------------- /docs/images/root-template-outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/root-template-outputs.png -------------------------------------------------------------------------------- /docs/images/selected-item-json.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/selected-item-json.jpeg -------------------------------------------------------------------------------- /docs/images/service-content-db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/service-content-db.png -------------------------------------------------------------------------------- /docs/images/simple-workflow.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/simple-workflow.jpeg -------------------------------------------------------------------------------- /docs/images/ssm-custom-namespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-custom-namespace.png -------------------------------------------------------------------------------- /docs/images/ssm-document-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-document-tag.png -------------------------------------------------------------------------------- /docs/images/ssm-documents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-documents.png -------------------------------------------------------------------------------- /docs/images/ssm-elemental-live-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-elemental-live-tag.png -------------------------------------------------------------------------------- /docs/images/ssm-live-encoder-metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-live-encoder-metrics.png -------------------------------------------------------------------------------- /docs/images/ssm-live-status-metric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-live-status-metric.png -------------------------------------------------------------------------------- /docs/images/ssm-managed-instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-managed-instances.png -------------------------------------------------------------------------------- /docs/images/ssm-node-in-alarm-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-node-in-alarm-state.png -------------------------------------------------------------------------------- /docs/images/ssm-node-subscribed-to-alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-node-subscribed-to-alarm.png -------------------------------------------------------------------------------- /docs/images/ssm-node-update-interval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-node-update-interval.png -------------------------------------------------------------------------------- /docs/images/ssm-remove-alarm-notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-remove-alarm-notification.png -------------------------------------------------------------------------------- /docs/images/ssm-subscribe-alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/ssm-subscribe-alarm.png -------------------------------------------------------------------------------- /docs/images/subscribe-alarms.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/subscribe-alarms.jpeg -------------------------------------------------------------------------------- /docs/images/subscribed-alarms-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/subscribed-alarms-tab.png -------------------------------------------------------------------------------- /docs/images/tag-generated-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/tag-generated-tile.png -------------------------------------------------------------------------------- /docs/images/tile-tag-input-channel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/tile-tag-input-channel.png -------------------------------------------------------------------------------- /docs/images/tiles-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/tiles-overview.png -------------------------------------------------------------------------------- /docs/images/usage-plan-stage.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/usage-plan-stage.jpeg -------------------------------------------------------------------------------- /docs/images/vertical-alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/vertical-alignment.png -------------------------------------------------------------------------------- /docs/images/video-sources-diagram-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/video-sources-diagram-tag.png -------------------------------------------------------------------------------- /docs/images/workflow-basic-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-basic-details.png -------------------------------------------------------------------------------- /docs/images/workflow-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-complete.png -------------------------------------------------------------------------------- /docs/images/workflow-diagram-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-diagram-layout.png -------------------------------------------------------------------------------- /docs/images/workflow-diagram-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-diagram-tile.png -------------------------------------------------------------------------------- /docs/images/workflow-video-outputs-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-video-outputs-1.png -------------------------------------------------------------------------------- /docs/images/workflow-video-outputs-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-video-outputs-2.png -------------------------------------------------------------------------------- /docs/images/workflow-video-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/images/workflow-video-source.png -------------------------------------------------------------------------------- /docs/logical-view.drawio: -------------------------------------------------------------------------------- 1 | 7Zxdc+I2FIZ/DTedoeNv4DIBsts222aazm53b3YEFqCpsagRCeTXr4zkLwljYjC28N4k+FgSss/j18evlXTM4XL7IQCrxSfsQq9jaO62Y446hqGbjk5/hZEdi/QdjQXmAXJ5oyTwjN4gD0bNNsiF60xDgrFH0CobnGLfh1OSiYEgwK/ZZjPsZb91BeZQCjxPgSdHvyCXLHhU17Rkx0eI5gv+1X2b71iCqDEPrBfAxa+pkDnumMMAY8I+LbdD6IUnLzovrN9Dzt54YgH0ySkdxqb73fnj7feHifVtonV747X2oTtgo7wAb8MPuGM4Hh3vfhVOmez4eXD+34TzvF+CYI78jnlH92qrLf1Jg/ujDeNdgldsn5XaR+CWdIGH5rzflE4YBsmY9NOc/95/8ySQIlHg7uk3OsDYd1cY0aPmu+kxT8QuNLZKYkbmYIwXGBBEc3zHJjXaT/ueT3HkwVnYC9NWM2+fsRmiZ9K8n2GfcEJ1g28/gCXyQrY/Qu8FhqPSHQuy9MJG8XeHXwi3uYnTYxzodQTxEpJgR5vwDhYHaJfdfE3hGMUWKRKjCw3wK2AeD5xAQj9wTt7BTHRpqgLNP4jOx9A+I/jaEmKMftOQ0dVC5gmtoIf8EJs7j2aiJdzYotT0agfHUAucT9BF4BG9hOT8Ddd4E0xhS+Axe1l4nPpVx1QQniEvZFvHj95vHD+WWvyMEJgHYNkSXqzGFTm2WriwurgVrIgFcaw19cHiqAXLvgwGk9YS04BSuKcWMZ/RegPCo/qLJsIDu5aAIzxCmfUrTV8tbpKyV+O1MMJ+S+BxRHqM2ulRzCF+BDu8aYtf0zMaVgJH81GFlpTW/IndttY29auMoZg/PPTwxv0CyHTRCR1iELTmoTtLTkxEfeTIVfF4S6DvrqUzTYN34RthujXx8PQ/ekZoKDqJDtt82J/ekZY9c/TsBLt/w/ivdrT5Nb1vtOWd2NYu2toikupGt76m9iSdwo2oj4/H7hw+83kneYOu9KZayBo9WqZmxY+dhF4nkBQqucxBOtFRUgPoARJ63umpHMo0H+6JvbyNmRKMY8sUYGFHxXslvEgDOWbBQOyopYH24MXHeAaLcqX9k8Vie76QRQbtVVh0nCxC8Ruxd7Mo3mPFgapmUa7bf7J4BDGjgSwKumjbZVkcCCyKA1XMonnoPUo+hD72oYAZx0VPwxKjcxiXBM00mClOc9C8IFTxgrWyN9tL31hLi5l4Yz1RzGhGQ98vbrZftbXOn7BUXg60o/OSzDY7055+YDO4LM3ya55fZJwpP49gAsNSPYBr9La3ziX9FMp88VlgggnByxhKvsaRD9WJVxYWVPRHrsncOr+rX0TCdIETPJutYTUaI79POTsrAXt+aVBaLpOV7hXTcsh8boH0Ry9r6pJ+Q1BsUysp/YZYO4hP8hVJv6U3UfrlojoCQiXp79+W9EckqC79x9OinPRbhzzdNkj/qRabnmO1XrrqN+xy0i9V/eJAFT82WsYtqK113FBXT23lRbVnZ6UGtS1Ii3pqe7bHkjLXcEAWeI594KUstxzTTj9Rj7MGoXZFPdZO1WO7Ej3u2YKMlrXx4ho+WsR4ZRvPko0PFfX4qsZHZGRdTwduwwcpyJJ68nxo8WaZYlg18bVPFN+KfJCecyHxHYhLra4tvrKRpqDrzC6D2xVf2R9SUnyPZ0k58bVlg+ia4lufE2GdKL56jvV0rvgKmmmUXdjT7xcMdCET2hZK9auYyrbskymo7HbO/ftGlN2WvSgVlb0gS+op+6G/8C6j7Kp5zCcr+6AaZRc85vjvKd9dVpsFA1VcVtuybaagp8Eug9vxmO0KnKY61PZ4WpRTW0e+CZZT21J1tN58ta3IxLBFtS27mMPKWTRxE3W0I9cCCtbR7Bq72Trake+3KtbRBVlqzv2Wbib/YZI1T/5Ppzn+AQ== -------------------------------------------------------------------------------- /docs/logical-view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/logical-view.jpg -------------------------------------------------------------------------------- /docs/physical-view.drawio: -------------------------------------------------------------------------------- 1 | 7Z1td6MoFIB/TT62R0VN8jEvzUzP6ex2m52d2U85RIlxq5JV0zTz6xcUjIp5bQxJ17bnVEAR4bn3wgWxBQb++5cQLubfsI28lqbY7y0wbGmaDtoG+Udj1mmMCjQljXFC12Zxm4ix+wuxSH7a0rVRVDgxxtiL3UUx0sJBgKy4EAfDEK+Kp82wV7zrAjpIiBhb0BNjf7h2PGexqqJsEr4i15mzW3cMluBDfjKLiObQxqtcFHhogUGIcZwe+e8D5NHa4/WSXjfakpoVLERBfMgFirIYfX2zoPvr5ffffg5ewn97b3cgzeUNekv2wL3nRxLxBcZoBdfkaIzCN9dC7BniNa8YvIw9N0CDrN6VFujPcBAPsIfD5BxAfke0MH0nhLaLCmld3RiOtFza0A1JRi4OSHqAQ1p7/ZnreblrjB5Q+gaJj+IQv6Jcyiz5ISk2jObIZsV5Q2HskrZ8glPkPePIZdlPcRxjP3dCz3MdmhDjBYmFLGSRUiFyg/489j0SVtkTMkpVjYdZrdBbwmiRVsfMfafl6JNmX9BE/92hInIPV5F+H6IIL0MLPVq0PH0STI+KZ8GFO3FYQ4C+2N4MAfoQ6D0Xxdr/C8I+ikPaiCz1TlV0RiOTR9Bh4dUG7nYnjZrnsOZxkImTk+W9IY4cMOiOANAQABx4eGn/gLE1p/RZpDWXHmmFc/M3ArreOZK//kAFhvm/4c+iLbFKWuI8+JndIn1dXTJ9ZjV9o5DUaqP7pLM3S9qhJtVnaJLhawvw/UBTEtFbLDzSMEl7aUp/ab2i+LwIBjhAImFau62qonZjJ18dXAvs0lwe3khmEcukErhpUoOTlRvPJ3j6D8klOhNTnUOQMi6IlK4KTAnkOCFeLg5/+qxrDac8B2V3rWidYq1UKPnslHytZLV39moBp1fL7mo+vbLkVYbWFSpjuA6gj4dEJpQ/ackjoXYSYUtKYvTJHynbQGkZJGVAQ/eaUYooh9vFCFUM0TyKEeVwuxihlrNXS/dXywXMRQihQvZK6f5KroDkD/Qrte8WLZvTcau5G6PxAlq0VldEQx2g9ypVWgorvWWV7UxSJ+RwkljRqj5DZhryNoAVeLfu9tCMmuSIPIUbOE9JaAh2WZ+C5ThQwsDhyliiJIlq5QmuCRtcji5htcmP3u9+PqvNVGgNwPBUs2imtI4mtz+oic6YMYpjImVRA9QNAKWXuz2SxxeaLvDEaqmh6eZokq+dKjx1pDZR0Fi7W8CpbO1UUzZPW3xv3PPLVVXD1NUypV8bUqJHbTCHBBSvMXm3wFNZR8meHdA6Ak89D4Z+Q9Mt0CR0x+XCZGgCTAI+tbtm29pprtm2WZtrVvRGPoeuD5NiP0F/akNyMFoGyfRY45hsHJNyHZOZGH/cMVmfUKnbphYfAxLl2mxu0fRohU1DcuTQoxc2M3sJszZUjAEp5qcza16isiYzprBqAWvrkE6yiVPF/lKKXeYoaIi7aeKEAZ9s4CqmMtEMLr108Q73nTfU3TR1VzcsVMRhYbJo9isM7LMvVWywuowyk00VEMeH3xekp0a0lPIbtivWZDRYXSNWZW0lfZkhEKeVM7BGIWkq6sxyGrxuA69rW8QKxEnmjC6GTqXfpqHrGukSu1qy8RJnncfjbyTiZVnhwBhg3yd9sAa2m4Dt2tbLAHFG+jnEForofE9D3SehTlz2Jxs70Vn7GFjYdwOH2tDcmogyeMlkZIPdTWAnLOeSTZ3oq836bamuawadN0uXKduZYYhjToGkuie7D3AbVk7LqbVNy21ZTkKiUtFrPIdSpA1sIe5mHNKq2HHNFnqT9vVIjeKGrdtg68q80ob4itMLckgdQHppsz6nWZ9zbetzDpc4eR0BQ/RqPbkRnbRO/AjKEEVW6E5pX/wxoNoJs5vmJQvZDuLtTbeMwA6VyodNbB4gFNg9uhsS1bAetl5plDdNwrzVktaGYczPY41Krhy5Hs9H4MHQjb6pkxSCTmBnrUXKHK5/5gN/0wCVFRYevudTh2seenfjn7lTSfBv/gzkeHMVDay3ssHUFpvn3zEMYisIyIM7aOcr36wDS2v9ED2u3IOuCopjPlGXZ9tIhciDsfuGCqWv4o7d8Jmq1c3d7szSwk6jXWI3rQl22QZfMSdjX05pVQk5EWyS/aL4aUzxby+yoRZv1GVGbCNYaZaly3n+eDaLUNwqi2LWEh+QTrE7NY7ZjMYcBg492PJ2yo3IZE4MM+HaIZOni5fJt2zbJ168n7FXvGoQHs0ogaicKjsauIzsgHbxPh3T2C46ZxOLirdr8i9s8bFsasISAclGtpeXFH5eEvkxYdGOEhZuwNSc9coZs2oDVhTo0+VNO9ScHSxvHG3lXm93ivbsrk6DBvaJUu1CeVGTI7qG/lgiWkGjsQUD8u852VXiMUaiw1+WKJ1ohU6TjxO7kxfoGXKZk2G6rkBMju73gZL3zFSV3SUrX6CDwgX1WDt+z5xE/r5AIazypPGR9tL3eomr7WT3Vk5Qt5J7zJ53oPy+F1c0ORZBxdC3tpGvKfqTan/ru9nsU8Zmn2pJn3T4C//S9vrUBPQubko/Nn6rtJy7Defp9o87PPbaP1Pi0O3OLI2EzPJmX4f7PfbltMX+nc3gHDBlet14njpiOp1R40BEgcwumnxEj+6imSWPSKezp4u254KaumiiF/3iEnPesdHpkgBuURIM41ySIOR0Lklol/eyLX9k4cgLapIE0WN9W7bjdPDNA8FvfwruO+XJmfJAre5OiugDFkC7JoW6mU3MTybu6zTTwDMKXVJbdET2UUKVQ3Xzp0D0VNV8NkRFV2qD6LkQNSUiqmplx+HJ83SqAspWunuv5H60iyLLh9r5CefkUegCPjxr7VoF0qyvurn1VWGygK5KRah6+6Hfa9W5uCq7xZ7FVcc7m8uffahahF65zkovW4jzfQ6j4vNmP8Yk4huyXbpukX3gpwaHc7vzoOjHOZy3rJ79rA5nn7bBJOItcCYIy29CyH7PRhffj+758FfV9lv5T0+dGcfmg1MSPjilXRuKor+Mo6iMwdmZM5UeAO3jmNvyCarPylwEalJ7HdnvUuuiRyq1vA8e8smDpq8P0NFAY3kvjh3K2qAeTSedPlPcFy6lb7yOYuRXbAn3DQbQab4yKkUJpm0y8VkT1MNk94Ib5fw1Gbzqs6nzYH3XYPj1+2wyGtyJC1+qFGLw2ihEiQpx4tEWqMcm10ggCW6+I556pTafYwcP/wE= -------------------------------------------------------------------------------- /docs/physical-view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/physical-view.jpg -------------------------------------------------------------------------------- /docs/use-cases.drawio: -------------------------------------------------------------------------------- 1 | 7Vxbl5s2EP41fmwPiIvxo9d2kj1t0j11000fFdAanQDiCPnWX1/JCAzIm7UJsbDLyxoNw4A+fYxmRmJH1izevacwDT+SAEUjYAS7kTUfAWDawOU/QrLPJZ5r5IIVxYFUOgqW+F8khYXaGgcoqykyQiKG07rQJ0mCfFaTQUrJtq72QqL6XVO4Qopg6cNIlT7jgIVSahrG8cQHhFehvLXnyBMxLJSlIAthQLYVkbUYWTNKCMuP4t0MRQK8Apf8unevnC0fjKKEnXPBwn3nv8eTcL41P9Kds/wNJd9+kVY2MFrLDk+DGCc4YxQyQuWTs30BB+9EKg7XcTT1hYL1sEGUYQ7Y7/Arip5IhhkmCVf5ShgjcUVhGuGVOMFIyqUhiyPeMPmhfAauhnavds4sIeNcQyRGjO65irygYJVkmQmcvL09jpklVcLKaBWXQcmSVWn4CCQ/kFhegCtQcJ1FZB08Q+aHXL70QxSsI3Q7ANuW0y+E7e8jvNjwjmU3A68L6gS2CjeiC15XgfePFN20T3AszZCOFUjn+wTGZP5wkx6hSVlXN76egu+DmP8FpDcIr2f2bEqbqA6XIsgQl/3FIzIBMoP+NwVgbpoHa7zxsA0xQ8sU+uLMlseLnYMGmm5UpaRpnwBt/LNAKwaxgtrnNOg5aibQDpsal85RhPoNW5mz6INNDTtVhJJgKvIi3vIjmGXYrwODdph94cfGr45s/VNrzUXvjaKxrzSeEMW8H9zHSlnC+/Sl2qhYEs2jqUOrsJU/MgqUxKwxMLxbZE199D1Acj0G6Qqxt3ybOtCVgXROjGMhoyiCDG/qj3tqcOUdngjmHamE1nUe2U0XnndTXlXN8JqGvIYhs2Eox0ExdOBa2e0foJ810K8V/UxjNPCvA/6pOeHAv7P498pEN/DvMv6pSXMZ7H0igfh5TERdgsi5Xlfk4nj1Ys5YTS2uHLiouXGJ3Cyv7IoUrK/4ma52ANXk93LX19JhdecyO3R94FzX5+p0fcCpE8ku3sRLXZ9lNAyBhqGf7frU6sDAv7P4Nx741wH/ilT89NTL8Av2oZxCluwg1jh9TJTSnu7CAVDrLf/v19c78/UFWjM3x+wocnZcvZFzQbdqcVku6Iv1fv5n+vSo9Z0Fk7qDs7SXloFabfkbZ2sYiT0UwPgT5YOvroHqhM3WXiMFJxaOS6pNI0jjrOCcyNl6Bt9EO3yOAt8SQXpYc38Ri8M9JZ4z1o6cWh14TLI0J14/QXNP7EK4MmhqYeAT3OBVHtnlM8PzcnSoEmQk0hvXNeEb658juigL3FNFVL6Db8d14PRIXymua6ws2nbbuG5cN2RNrhzXDWWBlvyzdPKv5Fsz2L01/hXPPfi/S/lnD/6vC/6pee3Av7P45/SKf63rKg3+XbuuYg07MlryT+uy0P3wb9iR0ZJ/WpeF7od/arVqsUsxlfsKGEry4otPaKC39OI1INdfr7LUelWJ3eHrmp4i53rakVOLVpd7vXvKesdnej1riPo68XrDZqCW/Buivk74p1b9jrv5lI80R/mWjHW/ZhGgfaWtuNmbKB4WLfuJom1oR3HY2XLyu+m3faHWb2LuxhfaYOBfK/7ZendW9Z9/vHn85yG5+vFfsFiL/wA= -------------------------------------------------------------------------------- /docs/use-cases.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/docs/use-cases.jpg -------------------------------------------------------------------------------- /solution-manifest.yaml: -------------------------------------------------------------------------------- 1 | id: SO0048 2 | name: media-services-application-mapper 3 | version: v1.13.2 4 | cloudformation_templates: 5 | - template: aws-media-services-application-mapper-release.template 6 | main_template: true 7 | - template: msam-browser-app-release.template 8 | - template: msam-core-release.template 9 | - template: msam-dynamodb-release.template 10 | - template: msam-events-release.template 11 | - template: msam-iam-roles-release.template 12 | build_environment: 13 | build_image: "aws/codebuild/standard:7.0" 14 | -------------------------------------------------------------------------------- /source/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | relative_files = True 3 | source = ./ 4 | omit = 5 | msam/chalicelib/test_*.py 6 | msam/chalicelib/run_unit_tests.py 7 | -------------------------------------------------------------------------------- /source/cdk/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project 2 | 3 | You should explore the contents of this project. It demonstrates a CDK app with an instance of a stack (`CdkStack`) 4 | which contains an Amazon SQS queue that is subscribed to an Amazon SNS topic. 5 | 6 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 7 | 8 | ## Useful commands 9 | 10 | * `npm run build` compile typescript to js 11 | * `npm run watch` watch for changes and compile 12 | * `npm run test` perform the jest unit tests 13 | * `cdk deploy` deploy this stack to your default AWS account/region 14 | * `cdk diff` compare deployed stack with current state 15 | * `cdk synth` emits the synthesized CloudFormation template 16 | -------------------------------------------------------------------------------- /source/cdk/bin/media-services-application-mapper.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 */ 4 | 5 | import * as cdk from 'aws-cdk-lib'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | import { AwsSolutionsChecks } from 'cdk-nag'; 8 | 9 | const cdkApplication = new cdk.App(); 10 | 11 | (function createMediaServicesApplicationMapperStack(app) { 12 | return new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 13 | })(cdkApplication); 14 | 15 | //cdk nag 16 | cdk.Aspects.of(cdkApplication).add(new AwsSolutionsChecks({ verbose: true })); 17 | -------------------------------------------------------------------------------- /source/cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/media-services-application-mapper.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 21 | "@aws-cdk/core:checkSecretUsage": true, 22 | "@aws-cdk/core:target-partitions": [ 23 | "aws", 24 | "aws-cn" 25 | ], 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 33 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 34 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 35 | "@aws-cdk/core:enablePartitionLiterals": true, 36 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 37 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 38 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 39 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 40 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 41 | "@aws-cdk/aws-route53-patters:useCertificate": true, 42 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 43 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 44 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /source/cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk", 3 | "version": "1.13.2", 4 | "description": "Synthesize templates for Media Services Application Mapper using AWS Cloud Development Kit (CDK).", 5 | "license": "Apache-2.0", 6 | "author": { 7 | "name": "Amazon Web Services", 8 | "url": "https://aws.amazon.com/solutions" 9 | }, 10 | "bin": { 11 | "cdk": "bin/media-services-application-mapper.js" 12 | }, 13 | "scripts": { 14 | "cleanup": "tsc --build ./ --clean && cdk context --clear && rm -rf dist cdk.out && rm -f package-lock.json", 15 | "build": "tsc", 16 | "watch": "tsc -w", 17 | "pretest": "sh pretest.sh", 18 | "test": "jest --coverage", 19 | "cdk": "cdk", 20 | "presynth": "sh pretest.sh && npm run build", 21 | "synth": "cdk synth -q" 22 | }, 23 | "devDependencies": { 24 | "@types/jest": "^29.4.0", 25 | "@types/node": "^20.1.1", 26 | "aws-cdk": "^2.87.0", 27 | "cdk-nag": "^2.27.73", 28 | "jest": "^29.4.2", 29 | "ts-jest": "^29.0.5", 30 | "ts-node": "^10.9.1", 31 | "typescript": "^5.0.4" 32 | }, 33 | "dependencies": { 34 | "aws-cdk-lib": "^2.87.0", 35 | "constructs": "^10.2.69" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /source/cdk/pretest.sh: -------------------------------------------------------------------------------- 1 | cdk_dir=$PWD # cdk directory 2 | msam_dir="$cdk_dir/../msam" # msam source directory 3 | 4 | echo "installing npm packages..." 5 | npm install 6 | 7 | echo "building core template via chalice..." 8 | cd $msam_dir 9 | chalice package ../cdk/dist 10 | 11 | cd $cdk_dir 12 | 13 | echo "renaming and cleaning up chalice output..." 14 | cd dist 15 | mv sam.json msam-core-release.template 16 | rm deployment.zip 17 | cd .. 18 | 19 | echo 'pretest.sh finished' 20 | -------------------------------------------------------------------------------- /source/cdk/test/media-services-application-mapper.test.ts: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as cdk from 'aws-cdk-lib'; 5 | import { Template } from 'aws-cdk-lib/assertions'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | 8 | test('media-services-application-mapper snapshot test', () => { 9 | const app = new cdk.App(); 10 | const stack = new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 11 | const template = Template.fromStack(stack); 12 | 13 | expect(template).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /source/cdk/test/msam-browser-app.test.ts: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as cdk from 'aws-cdk-lib'; 5 | import { Template } from 'aws-cdk-lib/assertions'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | 8 | test('msam-browser-app snapshot test', () => { 9 | const app = new cdk.App(); 10 | const stack = new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 11 | const template = Template.fromStack(stack.node.findChild('BrowserAppModuleStack') as cdk.NestedStack); 12 | 13 | expect(template).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /source/cdk/test/msam-core.test.ts: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as cdk from 'aws-cdk-lib'; 5 | import { Template } from 'aws-cdk-lib/assertions'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | 8 | test('msam-core snapshot test', () => { 9 | const app = new cdk.App(); 10 | const stack = new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 11 | const template = Template.fromStack(stack.node.findChild('CoreModuleStack') as cdk.NestedStack); 12 | 13 | expect(template).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /source/cdk/test/msam-dynamodb.test.ts: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as cdk from 'aws-cdk-lib'; 5 | import { Template } from 'aws-cdk-lib/assertions'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | 8 | test('msam-dynamodb snapshot test', () => { 9 | const app = new cdk.App(); 10 | const stack = new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 11 | const template = Template.fromStack(stack.node.findChild('DynamoDBModuleStack') as cdk.NestedStack); 12 | 13 | expect(template).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /source/cdk/test/msam-events.test.ts: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as cdk from 'aws-cdk-lib'; 5 | import { Template } from 'aws-cdk-lib/assertions'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | 8 | test('msam-events snapshot test', () => { 9 | const app = new cdk.App(); 10 | const stack = new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 11 | const template = Template.fromStack(stack.node.findChild('EventsModuleStack') as cdk.NestedStack); 12 | 13 | expect(template).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /source/cdk/test/msam-iam-roles.test.ts: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as cdk from 'aws-cdk-lib'; 5 | import { Template } from 'aws-cdk-lib/assertions'; 6 | import { MediaServicesApplicationMapper } from '../lib/media-services-application-mapper'; 7 | 8 | test('msam-iam-roles snapshot test', () => { 9 | const app = new cdk.App(); 10 | const stack = new MediaServicesApplicationMapper(app, 'MediaServicesApplicationMapper'); 11 | const template = Template.fromStack(stack.node.findChild('IAMModuleStack') as cdk.NestedStack); 12 | 13 | expect(template).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /source/cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2020" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ], 30 | "typeAcquisition": { 31 | "enable": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/events/.gitignore: -------------------------------------------------------------------------------- 1 | .aws-sam/ 2 | package/ 3 | events.zip -------------------------------------------------------------------------------- /source/events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/source/events/__init__.py -------------------------------------------------------------------------------- /source/events/cloudwatch_alarm.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """ 4 | This Lambda is responsible for receiving and storing CloudWatch alarm events. 5 | This Lambda must be installed into each region where alarms are subscribed to by MSAM nodes. 6 | """ 7 | 8 | import os 9 | import time 10 | 11 | import boto3 12 | from boto3.dynamodb.conditions import Key, Attr 13 | from botocore.exceptions import ClientError 14 | from botocore.config import Config 15 | 16 | # user-agent config 17 | SOLUTION_ID = os.environ['SOLUTION_ID'] 18 | USER_AGENT_EXTRA = {"user_agent_extra": SOLUTION_ID} 19 | MSAM_BOTO3_CONFIG = Config(**USER_AGENT_EXTRA) 20 | 21 | ALARMS_TABLE_NAME = os.environ["ALARMS_TABLE_NAME"] 22 | TABLE_REGION = os.environ["EVENTS_TABLE_REGION"] 23 | DYNAMO_RESOURCE = boto3.resource('dynamodb', region_name=TABLE_REGION, config=MSAM_BOTO3_CONFIG) 24 | ALARMS_TABLE = DYNAMO_RESOURCE.Table(ALARMS_TABLE_NAME) 25 | 26 | def lambda_handler(event, _): 27 | """ 28 | AWS Lambda entry point for receiving alarm state change events through CloudWatch event rule. 29 | """ 30 | print(event) 31 | try: 32 | updated_timestamp = int(time.time()) 33 | # process the data we got from the alarm state change event 34 | region = event['region'] 35 | alarm_name = event['detail']['alarmName'] 36 | cloudwatch_resource = boto3.resource('cloudwatch', region_name=region) 37 | alarm = cloudwatch_resource.Alarm(alarm_name) 38 | 39 | region_alarm_name = f"{region}:{alarm_name}" 40 | state = alarm.state_value 41 | state_updated = int(alarm.state_updated_timestamp.timestamp()) 42 | 43 | subscribers = subscribers_to_alarm(region_alarm_name) 44 | for resource_arn in subscribers: 45 | # only update alarm if it's already in alarm DB through node subscription 46 | ALARMS_TABLE.update_item( 47 | UpdateExpression='SET StateValue = :state, Updated = :updated, StateUpdated = :stateupdated', 48 | ConditionExpression=Attr('RegionAlarmName').eq(region_alarm_name), 49 | Key={'RegionAlarmName': region_alarm_name, 'ResourceArn': resource_arn}, 50 | ExpressionAttributeValues={':state': state, ':updated': updated_timestamp, ':stateupdated': state_updated} 51 | ) 52 | print(f"{resource_arn} updated via CloudWatch alarm change state event") 53 | except ClientError as error: 54 | if error.response['Error']['Code']=='ConditionalCheckFailedException': 55 | print(f"No update made. Alarm key {region_alarm_name} does not exist in database.") 56 | print(error) 57 | return True 58 | 59 | 60 | def subscribers_to_alarm(region_alarm_name): 61 | """ 62 | Returns subscribed nodes of a CloudWatch alarm in a region. 63 | """ 64 | subscribers = set() 65 | try: 66 | ddb_index_name = 'RegionAlarmNameIndex' 67 | response = ALARMS_TABLE.query( 68 | IndexName=ddb_index_name, 69 | KeyConditionExpression=Key('RegionAlarmName').eq( 70 | region_alarm_name)) 71 | for item in response["Items"]: 72 | subscribers.add(item["ResourceArn"]) 73 | while "LastEvaluatedKey" in response: 74 | response = ALARMS_TABLE.query( 75 | IndexName=ddb_index_name, 76 | KeyConditionExpression=Key('RegionAlarmName').eq( 77 | region_alarm_name), 78 | ExclusiveStartKey=response['LastEvaluatedKey']) 79 | for item in response["Items"]: 80 | subscribers.add(item["ResourceArn"]) 81 | except ClientError as error: 82 | print(error) 83 | return sorted(subscribers) 84 | -------------------------------------------------------------------------------- /source/events/requirements.txt: -------------------------------------------------------------------------------- 1 | networkx 2 | jsonpath_ng 3 | -------------------------------------------------------------------------------- /source/events/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/source/events/test/__init__.py -------------------------------------------------------------------------------- /source/events/test/run_unit_tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | Launch from the source/msam/ folder: 3 | python -m test.run_unit_tests 4 | 5 | """ 6 | import unittest 7 | 8 | # pylint: disable=W0611,E0611,W0614 9 | from test.test_cloudwatch_alarm import * 10 | from test.test_media_events import * 11 | 12 | if __name__ == '__main__': 13 | unittest.main(verbosity=3) 14 | -------------------------------------------------------------------------------- /source/events/test/test_cloudwatch_alarm.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is provides unit tests for the cloudwatch_alarm.py module. 3 | """ 4 | 5 | # pylint: disable=C0415,W0201 6 | 7 | import os 8 | import unittest 9 | from unittest.mock import patch, MagicMock 10 | from botocore.exceptions import ClientError 11 | 12 | ARN = "arn:msam:user-defined-node:global:111122223333:10AA8D40-2B6F-44FA-AA67-6B909F8B1DB9" 13 | CLIENT_ERROR = ClientError({"Error": {"Code": "400", "Message": "SomeClientError"}}, "ClientError") 14 | 15 | os.environ["SOLUTION_ID"] = "SO0166" 16 | os.environ["ALARMS_TABLE_NAME"] = "alarms_table" 17 | os.environ["EVENTS_TABLE_REGION"] = "us-east-1" 18 | 19 | @patch('boto3.client') 20 | @patch('boto3.resource') 21 | class TestCloudWatchAlarm(unittest.TestCase): 22 | """ 23 | This class extends TestCase with testing functions 24 | """ 25 | def test_subscribers_to_alarm(self, patched_resource, 26 | patched_client): 27 | """ 28 | Test the subscribers_to_alarm function 29 | """ 30 | import cloudwatch_alarm 31 | with patch.object(cloudwatch_alarm.ALARMS_TABLE, 'query', side_effect=[{"Items":[{"ResourceArn": ARN}], "LastEvaluatedKey": "token"}, 32 | {"Items":[{"ResourceArn": ARN}]}]): 33 | cloudwatch_alarm.subscribers_to_alarm("RegionAlarmName") 34 | self.assertEqual(cloudwatch_alarm.ALARMS_TABLE.query.call_count, 2) 35 | 36 | with patch.object(cloudwatch_alarm.ALARMS_TABLE, 'query', side_effect=CLIENT_ERROR): 37 | cloudwatch_alarm.subscribers_to_alarm("RegionAlarmName") 38 | self.assertRaises(ClientError) 39 | 40 | 41 | def test_lambda_handler(self, patched_resource, 42 | patched_client): 43 | """ 44 | Test the lambda_handler function 45 | """ 46 | import cloudwatch_alarm 47 | mocked_event = {"region": "us-east-1", "detail": {"alarmName": "alarmName"}} 48 | patched_resource.return_value.Alarm.return_value.alarm_value = "set" 49 | with patch.object(cloudwatch_alarm, 'subscribers_to_alarm', return_value=[ARN]): 50 | with patch.object(cloudwatch_alarm.ALARMS_TABLE, 'update_item', return_value={}): 51 | cloudwatch_alarm.lambda_handler(mocked_event, MagicMock()) 52 | cloudwatch_alarm.boto3.resource.return_value.Alarm.assert_called_once_with('alarmName') 53 | cloudwatch_alarm.ALARMS_TABLE.update_item.assert_called_once() 54 | self.assertTrue(cloudwatch_alarm.ALARMS_TABLE.update_item.call_args.kwargs['UpdateExpression'] == 'SET StateValue = :state, Updated = :updated, StateUpdated = :stateupdated') 55 | self.assertTrue(cloudwatch_alarm.ALARMS_TABLE.update_item.call_args.kwargs['Key'] == {'RegionAlarmName': 'us-east-1:alarmName', 'ResourceArn': ARN}) 56 | patched_resource.return_value.Alarm.side_effect = CLIENT_ERROR 57 | cloudwatch_alarm.lambda_handler(mocked_event, MagicMock()) 58 | self.assertRaises(ClientError) 59 | -------------------------------------------------------------------------------- /source/events/test/test_media_events.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is provides unit tests for the media_events.py module. 3 | """ 4 | 5 | # pylint: disable=C0415,W0201 6 | 7 | import os 8 | import unittest 9 | from unittest.mock import patch, MagicMock 10 | from botocore.exceptions import ClientError 11 | 12 | ARN = "arn:msam:user-defined-node:global:111122223333:10AA8D40-2B6F-44FA-AA67-6B909F8B1DB9" 13 | CLIENT_ERROR = ClientError({"Error": {"Code": "400", "Message": "SomeClientError"}}, "ClientError") 14 | 15 | os.environ["SOLUTION_ID"] = "SO0166" 16 | os.environ["EVENTS_TABLE_REGION"] = "us-east-1" 17 | os.environ["EVENTS_TABLE_NAME"] = "events_table" 18 | os.environ["CLOUDWATCH_EVENTS_TABLE_NAME"] = "cw_table" 19 | os.environ["CONTENT_TABLE_NAME"] = "content_table" 20 | os.environ["ITEM_TTL"] = "600" 21 | 22 | @patch('boto3.client') 23 | @patch('boto3.resource') 24 | class TestMediaEvents(unittest.TestCase): 25 | """ 26 | This class extends TestCase with testing functions 27 | """ 28 | def test_lambda_handler(self, patched_resource, 29 | patched_client): 30 | """ 31 | Test the lambda_handler function 32 | """ 33 | import media_events 34 | mocked_events = [{"time": "2022-07-19T17:04:40Z", "resources": [], 35 | "detail": {"alarm_id": "id", "alarm_state": "ALARM", "eventName": "MediaLive Alarm", 36 | "requestParameters": {"channelId": "9276485"}}, 37 | "region": "us-west-2", "account": "1234567890", 38 | "source": "aws.medialive", "detail-type": "MediaLive Alert BatchUpdateSchedule", 39 | "channel_arn": "arn:aws:medialive:us-west-2:1234567890:channel:9276485"}, 40 | {"time": "2022-07-19T17:04:40Z", "resources": [], "detail": {"error-id": "id", "errored": "ALARM", 41 | "error-code": "code", "error-message": "message"}, 42 | "source": "aws.mediaconnect", "detail-type": "MediaConnect Alert", 43 | "channel_arn": "arn:aws:medialive:us-west-2:1234567890:channel:9276485"}, 44 | {"time": "2022-07-19T17:04:40Z", "resources": [], "detail": {"error-id": "id", "errored": "ALARM", 45 | "error-code": "code", "error-message": "message"}, 46 | "source": "aws.mediapackage", "detail-type": "MediaPackage Alert HarvestJob", 47 | "channel_arn": "arn:aws:medialive:us-west-2:1234567890:channel:9276485"}, 48 | {"time": "2022-07-19T17:04:40Z", "resources": [], "detail": {"error-id": "id", "errored": "ALARM", 49 | "error-code": "code", "error-message": "message"}, 50 | "source": "aws.mediastore", "detail-type": "MediaStore Object State Change", 51 | "resource_arn": "arn:aws:mediastore:us-west-2:1234567890:container/mytestcontainer"}] 52 | mocked_event = {"time": "2022-07-19T17:04:40Z", "resources": [], "detail": {}, 53 | "source": "aws.cloudwatch", "detail-type": "CloudWatch Alarm State Change"} 54 | patched_client.return_value.describe_origin_endpoint.return_value = {"Arn": ARN} 55 | with patch.object(media_events.EVENTS_TABLE, 'put_item', return_value={}): 56 | with patch.object(media_events.CLOUDWATCH_EVENTS_TABLE, 'put_item', return_value={}): 57 | for event in mocked_events: 58 | result = media_events.lambda_handler(event, MagicMock()) 59 | self.assertTrue(result) 60 | self.assertTrue(media_events.EVENTS_TABLE.put_item.call_count == 7) 61 | self.assertTrue(media_events.CLOUDWATCH_EVENTS_TABLE.put_item.call_count == 7) 62 | 63 | patched_client.return_value.describe_origin_endpoint.side_effect = CLIENT_ERROR 64 | media_events.lambda_handler(mocked_event, MagicMock()) 65 | self.assertRaises(ClientError) 66 | -------------------------------------------------------------------------------- /source/html/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jquery": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": "latest", 10 | "sourceType": "module" 11 | }, 12 | "rules": {}, 13 | "globals": { 14 | "_": true, 15 | "afterEach": true, 16 | "Cookies": true, 17 | "expect": true, 18 | "Fuse": true, 19 | "filterXSS": true, 20 | "machina": true, 21 | "moment": true, 22 | "objectHash": true, 23 | "renderjson": true, 24 | "showdown": true, 25 | "SVG": true, 26 | "Tabulator": true, 27 | "test": true, 28 | "URI": true, 29 | "vis": true 30 | } 31 | } -------------------------------------------------------------------------------- /source/html/.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | *lcov* 3 | -------------------------------------------------------------------------------- /source/html/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 11, 3 | "browser": true, 4 | "loopfunc": true, 5 | "globals": { 6 | "console": true, 7 | "define": true, 8 | "require": true 9 | } 10 | } -------------------------------------------------------------------------------- /source/html/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "edge": "17", 8 | "firefox": "60", 9 | "chrome": "67", 10 | "safari": "11.1" 11 | }, 12 | "useBuiltIns": "usage", 13 | "corejs": "3.6.5" 14 | } 15 | ] 16 | ] 17 | } -------------------------------------------------------------------------------- /source/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/source/html/favicon.ico -------------------------------------------------------------------------------- /source/html/jest.config.js: -------------------------------------------------------------------------------- 1 | import * as lodash from 'lodash-es'; 2 | 3 | export default { 4 | transform: { 5 | "\\.[jt]sx?$": "babel-jest" 6 | }, 7 | testEnvironment: "jsdom", 8 | globals: { 9 | _: lodash, 10 | Cookies: {} 11 | }, 12 | moduleNameMapper: { 13 | underscore$: 'lodash-es' 14 | }, 15 | collectCoverage: true, 16 | coverageDirectory: ".", 17 | coverageReporters: ["lcov", "text"] 18 | } 19 | 20 | -------------------------------------------------------------------------------- /source/html/js/app/api_check.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "./server.js"; 5 | 6 | export function ping(url, api_key) { 7 | const current_endpoint = `${url}/ping`; 8 | return server.get(current_endpoint, api_key); 9 | } 10 | -------------------------------------------------------------------------------- /source/html/js/app/build-tmp.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | const version = "VERSION"; 5 | 6 | export function get_version() { 7 | return version; 8 | } 9 | -------------------------------------------------------------------------------- /source/html/js/app/build.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | const version = "VERSION"; 5 | 6 | export function get_version() { 7 | return version; 8 | } 9 | -------------------------------------------------------------------------------- /source/html/js/app/cloudwatch_events.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "./server.js"; 5 | import * as connections from "./connections.js"; 6 | 7 | export function get_cloudwatch_events(arn) { 8 | const current_connection = connections.get_current(); 9 | const url = current_connection[0]; 10 | const api_key = current_connection[1]; 11 | const current_endpoint = `${url}/cloudwatch/events/all/${encodeURIComponent( 12 | arn 13 | )}`; 14 | return new Promise(function (resolve, reject) { 15 | server 16 | .get(current_endpoint, api_key) 17 | .then(function (response) { 18 | resolve(response); 19 | }) 20 | .catch(function (error) { 21 | console.error(error); 22 | reject(error); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /source/html/js/app/connections.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | // Cookie: MSAM_CURRENT = "Cookie Name" or missing (not saved) 5 | // Cookie: MSAM_ENDPOINT_ = "{ URL, Key, Last }" 6 | 7 | const cookie_name_current = "MSAM_CURRENT", 8 | cookie_name_prefix = "MSAM_ENDPOINT_", 9 | session_current = "MSAM_CURRENT", 10 | max_age = 7; 11 | 12 | // return the stored connection objects 13 | const get_remembered = _.memoize(function () { 14 | const history = [], 15 | cookies = Cookies.get(); 16 | for (const name of Object.keys(cookies)) { 17 | if (name.startsWith(cookie_name_prefix)) { 18 | const payload = cookies[name], 19 | content = JSON.parse(window.atob(payload)); 20 | history.push(content); 21 | } 22 | } 23 | return history; 24 | }); 25 | 26 | // return the session copy or the cookie copy, or null 27 | const get_current = _.memoize(function () { 28 | let current = null; 29 | const encoded = window.sessionStorage.getItem(session_current); 30 | if (encoded) { 31 | current = JSON.parse(window.atob(encoded)); 32 | } 33 | // get the cookie and refresh the expiration 34 | let payload = Cookies.get(cookie_name_current); 35 | if (payload) { 36 | // set the cookie again for expiration 37 | Cookies.set(cookie_name_current, payload, { 38 | expires: max_age, 39 | }); 40 | const name = payload; 41 | payload = Cookies.get(name); 42 | // something? 43 | if (payload) { 44 | // set the cookie again for expiration 45 | Cookies.set(name, payload, { 46 | expires: max_age, 47 | }); 48 | // set the session storage if not already 49 | if (!current) { 50 | window.sessionStorage.setItem(session_current, payload); 51 | current = JSON.parse(window.atob(payload)); 52 | } 53 | } 54 | } 55 | return current; 56 | }); 57 | 58 | // update the history with another connection 59 | const set_current = function (url, api_key, store = true) { 60 | clear_function_cache(); 61 | const current = [url, api_key]; 62 | window.sessionStorage.setItem( 63 | session_current, 64 | window.btoa(JSON.stringify(current)) 65 | ); 66 | const cookie_name = cookie_name_prefix + objectHash.sha1(url); 67 | const encoded = window.btoa(JSON.stringify(current)); 68 | if (store) { 69 | // add or update MSAM_ENDPOINT_ cookie 70 | Cookies.set(cookie_name, encoded, { 71 | expires: max_age, 72 | }); 73 | // rewrite MSAM_CURRENT cookie 74 | Cookies.set(cookie_name_current, cookie_name, { 75 | expires: max_age, 76 | }); 77 | } else { 78 | Cookies.remove(cookie_name_current); 79 | Cookies.remove(cookie_name); 80 | } 81 | }; 82 | 83 | const clear_function_cache = function () { 84 | get_current.cache.clear(); 85 | get_remembered.cache.clear(); 86 | }; 87 | 88 | // is there a connection override on the URL parameters? 89 | const current_url = new URL(window.location); 90 | let endpoint = current_url.searchParams.get("endpoint"); 91 | const key = current_url.searchParams.get("key"); 92 | 93 | if (endpoint && key) { 94 | // strip any trailing slashes 95 | if (endpoint.endsWith("/")) { 96 | endpoint = endpoint.substring(0, endpoint.length - 1); 97 | } 98 | console.log("Connection override with URL parameters"); 99 | set_current(endpoint, key, false); 100 | } 101 | 102 | export { get_remembered, set_current, get_current }; 103 | -------------------------------------------------------------------------------- /source/html/js/app/main.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | // this module bootstraps the state machine, which starts the application running 5 | // wait for the signal from JQuery before loading and initializing 6 | 7 | $(window.document).ready(async function () { 8 | // say hello 9 | console.log("main"); 10 | try { 11 | // minimal modules required to start running 12 | const statemachine = await import("./statemachine.js"); 13 | await import("./ui/status_view.js"); 14 | // start the outer FSM at the beginning 15 | statemachine.getToolStateMachine().start(); 16 | } catch (error) { 17 | console.error(error); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/cloudfront_medialive_input.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + "/cached/cloudfront-distribution-medialive-input", 16 | api_key 17 | ) 18 | .then((results) => { 19 | for (const connection of results) { 20 | const data = JSON.parse(connection.data); 21 | items.push({ 22 | id: connection.arn, 23 | to: connection.to, 24 | from: connection.from, 25 | label: data.scheme, 26 | data: data, 27 | arrows: "to", 28 | color: { 29 | color: "black", 30 | }, 31 | }); 32 | } 33 | resolve(items); 34 | }); 35 | }); 36 | }; 37 | 38 | export const module_name = "CloudFront Distribution to MediaLive Input"; 39 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/helper.js: -------------------------------------------------------------------------------- 1 | export function checkAdditionalConnections(results, connection) { 2 | const shouldEndWith = connection.arn.endsWith("0") ? "1" : "0"; 3 | return _.filter(results, function (o) { 4 | if ( 5 | o.from === connection.from && 6 | o.to === connection.to 7 | ) { 8 | if (o.arn.endsWith(shouldEndWith)) return true; 9 | } 10 | return false; 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/link_device_medialive_input.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/link-device-medialive-input", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | label: data.scheme, 23 | data: data, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | }); 29 | } 30 | resolve(items); 31 | }); 32 | }); 33 | }; 34 | 35 | export const module_name = "Elemental Link to MediaLive Input"; 36 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediaconnect_flow_mediaconnect_flow.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/mediaconnect-flow-mediaconnect-flow", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | const human_type = data.scheme.replace("-", " "); 19 | items.push({ 20 | id: connection.arn, 21 | to: connection.to, 22 | from: connection.from, 23 | data: data, 24 | label: human_type, 25 | arrows: "to", 26 | color: { 27 | color: "black", 28 | }, 29 | dashes: false, 30 | }); 31 | } 32 | resolve(items); 33 | }); 34 | }); 35 | }; 36 | 37 | export const module_name = "MediaConnect Flow to MediaConnect Flow"; 38 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediaconnect_flow_medialive_input.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/mediaconnect-flow-medialive-input", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | const human_type = data.scheme.replace(/_/, " "); 19 | items.push({ 20 | id: connection.arn, 21 | to: connection.to, 22 | from: connection.from, 23 | data: data, 24 | label: human_type, 25 | arrows: "to", 26 | color: { 27 | color: "black", 28 | }, 29 | dashes: false, 30 | }); 31 | } 32 | resolve(items); 33 | }); 34 | }); 35 | }; 36 | 37 | export const module_name = "MediaConnect Flow to MediaLive Input"; 38 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_channel_input.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = () => { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(`${url}/cached/medialive-channel-medialive-input`, api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: data.scheme, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | dashes: false, 29 | }); 30 | } 31 | resolve(items); 32 | }); 33 | }); 34 | }; 35 | 36 | export const module_name = "MediaLive Channel to MediaLive Input"; 37 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_channel_mediaconnect_flow.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = () => { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(`${url}/cached/medialive-channel-mediaconnect-flow`, api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: data.scheme, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | dashes: false, 29 | }); 30 | } 31 | resolve(items); 32 | }); 33 | }); 34 | }; 35 | 36 | export const module_name = "MediaLive Channel to MediaConnect Flow"; 37 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_channel_mediapackage_channel.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | import { checkAdditionalConnections } from "./helper.js"; 7 | 8 | export const update = () => { 9 | const current = connections.get_current(); 10 | const url = current[0]; 11 | const api_key = current[1]; 12 | const items = []; 13 | return new Promise((resolve) => { 14 | server 15 | .get( 16 | `${url}/cached/medialive-channel-mediapackage-channel`, 17 | api_key 18 | ) 19 | .then((results) => { 20 | for (const connection of results) { 21 | const data = JSON.parse(connection.data); 22 | const options = { 23 | id: connection.arn, 24 | to: connection.to, 25 | from: connection.from, 26 | data: data, 27 | label: "HLS", 28 | arrows: "to", 29 | color: { color: "black" }, 30 | dashes: false, 31 | }; 32 | const hasMoreConnections = checkAdditionalConnections(results, connection); 33 | 34 | if (hasMoreConnections.length) { 35 | /** curve it */ 36 | options.smooth = { enabled: true }; 37 | options.smooth.type = "discrete"; 38 | 39 | if (_.has(data, "pipeline")) { 40 | options.label += ` ${data.pipeline}`; 41 | options.smooth.type = 42 | data.pipeline === 1 ? "curvedCCW" : "curvedCW"; 43 | options.smooth.roundness = 0.15; 44 | } 45 | } 46 | items.push(options); 47 | } 48 | resolve(items); 49 | }); 50 | }); 51 | }; 52 | 53 | export const module_name = "MediaLive Channel to MediaPackage Channel"; 54 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_channel_mediastore_container.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + "/cached/medialive-channel-mediastore-container", 16 | api_key 17 | ) 18 | .then((results) => { 19 | for (const connection of results) { 20 | const data = JSON.parse(connection.data); 21 | items.push({ 22 | id: connection.arn, 23 | to: connection.to, 24 | from: connection.from, 25 | data: data, 26 | label: data.scheme + ":", 27 | arrows: "to", 28 | color: { 29 | color: "black", 30 | }, 31 | dashes: false, 32 | }); 33 | } 34 | resolve(items); 35 | }); 36 | }); 37 | }; 38 | 39 | export const module_name = "MediaLive Channel to MediaStore Container"; 40 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_channel_multiplex.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | import { checkAdditionalConnections } from "./helper.js"; 7 | 8 | export const update = () => { 9 | const current = connections.get_current(); 10 | const url = current[0]; 11 | const api_key = current[1]; 12 | const items = []; 13 | return new Promise((resolve) => { 14 | const endpoint = `${url}/cached/medialive-channel-multiplex`; 15 | server.get(endpoint, api_key).then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | const options = { 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: data.program, 24 | arrows: "to", 25 | color: { color: "black" }, 26 | dashes: false, 27 | }; 28 | const hasMoreConnections = checkAdditionalConnections(results, connection); 29 | 30 | if (hasMoreConnections.length) { 31 | /** curve it */ 32 | options.smooth = { enabled: true }; 33 | options.smooth.type = "discrete"; 34 | 35 | if (_.has(data, "pipeline")) { 36 | options.label += ` ${data.pipeline}`; 37 | options.smooth.type = 38 | data.pipeline === 1 ? "curvedCCW" : "curvedCW"; 39 | options.smooth.roundness = 0.15; 40 | } 41 | } 42 | items.push(options); 43 | } 44 | resolve(items); 45 | }); 46 | }); 47 | }; 48 | 49 | export const module_name = "MediaLive Channel to Multiplex"; 50 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_channel_s3_bucket.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/medialive-channel-s3-bucket", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: data.scheme, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | dashes: false, 29 | }); 30 | } 31 | resolve(items); 32 | }); 33 | }); 34 | }; 35 | 36 | export const module_name = "MediaLive Channels to S3 Buckets"; 37 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/medialive_input_channel.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | import { checkAdditionalConnections } from "./helper.js"; 7 | 8 | export const update = function () { 9 | const current = connections.get_current(); 10 | const url = current[0]; 11 | const api_key = current[1]; 12 | const items = []; 13 | return new Promise((resolve) => { 14 | server 15 | .get(url + "/cached/medialive-input-medialive-channel", api_key) 16 | .then((results) => { 17 | for (const connection of results) { 18 | const data = JSON.parse(connection.data); 19 | const options = { 20 | id: connection.arn, 21 | to: connection.to, 22 | from: connection.from, 23 | data: data, 24 | label: data.type.replace(/_/, " "), 25 | arrows: "to", 26 | color: { color: "black" }, 27 | }; 28 | const hasMoreConnections = checkAdditionalConnections(results, connection); 29 | 30 | if (hasMoreConnections.length) { 31 | /** curve it */ 32 | options.smooth = { enabled: true }; 33 | options.smooth.type = "discrete"; 34 | 35 | if (_.has(data, "pipeline")) { 36 | options.label += ` ${data.pipeline}`; 37 | options.smooth.type = 38 | data.pipeline === 1 ? "curvedCCW" : "curvedCW"; 39 | options.smooth.roundness = 0.15; 40 | } 41 | } 42 | items.push(options); 43 | } 44 | resolve(items); 45 | }); 46 | }); 47 | }; 48 | 49 | export const module_name = "MediaLive Input to Channel Connections"; 50 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediapackage_channel_endpoint.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + 16 | "/cached/mediapackage-channel-mediapackage-origin-endpoint", 17 | api_key 18 | ) 19 | .then((results) => { 20 | for (const connection of results) { 21 | const data = JSON.parse(connection.data); 22 | items.push({ 23 | id: connection.arn, 24 | to: connection.to, 25 | from: connection.from, 26 | data: data, 27 | label: data.package, 28 | arrows: "to", 29 | color: { 30 | color: "black", 31 | }, 32 | smooth: { 33 | enabled: true, 34 | type: "discrete", 35 | }, 36 | }); 37 | } 38 | resolve(items); 39 | }); 40 | }); 41 | }; 42 | 43 | export const module_name = "MediaPackage Channel to Endpoint Connections"; 44 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediapackage_endpoint_cloudfront.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + 16 | "/cached/mediapackage-origin-endpoint-cloudfront-distribution", 17 | api_key 18 | ) 19 | .then((results) => { 20 | for (const connection of results) { 21 | const data = JSON.parse(connection.data); 22 | items.push({ 23 | id: connection.arn, 24 | to: connection.to, 25 | from: connection.from, 26 | data: data, 27 | label: data.scheme, 28 | arrows: "to", 29 | color: { 30 | color: "black", 31 | }, 32 | }); 33 | } 34 | resolve(items); 35 | }); 36 | }); 37 | }; 38 | 39 | export const module_name = "MediaPackage Endpoints to CloudFront Distributions"; 40 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediapackage_endpoint_mediatailor_configuration.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + 16 | "/cached/mediapackage-origin-endpoint-mediatailor-configuration", 17 | api_key 18 | ) 19 | .then((results) => { 20 | for (const connection of results) { 21 | const data = JSON.parse(connection.data); 22 | const human_type = data.scheme.replace(/_/, " "); 23 | items.push({ 24 | id: connection.arn, 25 | to: connection.to, 26 | from: connection.from, 27 | data: data, 28 | label: human_type, 29 | arrows: "to", 30 | color: { 31 | color: "black", 32 | }, 33 | }); 34 | } 35 | resolve(items); 36 | }); 37 | }); 38 | }; 39 | 40 | export const module_name = "MediaPackage Endpoint to MediaTailor Configuration"; 41 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediastore_container_cloudfront.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + "/cached/mediastore-container-cloudfront-distribution", 16 | api_key 17 | ) 18 | .then((results) => { 19 | for (const connection of results) { 20 | const data = JSON.parse(connection.data); 21 | items.push({ 22 | id: connection.arn, 23 | to: connection.to, 24 | from: connection.from, 25 | data: data, 26 | label: data.scheme, 27 | arrows: "to", 28 | color: { 29 | color: "black", 30 | }, 31 | }); 32 | } 33 | resolve(items); 34 | }); 35 | }); 36 | }; 37 | 38 | export const module_name = "MediaStore Containers to CloudFront Distributions"; 39 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediastore_container_medialive_input.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/mediastore-container-medialive-input", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: data.scheme + ":", 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | }); 29 | } 30 | resolve(items); 31 | }); 32 | }); 33 | }; 34 | 35 | export const module_name = "MediaStore Container to MediaLive Input"; 36 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/mediastore_container_mediatailor_configuration.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + "/cached/mediastore-container-mediatailor-configuration", 16 | api_key 17 | ) 18 | .then((results) => { 19 | for (const connection of results) { 20 | const data = JSON.parse(connection.data); 21 | items.push({ 22 | id: connection.arn, 23 | to: connection.to, 24 | from: connection.from, 25 | data: data, 26 | label: data.scheme, 27 | arrows: "to", 28 | color: { 29 | color: "black", 30 | }, 31 | }); 32 | } 33 | resolve(items); 34 | }); 35 | }); 36 | }; 37 | 38 | export const module_name = "MediaStore Container to MediaTailor Configurations"; 39 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/multiplex_mediaconnect_flow.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/multiplex-mediaconnect-flow", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: "MEDIACONNECT", 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | }); 29 | } 30 | resolve(items); 31 | }); 32 | }); 33 | }; 34 | 35 | export const module_name = "Multiplex to MediaConnect Flow"; 36 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/s3_bucket_cloudfront.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/s3-bucket-cloudfront-distribution", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | data: data, 23 | label: data.label, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | }); 29 | } 30 | resolve(items); 31 | }); 32 | }); 33 | }; 34 | 35 | export const module_name = "S3 Buckets to CloudFront Distributions"; 36 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/s3_bucket_medialive_input.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/s3-bucket-medialive-input", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | label: data.scheme, 23 | data: data, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | }); 29 | } 30 | resolve(items); 31 | }); 32 | }); 33 | }; 34 | 35 | export const module_name = "S3 Buckets to MediaLive Inputs"; 36 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/s3_bucket_mediatailor_configuration.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get(url + "/cached/s3-bucket-mediatailor-configuration", api_key) 15 | .then((results) => { 16 | for (const connection of results) { 17 | const data = JSON.parse(connection.data); 18 | items.push({ 19 | id: connection.arn, 20 | to: connection.to, 21 | from: connection.from, 22 | label: data.scheme, 23 | data: data, 24 | arrows: "to", 25 | color: { 26 | color: "black", 27 | }, 28 | }); 29 | } 30 | resolve(items); 31 | }); 32 | }); 33 | }; 34 | 35 | export const module_name = "S3 Buckets to MediaTailor Configurations"; 36 | -------------------------------------------------------------------------------- /source/html/js/app/mappers/connections/speke.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../../server.js"; 5 | import * as connections from "../../connections.js"; 6 | 7 | export const update = function () { 8 | const current = connections.get_current(); 9 | const url = current[0]; 10 | const api_key = current[1]; 11 | const items = []; 12 | return new Promise((resolve) => { 13 | server 14 | .get( 15 | url + "/cached/mediapackage-origin-endpoint-speke-keyserver", 16 | api_key 17 | ) 18 | .then((results) => { 19 | for (const connection of results) { 20 | const data = JSON.parse(connection.data); 21 | items.push({ 22 | id: connection.arn, 23 | to: connection.to, 24 | from: connection.from, 25 | label: data.scheme, 26 | data: data, 27 | arrows: "to", 28 | color: { 29 | color: "black", 30 | }, 31 | }); 32 | } 33 | resolve(items); 34 | }); 35 | }); 36 | }; 37 | 38 | export const module_name = "MediaPackage Origin Endpoint to SPEKE Keyserver"; 39 | -------------------------------------------------------------------------------- /source/html/js/app/model.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "./server.js"; 5 | import * as connections from "./connections.js"; 6 | import * as mappers from "./mappers/mappers.js"; 7 | 8 | const nodes = new vis.DataSet(); 9 | const edges = new vis.DataSet(); 10 | 11 | const reset = function () { 12 | nodes.clear(); 13 | edges.clear(); 14 | }; 15 | 16 | const map = function (callback) { 17 | new Promise(function (resolve) { 18 | const promises = []; 19 | for (const mapper of mappers.nodes) { 20 | console.log(mapper.module_name); 21 | promises.push(mapper.update()); 22 | Promise.all(promises).then(function (resolved_values) { 23 | for (const items of resolved_values) { 24 | nodes.update(items); 25 | } 26 | resolve(); 27 | }); 28 | } 29 | }) 30 | .then(function () { 31 | const promises = []; 32 | for (const mapper of mappers.connections) { 33 | console.log(mapper.module_name); 34 | promises.push(mapper.update()); 35 | } 36 | Promise.all(promises).then(function (resolved_values) { 37 | for (const items of resolved_values) { 38 | edges.update(items); 39 | } 40 | for (const node of nodes.get()) { 41 | // add stringified tags to the node, will be used during search 42 | node.stringtags = JSON.stringify(node.data.Tags); 43 | nodes.update(node); 44 | } 45 | if (typeof callback !== "undefined") { 46 | callback(); 47 | } 48 | }); 49 | }) 50 | .catch((error) => { 51 | console.error(error); 52 | }); 53 | }; 54 | 55 | const put_records = function (record) { 56 | const current = connections.get_current(); 57 | const url = current[0]; 58 | const api_key = current[1]; 59 | const current_endpoint = `${url}/cached`; 60 | if (record && !Array.isArray(record)) { 61 | record = [record]; 62 | } 63 | return new Promise(function (resolve, reject) { 64 | server 65 | .post(current_endpoint, api_key, record) 66 | .then(function (response) { 67 | console.log(response); 68 | resolve(response); 69 | }) 70 | .catch(function (error) { 71 | console.error(error); 72 | reject(error); 73 | }); 74 | }); 75 | }; 76 | 77 | const delete_record = function (arn) { 78 | const current = connections.get_current(); 79 | const url = current[0]; 80 | const api_key = current[1]; 81 | const current_endpoint = `${url}/cached/arn/${encodeURIComponent(arn)}`; 82 | return new Promise(function (resolve, reject) { 83 | server 84 | .delete_method(current_endpoint, api_key) 85 | .then(function (response) { 86 | console.log(response); 87 | resolve(response); 88 | }) 89 | .catch(function (error) { 90 | console.error(error); 91 | reject(error); 92 | }); 93 | }); 94 | }; 95 | 96 | // clear the model at module definition 97 | reset(); 98 | 99 | export { nodes, edges, reset, map, put_records, delete_record }; 100 | -------------------------------------------------------------------------------- /source/html/js/app/notes.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "./server.js"; 5 | import * as connections from "./connections.js"; 6 | 7 | const promise_get_closure = (endpoint, api_key) => { 8 | return function (resolve, reject) { 9 | server 10 | .get(endpoint, api_key) 11 | .then(function (response) { 12 | resolve(response); 13 | }) 14 | .catch(function (error) { 15 | console.error(error); 16 | reject(error); 17 | }); 18 | }; 19 | }; 20 | 21 | const get_resource_notes = function (arn) { 22 | const current_connection = connections.get_current(); 23 | const url = current_connection[0]; 24 | const api_key = current_connection[1]; 25 | const current_endpoint = `${url}/notes/${encodeURIComponent(arn)}`; 26 | return new Promise(promise_get_closure(current_endpoint, api_key)); 27 | }; 28 | 29 | const get_all_resource_notes = function () { 30 | const current_connection = connections.get_current(); 31 | const url = current_connection[0]; 32 | const api_key = current_connection[1]; 33 | const current_endpoint = `${url}/notes`; 34 | return new Promise(promise_get_closure(current_endpoint, api_key)); 35 | }; 36 | 37 | const update_resource_notes = function (arn, notes) { 38 | const current_connection = connections.get_current(); 39 | const url = current_connection[0]; 40 | const api_key = current_connection[1]; 41 | const current_endpoint = `${url}/notes/${encodeURIComponent(arn)}`; 42 | return new Promise(function (resolve, reject) { 43 | server 44 | .post(current_endpoint, api_key, notes) 45 | .then(function (response) { 46 | resolve(response); 47 | }) 48 | .catch(function (error) { 49 | console.error(error); 50 | reject(error); 51 | }); 52 | }); 53 | }; 54 | 55 | const promise_delete_closure = (endpoint, api_key) => { 56 | return function (resolve, reject) { 57 | server 58 | .delete_method(endpoint, api_key) 59 | .then(function (response) { 60 | resolve(response); 61 | }) 62 | .catch(function (error) { 63 | console.error(error); 64 | reject(error); 65 | }); 66 | }; 67 | }; 68 | 69 | const delete_resource_notes = function (arn) { 70 | const current_connection = connections.get_current(); 71 | const url = current_connection[0]; 72 | const api_key = current_connection[1]; 73 | const current_endpoint = `${url}/notes/${encodeURIComponent(arn)}`; 74 | return new Promise(promise_delete_closure(current_endpoint, api_key)); 75 | }; 76 | 77 | const delete_all_resource_notes = function () { 78 | const current_connection = connections.get_current(); 79 | const url = current_connection[0]; 80 | const api_key = current_connection[1]; 81 | const current_endpoint = `${url}/notes`; 82 | return new Promise(promise_delete_closure(current_endpoint, api_key)); 83 | }; 84 | 85 | export { 86 | get_resource_notes, 87 | get_all_resource_notes, 88 | update_resource_notes, 89 | delete_resource_notes, 90 | delete_all_resource_notes 91 | }; 92 | -------------------------------------------------------------------------------- /source/html/js/app/regions.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "./server.js"; 5 | import * as connections from "./connections.js"; 6 | 7 | let available = []; 8 | 9 | const get_available = function () { 10 | return available; 11 | }; 12 | 13 | export const refresh = _.memoize(function () { 14 | const current_connection = connections.get_current(); 15 | const url = current_connection[0]; 16 | const api_key = current_connection[1]; 17 | const all_endpoint = `${url}/regions`; 18 | return new Promise(function (resolve, reject) { 19 | server 20 | .get(all_endpoint, api_key) 21 | .then(function (data) { 22 | available = data; 23 | available.sort(function (a, b) { 24 | const nameA = a.RegionName; 25 | const nameB = b.RegionName; 26 | if (nameA < nameB) { 27 | return -1; 28 | } else if (nameA > nameB) { 29 | return 1; 30 | } else { 31 | // names must be equal 32 | return 0; 33 | } 34 | }); 35 | const module = { 36 | get_available: get_available, 37 | }; 38 | resolve(module); 39 | }) 40 | .catch(function (error) { 41 | console.error(error); 42 | reject(error); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /source/html/js/app/server.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | // default 60 second timeout 5 | let request_timeout = 60 * 1000; 6 | 7 | const get = function (url, api_key) { 8 | return new Promise((resolve, reject) => { 9 | const headers = { 10 | Accept: "application/json", 11 | "x-api-key": api_key, 12 | }; 13 | // get the library contents 14 | $.ajax({ 15 | url: url, 16 | type: "GET", 17 | headers: headers, 18 | success: (data) => { 19 | resolve(data); 20 | }, 21 | error: (data) => { 22 | reject(data); 23 | }, 24 | timeout: request_timeout, 25 | }); 26 | }); 27 | }; 28 | 29 | const post = function (url, api_key, data) { 30 | return new Promise((resolve, reject) => { 31 | const headers = { 32 | Accept: "application/json", 33 | "x-api-key": api_key, 34 | }; 35 | // get the library contents 36 | $.ajax({ 37 | url: url, 38 | type: "POST", 39 | data: JSON.stringify(data), 40 | headers: headers, 41 | contentType: "application/json", 42 | success: (response) => { 43 | resolve(response); 44 | }, 45 | error: (response) => { 46 | console.log(response); 47 | reject(response); 48 | }, 49 | timeout: request_timeout, 50 | }); 51 | }); 52 | }; 53 | 54 | const delete_method = function (url, api_key) { 55 | return new Promise((resolve, reject) => { 56 | const headers = { 57 | Accept: "application/json", 58 | "x-api-key": api_key, 59 | }; 60 | // get the library contents 61 | $.ajax({ 62 | url: url, 63 | type: "DELETE", 64 | headers: headers, 65 | contentType: "application/json", 66 | success: (data) => { 67 | resolve(data); 68 | }, 69 | error: (data) => { 70 | console.log(data); 71 | reject(data); 72 | }, 73 | timeout: request_timeout, 74 | }); 75 | }); 76 | }; 77 | 78 | const delete_method_with_body = function (url, api_key, data) { 79 | return new Promise((resolve, reject) => { 80 | const headers = { 81 | Accept: "application/json", 82 | "x-api-key": api_key, 83 | }; 84 | // get the library contents 85 | $.ajax({ 86 | url: url, 87 | type: "DELETE", 88 | data: JSON.stringify(data), 89 | headers: headers, 90 | contentType: "application/json", 91 | success: (response) => { 92 | resolve(response); 93 | }, 94 | error: (response) => { 95 | console.log(response); 96 | reject(response); 97 | }, 98 | timeout: request_timeout, 99 | }); 100 | }); 101 | }; 102 | 103 | export { get, post, delete_method, delete_method_with_body }; 104 | 105 | export function get_request_timeout() { 106 | return request_timeout; 107 | } 108 | 109 | export function set_request_timeout(timeout) { 110 | request_timeout = timeout; 111 | } 112 | -------------------------------------------------------------------------------- /source/html/js/app/settings.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "./server.js"; 5 | import * as connections from "./connections.js"; 6 | 7 | const get_setting = _.memoize(function (id) { 8 | // get the current connection data 9 | const current_connection = connections.get_current(); 10 | const url = current_connection[0]; 11 | const api_key = current_connection[1]; 12 | // create the resource url 13 | const current_endpoint = `${url}/settings/${id}`; 14 | // return a promise that response with result 15 | return new Promise((resolve, reject) => { 16 | server 17 | .get(current_endpoint, api_key) 18 | .then((response) => { 19 | resolve(response); 20 | }) 21 | .catch(function (error) { 22 | console.error(error); 23 | reject(error); 24 | }); 25 | }); 26 | }); 27 | 28 | const put_setting = function (id, value) { 29 | // get the current connection data 30 | const current_connection = connections.get_current(); 31 | const url = current_connection[0]; 32 | const api_key = current_connection[1]; 33 | // create the resource url 34 | const current_endpoint = `${url}/settings/${id}`; 35 | return new Promise((resolve, reject) => { 36 | const data = value; 37 | server 38 | .post(current_endpoint, api_key, data) 39 | .then((response) => { 40 | clear_function_cache(); 41 | resolve(response); 42 | }) 43 | .catch(function (error) { 44 | console.error(error); 45 | reject(error); 46 | }); 47 | }); 48 | }; 49 | 50 | const remove_setting = function (id) { 51 | // get the current connection data 52 | const current_connection = connections.get_current(); 53 | const url = current_connection[0]; 54 | const api_key = current_connection[1]; 55 | // create the resource url 56 | const current_endpoint = `${url}/settings/${id}`; 57 | return new Promise((resolve, reject) => { 58 | server 59 | .delete_method(current_endpoint, api_key) 60 | .then((response) => { 61 | clear_function_cache(); 62 | resolve(response); 63 | }) 64 | .catch(function (error) { 65 | console.error(error); 66 | reject(error); 67 | }); 68 | }); 69 | }; 70 | 71 | const clear_function_cache = function () { 72 | get_setting.cache.clear(); 73 | }; 74 | 75 | export { get_setting as get, put_setting as put, remove_setting as remove }; 76 | -------------------------------------------------------------------------------- /source/html/js/app/tools/build_numbers.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as check from "../api_check.js"; 5 | import * as build from "../build.js"; 6 | import * as connections from "../connections.js"; 7 | 8 | const module_name = "MSAM Build Numbers"; 9 | 10 | const run_tool = function () { 11 | return new Promise(function (resolve) { 12 | const current_connection = connections.get_current(); 13 | const endpoint = current_connection[0]; 14 | const api_key = current_connection[1]; 15 | const app_stamp = build.get_version(); 16 | check 17 | .ping(endpoint, api_key) 18 | .then(function (response) { 19 | const message = ` 20 |

This tool shows the versions for the currently running browser application and the currently connected endpoint.

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
#ComponentVersion
1Browser Application${app_stamp}
2Endpoint API${response.version}
42 | `; 43 | resolve({ 44 | name: module_name, 45 | success: true, 46 | message: message, 47 | }); 48 | }) 49 | .catch(function (event) { 50 | resolve({ 51 | name: module_name, 52 | success: false, 53 | message: "Error encountered: " + event, 54 | }); 55 | }); 56 | }); 57 | }; 58 | 59 | export { module_name as name, run_tool as run }; 60 | 61 | export const requires_single_selection = false; 62 | export const requires_multi_selection = false; 63 | export const selection_id_regex = ".*"; 64 | -------------------------------------------------------------------------------- /source/html/js/app/tools/clear_http.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | const module_name = "HTTP Connections (Clear)"; 5 | 6 | const run_tool = function () { 7 | return Promise.resolve({ 8 | name: module_name, 9 | success: true, 10 | message: 11 | "This is a placeholder. This tool will find and show cloud resource interconnections that use HTTP (clear) instead of HTTPS (encrypted).", 12 | }); 13 | }; 14 | 15 | export { module_name as name, run_tool as run }; 16 | 17 | export const requires_single_selection = false; 18 | export const requires_multi_selection = false; 19 | export const selection_id_regex = ".*"; 20 | -------------------------------------------------------------------------------- /source/html/js/app/tools/duplicate_names.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | const module_name = "Find Duplicate Named Services"; 5 | 6 | const run_tool = function () { 7 | return Promise.resolve({ 8 | name: module_name, 9 | success: false, 10 | message: 11 | "This is a placeholder. This tool will check names or IDs of services and warn on duplicates.", 12 | }); 13 | }; 14 | 15 | export { module_name as name, run_tool as run }; 16 | 17 | export const requires_single_selection = false; 18 | export const requires_multi_selection = false; 19 | export const selection_id_regex = ".*"; 20 | -------------------------------------------------------------------------------- /source/html/js/app/ui/alarm_indicators.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as model from "../model.js"; 5 | import * as alarms from "../alarms.js"; 6 | import * as diagrams from "./diagrams.js"; 7 | 8 | function get_alarming_nodes(current_alarming_subscribers) { 9 | const alarming_nodes = []; 10 | for (const subscriber of current_alarming_subscribers) { 11 | const node = model.nodes.get(subscriber.ResourceArn); 12 | if (!node) { 13 | continue; 14 | } 15 | node.alarming = true; 16 | // track which nodes are signaling an alert 17 | if (!alarming_nodes.includes(subscriber.ResourceArn)) { 18 | alarming_nodes.push(subscriber.ResourceArn); 19 | const selected = node.render.alert_selected(); 20 | const unselected = node.render.alert_unselected(); 21 | // only update the node if the SVG changes 22 | if ( 23 | selected != node.image.selected || 24 | unselected != node.image.unselected 25 | ) { 26 | node.image.selected = selected; 27 | node.image.unselected = unselected; 28 | model.nodes.update(node); 29 | const matches = diagrams.have_all([node.id]); 30 | for (const diagram of matches) { 31 | diagram.nodes.update(node); 32 | diagram.alert(true); 33 | } 34 | } 35 | } 36 | } 37 | return alarming_nodes; 38 | } 39 | 40 | function get_inactive_nodes(previous_alarming_subscribers, alarming_nodes) { 41 | const inactive_nodes = []; 42 | for (const subscriber of previous_alarming_subscribers) { 43 | let found = false; 44 | for (const node_id of alarming_nodes) { 45 | found = found || node_id == subscriber.ResourceArn; 46 | } 47 | if (!found) { 48 | inactive_nodes.push(subscriber.ResourceArn); 49 | } 50 | } 51 | return inactive_nodes; 52 | } 53 | 54 | const updateAlarmState = function ( 55 | current_alarming_subscribers, 56 | previous_alarming_subscribers 57 | ) { 58 | // iterate through current 'set' alerts 59 | const alarming_nodes = get_alarming_nodes(current_alarming_subscribers); 60 | 61 | // calculate the current alerts not included in the previous alerts 62 | const inactive_nodes = get_inactive_nodes(previous_alarming_subscribers, alarming_nodes); 63 | 64 | // 'unalert' the nodes that are no longer alerting 65 | for (const node_id of inactive_nodes) { 66 | const node = model.nodes.get(node_id); 67 | if (node) { 68 | node.alarming = false; 69 | // only switch the node render if the node is neither alarming nor alerting 70 | const selected = node.render.normal_selected(); 71 | const unselected = node.render.normal_unselected(); 72 | if ( 73 | selected != node.image.selected || 74 | unselected != node.image.unselected 75 | ) { 76 | node.image.selected = selected; 77 | node.image.unselected = unselected; 78 | model.nodes.update(node); 79 | const matches = diagrams.have_all([node.id]); 80 | for (const diagram of matches) { 81 | diagram.nodes.update(node); 82 | diagram.alert(false); 83 | } 84 | } 85 | } 86 | } 87 | }; 88 | 89 | alarms.add_callback(updateAlarmState); 90 | -------------------------------------------------------------------------------- /source/html/js/app/ui/alert.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | const timeout = 5000; 5 | 6 | const div_id = "alert_div"; 7 | 8 | export const show = function (message) { 9 | $("#" + div_id).html(message); 10 | $("#" + div_id).fadeIn("slow"); 11 | setTimeout(function () { 12 | $("#" + div_id).fadeOut("slow"); 13 | }, timeout); 14 | }; 15 | -------------------------------------------------------------------------------- /source/html/js/app/ui/confirmation.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | export const show = function (html, on_proceed) { 5 | $("#confirmation_dialog_proceed").on("click", function (event) { 6 | console.log(event); 7 | on_proceed(); 8 | $("#confirmation_dialog").modal("hide"); 9 | }); 10 | $("#confirmation_dialog").on("hide.bs.modal", function (event) { 11 | console.log(event); 12 | $("#confirmation_dialog_proceed").unbind("click"); 13 | }); 14 | $("#confirmation_dialog_body").html(html); 15 | $("#confirmation_dialog").modal("show"); 16 | }; 17 | -------------------------------------------------------------------------------- /source/html/js/app/ui/help_menu.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as build_numbers from "../tools/build_numbers.js"; 5 | 6 | const show_result = (success, message) => { 7 | const alert_class = success ? "alert-success" : "alert-danger"; 8 | const prefix = success ? "" : "Failed: "; 9 | const html = ``; 10 | $("#tool_output_modal_title").html(html); 11 | $("#tool_output_modal_message").html(message); 12 | $("#tool_output_modal").modal("show"); 13 | }; 14 | 15 | $("#build-version-button").on("click", function () { 16 | build_numbers 17 | .run() 18 | .then(function (result) { 19 | show_result(result.success, result.message); 20 | }) 21 | .catch(function (error) { 22 | show_result(false, error); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /source/html/js/app/ui/information_compartment.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as settings from "../settings.js"; 5 | 6 | let compartment_state = "max"; 7 | const settings_key = "info-compartment-state"; 8 | 9 | $("#information-compartment-up-down-button").click(() => { 10 | try { 11 | if (compartment_state === "min") { 12 | maximize_compartment(); 13 | } else if (compartment_state === "max") { 14 | minimize_compartment(); 15 | } 16 | } catch (error) { 17 | console.log(error); 18 | } 19 | $("#information-compartment-up-down-button").blur(); 20 | }); 21 | 22 | const maximize_compartment = () => { 23 | $("#information").fadeToggle("fast"); 24 | $("#information-compartment-flex").fadeToggle("fast"); 25 | $("#info-nav-flex").removeClass("border-bottom"); 26 | $("#diagram").animate({ height: "-=28%" }); 27 | compartment_state = "max"; 28 | settings.put(settings_key, { state: compartment_state }); 29 | }; 30 | 31 | const minimize_compartment = () => { 32 | $("#information").fadeToggle("fast"); 33 | $("#information-compartment-flex").fadeToggle("fast"); 34 | $("#info-nav-flex").addClass("border-bottom"); 35 | $("#diagram").animate({ height: "+=28%" }); 36 | compartment_state = "min"; 37 | settings.put(settings_key, { state: compartment_state }); 38 | }; 39 | 40 | const restore_state = async () => { 41 | try { 42 | const value = await settings.get(settings_key); 43 | console.log(`saved info compartment state = ${JSON.stringify(value)}`); 44 | if (value === null) { 45 | // no setting, keep the default state of maximized 46 | settings.put(settings_key, { state: "max" }); 47 | } else if (value.state === "max") { 48 | // do nothing, default state 49 | } else if (value.state === "min") { 50 | minimize_compartment(); 51 | } 52 | } catch (error) { 53 | console.log(JSON.stringify(error)); 54 | } 55 | }; 56 | 57 | // initialization 58 | restore_state(); 59 | -------------------------------------------------------------------------------- /source/html/js/app/ui/informational_overlays.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as model from "../model.js"; 5 | import * as overlays from "./overlays/overlays.js"; 6 | import * as diagrams from "./diagrams.js"; 7 | 8 | let intervalID; 9 | // interval in millis to update the cache 10 | const update_interval = 5000; 11 | 12 | function handle_node(node) { 13 | let selected = node.render.normal_selected(); 14 | let unselected = node.render.normal_unselected(); 15 | 16 | if (node.degraded) { 17 | selected = node.render.degraded_selected(); 18 | unselected = node.render.degraded_unselected(); 19 | } else if (node.alerting || node.alarming) { 20 | selected = node.render.alert_selected(); 21 | unselected = node.render.alert_unselected(); 22 | } 23 | 24 | // only update the node if the SVG changes 25 | if ( 26 | selected != node.image.selected || 27 | unselected != node.image.unselected 28 | ) { 29 | node.image.selected = selected; 30 | node.image.unselected = unselected; 31 | model.nodes.update(node); 32 | 33 | const matches = diagrams.have_all([node.id]); 34 | 35 | for (const diagram of matches) { 36 | diagram.nodes.update(node); 37 | } 38 | } 39 | } 40 | 41 | const update_overlay = function () { 42 | // console.log("info overlay update"); 43 | // get all the overlays 44 | for (const ov of overlays.all) { 45 | if (!ov.informational) { 46 | continue; 47 | } 48 | const nodes = model.nodes.get({ 49 | filter: (item) => { 50 | return ( 51 | ov.match_type == (item.generic_node_type || item.title) 52 | ); 53 | }, 54 | }); 55 | 56 | for (const node of nodes) { 57 | handle_node(node); 58 | } 59 | } 60 | }; 61 | 62 | const schedule_interval = function () { 63 | if (intervalID) { 64 | clearInterval(intervalID); 65 | } 66 | intervalID = setInterval(update_overlay, update_interval); 67 | console.log( 68 | "informational overlays: interval scheduled " + 69 | update_interval + 70 | "ms, intervalID = " + 71 | intervalID 72 | ); 73 | }; 74 | 75 | schedule_interval(); 76 | -------------------------------------------------------------------------------- /source/html/js/app/ui/layout.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as server from "../server.js"; 5 | import * as connections from "../connections.js"; 6 | import * as alert from "./alert.js"; 7 | 8 | const retrieve_layout = function (diagram) { 9 | const current_connection = connections.get_current(); 10 | const url = current_connection[0]; 11 | const api_key = current_connection[1]; 12 | const current_endpoint = `${url}/layout/view/${encodeURIComponent( 13 | diagram.view_id 14 | )}`; 15 | return server.get(current_endpoint, api_key); 16 | }; 17 | 18 | const delete_layout = function (diagram, node_ids) { 19 | node_ids = node_ids || diagram.nodes.getIds(); 20 | alert.show("Deleting layout"); 21 | const current_connection = connections.get_current(); 22 | const url = current_connection[0]; 23 | const api_key = current_connection[1]; 24 | for (const node_id of node_ids) { 25 | const current_endpoint = `${url}/layout/nodes/${encodeURIComponent( 26 | diagram.view_id 27 | )}/${encodeURIComponent(node_id)}`; 28 | server.delete_method(current_endpoint, api_key); 29 | } 30 | }; 31 | 32 | const save_layout = function (diagram, node_ids) { 33 | node_ids = node_ids || diagram.nodes.getIds(); 34 | alert.show("Saving layout"); 35 | const network = diagram.network; 36 | const positions = network.getPositions(node_ids); 37 | const layout = []; 38 | for (const key of Object.keys(positions)) { 39 | const entry = { 40 | view: diagram.view_id, 41 | id: key, 42 | x: positions[key].x, 43 | y: positions[key].y, 44 | }; 45 | layout.push(entry); 46 | } 47 | const current_connection = connections.get_current(); 48 | const url = current_connection[0]; 49 | const api_key = current_connection[1]; 50 | const current_endpoint = `${url}/layout/nodes`; 51 | server 52 | .post(current_endpoint, api_key, layout) 53 | .then(function () { 54 | alert.show("Layout saved"); 55 | console.log("layout changes are saved"); 56 | }) 57 | .catch(function (error) { 58 | console.error(error); 59 | }); 60 | }; 61 | 62 | function delete_all() { 63 | const current_connection = connections.get_current(); 64 | const url = current_connection[0]; 65 | const api_key = current_connection[1]; 66 | const current_endpoint = `${url}/layout/views`; 67 | return new Promise((resolve, reject) => { 68 | server 69 | .delete_method(current_endpoint, api_key) 70 | .then((response) => { 71 | resolve(response); 72 | }) 73 | .catch(function (error) { 74 | console.error(error); 75 | reject(error); 76 | }); 77 | }); 78 | } 79 | 80 | export { retrieve_layout, delete_layout, save_layout, delete_all }; 81 | -------------------------------------------------------------------------------- /source/html/js/app/ui/nodes_menu.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as ui_util from "./util.js"; 5 | import * as layout from "./layout.js"; 6 | import * as alert from "./alert.js"; 7 | import * as diagrams from "./diagrams.js"; 8 | 9 | $("#nodes_layout_vertical").on("click", function () { 10 | const shown = diagrams.shown(); 11 | if (shown) { 12 | shown.layout_vertical(true); 13 | } 14 | }); 15 | 16 | $("#nodes_layout_horizontal").on("click", function () { 17 | const shown = diagrams.shown(); 18 | if (shown) { 19 | shown.layout_horizontal(true); 20 | } 21 | }); 22 | 23 | $("#nodes_layout_isolated").on("click", function () { 24 | const shown = diagrams.shown(); 25 | if (shown) { 26 | shown.layout_isolated(true); 27 | } 28 | }); 29 | 30 | $("#nodes_select_downstream").on("click", function () { 31 | const shown = diagrams.shown(); 32 | if (shown) { 33 | const selected = shown.network.getSelectedNodes(); 34 | const connected = []; 35 | for (const node_id of selected) { 36 | if (!connected.includes(node_id)) { 37 | connected.push(node_id); 38 | ui_util.get_downstream(shown.edges, node_id, connected); 39 | } 40 | } 41 | console.log(connected); 42 | alert.show(connected.length + " selected"); 43 | shown.network.selectNodes(connected); 44 | } 45 | }); 46 | 47 | $("#nodes_select_upstream").on("click", function () { 48 | const shown = diagrams.shown(); 49 | if (shown) { 50 | const selected = shown.network.getSelectedNodes(); 51 | const connected = []; 52 | for (const node_id of selected) { 53 | if (!connected.includes(node_id)) { 54 | connected.push(node_id); 55 | ui_util.get_upstream(shown.edges, node_id, connected); 56 | } 57 | } 58 | console.log(connected); 59 | alert.show(connected.length + " selected"); 60 | shown.network.selectNodes(connected); 61 | } 62 | }); 63 | 64 | $("#nodes_align_vertical").on("click", function () { 65 | const diagram = diagrams.shown(); 66 | if (diagram) { 67 | const selected = diagram.network.getSelectedNodes(); 68 | const positions = diagram.network.getPositions(selected); 69 | let average_x = 0; 70 | for (const node_id of Object.keys(positions)) { 71 | average_x += positions[node_id].x; 72 | } 73 | average_x = Math.round(average_x / selected.length); 74 | for (const node_id of Object.keys(positions)) { 75 | diagram.network.moveNode(node_id, average_x, positions[node_id].y); 76 | } 77 | layout.save_layout(diagram, Object.keys(positions)); 78 | alert.show("Alignment complete"); 79 | } 80 | }); 81 | 82 | $("#nodes_align_horizontal").on("click", function () { 83 | const diagram = diagrams.shown(); 84 | if (diagram) { 85 | const selected = diagram.network.getSelectedNodes(); 86 | const positions = diagram.network.getPositions(selected); 87 | let average_y = 0; 88 | for (const node_id of Object.keys(positions)) { 89 | average_y += positions[node_id].y; 90 | } 91 | average_y = Math.round(average_y / selected.length); 92 | for (const node_id of Object.keys(positions)) { 93 | diagram.network.moveNode(node_id, positions[node_id].x, average_y); 94 | } 95 | layout.save_layout(diagram, Object.keys(positions)); 96 | alert.show("Alignment complete"); 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/alarms_only.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alarms from "../../alarms.js"; 5 | import * as tools from "./overlay_tools.js"; 6 | 7 | export const match_type = "Default Overlay for Alarms"; 8 | 9 | const decorate_alarms = function (drawing, font_size, width, height, id) { 10 | let alarm_count = 0; 11 | for (const item of alarms.get_subscribers_with_alarms().current) { 12 | if (item.ResourceArn == id) { 13 | alarm_count += item.AlarmCount; 14 | } 15 | } 16 | tools.set_alarm_text(alarm_count, drawing, font_size, width); 17 | }; 18 | 19 | export const decorate = function (drawing, font_size, width, height, id) { 20 | decorate_alarms(drawing, font_size, width, height, id); 21 | }; 22 | 23 | export const informational = false; 24 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/ec2_instance.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alarms from "../../alarms.js"; 5 | import * as tools from "./overlay_tools.js"; 6 | import * as model from "../../model.js"; 7 | 8 | export const match_type = "EC2 Instance"; 9 | 10 | const decorate_alarms = function (drawing, font_size, width, height, id) { 11 | let alarm_count = 0; 12 | for (const item of alarms.get_subscribers_with_alarms().current) { 13 | if (item.ResourceArn == id) { 14 | alarm_count += item.AlarmCount; 15 | } 16 | } 17 | tools.set_alarm_text(alarm_count, drawing, font_size, width); 18 | }; 19 | 20 | const decorate_information = function (drawing, font_size, width, height, id) { 21 | const node = model.nodes.get(id); 22 | if (node) { 23 | if (node.data.Tags.Name) { 24 | const computer_name = node.data.Tags.Name; 25 | tools.set_info_text( 26 | computer_name, 27 | drawing, 28 | font_size, 29 | width, 30 | "Name" 31 | ); 32 | } 33 | } 34 | }; 35 | 36 | export const decorate = function (drawing, font_size, width, height, id) { 37 | decorate_alarms(drawing, font_size, width, height, id); 38 | decorate_information(drawing, font_size, width, height, id); 39 | }; 40 | 41 | export const informational = true; 42 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/mediaconnect_flow.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alert_events from "../../events.js"; 5 | import * as alarms from "../../alarms.js"; 6 | import * as tools from "./overlay_tools.js"; 7 | import * as model from "../../model.js"; 8 | 9 | export const match_type = "MediaConnect Flow"; 10 | 11 | const decorate_alarms = function (drawing, font_size, width, height, id) { 12 | let alarm_count = 0; 13 | for (const item of alarms.get_subscribers_with_alarms().current) { 14 | if (item.ResourceArn == id) { 15 | alarm_count += item.AlarmCount; 16 | } 17 | } 18 | tools.set_alarm_text(alarm_count, drawing, font_size, width); 19 | }; 20 | 21 | const decorate_information = function (drawing, font_size, width, height, id) { 22 | const node = model.nodes.get(id); 23 | if (node) { 24 | let source_type = "Standard"; 25 | if (node.data.Source.EntitlementArn) { 26 | source_type = "Entitlement"; 27 | } 28 | else if (node.data.VpcInterfaces) { 29 | source_type = "VPC"; 30 | } 31 | tools.set_info_text(source_type, drawing, font_size, width); 32 | } 33 | }; 34 | 35 | const decorate_alerts = function (drawing, font_size, width, height, id) { 36 | let alert_count = 0; 37 | for (const item of alert_events.get_cached_events().current_mediaconnect) { 38 | if (item.resource_arn == id) { 39 | alert_count += 1; 40 | } 41 | } 42 | tools.set_event_text(alert_count, drawing, font_size, width, "Alerts"); 43 | }; 44 | 45 | export const decorate = function (drawing, font_size, width, height, id) { 46 | decorate_alarms(drawing, font_size, width, height, id); 47 | decorate_alerts(drawing, font_size, width, height, id); 48 | decorate_information(drawing, font_size, width, height, id); 49 | }; 50 | 51 | export const informational = true; 52 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/medialive_channel.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alert_events from "../../events.js"; 5 | import * as alarms from "../../alarms.js"; 6 | import * as tools from "./overlay_tools.js"; 7 | 8 | export const match_type = "MediaLive Channel"; 9 | 10 | const decorate_alarms = function (drawing, font_size, width, height, id) { 11 | let alarm_count = 0; 12 | const cached_alarms = alarms.get_subscribers_with_alarms(); 13 | 14 | for (const item of cached_alarms.current) { 15 | if (item.ResourceArn == id) { 16 | alarm_count += item.AlarmCount; 17 | } 18 | } 19 | tools.set_alarm_text(alarm_count, drawing, font_size, width); 20 | }; 21 | 22 | const decorate_events = function (drawing, font_size, width, height, id, data) { 23 | const isSinglePipeline = tools.has_single_pipeline(id, data); 24 | let pipeline_alerts = isSinglePipeline ? 0 : [0, 0]; 25 | for (const item of alert_events.get_cached_events().current_medialive) { 26 | if (item.resource_arn == id) { 27 | if (isSinglePipeline) pipeline_alerts += 1; 28 | else pipeline_alerts[parseInt(item.detail.pipeline)] += 1; 29 | } 30 | } 31 | 32 | tools.set_event_text(pipeline_alerts, drawing, font_size, width); 33 | }; 34 | 35 | export const decorate = function (drawing, font_size, width, height, id, data) { 36 | decorate_alarms(drawing, font_size, width, height, id); 37 | decorate_events(drawing, font_size, width, height, id, data); 38 | }; 39 | 40 | export const informational = true; 41 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/medialive_multiplex.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alert_events from "../../events.js"; 5 | import * as alarms from "../../alarms.js"; 6 | import * as tools from "./overlay_tools.js"; 7 | 8 | export const match_type = "MediaLive Multiplex"; 9 | 10 | const decorate_alarms = function (drawing, font_size, width, height, id) { 11 | let alarm_count = 0; 12 | for (const item of alarms.get_subscribers_with_alarms().current) { 13 | if (item.ResourceArn == id) { 14 | alarm_count += item.AlarmCount; 15 | } 16 | } 17 | tools.set_alarm_text(alarm_count, drawing, font_size, width); 18 | }; 19 | 20 | const decorate_events = function (drawing, font_size, width, height, id, data) { 21 | const isSinglePipeline = tools.has_single_pipeline(id, data); 22 | let pipeline_alerts = isSinglePipeline ? 0 : [0, 0]; 23 | for (const item of alert_events.get_cached_events().current_medialive) { 24 | if (item.resource_arn == id) { 25 | if (isSinglePipeline) pipeline_alerts += 1; 26 | else pipeline_alerts[parseInt(item.detail.pipeline)] += 1; 27 | } 28 | } 29 | 30 | tools.set_event_text(pipeline_alerts, drawing, font_size, width); 31 | }; 32 | 33 | export const decorate = function (drawing, font_size, width, height, id, data) { 34 | decorate_alarms(drawing, font_size, width, height, id); 35 | decorate_events(drawing, font_size, width, height, id, data); 36 | }; 37 | 38 | export const informational = true; 39 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/overlay_tools.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alert_events from "../../events.js"; 5 | 6 | const info_y = 80; 7 | const event_y = 125; 8 | const alarm_y = 154; 9 | const generateText = (type, data) => `${type}: ${JSON.stringify(data)}`; 10 | 11 | const fetch_running_pipelines_count = (data) => { 12 | let pipelines_count = 0; 13 | 14 | if (_.has(data, "ChannelClass")) { 15 | if (data.ChannelClass === "STANDARD") pipelines_count = 2; 16 | else pipelines_count = 1; 17 | } else if (_.has(data, "Destinations")) 18 | pipelines_count = data.Destinations.length; 19 | 20 | return pipelines_count; 21 | }; 22 | 23 | const has_single_pipeline = (id, data) => { 24 | const pipelines = {}; 25 | let pipelines_count = 0; 26 | 27 | if (data) { 28 | if ( 29 | _.has(data, "PipelinesRunningCount") && 30 | data.PipelinesRunningCount > 1 31 | ) 32 | return false; 33 | 34 | pipelines_count = fetch_running_pipelines_count(data); 35 | 36 | if (pipelines_count > 1) return false; 37 | } 38 | 39 | const cached_events = alert_events.get_cached_events(); 40 | 41 | for (const item of cached_events.current_medialive) { 42 | if (item.resource_arn == id) 43 | pipelines[parseInt(item.detail.pipeline)] += 1; 44 | if (_.keys(pipelines).length > 1 && pipelines_count > 1) return false; 45 | } 46 | 47 | return true; 48 | }; 49 | 50 | const draw_font_set_position = function (typeLabel, font_size, width) { 51 | typeLabel.font({ family: "Arial", size: font_size }); 52 | typeLabel.cx(width / 2); 53 | }; 54 | 55 | const set_alarm_text = function (data, drawing, font_size, width) { 56 | const typeLabel = drawing 57 | .text(generateText("Active alarms", data)) 58 | .y(alarm_y); 59 | draw_font_set_position(typeLabel, font_size, width); 60 | }; 61 | 62 | const set_event_text = function ( 63 | data, 64 | drawing, 65 | font_size, 66 | width, 67 | text = "Pipeline alerts" 68 | ) { 69 | const typeLabel = drawing.text(generateText(text, data)).y(event_y); 70 | draw_font_set_position(typeLabel, font_size, width); 71 | }; 72 | 73 | const set_info_text = function ( 74 | data, 75 | drawing, 76 | font_size, 77 | width, 78 | label = "Type" 79 | ) { 80 | const typeLabel = drawing.text(generateText(label, data)).y(info_y); 81 | draw_font_set_position(typeLabel, font_size, width); 82 | }; 83 | 84 | export { set_alarm_text, set_event_text, set_info_text, has_single_pipeline }; 85 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/overlays.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as overlay_mediaconnect_flow from "./mediaconnect_flow.js"; 5 | import * as overlay_medialive_channel from "./medialive_channel.js"; 6 | import * as overlay_medialive_multiplex from "./medialive_multiplex.js"; 7 | import * as overlay_ssm_managed_instance from "./ssm_managed_instance.js"; 8 | import * as overlay_ec2_instance from "./ec2_instance.js"; 9 | import * as overlay_alarms_only from "./alarms_only.js"; 10 | 11 | export const all = [ 12 | overlay_mediaconnect_flow, 13 | overlay_medialive_channel, 14 | overlay_medialive_multiplex, 15 | overlay_ssm_managed_instance, 16 | overlay_ec2_instance, 17 | ]; 18 | 19 | export const default_overlay = overlay_alarms_only; 20 | -------------------------------------------------------------------------------- /source/html/js/app/ui/overlays/ssm_managed_instance.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as alarms from "../../alarms.js"; 5 | import * as tools from "./overlay_tools.js"; 6 | import * as model from "../../model.js"; 7 | 8 | export const match_type = "SSM Managed Instance"; 9 | 10 | const decorate_alarms = function (drawing, font_size, width, height, id) { 11 | let alarm_count = 0; 12 | for (const item of alarms.get_subscribers_with_alarms().current) { 13 | if (item.ResourceArn == id) { 14 | alarm_count += item.AlarmCount; 15 | } 16 | } 17 | tools.set_alarm_text(alarm_count, drawing, font_size, width); 18 | }; 19 | 20 | const decorate_information = function (drawing, font_size, width, height, id) { 21 | const node = model.nodes.get(id); 22 | if (node) { 23 | if (node.data.Data["AWS:InstanceInformation"].Content[0].ComputerName) { 24 | const computer_name = 25 | node.data.Data["AWS:InstanceInformation"].Content[0] 26 | .ComputerName; 27 | tools.set_info_text( 28 | computer_name, 29 | drawing, 30 | font_size, 31 | width, 32 | "Name" 33 | ); 34 | } 35 | } 36 | }; 37 | 38 | export const decorate = function (drawing, font_size, width, height, id) { 39 | decorate_alarms(drawing, font_size, width, height, id); 40 | decorate_information(drawing, font_size, width, height, id); 41 | }; 42 | 43 | export const informational = true; 44 | -------------------------------------------------------------------------------- /source/html/js/app/ui/status_view.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import * as model from "../model.js"; 5 | import * as statemachine from "../statemachine.js"; 6 | 7 | const current_tab = ""; 8 | let progressTimer; 9 | 10 | const calculate_progress = function (fsm) { 11 | // get the total number of states for this FSM 12 | const states = Object.keys(fsm.states); 13 | // get our current position within the states 14 | const index = states.indexOf(fsm.state); 15 | // calculate the current state position as a percentage 16 | return Number.parseInt(((index + 1) / states.length) * 100); 17 | }; 18 | 19 | const show = function () { 20 | if (typeof progressTimer === "undefined") { 21 | progressTimer = setTimeout(update, 500); 22 | } 23 | }; 24 | 25 | const update = function () { 26 | const id = "#nav-status"; 27 | const tab = id + "-tab"; 28 | if (current_tab !== tab) { 29 | $(tab).tab("show"); 30 | } 31 | const configuration_percent = calculate_progress( 32 | statemachine.getConfigurationStateMachine() 33 | ); 34 | const model_data_percent = calculate_progress( 35 | statemachine.getModelDataStateMachine() 36 | ); 37 | const configuration_class = 38 | configuration_percent < 100 ? "progress-bar-striped progress-bar-animated bg-warning" : "bg-success"; 39 | const model_data_class = 40 | model_data_percent < 100 ? "progress-bar-striped progress-bar-animated bg-warning" : "bg-success"; 41 | const configuration_stats = 42 | configuration_percent < 100 ? configuration_percent + "%" : "Ready"; 43 | const model_stats = `${model.nodes.length} Nodes, ${model.edges.length} Connections`; 44 | const html = ` 45 | 46 | 47 | 48 | 49 | 54 | 55 | 56 | 57 | 62 | 63 | 64 |
Configuration 50 |
51 |
${configuration_stats}
52 |
53 |
Model Contents 58 |
59 |
${model_stats}
60 |
61 |
`; 65 | $(id).html(html); 66 | progressTimer = undefined; 67 | }; 68 | 69 | model.nodes.on("add", function () { 70 | show(); 71 | }); 72 | 73 | model.edges.on("add", function () { 74 | show(); 75 | }); 76 | 77 | statemachine.getToolStateMachine().on("transition", function () { 78 | show(); 79 | }); 80 | -------------------------------------------------------------------------------- /source/html/js/app/ui/util.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | export const makeid = function (id_len = 10) { 5 | let text = ""; 6 | const possible = 7 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 8 | for (let i = 0; i < id_len; i++) { 9 | text += possible.charAt(get_random_number(possible.length)); 10 | } 11 | return text; 12 | }; 13 | 14 | export function get_random_number(max) { 15 | const randomBuffer = new Uint32Array(1); 16 | crypto.getRandomValues(randomBuffer); 17 | const randomNumber = randomBuffer[0] / (0xffffffff + 1); 18 | return Math.floor(randomNumber * (max)); 19 | } 20 | 21 | export function vary(value, limit) { 22 | return ( 23 | value + get_random_number(limit) * (get_random_number(2) == 0 ? -1 : 1) 24 | ); 25 | } 26 | 27 | export const get_downstream = function (edges, node_id, connected_nodes) { 28 | const downstream_edges = edges.get({ 29 | filter: function (item) { 30 | return item.from === node_id; 31 | }, 32 | }); 33 | for (const edge of downstream_edges) { 34 | if (!connected_nodes.includes(edge.to)) { 35 | connected_nodes.push(edge.to); 36 | get_downstream(edges, edge.to, connected_nodes); 37 | } 38 | } 39 | }; 40 | 41 | export const get_upstream = function (edges, node_id, connected_nodes) { 42 | const upstream_edges = edges.get({ 43 | filter: function (item) { 44 | return item.to === node_id; 45 | }, 46 | }); 47 | for (const edge of upstream_edges) { 48 | if (!connected_nodes.includes(edge.from)) { 49 | connected_nodes.push(edge.from); 50 | get_upstream(edges, edge.from, connected_nodes); 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /source/html/js/test/alarms.test.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | /* eslint no-import-assign: "off" */ 5 | 6 | import { jest } from '@jest/globals'; 7 | import * as alarms from "../app/alarms.js"; 8 | import * as server from "../app/server.js"; 9 | import * as connections from "../app/connections.js"; 10 | 11 | const internals = alarms.exported_for_unit_tests; 12 | 13 | afterEach(() => { 14 | jest.restoreAllMocks(); 15 | }); 16 | 17 | test('get_subscribers_with_alarms', () => { 18 | expect(alarms.get_subscribers_with_alarms()).toMatchObject({ 19 | current: [], 20 | previous: [], 21 | } 22 | ); 23 | }); 24 | 25 | test('add_callback', () => { 26 | const caller = jest.fn(); 27 | alarms.add_callback(caller); 28 | expect(internals.listeners).toContain(caller); 29 | }); 30 | 31 | test('all_alarms_for_region', () => { 32 | connections.get_current = jest.fn(() => { 33 | return ["url", "api-key"]; 34 | }); 35 | server.get = jest.fn(() => { 36 | return Promise.resolve(true); 37 | }); 38 | expect(alarms.all_alarms_for_region("us-west-2")).resolves.toBeTruthy(); 39 | }); 40 | 41 | test('subscribe_to_alarm', () => { 42 | connections.get_current = jest.fn(() => { 43 | return ["url", "api-key"]; 44 | }); 45 | server.post = jest.fn(() => { 46 | return Promise.resolve(true); 47 | }); 48 | expect(alarms.subscribe_to_alarm("us-west-2", "TEST-ALARM", ["ARN-1", "ARN-2"])).resolves.toBeTruthy(); 49 | }); 50 | 51 | test('unsubscribe_to_alarm', () => { 52 | connections.get_current = jest.fn(() => { 53 | return ["url", "api-key"]; 54 | }); 55 | server.post = jest.fn(() => { 56 | return Promise.resolve(true); 57 | }); 58 | expect(alarms.unsubscribe_from_alarm("us-west-2", "TEST-ALARM", ["ARN-1", "ARN-2"])).resolves.toBeTruthy(); 59 | }); 60 | 61 | test('alarms_for_subscriber', () => { 62 | connections.get_current = jest.fn(() => { 63 | return ["url", "api-key"]; 64 | }); 65 | server.get = jest.fn(() => { 66 | return Promise.resolve(true); 67 | }); 68 | expect(alarms.alarms_for_subscriber("ARN-1")).resolves.toBeTruthy(); 69 | }); 70 | 71 | test('delete_all_subscribers', () => { 72 | connections.get_current = jest.fn(() => { 73 | return ["url", "api-key"]; 74 | }); 75 | server.delete_method = jest.fn(() => { 76 | return Promise.resolve(true); 77 | }); 78 | expect(alarms.delete_all_subscribers()).resolves.toBeTruthy(); 79 | }); 80 | 81 | // set_update_interval 82 | // get_update_interval 83 | -------------------------------------------------------------------------------- /source/html/js/test/always.test.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | // two simple checks of the testing framework 5 | 6 | test('always passes', () => { 7 | expect(true).toBe(true); 8 | }); 9 | 10 | test('always passes', () => { 11 | expect("Test").toMatch("Test"); 12 | }); 13 | -------------------------------------------------------------------------------- /source/html/js/test/api_check.test.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | /* eslint no-import-assign: "off" */ 5 | 6 | import { jest } from '@jest/globals' 7 | import * as server from "../app/server.js"; 8 | import * as api_check from "../app/api_check.js"; 9 | 10 | afterEach(() => { 11 | jest.restoreAllMocks(); 12 | }); 13 | 14 | test('ping', () => { 15 | // mock the server.get module function 16 | server.get = jest.fn(() => { return "success" }); 17 | expect(api_check.ping('fake-url', 'fake-api-key')).toBe("success"); 18 | }); 19 | -------------------------------------------------------------------------------- /source/html/js/test/connections.test.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | import { jest } from '@jest/globals'; 5 | import * as connections from "../app/connections.js"; 6 | 7 | const mock_cookies = { 8 | "MSAM_CURRENT": "MSAM_ENDPOINT_TEST", 9 | "MSAM_ENDPOINT_TEST": window.btoa(JSON.stringify({ name: "connection" })) 10 | }; 11 | 12 | const mock_connections = { 13 | "MSAM_ENDPOINT_TEST": { name: "connection" } 14 | }; 15 | 16 | // mock the Cookies global to return test cookies 17 | Cookies.get = jest.fn(); 18 | Cookies.set = jest.fn(); 19 | 20 | afterEach(() => { 21 | jest.restoreAllMocks(); 22 | }); 23 | 24 | test('get_remembered', () => { 25 | Cookies.get.mockReturnValue(mock_cookies) 26 | expect(connections.get_remembered()).toMatchObject([mock_connections["MSAM_ENDPOINT_TEST"]]); 27 | }); 28 | 29 | test('get_current', () => { 30 | Cookies.get.mockReturnValueOnce(mock_cookies["MSAM_CURRENT"]); 31 | Cookies.get.mockReturnValueOnce(mock_cookies["MSAM_ENDPOINT_TEST"]); 32 | expect(connections.get_current()).toMatchObject(mock_connections["MSAM_ENDPOINT_TEST"]); 33 | }); 34 | -------------------------------------------------------------------------------- /source/html/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "Apache-2.0", 3 | "description": "Media Services Application Mapper Browser Application", 4 | "version": "1.13.2", 5 | "dependencies": { 6 | "@fortawesome/fontawesome-free": "^5.15.4", 7 | "bootstrap": "^5.1.3", 8 | "bootstrap-datepicker": "^1.9.0", 9 | "core-js": "^3.23.4", 10 | "fuse.js": "^6.5.3", 11 | "jquery": "^3.6.0", 12 | "jqueryui": "^1.11.1", 13 | "js-cookie": "^2.0.2", 14 | "lodash": "^4.17.21", 15 | "lodash-es": "^4.17.21", 16 | "machina": "^4.0.2", 17 | "material-icons": "^1.10.5", 18 | "moment": "^2.29.2", 19 | "object-hash": "^2.2.0", 20 | "renderjson": "^1.4.0", 21 | "showdown": "^2.0.3", 22 | "svg.js": "^2.6.6", 23 | "tabulator-tables": "^4.9.3", 24 | "vis-network": "^9.1.0", 25 | "xss": "^1.0.11" 26 | }, 27 | "scripts": { 28 | "test": "jest --runInBand" 29 | }, 30 | "type": "module", 31 | "devDependencies": { 32 | "@babel/cli": "^7.18.6", 33 | "@babel/core": "^7.21.0", 34 | "@babel/preset-env": "^7.18.6", 35 | "babel-jest": "^28.1.3", 36 | "jest": "^28.1.3", 37 | "jest-environment-jsdom": "^28.1.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/msam/.chalice/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_gateway_stage": "msam", 3 | "version": "2.0", 4 | "app_name": "msam", 5 | "manage_iam_role": false, 6 | "iam_role_arn": "core-iam-role-arn", 7 | "autogen_policy": false, 8 | "lambda_timeout": 300, 9 | "lambda_memory_size": 2560, 10 | "environment_variables": { 11 | "ALARMS_TABLE_NAME": "media-services-application-mapper-channels", 12 | "BUILD_STAMP": "DEV_0_0_0", 13 | "CACHE_ITEM_TTL": "7200", 14 | "CHANNELS_TABLE_NAME": "media-services-application-mapper-channels", 15 | "CONTENT_TABLE_NAME": "media-services-application-mapper-content", 16 | "EVENTS_TABLE_NAME": "media-services-application-mapper-events", 17 | "LAYOUT_TABLE_NAME": "media-services-application-mapper-layout", 18 | "SETTINGS_TABLE_NAME": "media-services-application-mapper-settings", 19 | "CLOUDWATCH_EVENTS_TABLE_NAME": "media-services-application-mapper-cloudwatchevents", 20 | "NOTES_TABLE_NAME": "media-services-application-mapper-resourcenotes", 21 | "DELETE_NOTES_FUNCTION": "delete_notes_function", 22 | "SOLUTION_ID": "AwsSolution/SO0048/%%VERSION%%", 23 | "VERSION": "%%VERSION%%" 24 | } 25 | } -------------------------------------------------------------------------------- /source/msam/.chalice/policy-dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [{ 4 | "Action": [ 5 | "mediapackage:List*", 6 | "mediapackage:Describe*", 7 | "medialive:List*", 8 | "medialive:Describe*", 9 | "mediastore:List*", 10 | "mediastore:Get*", 11 | "mediaconnect:List*", 12 | "mediaconnect:Describe*", 13 | "mediatailor:Get*", 14 | "mediatailor:List*", 15 | "ssm:List*", 16 | "ssm:Get*", 17 | "ssm:SendCommand" 18 | ], 19 | "Effect": "Allow", 20 | "Resource": "*" 21 | }, 22 | { 23 | "Effect": "Allow", 24 | "Action": "ec2:Describe*", 25 | "Resource": "*" 26 | }, 27 | { 28 | "Effect": "Allow", 29 | "Action": [ 30 | "cloudwatch:ListMetrics", 31 | "cloudwatch:GetMetricStatistics", 32 | "cloudwatch:Describe*", 33 | "cloudwatch:PutMetricData" 34 | ], 35 | "Resource": "*" 36 | }, 37 | { 38 | "Action": [ 39 | "dynamodb:Query", 40 | "dynamodb:DeleteItem", 41 | "dynamodb:PutItem", 42 | "dynamodb:GetItem", 43 | "dynamodb:Scan" 44 | ], 45 | "Effect": "Allow", 46 | "Resource": "*" 47 | }, 48 | { 49 | "Action": [ 50 | "application-autoscaling:DescribeScalableTargets", 51 | "application-autoscaling:DescribeScalingActivities", 52 | "application-autoscaling:DescribeScalingPolicies", 53 | "cloudwatch:DescribeAlarmHistory", 54 | "cloudwatch:DescribeAlarms", 55 | "cloudwatch:DescribeAlarmsForMetric", 56 | "cloudwatch:GetMetricStatistics", 57 | "cloudwatch:ListMetrics", 58 | "ec2:DescribeVpcs", 59 | "ec2:DescribeSubnets", 60 | "ec2:DescribeSecurityGroups", 61 | "iam:GetRole", 62 | "iam:ListRoles", 63 | "sns:ListSubscriptions", 64 | "sns:ListSubscriptionsByTopic", 65 | "sns:ListTopics", 66 | "lambda:List*", 67 | "lambda:Get*" 68 | ], 69 | "Effect": "Allow", 70 | "Resource": "*" 71 | }, 72 | { 73 | "Effect": "Allow", 74 | "Action": [ 75 | "s3:Get*", 76 | "s3:List*" 77 | ], 78 | "Resource": "*" 79 | }, 80 | { 81 | "Action": [ 82 | "logs:CreateLogGroup", 83 | "logs:CreateLogStream", 84 | "logs:PutLogEvents", 85 | "logs:GetLogEvents" 86 | ], 87 | "Effect": "Allow", 88 | "Resource": "*" 89 | }, 90 | { 91 | "Action": [ 92 | "acm:ListCertificates", 93 | "cloudfront:Get*", 94 | "cloudfront:List*", 95 | "iam:ListServerCertificates", 96 | "route53:List*", 97 | "waf:ListWebACLs", 98 | "waf:GetWebACL" 99 | ], 100 | "Effect": "Allow", 101 | "Resource": "*" 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /source/msam/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | relative_files = True 3 | source = . 4 | omit = *test* 5 | -------------------------------------------------------------------------------- /source/msam/.gitignore: -------------------------------------------------------------------------------- 1 | .chalice/deployments/ 2 | .chalice/venv/ 3 | __pycache__/ 4 | db/package/ 5 | build/msam-core-release.template 6 | -------------------------------------------------------------------------------- /source/msam/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/source/msam/__init__.py -------------------------------------------------------------------------------- /source/msam/chalicelib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/source/msam/chalicelib/__init__.py -------------------------------------------------------------------------------- /source/msam/chalicelib/content.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """ 4 | This file contains helper functions related to the content DynamoDB table. 5 | """ 6 | 7 | import os 8 | 9 | import boto3 10 | from botocore.config import Config 11 | 12 | # TTL provided via CloudFormation 13 | CACHE_ITEM_TTL = int(os.environ["CACHE_ITEM_TTL"]) 14 | 15 | # table names generated by CloudFormation 16 | CONTENT_TABLE_NAME = os.environ["CONTENT_TABLE_NAME"] 17 | 18 | # user-agent config 19 | SOLUTION_ID = os.environ['SOLUTION_ID'] 20 | USER_AGENT_EXTRA = {"user_agent_extra": SOLUTION_ID} 21 | MSAM_BOTO3_CONFIG = Config(**USER_AGENT_EXTRA) 22 | 23 | def put_ddb_items(items): 24 | """ 25 | Add a list of cache items to the content (cache) DynamoDB table. 26 | """ 27 | ddb_table_name = CONTENT_TABLE_NAME 28 | # shared resource and table 29 | ddb_resource = boto3.resource('dynamodb', config=MSAM_BOTO3_CONFIG) 30 | ddb_table = ddb_resource.Table(ddb_table_name) 31 | for item in items: 32 | ddb_table.put_item(Item=item) 33 | return True 34 | -------------------------------------------------------------------------------- /source/msam/chalicelib/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """ 4 | This file contains helper functions related to settings. 5 | """ 6 | 7 | import os 8 | from urllib.parse import unquote 9 | 10 | import boto3 11 | from botocore.exceptions import ClientError 12 | from botocore.config import Config 13 | 14 | SETTINGS_TABLE_NAME = os.environ["SETTINGS_TABLE_NAME"] 15 | 16 | # user-agent config 17 | SOLUTION_ID = os.environ['SOLUTION_ID'] 18 | USER_AGENT_EXTRA = {"user_agent_extra": SOLUTION_ID} 19 | MSAM_BOTO3_CONFIG = Config(**USER_AGENT_EXTRA) 20 | 21 | # DynamoDB 22 | DYNAMO_RESOURCE = boto3.resource("dynamodb", config=MSAM_BOTO3_CONFIG) 23 | 24 | def put_setting(key, value): 25 | """ 26 | Put a string value into the setting table under key. 27 | """ 28 | table = DYNAMO_RESOURCE.Table(SETTINGS_TABLE_NAME) 29 | # write to the database 30 | table.put_item(Item={"id": key, "value": value}) 31 | 32 | 33 | def get_setting(key): 34 | """ 35 | Retrieve a setting object from the database. 36 | """ 37 | table = DYNAMO_RESOURCE.Table(SETTINGS_TABLE_NAME) 38 | # get the settings object 39 | setting = None 40 | try: 41 | response = table.get_item(Key={'id': key}) 42 | # return the response or an empty object 43 | if "Item" in response: 44 | setting = response["Item"]["value"] 45 | else: 46 | setting = None 47 | except ClientError as error: 48 | print(error) 49 | return setting 50 | 51 | 52 | def application_settings(request, item_key): 53 | """ 54 | API entry point to get or set the object value for a setting. 55 | """ 56 | try: 57 | item_key = unquote(item_key) 58 | print(item_key) 59 | settings = {} 60 | print(request.method) 61 | if request.method in ('PUT', 'POST'): 62 | print(request.json_body) 63 | settings = request.json_body 64 | put_setting(item_key, settings) 65 | settings = {"message": "saved"} 66 | print(settings) 67 | elif request.method == 'GET': 68 | settings = get_setting(item_key) 69 | elif request.method == 'DELETE': 70 | table = DYNAMO_RESOURCE.Table(SETTINGS_TABLE_NAME) 71 | table.delete_item(Key={"id": item_key}) 72 | except ClientError as error: 73 | # send the exception back in the object 74 | print(error) 75 | settings = {"exception": str(error)} 76 | return settings 77 | -------------------------------------------------------------------------------- /source/msam/db/makezip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | ORIGIN=`pwd` 7 | ZIP="$ORIGIN/dynamodb_resource.zip" 8 | 9 | rm -f $ZIP error.txt 10 | pip install --upgrade --force-reinstall --target ./package crhelper 2> error.txt 11 | RETVAL=$? 12 | if [ "$RETVAL" -ne "0" ]; then 13 | echo "ERROR: Database package installation failed." 14 | cat error.txt 15 | exit $RETVAL 16 | fi 17 | cd package 18 | zip -r9 $ZIP . 19 | cd $ORIGIN 20 | zip -g $ZIP lambda_function.py 21 | -------------------------------------------------------------------------------- /source/msam/requirements.txt: -------------------------------------------------------------------------------- 1 | networkx 2 | jsonpath_ng 3 | stringcase 4 | requests 5 | defusedxml 6 | crhelper 7 | urllib3<2 8 | -------------------------------------------------------------------------------- /source/msam/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/9230ad89831347ae1a429935cf6619e6acad9107/source/msam/test/__init__.py -------------------------------------------------------------------------------- /source/msam/test/run_unit_tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | Launch from the source/msam/ folder: 3 | python -m test.run_unit_tests 4 | 5 | """ 6 | import unittest 7 | 8 | # pylint: disable=W0611,E0611 9 | from test.test_cache import * 10 | from test.test_channels import * 11 | from test.test_cloudwatch import * 12 | from test.test_connections import * 13 | from test.test_layout import * 14 | from test.test_nodes import * 15 | from test.test_tags import * 16 | from test.test_content import * 17 | from test.test_notes import * 18 | from test.test_settings import * 19 | from test.test_periodic import * 20 | from test.test_app import * 21 | from test.test_db import * 22 | 23 | if __name__ == '__main__': 24 | unittest.main(verbosity=3) 25 | -------------------------------------------------------------------------------- /source/msam/test/test_content.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides unit tests for the content.py module. 3 | """ 4 | 5 | # pylint: disable=C0415,W0201 6 | 7 | import unittest 8 | from unittest.mock import patch 9 | 10 | 11 | @patch('os.environ') 12 | @patch('boto3.resource') 13 | @patch('boto3.client') 14 | class TestContent(unittest.TestCase): 15 | """ 16 | This class extends TestCase with testing functions 17 | """ 18 | def test_put_ddb_items(self, patched_env, patched_resource, 19 | patched_client): 20 | """ 21 | Test the put_ddb_item function 22 | """ 23 | from chalicelib import content 24 | content.put_ddb_items(["us-east-1"]) 25 | content.boto3.resource.assert_called_once() 26 | content.boto3.resource.return_value.Table.assert_called_once_with('content_table') 27 | content.boto3.resource.return_value.Table.return_value.put_item.assert_called_once_with(Item='us-east-1') 28 | print() 29 | -------------------------------------------------------------------------------- /source/msam/test/test_db.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is provides unit tests for the db/lambda_function.py module. 3 | """ 4 | 5 | # pylint: disable=C0415 6 | 7 | import unittest 8 | from unittest.mock import MagicMock, patch 9 | from botocore.exceptions import ClientError 10 | 11 | CLIENT_ERROR = ClientError({"Error": {"Code": "400", "Message": "SomeClientError"}}, "MockedFunction") 12 | 13 | @patch('os.environ') 14 | @patch('boto3.resource') 15 | @patch('boto3.client') 16 | class TestDb(unittest.TestCase): 17 | """ 18 | This class extends TestCase with testing functions 19 | """ 20 | 21 | def assertion_helper(self, lambda_function): 22 | """ 23 | Helper function to run repetitive assertion statements 24 | """ 25 | lambda_function.boto3.client.assert_called_once() 26 | lambda_function.boto3.client.return_value.describe_regions.assert_called_once_with() 27 | lambda_function.boto3.resource.assert_called_once() 28 | lambda_function.boto3.resource.return_value.Table.assert_called_once_with('settings_table') 29 | lambda_function.boto3.resource.return_value.Table.return_value.get_item.assert_called_once_with(Key={"id": "never-cache-regions"}) 30 | self.assertEqual(lambda_function.boto3.resource.return_value.Table.return_value.put_item.call_count, 9) 31 | 32 | def test_create_update(self, patched_client, patched_resource, patched_env): 33 | """ 34 | Test the create_udpate function 35 | """ 36 | from db import lambda_function 37 | mocked_event = {"ResourceProperties": {"SettingsTable": "settings_table"}} 38 | lambda_function.create_update(mocked_event, MagicMock()) 39 | self.assertion_helper(lambda_function) 40 | 41 | 42 | def test_lambda_handler(self, patched_client, patched_resource, patched_env): 43 | """ 44 | Test the lambda_handler function 45 | """ 46 | from db import lambda_function 47 | mocked_event = {"ResourceProperties": {"SettingsTable": "settings_table"}} 48 | with patch.object(lambda_function, 'helper'): 49 | lambda_function.lambda_handler(mocked_event, MagicMock()) 50 | lambda_function.helper.assert_called_once() 51 | 52 | def test_make_default_settings(self, patched_client, patched_resource, patched_env): 53 | """ 54 | Test the make_default_settings function 55 | """ 56 | from db import lambda_function 57 | lambda_function.make_default_settings("settings_table") 58 | self.assertion_helper(lambda_function) 59 | lambda_function.boto3.client.reset_mock() 60 | lambda_function.boto3.resource.reset_mock() 61 | 62 | patched_resource.return_value.Table.return_value.put_item.side_effect = CLIENT_ERROR 63 | lambda_function.make_default_settings("settings_table") 64 | self.assertion_helper(lambda_function) 65 | -------------------------------------------------------------------------------- /source/msam/test/test_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is provides unit tests for the settings.py module. 3 | """ 4 | 5 | # pylint: disable=C0415,W0201 6 | 7 | import unittest 8 | from unittest.mock import patch, MagicMock 9 | from botocore.exceptions import ClientError 10 | 11 | KEY = 'key' 12 | VALUE = 'value' 13 | CLIENT_ERROR = ClientError({"Error": {"Code": "400", "Message": "SomeClientError"}}, "ClientError") 14 | 15 | @patch('os.environ') 16 | @patch('boto3.resource') 17 | @patch('boto3.client') 18 | class TestSettings(unittest.TestCase): 19 | """ 20 | This class extends TestCase with testing functions 21 | """ 22 | 23 | def setUp(self): 24 | from chalicelib import settings 25 | settings.DYNAMO_RESOURCE.reset_mock() 26 | settings.DYNAMO_RESOURCE.Table.return_value.put_item.reset_mock() 27 | 28 | def test_put_setting(self, patched_env, patched_resource, 29 | patched_client): 30 | """ 31 | Test the put_setting function 32 | """ 33 | from chalicelib import settings 34 | settings.put_setting(KEY, VALUE) 35 | settings.DYNAMO_RESOURCE.Table.return_value.put_item.assert_called_once_with(Item={"id": KEY, "value": VALUE}) 36 | 37 | def test_get_setting(self, patched_env, patched_resource, patched_client): 38 | """ 39 | Test the get_setting function 40 | """ 41 | from chalicelib import settings 42 | settings.get_setting(KEY) 43 | settings.DYNAMO_RESOURCE.Table.return_value.get_item.assert_any_call(Key={'id': KEY}) 44 | 45 | table_mock = MagicMock() 46 | table_mock.get_item.return_value = {"Item": {"value": VALUE}} 47 | with patch.object(settings.DYNAMO_RESOURCE, 'Table', return_value=table_mock): 48 | setting = settings.get_setting(KEY) 49 | settings.DYNAMO_RESOURCE.Table.return_value.get_item.assert_any_call(Key={'id': KEY}) 50 | self.assertEqual(setting, 'value') 51 | 52 | with patch.object(settings.DYNAMO_RESOURCE.Table.return_value, 'get_item', side_effect=CLIENT_ERROR): 53 | setting = settings.get_setting(KEY) 54 | settings.DYNAMO_RESOURCE.Table.return_value.get_item.assert_any_call(Key={'id': KEY}) 55 | self.assertEqual(setting, None) 56 | 57 | 58 | def test_application_settings(self, patched_env, patched_resource, patched_client): 59 | """ 60 | Test the application_settings function 61 | """ 62 | from chalicelib import settings 63 | mocked_req = MagicMock() 64 | mocked_req.method = "PUT" 65 | put_setting_mock = MagicMock() 66 | original_put_setting = settings.put_setting 67 | settings.put_setting = put_setting_mock 68 | settings.application_settings(mocked_req, KEY) 69 | settings.put_setting.assert_called_once() 70 | settings.put_setting = original_put_setting 71 | mocked_req.method = "DELETE" 72 | settings.application_settings(mocked_req, KEY) 73 | settings.DYNAMO_RESOURCE.Table.assert_called_once_with('settings_table') 74 | settings.DYNAMO_RESOURCE.Table.return_value.delete_item.assert_any_call(Key={'id': KEY}) 75 | mocked_req.method = "GET" 76 | get_setting_mock = MagicMock() 77 | original_get_setting = settings.get_setting 78 | settings.get_setting = get_setting_mock 79 | settings.application_settings(mocked_req, KEY) 80 | settings.get_setting.assert_called_once() 81 | settings.get_setting = original_get_setting 82 | with patch.object(settings, 'get_setting', side_effect=CLIENT_ERROR): 83 | result = settings.application_settings(mocked_req, KEY) 84 | self.assertTrue("exception" in result) 85 | -------------------------------------------------------------------------------- /source/tools/autopep8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | autopep8 -i -a -a -r . 7 | -------------------------------------------------------------------------------- /source/tools/copy_table.py: -------------------------------------------------------------------------------- 1 | """ 2 | This program is responsible for copying DynamoDB tables, 3 | and is used for backups and migrations in MSAM. 4 | """ 5 | 6 | import argparse 7 | import boto3 8 | 9 | def main(): 10 | """ 11 | This is the entry point for the program 12 | """ 13 | try: 14 | parser = argparse.ArgumentParser(description='Copy database items from one DynamoDB table to another.') 15 | parser.add_argument('--source', required=True, help='source table name') 16 | parser.add_argument('--destination', required=True, help='destination table name') 17 | parser.add_argument('--region', default='us-west-2', help='the region where the tables reside (if not provided, default is us-west-2)') 18 | parser.add_argument('--profile', default='default', help='the AWS profile to use (if not provided, default profile is used)') 19 | 20 | args = parser.parse_args() 21 | 22 | session = boto3.Session(profile_name=args.profile, region_name=args.region) 23 | dynamo_resource = session.resource('dynamodb') 24 | source_table = dynamo_resource.Table(args.source) 25 | destination_table = dynamo_resource.Table(args.destination) 26 | 27 | scanned_items = [] 28 | response = source_table.scan() 29 | if "Items" in response: 30 | scanned_items = response["Items"] 31 | while "LastEvaluatedKey" in response: 32 | response = source_table.scan(ExclusiveStartKey=response['LastEvaluatedKey']) 33 | if "Items" in response: 34 | scanned_items = scanned_items + response["Items"] 35 | #print(scanned_items) 36 | 37 | for item in scanned_items: 38 | destination_table.put_item( 39 | Item=item 40 | ) 41 | except Exception as error: 42 | print(error) 43 | 44 | 45 | if __name__ == "__main__": 46 | main() 47 | -------------------------------------------------------------------------------- /source/tools/create_test_alarms.py: -------------------------------------------------------------------------------- 1 | """ 2 | This program generates CloudWatch Alarms to use with testing scale of MSAM. 3 | These are not valid alarms - they will not change state. 4 | """ 5 | 6 | import copy 7 | import json 8 | import time 9 | import boto3 10 | 11 | ALARM_TEMPLATE = { 12 | "AlarmName": "1193839-0 Active Alerts", 13 | "AlarmDescription": "1193839-0 Active Alerts", 14 | "ActionsEnabled": True, 15 | "OKActions": [ 16 | ], 17 | "AlarmActions": [ 18 | ], 19 | "InsufficientDataActions": [], 20 | "MetricName": "ActiveAlerts", 21 | "Namespace": "MediaLive", 22 | "Statistic": "Maximum", 23 | "Dimensions": [{ 24 | "Name": "ChannelId", 25 | "Value": "1193839" 26 | }, 27 | { 28 | "Name": "Pipeline", 29 | "Value": "0" 30 | } 31 | ], 32 | "Period": 10, 33 | "EvaluationPeriods": 1, 34 | "DatapointsToAlarm": 1, 35 | "Threshold": 1.0, 36 | "ComparisonOperator": "GreaterThanOrEqualToThreshold", 37 | "TreatMissingData": "missing" 38 | } 39 | 40 | TOTAL_ALARMS = 500 41 | 42 | client = boto3.client("cloudwatch") 43 | for index in range(TOTAL_ALARMS): 44 | print(index) 45 | alarm_configuration = copy.deepcopy(ALARM_TEMPLATE) 46 | alarm_configuration["AlarmName"] = f"MSAM Test Alarm {time.time()}" 47 | alarm_configuration["AlarmDescription"] = "MSAM Testing Only, Do Not Use" 48 | print(json.dumps(alarm_configuration)) 49 | response = client.put_metric_alarm(**alarm_configuration) 50 | print(json.dumps(response)) 51 | time.sleep(0.25) 52 | -------------------------------------------------------------------------------- /source/tools/create_test_alarms.sh: -------------------------------------------------------------------------------- 1 | AWS_PROFILE=personal AWS_DEFAULT_REGION=us-west-2 python create_test_alarms.py 2 | -------------------------------------------------------------------------------- /source/tools/delete_disconnected.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | """ 4 | This is a tool to clean nodes without connections from the content database table. 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | import boto3 11 | 12 | CONNECTION_TYPES = [ 13 | "cloudfront-distribution-medialive-input", "medialive-channel-mediapackage-channel", "medialive-channel-mediastore-container", "medialive-input-medialive-channel", 14 | "mediapackage-channel-mediapackage-origin-endpoint", "mediapackage-origin-endpoint-cloudfront-distribution", "mediastore-container-medialive-input", "s3-bucket-cloudfront-distribution", 15 | "s3-bucket-medialive-input", "mediapackage-origin-endpoint-speke-keyserver", "user-defined-connection" 16 | ] 17 | NODE_TYPES = ["s3", "cloudfront-distribution"] 18 | 19 | # 20 | # CONTENT_TABLE_NAME="IBC2018-DynamoDB-Content-17I5MRXA2FBF7" CACHE_ITEM_TTL=7200 python delete-disconnected.py 21 | # 22 | 23 | 24 | def delete_disconnected(): 25 | """ 26 | This function will clean nodes without connections from the content database table. 27 | """ 28 | node_type = "S3" 29 | nodes = [] 30 | connections = [] 31 | for node_type in NODE_TYPES: 32 | nodes = nodes + cached_by_service(node_type) 33 | for conn_type in CONNECTION_TYPES: 34 | connections = connections + cached_by_service(conn_type) 35 | remove_nodes = [] 36 | # scan for connections with 'to' or 'from' set with 37 | for node in nodes: 38 | found = False 39 | for conn in connections: 40 | if node["arn"] == conn["from"] or node["arn"] == conn["to"]: 41 | found = True 42 | if not found: 43 | remove_nodes.append(node) 44 | 45 | resource = boto3.resource("dynamodb") 46 | table = resource.Table(os.environ["CONTENT_TABLE_NAME"]) 47 | for node in remove_nodes: 48 | print(node) 49 | table.delete_item(Key={"arn": node["arn"]}) 50 | 51 | 52 | if __name__ == "__main__": 53 | sys.path.insert(0, '../api/msam') 54 | from chalicelib.cache import cached_by_service 55 | delete_disconnected() 56 | -------------------------------------------------------------------------------- /source/tools/docker_msam_browser_app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -f index.html ]; then 4 | echo "*** index.html is not here, are you in the right place? ***" 5 | else 6 | docker stop msam-browser-app 7 | docker container rm msam-browser-app 8 | docker run --name msam-browser-app -v `pwd`:/var/www/html:ro -d -p 38080:80 public.ecr.aws/ubuntu/nginx:latest 9 | fi 10 | -------------------------------------------------------------------------------- /source/tools/js-beautify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | find . -iname '*js' -type f -print | xargs -n 1 js-beautify -r 7 | -------------------------------------------------------------------------------- /source/tools/js-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # http://www.apache.org/licenses/LICENSE-2.0 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | # KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations 10 | # under the License. 11 | 12 | # Run this from the root of the html folder 13 | 14 | set -euo pipefail 15 | 16 | if [ ! -f index.html ]; then 17 | echo "*** index.html is not here, are you in the right place? ***" 18 | else 19 | echo 20 | echo ========================= 21 | echo JSHINT OUTPUT 22 | echo ========================= 23 | 24 | find js/app -name '*.js' -type f -print | xargs jshint 25 | 26 | echo 27 | echo ========================= 28 | echo ESLINT OUTPUT 29 | echo ========================= 30 | 31 | eslint js/app 32 | 33 | echo 34 | fi 35 | -------------------------------------------------------------------------------- /source/tools/jshint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # http://www.apache.org/licenses/LICENSE-2.0 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | # KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations 10 | # under the License. 11 | 12 | # Run this from the root of the html folder 13 | 14 | set -euo pipefail 15 | 16 | find . -name '*.js' -type f -print | \ 17 | grep --invert-match "/external/" | \ 18 | xargs -n 1 jshint 19 | -------------------------------------------------------------------------------- /source/tools/jslint-report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | find . -name '*js' -type f -print | xargs -n 1 jslint --browser --long --predef define --fudge --white --edition es6 > jslint-report.txt 7 | -------------------------------------------------------------------------------- /source/tools/pa11y.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail", 4 | "WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.Fail" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /source/tools/pylint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # http://www.apache.org/licenses/LICENSE-2.0 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | # KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations 10 | # under the License. 11 | 12 | # IGNORED RULES 13 | # E0401 Unable to import module (packaged via Lambda layers instead) 14 | # C0103 Constant name 15 | # C0301 Line too long 16 | # C0302 Too many lines in module 17 | # C0303 Trailing whitespace 18 | # C0411 Order of import 19 | # C0412 Grouping of import 20 | # C0413 Wrong import position (in unit tests) 21 | # R0201 Method could be a function (in unit tests) 22 | # R0801 Similar lines in files 23 | # R1702 Too many nested blocks 24 | # R0912 Too many branches 25 | # R0913 Too many arguments 26 | # R0914 Too many local variables 27 | # R0915 Too many statements 28 | # W0105 No effect string (comment in test) 29 | # W0401 Wildcard import 30 | # W0603 Global statement 31 | # W0621 Redefining name 32 | # W0703 Catching too general exception 33 | # W0613 Unused argument (like passing 'event' or 'context' into lambda) 34 | # W0640 Cell variable 35 | 36 | set -euo pipefail 37 | 38 | find . -iname '*.py' | \ 39 | grep "/package/" --invert-match | \ 40 | grep "/.aws-sam/" --invert-match | \ 41 | xargs pylint -d E0401,C0103,C0301,C0302,C0303,C0411,C0412,C0413,R0201,R0801,R1702,R0912,R0913,R0914,R0915,W0105,W0401,W0703,W0603,W0613,W0621,W0640 42 | -------------------------------------------------------------------------------- /source/tools/yapf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # http://www.apache.org/licenses/LICENSE-2.0 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | # KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations 10 | # under the License. 11 | 12 | find . -iname '*.py' -print0 | \ 13 | xargs -0 yapf -i --style='{based_on_style: pep8, join_multiple_lines: true, column_limit: 200, indent_width: 4}' 14 | -------------------------------------------------------------------------------- /source/web-cloudformation/.gitignore: -------------------------------------------------------------------------------- 1 | package/ 2 | *.zip -------------------------------------------------------------------------------- /source/web-cloudformation/cfn_invalidate_resource.py: -------------------------------------------------------------------------------- 1 | """ 2 | This custom resource is responsible for issuing an 3 | invalidation to CloudFront after the web content is changed. 4 | """ 5 | 6 | import logging 7 | import time 8 | 9 | import boto3 10 | from crhelper import CfnResource 11 | 12 | logger = logging.getLogger(__name__) 13 | helper = CfnResource() 14 | 15 | 16 | @helper.update 17 | def invalidate_on_update(event, _): 18 | """ 19 | This function issues an invalidation command to CloudFront. 20 | """ 21 | distribution_id = event['ResourceProperties']['DistributionId'] 22 | cloudfront = boto3.client("cloudfront") 23 | response = cloudfront.create_invalidation(DistributionId=distribution_id, 24 | InvalidationBatch={ 25 | 'Paths': { 26 | 'Quantity': 1, 27 | 'Items': [ 28 | '/*', 29 | ] 30 | }, 31 | 'CallerReference': 32 | str(int(time.time())) 33 | }) 34 | logger.info(response) 35 | 36 | 37 | def handler(event, context): 38 | """ 39 | This is the Lambda custom resource entry point. 40 | """ 41 | helper(event, context) 42 | -------------------------------------------------------------------------------- /source/web-cloudformation/makezip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | ORIGIN=`pwd` 7 | ZIP="$ORIGIN/webcontent_resource.zip" 8 | HTML_ZIP=$1 9 | 10 | 11 | 12 | if [ -d "package" ]; then 13 | rm -rf package 14 | fi 15 | 16 | rm -f $ZIP 17 | rm -f error.txt 18 | 19 | pip install --force-reinstall --target ./package requests crhelper urllib3==1.26.18 2> error.txt 20 | RETVAL=$? 21 | if [ "$RETVAL" -ne "0" ]; then 22 | echo "ERROR: System package installation failed." 23 | cat error.txt 24 | exit $RETVAL 25 | fi 26 | cd package 27 | zip -r9 $ZIP . 28 | cd $ORIGIN 29 | zip -g $ZIP cfn_bucket_loader.py cfn_invalidate_resource.py $HTML_ZIP 30 | rm $HTML_ZIP 31 | --------------------------------------------------------------------------------