├── source
├── msam
│ ├── __init__.py
│ ├── test
│ │ ├── __init__.py
│ │ ├── run_unit_tests.py
│ │ ├── test_content.py
│ │ ├── test_db.py
│ │ ├── test_settings.py
│ │ └── test_tags.py
│ ├── chalicelib
│ │ ├── __init__.py
│ │ ├── content.py
│ │ └── settings.py
│ ├── .coveragerc
│ ├── requirements.txt
│ ├── .gitignore
│ ├── db
│ │ └── makezip.sh
│ └── .chalice
│ │ ├── config.json
│ │ └── policy-dev.json
├── events
│ ├── __init__.py
│ ├── test
│ │ ├── __init__.py
│ │ ├── run_unit_tests.py
│ │ ├── test_cloudwatch_alarm.py
│ │ └── test_media_events.py
│ ├── .gitignore
│ ├── requirements.txt
│ └── cloudwatch_alarm.py
├── html
│ ├── .gitignore
│ ├── favicon.ico
│ ├── .jshintrc
│ ├── js
│ │ ├── app
│ │ │ ├── build-tmp.js
│ │ │ ├── build.js
│ │ │ ├── api_check.js
│ │ │ ├── ui
│ │ │ │ ├── alert.js
│ │ │ │ ├── confirmation.js
│ │ │ │ ├── overlays
│ │ │ │ │ ├── overlays.js
│ │ │ │ │ ├── alarms_only.js
│ │ │ │ │ ├── ec2_instance.js
│ │ │ │ │ ├── medialive_multiplex.js
│ │ │ │ │ ├── ssm_managed_instance.js
│ │ │ │ │ ├── medialive_channel.js
│ │ │ │ │ ├── mediaconnect_flow.js
│ │ │ │ │ └── overlay_tools.js
│ │ │ │ ├── help_menu.js
│ │ │ │ ├── util.js
│ │ │ │ ├── information_compartment.js
│ │ │ │ ├── informational_overlays.js
│ │ │ │ ├── layout.js
│ │ │ │ ├── status_view.js
│ │ │ │ ├── alarm_indicators.js
│ │ │ │ └── nodes_menu.js
│ │ │ ├── mappers
│ │ │ │ └── connections
│ │ │ │ │ ├── helper.js
│ │ │ │ │ ├── s3_bucket_medialive_input.js
│ │ │ │ │ ├── link_device_medialive_input.js
│ │ │ │ │ ├── multiplex_mediaconnect_flow.js
│ │ │ │ │ ├── s3_bucket_cloudfront.js
│ │ │ │ │ ├── s3_bucket_mediatailor_configuration.js
│ │ │ │ │ ├── mediastore_container_medialive_input.js
│ │ │ │ │ ├── medialive_channel_input.js
│ │ │ │ │ ├── medialive_channel_s3_bucket.js
│ │ │ │ │ ├── medialive_channel_mediaconnect_flow.js
│ │ │ │ │ ├── speke.js
│ │ │ │ │ ├── cloudfront_medialive_input.js
│ │ │ │ │ ├── mediastore_container_cloudfront.js
│ │ │ │ │ ├── mediastore_container_mediatailor_configuration.js
│ │ │ │ │ ├── mediaconnect_flow_medialive_input.js
│ │ │ │ │ ├── mediapackage_endpoint_cloudfront.js
│ │ │ │ │ ├── mediaconnect_flow_mediaconnect_flow.js
│ │ │ │ │ ├── medialive_channel_mediastore_container.js
│ │ │ │ │ ├── mediapackage_endpoint_mediatailor_configuration.js
│ │ │ │ │ ├── mediapackage_channel_endpoint.js
│ │ │ │ │ ├── medialive_channel_multiplex.js
│ │ │ │ │ ├── medialive_input_channel.js
│ │ │ │ │ └── medialive_channel_mediapackage_channel.js
│ │ │ ├── tools
│ │ │ │ ├── duplicate_names.js
│ │ │ │ ├── clear_http.js
│ │ │ │ └── build_numbers.js
│ │ │ ├── main.js
│ │ │ ├── cloudwatch_events.js
│ │ │ ├── regions.js
│ │ │ ├── settings.js
│ │ │ ├── notes.js
│ │ │ ├── server.js
│ │ │ ├── model.js
│ │ │ └── connections.js
│ │ └── test
│ │ │ ├── always.test.js
│ │ │ ├── api_check.test.js
│ │ │ ├── connections.test.js
│ │ │ └── alarms.test.js
│ ├── babel.config.json
│ ├── jest.config.js
│ ├── .eslintrc.json
│ └── package.json
├── web-cloudformation
│ ├── .gitignore
│ ├── makezip.sh
│ └── cfn_invalidate_resource.py
├── tools
│ ├── create_test_alarms.sh
│ ├── autopep8.sh
│ ├── pa11y.json
│ ├── js-beautify.sh
│ ├── jslint-report.sh
│ ├── docker_msam_browser_app.sh
│ ├── yapf.sh
│ ├── jshint.sh
│ ├── js-check.sh
│ ├── create_test_alarms.py
│ ├── pylint.sh
│ ├── copy_table.py
│ └── delete_disconnected.py
├── .coveragerc
└── cdk
│ ├── pretest.sh
│ ├── test
│ ├── media-services-application-mapper.test.ts
│ ├── msam-core.test.ts
│ ├── msam-events.test.ts
│ ├── msam-dynamodb.test.ts
│ ├── msam-iam-roles.test.ts
│ └── msam-browser-app.test.ts
│ ├── bin
│ └── media-services-application-mapper.ts
│ ├── README.md
│ ├── tsconfig.json
│ ├── package.json
│ └── cdk.json
├── docs
├── use-cases.jpg
├── logical-view.jpg
├── physical-view.jpg
├── deployment-view.jpg
├── github-workflows.jpg
├── images
│ ├── api-key.jpeg
│ ├── added-tile.png
│ ├── S3-diagram-tag.png
│ ├── channel-tiles.png
│ ├── custom-nodes.jpeg
│ ├── diagram-locked.png
│ ├── ec2-nodetype.png
│ ├── monitor-tab.jpeg
│ ├── removed-tile.png
│ ├── ssm-documents.png
│ ├── tiles-overview.png
│ ├── cfn-browser-url.jpeg
│ ├── cfn-browser-url.png
│ ├── diagram-complex.png
│ ├── diagram-unlocked.png
│ ├── diagram-with-EMT.png
│ ├── remember-button.jpeg
│ ├── simple-workflow.jpeg
│ ├── ssm-document-tag.png
│ ├── ack-iam-resources.png
│ ├── advanced-settings.jpeg
│ ├── advanced-settings.png
│ ├── alarm-channel-tiles.png
│ ├── cfn-core-endpoint.jpeg
│ ├── cfn-core-endpoint.png
│ ├── cfn-delete-stack.jpeg
│ ├── cfn-dynamodb-tables.png
│ ├── channel-tile-json.jpeg
│ ├── cloudwatch-diagram.jpeg
│ ├── cloudwatch-diagram.png
│ ├── complex-workflow.jpeg
│ ├── connection-settings.png
│ ├── diagram-nodes-edges.png
│ ├── pipeline-alerts-tab.png
│ ├── selected-item-json.jpeg
│ ├── service-content-db.png
│ ├── ssm-subscribe-alarm.png
│ ├── subscribe-alarms.jpeg
│ ├── tag-generated-tile.png
│ ├── usage-plan-stage.jpeg
│ ├── vertical-alignment.png
│ ├── workflow-complete.png
│ ├── cfn-dynamodb-tables.jpeg
│ ├── cloudfront-dns-error.png
│ ├── create-channel-tile.jpeg
│ ├── ec2-diagram-nodetype.png
│ ├── horizontal-alignment.png
│ ├── iam-template-outputs.png
│ ├── root-template-outputs.png
│ ├── ssm-custom-namespace.png
│ ├── ssm-managed-instances.png
│ ├── subscribed-alarms-tab.png
│ ├── workflow-diagram-tile.png
│ ├── workflow-video-source.png
│ ├── cloudwatch-channel-tile.jpeg
│ ├── connections-content-db.png
│ ├── master-template-params.png
│ ├── recent-cloudwatch-events.png
│ ├── ssm-elemental-live-tag.png
│ ├── ssm-live-encoder-metrics.png
│ ├── ssm-live-status-metric.png
│ ├── ssm-node-in-alarm-state.png
│ ├── ssm-node-update-interval.png
│ ├── tile-tag-input-channel.png
│ ├── workflow-basic-details.png
│ ├── workflow-diagram-layout.png
│ ├── workflow-video-outputs-1.png
│ ├── workflow-video-outputs-2.png
│ ├── api-gateway-documentation.png
│ ├── video-sources-diagram-tag.png
│ ├── mediapackage-cloudfront-tag.png
│ ├── ssm-node-subscribed-to-alarm.png
│ └── ssm-remove-alarm-notification.png
├── behav-frontend-update.jpg
├── behav-webtool-bootstrap.jpg
├── behav-general-api-invoke.jpg
├── behav-general-backend-notify.jpg
├── use-cases.drawio
├── logical-view.drawio
├── github-workflows.drawio
└── physical-view.drawio
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── deployment
├── requirements.txt
├── cdk-solution-helper
│ ├── package-lock.json
│ ├── package.json
│ └── README.md
├── venv_check.py
├── build-s3-dist-env.sh
├── test-release.py
├── reduce_contents.py
├── run-unit-tests.sh
└── deploy.sh
├── solution-manifest.yaml
├── .gitignore
├── CONTRIBUTING.md
└── CODE_OF_CONDUCT.md
/source/msam/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/events/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/msam/test/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/events/test/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/msam/chalicelib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/html/.gitignore:
--------------------------------------------------------------------------------
1 | *.zip
2 | *lcov*
3 |
--------------------------------------------------------------------------------
/source/web-cloudformation/.gitignore:
--------------------------------------------------------------------------------
1 | package/
2 | *.zip
--------------------------------------------------------------------------------
/source/events/.gitignore:
--------------------------------------------------------------------------------
1 | .aws-sam/
2 | package/
3 | events.zip
--------------------------------------------------------------------------------
/source/events/requirements.txt:
--------------------------------------------------------------------------------
1 | networkx
2 | jsonpath_ng
3 |
--------------------------------------------------------------------------------
/source/msam/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | relative_files = True
3 | source = .
4 | omit = *test*
5 |
--------------------------------------------------------------------------------
/source/tools/create_test_alarms.sh:
--------------------------------------------------------------------------------
1 | AWS_PROFILE=personal AWS_DEFAULT_REGION=us-west-2 python create_test_alarms.py
2 |
--------------------------------------------------------------------------------
/docs/use-cases.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/use-cases.jpg
--------------------------------------------------------------------------------
/docs/logical-view.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/logical-view.jpg
--------------------------------------------------------------------------------
/docs/physical-view.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/physical-view.jpg
--------------------------------------------------------------------------------
/source/msam/requirements.txt:
--------------------------------------------------------------------------------
1 | networkx
2 | jsonpath_ng
3 | stringcase
4 | requests
5 | defusedxml
6 | crhelper
7 | urllib3<2
8 |
--------------------------------------------------------------------------------
/docs/deployment-view.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/deployment-view.jpg
--------------------------------------------------------------------------------
/docs/github-workflows.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/github-workflows.jpg
--------------------------------------------------------------------------------
/docs/images/api-key.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/api-key.jpeg
--------------------------------------------------------------------------------
/source/html/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/source/html/favicon.ico
--------------------------------------------------------------------------------
/docs/images/added-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/added-tile.png
--------------------------------------------------------------------------------
/source/msam/.gitignore:
--------------------------------------------------------------------------------
1 | .chalice/deployments/
2 | .chalice/venv/
3 | __pycache__/
4 | db/package/
5 | build/msam-core-release.template
6 |
--------------------------------------------------------------------------------
/docs/behav-frontend-update.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/behav-frontend-update.jpg
--------------------------------------------------------------------------------
/docs/images/S3-diagram-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/S3-diagram-tag.png
--------------------------------------------------------------------------------
/docs/images/channel-tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/channel-tiles.png
--------------------------------------------------------------------------------
/docs/images/custom-nodes.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/custom-nodes.jpeg
--------------------------------------------------------------------------------
/docs/images/diagram-locked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/diagram-locked.png
--------------------------------------------------------------------------------
/docs/images/ec2-nodetype.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ec2-nodetype.png
--------------------------------------------------------------------------------
/docs/images/monitor-tab.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/monitor-tab.jpeg
--------------------------------------------------------------------------------
/docs/images/removed-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/removed-tile.png
--------------------------------------------------------------------------------
/docs/images/ssm-documents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-documents.png
--------------------------------------------------------------------------------
/docs/images/tiles-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/tiles-overview.png
--------------------------------------------------------------------------------
/docs/behav-webtool-bootstrap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/behav-webtool-bootstrap.jpg
--------------------------------------------------------------------------------
/docs/images/cfn-browser-url.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-browser-url.jpeg
--------------------------------------------------------------------------------
/docs/images/cfn-browser-url.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-browser-url.png
--------------------------------------------------------------------------------
/docs/images/diagram-complex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/diagram-complex.png
--------------------------------------------------------------------------------
/docs/images/diagram-unlocked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/diagram-unlocked.png
--------------------------------------------------------------------------------
/docs/images/diagram-with-EMT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/diagram-with-EMT.png
--------------------------------------------------------------------------------
/docs/images/remember-button.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/remember-button.jpeg
--------------------------------------------------------------------------------
/docs/images/simple-workflow.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/simple-workflow.jpeg
--------------------------------------------------------------------------------
/docs/images/ssm-document-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-document-tag.png
--------------------------------------------------------------------------------
/docs/behav-general-api-invoke.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/behav-general-api-invoke.jpg
--------------------------------------------------------------------------------
/docs/images/ack-iam-resources.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ack-iam-resources.png
--------------------------------------------------------------------------------
/docs/images/advanced-settings.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/advanced-settings.jpeg
--------------------------------------------------------------------------------
/docs/images/advanced-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/advanced-settings.png
--------------------------------------------------------------------------------
/docs/images/alarm-channel-tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/alarm-channel-tiles.png
--------------------------------------------------------------------------------
/docs/images/cfn-core-endpoint.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-core-endpoint.jpeg
--------------------------------------------------------------------------------
/docs/images/cfn-core-endpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-core-endpoint.png
--------------------------------------------------------------------------------
/docs/images/cfn-delete-stack.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-delete-stack.jpeg
--------------------------------------------------------------------------------
/docs/images/cfn-dynamodb-tables.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-dynamodb-tables.png
--------------------------------------------------------------------------------
/docs/images/channel-tile-json.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/channel-tile-json.jpeg
--------------------------------------------------------------------------------
/docs/images/cloudwatch-diagram.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cloudwatch-diagram.jpeg
--------------------------------------------------------------------------------
/docs/images/cloudwatch-diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cloudwatch-diagram.png
--------------------------------------------------------------------------------
/docs/images/complex-workflow.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/complex-workflow.jpeg
--------------------------------------------------------------------------------
/docs/images/connection-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/connection-settings.png
--------------------------------------------------------------------------------
/docs/images/diagram-nodes-edges.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/diagram-nodes-edges.png
--------------------------------------------------------------------------------
/docs/images/pipeline-alerts-tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/pipeline-alerts-tab.png
--------------------------------------------------------------------------------
/docs/images/selected-item-json.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/selected-item-json.jpeg
--------------------------------------------------------------------------------
/docs/images/service-content-db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/service-content-db.png
--------------------------------------------------------------------------------
/docs/images/ssm-subscribe-alarm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-subscribe-alarm.png
--------------------------------------------------------------------------------
/docs/images/subscribe-alarms.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/subscribe-alarms.jpeg
--------------------------------------------------------------------------------
/docs/images/tag-generated-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/tag-generated-tile.png
--------------------------------------------------------------------------------
/docs/images/usage-plan-stage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/usage-plan-stage.jpeg
--------------------------------------------------------------------------------
/docs/images/vertical-alignment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/vertical-alignment.png
--------------------------------------------------------------------------------
/docs/images/workflow-complete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-complete.png
--------------------------------------------------------------------------------
/docs/behav-general-backend-notify.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/behav-general-backend-notify.jpg
--------------------------------------------------------------------------------
/docs/images/cfn-dynamodb-tables.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cfn-dynamodb-tables.jpeg
--------------------------------------------------------------------------------
/docs/images/cloudfront-dns-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cloudfront-dns-error.png
--------------------------------------------------------------------------------
/docs/images/create-channel-tile.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/create-channel-tile.jpeg
--------------------------------------------------------------------------------
/docs/images/ec2-diagram-nodetype.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ec2-diagram-nodetype.png
--------------------------------------------------------------------------------
/docs/images/horizontal-alignment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/horizontal-alignment.png
--------------------------------------------------------------------------------
/docs/images/iam-template-outputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/iam-template-outputs.png
--------------------------------------------------------------------------------
/docs/images/root-template-outputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/root-template-outputs.png
--------------------------------------------------------------------------------
/docs/images/ssm-custom-namespace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-custom-namespace.png
--------------------------------------------------------------------------------
/docs/images/ssm-managed-instances.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-managed-instances.png
--------------------------------------------------------------------------------
/docs/images/subscribed-alarms-tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/subscribed-alarms-tab.png
--------------------------------------------------------------------------------
/docs/images/workflow-diagram-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-diagram-tile.png
--------------------------------------------------------------------------------
/docs/images/workflow-video-source.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-video-source.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/docs/images/cloudwatch-channel-tile.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/cloudwatch-channel-tile.jpeg
--------------------------------------------------------------------------------
/docs/images/connections-content-db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/connections-content-db.png
--------------------------------------------------------------------------------
/docs/images/master-template-params.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/master-template-params.png
--------------------------------------------------------------------------------
/docs/images/recent-cloudwatch-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/recent-cloudwatch-events.png
--------------------------------------------------------------------------------
/docs/images/ssm-elemental-live-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-elemental-live-tag.png
--------------------------------------------------------------------------------
/docs/images/ssm-live-encoder-metrics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-live-encoder-metrics.png
--------------------------------------------------------------------------------
/docs/images/ssm-live-status-metric.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-live-status-metric.png
--------------------------------------------------------------------------------
/docs/images/ssm-node-in-alarm-state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-node-in-alarm-state.png
--------------------------------------------------------------------------------
/docs/images/ssm-node-update-interval.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-node-update-interval.png
--------------------------------------------------------------------------------
/docs/images/tile-tag-input-channel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/tile-tag-input-channel.png
--------------------------------------------------------------------------------
/docs/images/workflow-basic-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-basic-details.png
--------------------------------------------------------------------------------
/docs/images/workflow-diagram-layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-diagram-layout.png
--------------------------------------------------------------------------------
/docs/images/workflow-video-outputs-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-video-outputs-1.png
--------------------------------------------------------------------------------
/docs/images/workflow-video-outputs-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/workflow-video-outputs-2.png
--------------------------------------------------------------------------------
/docs/images/api-gateway-documentation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/api-gateway-documentation.png
--------------------------------------------------------------------------------
/docs/images/video-sources-diagram-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/video-sources-diagram-tag.png
--------------------------------------------------------------------------------
/docs/images/mediapackage-cloudfront-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/mediapackage-cloudfront-tag.png
--------------------------------------------------------------------------------
/docs/images/ssm-node-subscribed-to-alarm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-node-subscribed-to-alarm.png
--------------------------------------------------------------------------------
/docs/images/ssm-remove-alarm-notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-solutions/media-services-application-mapper/main/docs/images/ssm-remove-alarm-notification.png
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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-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-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/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/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-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/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/.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 | }
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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 = `
${prefix}MSAM Version
`;
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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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_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/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/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_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_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/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/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/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/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_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/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/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/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/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/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/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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=
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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==
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 | Component |
26 | Version |
27 |
28 |
29 |
30 |
31 | | 1 |
32 | Browser Application |
33 | ${app_stamp} |
34 |
35 |
36 | | 2 |
37 | Endpoint API |
38 | ${response.version} |
39 |
40 |
41 |
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/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/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 |
--------------------------------------------------------------------------------
/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=
--------------------------------------------------------------------------------
/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/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/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/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/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 | | Configuration |
49 |
50 |
51 | ${configuration_stats}
52 |
53 | |
54 |
55 |
56 | | Model Contents |
57 |
58 |
61 | |
62 |
63 |
64 |
`;
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 |
--------------------------------------------------------------------------------
/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)
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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=
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/msam/test/test_tags.py:
--------------------------------------------------------------------------------
1 | """
2 | This module is provides unit tests for the tags.py module.
3 | """
4 |
5 | # pylint: disable=C0415,W0201
6 | import unittest
7 | from unittest.mock import MagicMock, patch
8 | from botocore.exceptions import ClientError
9 |
10 | class TestTags(unittest.TestCase):
11 | """
12 | This class extends TestCase with testing functions
13 | """
14 |
15 | @patch('os.environ')
16 | @patch('boto3.resource')
17 | def test_update_diagrams(self, patched_resource, patched_env):
18 | """
19 | Test the update_diagrams function
20 | """
21 | from chalicelib import tags
22 | from chalicelib import settings
23 | # diagram does not exist
24 | data = "{\"Tags\": {\"MSAM-Diagram\": \"new-diagram\"}}"
25 | mock_table = MagicMock()
26 | mock_table.scan.return_value = {"Items": [{"data": data, "arn": "some-arn"}]}
27 | patched_resource.return_value.Table.return_value = mock_table
28 | tags.update_diagrams()
29 |
30 | # diagram exists
31 | with patch.object(settings, 'get_setting', return_value = [{"name": "new-diagram", "view_id": "NewDiagram"}]):
32 | tags.update_diagrams()
33 | with patch.object(settings, 'get_setting',
34 | side_effect=ClientError({"Error": {"Code": "400", "Message": "SomeClientError"}}, "get_setting")):
35 | tags.update_diagrams()
36 | self.assertRaises(ClientError)
37 | self.assertEqual(tags.boto3.resource.call_count, 3)
38 | tags.boto3.resource.assert_any_call('dynamodb', config=tags.MSAM_BOTO3_CONFIG)
39 | self.assertEqual(tags.boto3.resource.return_value.Table.call_count, 3)
40 | tags.boto3.resource.return_value.Table.assert_any_call('content_table')
41 | self.assertEqual(tags.boto3.resource.return_value.Table.return_value.scan.call_count, 3)
42 | tags.boto3.resource.return_value.Table.return_value.scan.assert_any_call(
43 | FilterExpression="contains(#data, :tagname)",
44 | ExpressionAttributeNames={"#data": "data"},
45 | ExpressionAttributeValues={":tagname": "MSAM-Diagram"})
46 |
47 | @patch('os.environ')
48 | @patch('boto3.resource')
49 | def test_update_tiles(self, patched_resource, patched_env):
50 | """
51 | Test the update_tiles function
52 | """
53 | from chalicelib import tags
54 | from chalicelib import channels
55 |
56 | data = "{\"Tags\": {\"MSAM-Tile\": \"new-tile\"}}"
57 | mock_table = MagicMock()
58 | mock_table.scan.return_value = {"Items": [{"data": data, "arn": "some-arn"}]}
59 | patched_resource.return_value.Table.return_value = mock_table
60 | tags.update_tiles()
61 |
62 | with patch.object(channels, 'get_channel_nodes', return_value = [{"name": "new-tile", "id": "newtile"}]):
63 | tags.update_tiles()
64 | with patch.object(channels, 'get_channel_nodes',
65 | side_effect=ClientError({"Error": {"Code": "400", "Message": "SomeClientError"}}, "get_channel_nodes")):
66 | tags.update_tiles()
67 | self.assertRaises(ClientError)
68 | self.assertEqual(tags.boto3.resource.call_count, 3)
69 | tags.boto3.resource.assert_any_call('dynamodb', config=tags.MSAM_BOTO3_CONFIG)
70 | self.assertEqual(tags.boto3.resource.return_value.Table.call_count, 3)
71 | tags.boto3.resource.return_value.Table.assert_any_call('content_table')
72 | self.assertEqual(tags.boto3.resource.return_value.Table.return_value.scan.call_count, 3)
73 | tags.boto3.resource.return_value.Table.return_value.scan.assert_any_call(
74 | FilterExpression="contains(#data, :tagname)",
75 | ExpressionAttributeNames={"#data": "data"},
76 | ExpressionAttributeValues={":tagname": "MSAM-Tile"})
77 |
--------------------------------------------------------------------------------