├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── SECURITY.md ├── deployment ├── build-s3-dist.sh ├── cdk-solution-helper │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json └── get-cdk-version.js └── source ├── .eslintignore ├── .prettierignore ├── .prettierrc.yml ├── email-templates ├── processing_complete.email.template └── processing_failure.email.template ├── images ├── architecture.png └── ui-components.png ├── infrastructure ├── .gitignore ├── .npmignore ├── bin │ └── dus.ts ├── cdk.json ├── jest.config.js ├── lib │ ├── api │ │ ├── case-manager.ts │ │ ├── model-schema │ │ │ ├── case-response.ts │ │ │ ├── create-case-body.ts │ │ │ ├── document-response.ts │ │ │ ├── index.ts │ │ │ ├── redact-request-body.ts │ │ │ └── upload-document-body.ts │ │ ├── request-processor.ts │ │ ├── rest-api-documentation │ │ │ ├── api-documentation.ts │ │ │ ├── case.ts │ │ │ ├── cases.ts │ │ │ ├── document.ts │ │ │ ├── inferences.ts │ │ │ ├── redact.ts │ │ │ ├── root.ts │ │ │ └── search.ts │ │ └── rest-endpoint.ts │ ├── application-setup.ts │ ├── dus-stack.ts │ ├── govcloud │ │ └── cfn-resource-observer.ts │ ├── layers │ │ ├── java-user-agent.ts │ │ ├── node-user-agent.ts │ │ ├── python-user-agent.ts │ │ ├── runtime-libs.ts │ │ └── shared-lib.ts │ ├── metrics │ │ └── custom-dashboard.ts │ ├── notifications │ │ └── notification-manager.ts │ ├── s3web │ │ ├── static-site.ts │ │ └── ui-asset.ts │ ├── samples │ │ └── copy-samples.ts │ ├── search │ │ ├── indexed-storage-params.ts │ │ ├── indexed-storage.ts │ │ ├── kendra-case-storage.ts │ │ └── open-search-case-storage.ts │ ├── ui-infrastructure.ts │ ├── utils │ │ ├── app-registry-aspects.ts │ │ ├── asset-bundling.ts │ │ ├── aws-deployment-partition-aspects.ts │ │ ├── cfn-nag-suppressions.ts │ │ ├── common-utils.ts │ │ ├── constants.ts │ │ ├── custom-infra-setup.ts │ │ ├── lambda-aspect.ts │ │ ├── lambda-runtimes.ts │ │ ├── nested-stack-parameters.ts │ │ └── solution-helper.ts │ ├── vpc │ │ └── vpc-stack.ts │ └── workflow │ │ ├── entity-detection │ │ └── entity-detection-workflow.ts │ │ ├── fragments │ │ ├── failure-fragment.ts │ │ └── stepfunction-callbacks.ts │ │ ├── redaction │ │ └── redaction-workflow.ts │ │ ├── standard │ │ ├── abstract-workflow.ts │ │ └── standard-workflow.ts │ │ ├── textract │ │ └── textract-workflow.ts │ │ └── workflow-config.ts ├── package-lock.json ├── package.json ├── test │ ├── api │ │ ├── case-manager.test.ts │ │ ├── request-processor.test.ts │ │ ├── rest-api-documentation │ │ │ └── api-documentation.test.ts │ │ └── rest-end-point.test.ts │ ├── application-setup.test.ts │ ├── dus-stack.test.ts │ ├── framework │ │ ├── Dockerfile │ │ ├── cdk-to-asl.ts │ │ ├── sfn-client-wrapper.ts │ │ ├── sfn-local-container.ts │ │ ├── sfn-mock-config-validator.ts │ │ ├── sfn-test-builder.ts │ │ └── test │ │ │ ├── cdk-to-asl.test.ts │ │ │ ├── sfn-client-wrapper.test.ts │ │ │ ├── sfn-local-container.test.ts │ │ │ ├── sfn-mock-config-validator.test.ts │ │ │ └── sfn-test-builder.test.ts │ ├── govcloud │ │ └── cfn-resource-observer.test.ts │ ├── integration │ │ ├── entity-detection │ │ │ ├── integ-test-data │ │ │ │ ├── entity-detection-workflow.integ.mockconfig.json │ │ │ │ ├── expected-test-outputs.json │ │ │ │ └── input-data.json │ │ │ └── integ.entity-detection-workflow.test.ts │ │ ├── redaction │ │ │ ├── integ-test-data │ │ │ │ ├── expected-test-outputs.json │ │ │ │ ├── input-data.json │ │ │ │ └── redaction-workflow.integ.mockconfig.json │ │ │ └── integ.redaction-workflow.test.ts │ │ └── textract │ │ │ ├── integ-test-data │ │ │ ├── expected-test-outputs.json │ │ │ ├── input-data.json │ │ │ ├── makefile │ │ │ └── textract-workflow.integ.mockconfig.json │ │ │ └── integ.textract-workflow.test.ts │ ├── layers │ │ ├── java-user-agent.test.ts │ │ ├── node-user-agent.test.ts │ │ ├── python-user-agent.test.ts │ │ ├── runtime-lib.test.ts │ │ └── shared-lib.test.ts │ ├── metrics │ │ └── custom-dashboard.test.ts │ ├── mock-lambda-func │ │ ├── .gitignore │ │ ├── infrastructure │ │ │ └── test │ │ │ │ └── mock-lambda-func │ │ │ │ └── python-lambda │ │ │ │ ├── poetry.lock │ │ │ │ └── pyproject.toml │ │ ├── java-lambda │ │ │ ├── checkstyle.xml │ │ │ ├── pom.xml │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── java │ │ │ │ └── example │ │ │ │ └── Handler.java │ │ ├── node-lambda │ │ │ ├── package-lock.json │ │ │ └── package.json │ │ └── python-lambda │ │ │ ├── .gitignore │ │ │ ├── __init__.py │ │ │ ├── function.py │ │ │ ├── poetry.lock │ │ │ └── pyproject.toml │ ├── mock-ui │ │ ├── .gitignore │ │ ├── package-lock.json │ │ ├── package.json │ │ └── public │ │ │ └── index.html │ ├── notifications │ │ └── notification-manager.test.ts │ ├── s3web │ │ ├── static-site.test.ts │ │ └── ui-asset.test.ts │ ├── samples │ │ └── copy_samples.test.ts │ ├── search │ │ ├── indexed-storage.test.ts │ │ ├── kendra-case-storage.test.ts │ │ └── open-search-case-storage.test.ts │ ├── ui-infrstructure.test.ts │ ├── utils │ │ ├── app-registry.test.ts │ │ ├── asset-bundling.test.ts │ │ ├── aws-deployment-partition-aspect.test.ts │ │ ├── cfn-nag-suppressions.test.ts │ │ ├── common-utils.test.ts │ │ ├── custom-infra-setup.test.ts │ │ ├── lambda-aspect.test.ts │ │ ├── lambda-runtimes.test.ts │ │ └── solution-helper.test.ts │ ├── vpc │ │ └── vpc-stack.test.ts │ └── workflow │ │ ├── entity-detection │ │ └── entity-detection-workflow.test.ts │ │ ├── fragments │ │ └── failure-fragment.test.ts │ │ ├── redaction │ │ └── redaction-workflow.test.ts │ │ ├── standard │ │ └── standard-workflow-state-machine.test.ts │ │ ├── textract │ │ └── textract-workflow.test.ts │ │ └── workflow-config.test.ts └── tsconfig.json ├── lambda ├── .eslintrc.js ├── create-presigned-url │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── index.spec.js │ │ └── utils │ │ │ ├── env-setup.spec.js │ │ │ └── generate-signed-url.spec.js │ └── utils │ │ ├── env-setup.js │ │ └── generate-signed-url.js ├── custom-resource │ ├── .coveragerc │ ├── .gitignore │ ├── __init__.py │ ├── cfn_response.py │ ├── lambda_func.py │ ├── lambda_ops_metrics.py │ ├── operations │ │ ├── __init__.py │ │ ├── anonymous_metrics.py │ │ ├── copy_email_templates.py │ │ ├── copy_sample_documents.py │ │ ├── copy_web_ui.py │ │ ├── copy_workflow_config_to_ddb.py │ │ ├── cw_loggroup_policy.py │ │ ├── gen_uuid.py │ │ ├── operation_types.py │ │ ├── shared.py │ │ ├── update_s3_policy.py │ │ └── webconfig.py │ ├── poetry.lock │ ├── pyproject.toml │ ├── test │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── fixtures │ │ │ ├── __init__.py │ │ │ ├── anonymous_metrics_events.py │ │ │ ├── copy_sample_documents.py │ │ │ ├── copy_template_events.py │ │ │ ├── copy_web_ui_events.py │ │ │ ├── copy_workflow_config_events.py │ │ │ ├── cw_loggroup_policy_events.py │ │ │ ├── gen_uuid_events.py │ │ │ ├── update_s3_policy_events.py │ │ │ └── webconfig_events.py │ │ ├── operations │ │ │ ├── __init__.py │ │ │ ├── test_anonymous_metric.py │ │ │ ├── test_copy_email_templates.py │ │ │ ├── test_copy_samples.py │ │ │ ├── test_copy_web_ui.py │ │ │ ├── test_copy_workflow_config_to_ddb.py │ │ │ ├── test_cw_loggroup_policy.py │ │ │ ├── test_gen_uuid.py │ │ │ ├── test_shared.py │ │ │ ├── test_update_s3_policy.py │ │ │ └── test_webconfig.py │ │ ├── test_lambda_func.py │ │ ├── test_lambda_ops_metrics.py │ │ └── utils │ │ │ ├── __init__.py │ │ │ ├── test_lambda_context_parser.py │ │ │ ├── test_metrics.py │ │ │ └── test_metrics_payload.py │ └── utils │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── data.py │ │ ├── lambda_context_parser.py │ │ ├── metrics.py │ │ └── metrics_payload.py ├── entity-detection │ ├── entity-detection-sync.js │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── entity-detection-sync.spec.js │ │ ├── event-test-data.js │ │ └── utils │ │ │ ├── entity │ │ │ ├── entity-context.spec.js │ │ │ ├── entity-detector.spec.js │ │ │ ├── generic-entity-detection-strategy.spec.js │ │ │ ├── medical-entity-detection-strategy.spec.js │ │ │ └── pii-entity-detection-stategy.spec.js │ │ │ ├── generic.spec.js │ │ │ └── sync.spec.js │ └── utils │ │ ├── entity │ │ ├── entity-context.js │ │ ├── entity-detector.js │ │ ├── generic-entity-detection-strategy.js │ │ ├── medical-entity-detection-strategy.js │ │ └── pii-entity-detection-strategy.js │ │ ├── generic.js │ │ └── sync.js ├── fetch-records │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── index.spec.js │ │ └── utils │ │ │ ├── doc-access-checkers.spec.js │ │ │ ├── env-setup.spec.js │ │ │ ├── fetch-case.spec.js │ │ │ └── fetch-document.spec.js │ └── utils │ │ ├── doc-access-checkers.js │ │ ├── env-setup.js │ │ ├── fetch-case.js │ │ └── fetch-document.js ├── get-inferences │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── index.spec.js │ │ └── utils │ │ │ └── get-inferences.spec.js │ └── utils │ │ └── get-inferences.js ├── layers │ ├── aws-node-user-agent-config │ │ ├── index.js │ │ ├── jest.config.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── test │ │ │ └── index.spec.js │ ├── aws-sdk-lib │ │ ├── package-lock.json │ │ └── package.json │ ├── aws_boto3 │ │ ├── .gitignore │ │ ├── poetry.lock │ │ └── pyproject.toml │ ├── common-node-lib │ │ ├── aoss │ │ │ └── aoss-proxy.js │ │ ├── batch-job │ │ │ └── initiate-job.js │ │ ├── cognito │ │ │ └── decode-jwt-token.js │ │ ├── constants.js │ │ ├── ddb │ │ │ ├── ddb-case-update.js │ │ │ └── ddb-get-case.js │ │ ├── event-bridge │ │ │ └── event-dispatcher.js │ │ ├── index.js │ │ ├── jest.config.js │ │ ├── metrics │ │ │ ├── case-status.js │ │ │ ├── cloudwatch.js │ │ │ ├── documents.js │ │ │ ├── file-types.js │ │ │ ├── utils │ │ │ │ └── send-metrics.js │ │ │ └── workflows.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── response-formatter │ │ │ ├── error-response.js │ │ │ └── success-response.js │ │ ├── s3 │ │ │ ├── get-request-account-id.js │ │ │ ├── s3-download.js │ │ │ ├── s3-get-head-object.js │ │ │ ├── s3-get-tags.js │ │ │ ├── s3-inferences.js │ │ │ └── s3-upload.js │ │ ├── sqs │ │ │ └── sqs-poller.js │ │ ├── stepfunctions │ │ │ └── task-notify.js │ │ ├── test │ │ │ ├── aoss │ │ │ │ └── aoss-proxy.spec.js │ │ │ ├── batch-job │ │ │ │ └── initiate-job.spec.js │ │ │ ├── cognito │ │ │ │ └── decode-jwt-token.spec.js │ │ │ ├── ddb │ │ │ │ ├── ddb-case-update.spec.js │ │ │ │ └── ddb-get-case.spec.js │ │ │ ├── event-bridge │ │ │ │ └── event-dispatcher.spec.js │ │ │ ├── event-test-data.js │ │ │ ├── metrics │ │ │ │ ├── case-status.spec.js │ │ │ │ ├── cloudwatch.spec.js │ │ │ │ ├── documents.spec.js │ │ │ │ ├── file-types.spec.js │ │ │ │ ├── utils │ │ │ │ │ └── send-metrics.spec.js │ │ │ │ └── workflows.spec.js │ │ │ ├── response-formatter │ │ │ │ ├── error-response.spec.js │ │ │ │ └── success-response.spec.js │ │ │ ├── s3 │ │ │ │ ├── get-request-account-id.spec.js │ │ │ │ ├── s3-download.spec.js │ │ │ │ ├── s3-get-tags.spec.js │ │ │ │ ├── s3-inferences.spec.js │ │ │ │ └── s3-upload.spec.js │ │ │ ├── sqs │ │ │ │ └── sqs-poller.spec.js │ │ │ ├── stepfunctions │ │ │ │ └── stepfunctions-task-notify.spec.js │ │ │ └── utils │ │ │ │ ├── compare-maps.spec.js │ │ │ │ └── validate-case-access.spec.js │ │ └── utils │ │ │ ├── compare-maps.js │ │ │ └── validate-case-access.js │ ├── custom-java-sdk-config │ │ ├── checkstyle.xml │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── builder │ │ │ │ │ └── config │ │ │ │ │ └── CustomUserAgentConfig.java │ │ │ └── resources │ │ │ │ └── log4j2.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── builder │ │ │ └── config │ │ │ └── CustomUserAgentConfigTest.java │ └── custom_boto3_init │ │ ├── .coveragerc │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── custom_config.py │ │ ├── helper.py │ │ ├── poetry.lock │ │ ├── pyproject.toml │ │ ├── setup.py │ │ └── test │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_layer_env_config.py │ │ └── test_service_instance_creation.py ├── redact-content │ ├── checkstyle.xml │ ├── pom.xml │ ├── resources │ │ └── log4j2.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── builder │ │ │ └── lambda │ │ │ ├── RedactionApiHandler.java │ │ │ ├── RedactionSfnHandler.java │ │ │ ├── model │ │ │ ├── ApiRequestBody.java │ │ │ ├── BoundingBox.java │ │ │ ├── Document.java │ │ │ ├── EntityDetails.java │ │ │ ├── EventDataBody.java │ │ │ ├── EventDataDocument.java │ │ │ ├── EventDataInput.java │ │ │ ├── FileType.java │ │ │ ├── PhraseRedaction.java │ │ │ ├── TextractBlock.java │ │ │ ├── TextractDetectText.java │ │ │ ├── TextractGeometry.java │ │ │ └── TextractRelationship.java │ │ │ └── utils │ │ │ ├── ApiRequestProcessor.java │ │ │ ├── CloudWatchMetrics.java │ │ │ ├── Constants.java │ │ │ ├── DependencyFactory.java │ │ │ ├── FileUtils.java │ │ │ ├── ImageRedactor.java │ │ │ ├── LambdaContextParser.java │ │ │ ├── PdfRedactor.java │ │ │ ├── PhraseFinder.java │ │ │ ├── Redactor.java │ │ │ ├── RequestProcessor.java │ │ │ ├── S3Storage.java │ │ │ ├── SfnRequestProcessor.java │ │ │ └── StepFunctionConnector.java │ │ └── test │ │ └── java │ │ ├── com │ │ └── builder │ │ │ └── lambda │ │ │ ├── RedactionApiHandlerTest.java │ │ │ ├── RedactionSfnHandlerTest.java │ │ │ ├── model │ │ │ ├── ApiRequestBodyTest.java │ │ │ ├── BoundingBoxTest.java │ │ │ ├── DocumentTest.java │ │ │ ├── EntityDetailsTest.java │ │ │ ├── EventDataBodyTest.java │ │ │ ├── EventDataDocumentTest.java │ │ │ ├── EventDataInputTest.java │ │ │ ├── PhraseRedactionTest.java │ │ │ ├── TextractBlockTest.java │ │ │ ├── TextractDetectTextTest.java │ │ │ ├── TextractGeometryTest.java │ │ │ └── TextractRelationshipTest.java │ │ │ └── utils │ │ │ ├── ApiRequestProcessorTest.java │ │ │ ├── CloudWatchMetricsTest.java │ │ │ ├── DependencyFactoryTest.java │ │ │ ├── FileUtilsTest.java │ │ │ ├── ImageRedactorTest.java │ │ │ ├── LambdaContextParserTest.java │ │ │ ├── PdfRedactorTest.java │ │ │ ├── PhraseFinderTest.java │ │ │ ├── S3StorageTest.java │ │ │ ├── SfnRequestProcessorTest.java │ │ │ └── StepFunctionConnectorTest.java │ │ └── resources │ │ ├── apiRequestBody-entities.json │ │ ├── apiRequestBody-phrases.json │ │ ├── apiRequestBody.json │ │ ├── badEventBody1.json │ │ ├── eventBody1.json │ │ ├── eventBody2.json │ │ ├── patient_intake_form_sample.jpg │ │ ├── redactData.json │ │ └── textract-detectText.json ├── search │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── event-test-data.js │ │ ├── index.spec.js │ │ └── utils │ │ │ ├── env-setup.spec.js │ │ │ └── seach-kendra.spec.js │ └── utils │ │ ├── env-setup.js │ │ └── search-kendra.js ├── send-notification │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── event-test-data.js │ │ ├── index.spec.js │ │ └── utils │ │ │ ├── s3-read.spec.js │ │ │ └── sns-send-notification.spec.js │ └── utils │ │ ├── s3-read.js │ │ └── sns-send-notification.js ├── text-extract │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── event-test-data.js │ │ ├── index.spec.js │ │ ├── textract-sync.spec.js │ │ └── utils │ │ │ ├── generic.spec.js │ │ │ ├── pdf-splitter.spec.js │ │ │ └── sync.spec.js │ ├── textract-sync.js │ └── utils │ │ ├── generic.js │ │ ├── pdf-splitter.js │ │ └── sync.js ├── upload-document │ ├── config │ │ └── ddb-loader.js │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── config │ │ │ ├── config-data.js │ │ │ └── ddb-loader.spec.js │ │ ├── index.spec.js │ │ └── utils │ │ │ ├── create-case.spec.js │ │ │ ├── env-setup.spec.js │ │ │ ├── fetch-case.spec.js │ │ │ ├── start-job.spec.js │ │ │ └── upload-document.spec.js │ └── utils │ │ ├── create-case.js │ │ ├── env-setup.js │ │ ├── fetch-case.js │ │ ├── start-job.js │ │ └── upload-document.js └── workflow-orchestrator │ ├── config │ └── ddb-loader.js │ ├── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── test │ ├── config │ │ ├── config-data.js │ │ └── ddb-loader.spec.js │ ├── event-test-data.js │ ├── index.spec.js │ └── utils │ │ ├── env-setup.spec.js │ │ ├── kendra-upload.spec.js │ │ ├── open-search-upload │ │ ├── open-search-upload.spec.js │ │ └── strategies │ │ │ ├── generic-entity-strategy.spec.js │ │ │ ├── medical-entity-strategy.spec.js │ │ │ └── textract-strategy.spec.js │ │ ├── s3-event-trigger.spec.js │ │ └── sfn-event-trigger.spec.js │ └── utils │ ├── env-setup.js │ ├── kendra-upload.js │ ├── open-search-upload │ ├── open-search-upload.js │ └── strategies │ │ ├── generic-entity-strategy.js │ │ ├── medical-entity-strategy.js │ │ └── textract-strategy.js │ ├── s3-event-trigger.js │ ├── search-storage-utils.js │ └── sfn-event-trigger.js ├── package-lock.json ├── pre-build-jars.sh ├── run-all-tests.sh ├── sample-documents ├── Finance │ ├── Lacey city bonds.png │ ├── Spokane accounting.png │ ├── USDC balance sheet.png │ └── USDE balance sheet.png ├── Medical │ ├── Causes_of_Chronic_Kidney_Disease_NIDDK.pdf │ ├── Chronic_Kidney_Disease_Tests_Diagnosis__NIDDK.pdf │ ├── Diabetes_Diet_Eating_and_Physical_Activity_NIDDK.pdf │ ├── Diabetes_Gum_Disease_and_Other_Dental_Problems_NIDDK.pdf │ ├── Diabetes_Heart_Disease_and_Stroke_NIDDK.pdf │ ├── Diabetes_Tests_and_Diagnosis_NIDDK.pdf │ ├── Eating_Right_for_Chronic_Kidney_Disease_NIDDK.pdf │ ├── HIPAA Release Form.pdf │ ├── High_Blood_Pressure_Kidney_Disease_NIDDK.pdf │ ├── Identify_Evaluate_Patients_with_Chronic_Kidney_Disease_NIDDK.pdf │ ├── Insulin_Medicines_and_Other_Diabetes_Treatments_NIDDK.pdf │ ├── Kidney_Transplant_NIDDK.pdf │ ├── Managing_Chronic_Kidney_Disease_NIDDK.pdf │ ├── Managing_Diabetes_NIDDK.pdf │ ├── Medical History Form.png │ ├── Medical Insurance Claim Form.pdf │ ├── Medical Progress Tracker.png │ ├── Nutrition_for_Advanced_Chronic_Kidney_Disease_in_Adults_NIDDK.pdf │ ├── PersonaSpecific │ │ ├── GeneralPublic │ │ │ └── pdf │ │ │ │ └── Creatine_Kinase_-_MedlinePlus_Medical_Test.pdf │ │ ├── HealthcareProvider │ │ │ └── pdf │ │ │ │ ├── Muscular_Dystrophy_NIH.pdf │ │ │ │ └── What_is_Muscular_Dystrophy_-_CDC.pdf │ │ └── Scientist │ │ │ └── pdf │ │ │ ├── How_is_muscular_dystrophy_diagnosed__NICHD.pdf │ │ │ ├── Muscular_Dystrophy_Information_Page_National_Institute_of_Neurological_Disorders_and_Stroke.pdf │ │ │ └── Muscular_Dystrophy__Hope_Through_Research_-_National_Institute_Of_Neurological_Disorders_And_Stroke.pdf │ ├── Pregnancy_if_You_Have_Diabetes_NIDDK.pdf │ ├── Preventing_Chronic_Kidney_Disease_NIDDK.pdf │ ├── Preventing_Diabetes_Problems_NIDDK.pdf │ ├── Preventing_Type_2_Diabetes_NIDDK.pdf │ ├── The_A1C_Test_Diabetes_NIDDK.pdf │ ├── What_If_My_Kidneys_Fail_NIDDK.pdf │ ├── What_Is_Chronic_Kidney_Disease_NIDDK.pdf │ └── What_is_Diabetes_NIDDK.pdf ├── Misc │ ├── employmentapp.png │ ├── expense.png │ └── management.png └── Research │ └── employmentapp.png ├── ui ├── .gitignore ├── README.md ├── jest.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.png │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── mockServiceWorker.js │ └── robots.txt ├── src │ ├── App.css │ ├── App.js │ ├── __test__ │ │ ├── CreateCaseView.test.tsx │ │ ├── DocumentRenderer.test.tsx │ │ ├── DocumentTable.test.tsx │ │ ├── DocumentView.test.tsx │ │ ├── EntitiesList.test.tsx │ │ ├── EntityDetectionTab.test.tsx │ │ ├── FileUpload.test.tsx │ │ ├── KendraDocumentResults.test.tsx │ │ ├── KendraHighlightedText.test.tsx │ │ ├── KendraResultPage.test.tsx │ │ ├── KendraResultTitle.test.tsx │ │ ├── KendraResults.test.tsx │ │ ├── KendraTopResults.test.tsx │ │ ├── KeyValueList.test.tsx │ │ ├── Pdf.test.tsx │ │ ├── RawTextLines.test.tsx │ │ ├── SearchView.test.tsx │ │ ├── SelectedFile.test.tsx │ │ ├── SelectedFileList.test.tsx │ │ ├── TableResults.test.tsx │ │ ├── TextractTab.test.tsx │ │ ├── UploadDocumentView.test.tsx │ │ ├── __snapshots__ │ │ │ └── TableResults.test.tsx.snap │ │ ├── index.test.js │ │ ├── internal.test.tsx │ │ ├── mocks │ │ │ └── App.js │ │ ├── test_data.js │ │ └── utils │ │ │ └── tesUtils.tsx │ ├── components │ │ ├── CreateCaseView │ │ │ ├── CreateCaseView.tsx │ │ │ └── form-content.tsx │ │ ├── DocumentRenderer │ │ │ ├── DocumentRenderer.css │ │ │ └── DocumentRenderer.tsx │ │ ├── DocumentTable │ │ │ ├── DocumentTable.tsx │ │ │ ├── common-components.tsx │ │ │ ├── full-page-header.tsx │ │ │ ├── i18n-strings │ │ │ │ ├── app-layout.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pagination.ts │ │ │ │ ├── split-panel.ts │ │ │ │ └── text-filter.ts │ │ │ ├── info-link.tsx │ │ │ ├── interfaces.ts │ │ │ ├── table-config.tsx │ │ │ ├── use-collection.ts │ │ │ └── utils.tsx │ │ ├── DocumentView │ │ │ ├── DocumentView.css │ │ │ └── DocumentView.tsx │ │ ├── EntitiesList.css │ │ ├── EntitiesList.tsx │ │ ├── EntityDetectionTab.tsx │ │ ├── FileUpload │ │ │ ├── FileUpload.tsx │ │ │ ├── SelectedFile │ │ │ │ ├── SelectedFile.css │ │ │ │ └── SelectedFile.tsx │ │ │ ├── SelectedFileList │ │ │ │ ├── SelectedFileList.css │ │ │ │ └── SelectedFileList.tsx │ │ │ ├── interfaces.ts │ │ │ └── internal.ts │ │ ├── KeyValueList.tsx │ │ ├── Pdf │ │ │ ├── Pdf.css │ │ │ └── Pdf.tsx │ │ ├── RawTextLines │ │ │ ├── RawTextLines.css │ │ │ └── RawTextLines.tsx │ │ ├── SearchView │ │ │ ├── Kendra │ │ │ │ ├── KendraDocumentResults.tsx │ │ │ │ ├── KendraHighlightedText.tsx │ │ │ │ ├── KendraResultPage.tsx │ │ │ │ ├── KendraResultTitle.tsx │ │ │ │ ├── KendraResults.tsx │ │ │ │ └── KendraTopResults.tsx │ │ │ ├── OpenSearch │ │ │ │ ├── OpenSearchQueryResults.tsx │ │ │ │ ├── OpenSearchResultPage.tsx │ │ │ │ ├── OpenSearchResultTitle.tsx │ │ │ │ └── OpenSearchResults.tsx │ │ │ └── SearchView.tsx │ │ ├── TableResults │ │ │ ├── TableResults.css │ │ │ └── TableResults.tsx │ │ ├── TextractTab.tsx │ │ ├── UploadDocumentView.tsx │ │ ├── buttons │ │ │ └── StartJobButton.tsx │ │ ├── entityUtils.ts │ │ └── makeData.ts │ ├── index.css │ ├── index.js │ ├── jest.setup.ts │ ├── mock │ │ └── api │ │ │ ├── handler.ts │ │ │ └── server.ts │ ├── models │ │ └── requests │ │ │ ├── baseRequest.ts │ │ │ ├── getDocumentRequest.ts │ │ │ ├── inferenceRequest.ts │ │ │ └── uploadDocumentRequest.ts │ ├── resolver.js │ ├── store │ │ ├── api │ │ │ └── api.ts │ │ ├── hooks │ │ │ └── hooks.ts │ │ ├── reducers │ │ │ ├── caseApiSlice.ts │ │ │ ├── documentApiSlice.ts │ │ │ ├── documentSlice.ts │ │ │ ├── entitySlice.ts │ │ │ ├── inferenceApiSlice.ts │ │ │ ├── redactApiSlice.ts │ │ │ ├── rootReducer.ts │ │ │ └── searchApiSlice.ts │ │ └── store.ts │ ├── test_data │ │ ├── comprehend-responses │ │ │ └── insulinPdfEntities.json │ │ ├── kendra │ │ │ └── kendraQueryResponse.json │ │ └── textract-responses │ │ │ ├── dischargeSummaryPng.json │ │ │ ├── textractResp2PagePdf.json │ │ │ ├── textractRespMulti.json │ │ │ └── textractResponseInsulinPdf.json │ └── utils │ │ ├── __test__ │ │ ├── common-renderers.test.tsx │ │ └── document.test.js │ │ ├── apiHelper.ts │ │ ├── common-renderers.tsx │ │ ├── constants.ts │ │ ├── document.ts │ │ ├── info-panel-contents.tsx │ │ └── interfaces.ts └── tsconfig.json └── workflow-config ├── default.json ├── multi-doc-textract-entity-medical-pii.json ├── multi-doc-textract-entity-pii.json ├── multi-doc-textract-entity.json ├── multi-doc-textract-with-feature-type.json ├── single-doc-entity-detection.json ├── single-doc-entity-redaction.json ├── single-doc-textract-entitty-pii.json ├── single-doc-textract-entity-medical.json ├── single-doc-textract-entity-pii.json ├── single-doc-textract-entity.json └── single-doc-textract.json /.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 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior. 14 | 15 | **Expected behavior** 16 | A clear and concise description of what you expected to happen. 17 | 18 | **Please complete the following information about the solution:** 19 | 20 | - [ ] Version: [e.g. v1.0.0] 21 | 22 | To get the version of the solution, you can look at the description of the created CloudFormation stack. For example, "(SO0281) - Enhanced Document Understanding on AWS. Version v1.0.0". 23 | 24 | - [ ] Region: [e.g. us-east-1] 25 | - [ ] Was the solution modified from the version published on this repository? 26 | - [ ] If the answer to the previous question was yes, are the changes available on GitHub? 27 | - [ ] Have you checked your [service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) for the sevices this solution uses? 28 | - [ ] Were there any errors in the CloudWatch Logs? 29 | 30 | **Screenshots** 31 | If applicable, add screenshots to help explain your problem (please **DO NOT include sensitive information**). 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this solution 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 feature you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | deployment/global-s3-assets 2 | deployment/regional-s3-assets 3 | deployment/open-source 4 | 5 | **/coverage/ 6 | **/coverage-reports/ 7 | .coverage 8 | 9 | node_modules/ 10 | 11 | cfn_nag_suppressions.log 12 | 13 | **.DS_STORE** 14 | 15 | **/.venv*/** 16 | __pycache__/ 17 | *.egg-info 18 | *.egg 19 | 20 | codebuild_build.sh 21 | .vscode/settings.json 22 | .vscode/launch.json 23 | *tmp* 24 | .pytest_cache 25 | 26 | .idea/** 27 | 28 | aws_lambda_powertools/ 29 | aws_xray_sdk/ 30 | boto3/ 31 | botocore/ 32 | build/lib/ 33 | dateutil/ 34 | urllib3/ 35 | jmespath/ 36 | python_dateutil/ 37 | s3transfer/ 38 | coverage/ 39 | coverage-reports/ 40 | six.py 41 | typing_extensions.py 42 | wrapt 43 | THIRD-PARTY-LICENSES 44 | 45 | target/ 46 | *.jar 47 | source/infrastructure/test/mock-lambda-func/java-lambda/checkstyle-result.xml 48 | **/*dependency-reduced-pom.xml 49 | .coverage.* 50 | 51 | *.log 52 | .venv/** 53 | source/ui/public/pdf.worker.min.mjs 54 | .viperlightrc 55 | 56 | .temp_redpencil 57 | bom.json -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting Security Issues 2 | 3 | We take all security reports seriously. When we receive such reports, we will investigate and subsequently address any potential vulnerabilities as quickly as possible. 4 | If you discover a potential security issue in this project, please notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to [AWS Security](mailto:aws-security@amazon.com). 5 | Please do not create a public GitHub issue in this project. 6 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-solution-helper", 3 | "version": "0.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cdk-solution-helper", 9 | "version": "0.1.0", 10 | "license": "Apache-2.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-solution-helper", 3 | "version": "0.1.0", 4 | "description": "This script performs token replacement as part of the build pipeline", 5 | "license": "Apache-2.0", 6 | "author": { 7 | "name": "Amazon Web Services", 8 | "url": "https://aws.amazon.com/solutions" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /deployment/get-cdk-version.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const pkg = require("../source/infrastructure/node_modules/aws-cdk/package.json"); 5 | console.log(pkg.version); 6 | -------------------------------------------------------------------------------- /source/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | cdk.out 4 | # don't lint build output (make sure it's set to your correct build folder name) 5 | dist 6 | # don't lint test folders 7 | test 8 | # don't lint coverage output 9 | coverage 10 | *.config.js 11 | # dont lint eslint config 12 | *.eslint* 13 | -------------------------------------------------------------------------------- /source/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/*.d.ts 3 | coverage 4 | cdk.out 5 | .eslintrc.js 6 | cdk.json 7 | tsconfig.json 8 | jest.config.js 9 | package*.json -------------------------------------------------------------------------------- /source/.prettierrc.yml: -------------------------------------------------------------------------------- 1 | # .prettierrc or .prettierrc.yaml 2 | proseWrap: 'preserve' 3 | trailingComma: 'none' 4 | tabWidth: 4 5 | semi: true 6 | singleQuote: true 7 | quoteProps: 'preserve' 8 | printWidth: 120 9 | overrides: 10 | - files: 11 | - '*.json' 12 | - '*.yml' 13 | options: 14 | tabWidth: 2 15 | -------------------------------------------------------------------------------- /source/email-templates/processing_complete.email.template: -------------------------------------------------------------------------------- 1 | Hello 2 | 3 | Your case with ID <> has been processed successfully. 4 | 5 | Thank You. -------------------------------------------------------------------------------- /source/email-templates/processing_failure.email.template: -------------------------------------------------------------------------------- 1 | Hello 2 | 3 | Your case with ID <> failed to process. 4 | 5 | Thank You. -------------------------------------------------------------------------------- /source/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/images/architecture.png -------------------------------------------------------------------------------- /source/images/ui-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/images/ui-components.png -------------------------------------------------------------------------------- /source/infrastructure/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | !jest.setEnvVars.js 4 | *.d.ts 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | 10 | *tmp* 11 | 12 | 13 | -------------------------------------------------------------------------------- /source/infrastructure/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /source/infrastructure/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | testEnvironment: 'node', 6 | roots: ['/test'], 7 | testMatch: ['**/*.test.ts'], 8 | collectCoverage: true, 9 | transform: { 10 | '^.+\\.tsx?$': 'ts-jest' 11 | }, 12 | coverageReporters: ['text', ['lcov', { projectRoot: '../../' }]], 13 | setupFiles: ['/test/jest.setEnvVars.js'] 14 | }; 15 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/model-schema/create-case-body.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { JsonSchema, JsonSchemaType } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | export const caseBodySchema: JsonSchema = { 7 | type: JsonSchemaType.OBJECT, 8 | properties: { 9 | caseId: { 10 | type: JsonSchemaType.STRING, 11 | description: 'The ID of the case to whom the document belongs', 12 | pattern: '\\w+' 13 | } 14 | }, 15 | required: ['caseId'] 16 | } 17 | 18 | export const createCaseBodySchema: JsonSchema = { 19 | type: JsonSchemaType.OBJECT, 20 | properties: { 21 | caseName: { 22 | type: JsonSchemaType.STRING, 23 | description: 'The ID of the case to whom the document belongs', 24 | pattern: '\\w+', 25 | minLength: 3, 26 | maxLength: 50 27 | }, 28 | enableBackendUpload: { 29 | type: JsonSchemaType.BOOLEAN, 30 | description: 'Enables bulk uplod process' 31 | } 32 | }, 33 | required: ['caseName', 'enableBackendUpload'] 34 | }; 35 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/model-schema/document-response.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { JsonSchema, JsonSchemaType } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | export const getDocInfoResponseSchema: JsonSchema = { 7 | type: JsonSchemaType.OBJECT, 8 | description: 9 | 'Location of the document that is requested to be downloaded. This is used by a lambda function to create an authenticated signed-url', 10 | properties: { 11 | DocId: { 12 | type: JsonSchemaType.STRING, 13 | description: 'ID of the requested document' 14 | }, 15 | Bucket: { 16 | type: JsonSchemaType.STRING, 17 | description: 'S3 bucket name' 18 | }, 19 | key: { 20 | type: JsonSchemaType.STRING, 21 | description: 'Location of the requested document' 22 | }, 23 | FileName: { 24 | type: JsonSchemaType.STRING, 25 | description: 'Filename' 26 | } 27 | } 28 | }; 29 | 30 | export const downloadDocResponseSchema: JsonSchema = { 31 | type: JsonSchemaType.OBJECT, 32 | description: 'Object containing the presigned url to download a requested document', 33 | properties: { 34 | downloadUrl: { 35 | type: JsonSchemaType.STRING, 36 | description: 'Presigned URL to download file' 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/model-schema/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export * from './redact-request-body'; 5 | export * from './upload-document-body'; 6 | export * from './create-case-body'; 7 | export * from './case-response'; 8 | export * from './document-response'; 9 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/model-schema/upload-document-body.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { JsonSchema, JsonSchemaType } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | export const uploadDocumentBodySchema: JsonSchema = { 7 | type: JsonSchemaType.OBJECT, 8 | properties: { 9 | userId: { 10 | type: JsonSchemaType.STRING, 11 | description: 'The ID of the user to whom the document belongs', 12 | pattern: '^[a-zA-Z0-9_+:-]{1,}' 13 | }, 14 | caseId: { 15 | type: JsonSchemaType.STRING, 16 | description: 'The ID of the case to whom the document belongs', 17 | pattern: '^[a-zA-Z0-9_+:-]{1,}' 18 | }, 19 | caseName: { 20 | type: JsonSchemaType.STRING, 21 | description: 'The ID of the case to whom the document belongs', 22 | pattern: '\\w+', 23 | minLength: 3, 24 | maxLength: 50 25 | }, 26 | fileName: { 27 | type: JsonSchemaType.STRING, 28 | description: 'The name of the file', 29 | pattern: '\\w+', 30 | minLength: 3, 31 | maxLength: 50 32 | }, 33 | fileExtension: { 34 | type: JsonSchemaType.STRING, 35 | description: 'The extension of the file. Must begin with period', 36 | pattern: '^.(pdf|jpg|jpeg|png)$' 37 | }, 38 | documentType: { 39 | type: JsonSchemaType.STRING, 40 | description: 'The type of document based on business rules, such as driving license, passport, etc', 41 | pattern: '\\w+' 42 | } 43 | }, 44 | required: ['userId', 'caseId', 'caseName', 'fileName', 'fileExtension', 'documentType'] 45 | }; 46 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/rest-api-documentation/cases.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { CfnDocumentationPartProps } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | // Resources for the case API 7 | export const casesResource: Partial = { 8 | location: { 9 | type: 'RESOURCE', 10 | path: '/cases' 11 | }, 12 | properties: JSON.stringify({ 13 | description: 'Cases is used to retrieve all records accessible to an authenticated user' 14 | }) 15 | }; 16 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/rest-api-documentation/document.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { CfnDocumentationPartProps } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | // query params for for fetching records of documents 7 | export const getDocQueryParam: Partial = { 8 | location: { 9 | type: 'QUERY_PARAMETER', 10 | path: '/document/{caseId}/{documentId}', 11 | method: 'GET', 12 | name: 'redacted' 13 | }, 14 | properties: JSON.stringify({ 15 | description: 'Boolean value to specify whether to return redacted or unredacted version of document.' 16 | }) 17 | }; 18 | 19 | export const downloadDocQueryParam: Partial = { 20 | location: { 21 | type: 'QUERY_PARAMETER', 22 | path: '/document/download', 23 | method: 'GET', 24 | name: 'key' 25 | }, 26 | properties: JSON.stringify({ 27 | description: 'S3 key of the document to download. Will only work if request is sent by an authorized user.' 28 | }) 29 | }; 30 | 31 | export const downloadResourceDescription: Partial = { 32 | location: { 33 | type: 'METHOD', 34 | path: '/document/download', 35 | method: 'POST' 36 | }, 37 | properties: JSON.stringify({ 38 | description: 39 | 'This endpoint is used to download a document using a S3 signed url. All requests are validated and only documents that a user has permissions to access are returned.' 40 | }) 41 | }; 42 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/rest-api-documentation/inferences.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { CfnDocumentationPartProps } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | export const getInferenceByTypeMethod: Partial = { 7 | location: { 8 | type: 'METHOD', 9 | path: '/inferences/{caseId}/{documentId}/{inferenceType}', 10 | method: 'GET' 11 | }, 12 | properties: JSON.stringify({ 13 | description: 14 | 'This endpoint is used to retrieve inference for a document within a case. The `inferenceType` path parameter is the file name of the inference results object that was stored in the S3 inference bucket.' 15 | }) 16 | }; 17 | 18 | export const listInferencesMethod: Partial = { 19 | location: { 20 | type: 'METHOD', 21 | path: '/inferences/{caseId}/{documentId}', 22 | method: 'GET' 23 | }, 24 | properties: JSON.stringify({ 25 | description: 'This endpoint is used to list the inference results available for a document.' 26 | }) 27 | }; 28 | -------------------------------------------------------------------------------- /source/infrastructure/lib/api/rest-api-documentation/redact.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { CfnDocumentationPartProps } from 'aws-cdk-lib/aws-apigateway'; 5 | 6 | export const redactMethod: Partial = { 7 | location: { 8 | type: 'METHOD', 9 | path: '/redact/{caseId}/{documentId}', 10 | method: 'POST' 11 | }, 12 | properties: JSON.stringify({ 13 | description: [ 14 | `This endpoint is used to redact specific entities from a document. 15 | The request body describes the requried structure. A successful request will 16 | redact a document with a given ID, store it in the S3 bucket, and update the case records. `, 17 | 'To download a redacted document, send a request to `GET:/document/{caseId}/{documentId}?redacted=true`' 18 | ].join('') 19 | }) 20 | }; 21 | 22 | export const redactSuccessResponse: Partial = { 23 | location: { 24 | type: 'RESPONSE', 25 | path: '/redact/{caseId}/{documentId}', 26 | statusCode: '201' 27 | }, 28 | properties: JSON.stringify({ 29 | description: 'Successully request to redact entities from a document' 30 | }) 31 | }; 32 | -------------------------------------------------------------------------------- /source/infrastructure/lib/layers/java-user-agent.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 lambda from 'aws-cdk-lib/aws-lambda'; 6 | import { Construct } from 'constructs'; 7 | import * as path from 'path'; 8 | import { getCommandsForJavaDockerBundling } from '../utils/asset-bundling'; 9 | import { getJavaLayerLocalBundling, LayerProps } from '../utils/common-utils'; 10 | import { COMMERCIAL_REGION_LAMBDA_JAVA_RUNTIME } from '../utils/constants'; 11 | 12 | /** 13 | * User-agent layer for lambda Java runtime lambda functions 14 | */ 15 | export class JavaUserAgentLayer extends lambda.LayerVersion { 16 | constructor(scope: Construct, id: string, props: LayerProps) { 17 | const compatibleRuntimes = props.compatibleRuntimes ?? [COMMERCIAL_REGION_LAMBDA_JAVA_RUNTIME]; 18 | 19 | for (const runtime of compatibleRuntimes) { 20 | if (runtime && runtime.family !== lambda.RuntimeFamily.JAVA) { 21 | throw new Error(`Only ${compatibleRuntimes.join(',')} runtimes are supported`); 22 | } 23 | } 24 | 25 | const entry = path.resolve(props.entry); 26 | 27 | super(scope, id, { 28 | code: lambda.Code.fromAsset(entry, { 29 | bundling: { 30 | image: COMMERCIAL_REGION_LAMBDA_JAVA_RUNTIME.bundlingImage, 31 | local: getJavaLayerLocalBundling(entry), 32 | command: getCommandsForJavaDockerBundling('/asset-output/java/lib', 'Java user-agent lambda layer'), 33 | user: 'root' 34 | } 35 | }), 36 | compatibleRuntimes, 37 | description: props.description 38 | } as lambda.LayerVersionProps); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/infrastructure/lib/utils/cfn-nag-suppressions.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 | 7 | /** 8 | * The CFN NAG suppress rule interface 9 | * @interface CfnNagSuppressRule 10 | */ 11 | export interface CfnNagSuppressRule { 12 | readonly id: string; 13 | readonly reason: string; 14 | } 15 | 16 | /** 17 | * Adds CFN NAG suppress rules to the CDK resource. 18 | * @param resource The CDK resource 19 | * @param rules The CFN NAG suppress rules 20 | */ 21 | export function addCfnSuppressRules(resource: cdk.Resource | cdk.CfnResource, rules: CfnNagSuppressRule[]) { 22 | if (resource instanceof cdk.Resource) { 23 | resource = resource.node.defaultChild as cdk.CfnResource; 24 | } 25 | 26 | if (resource.cfnOptions.metadata?.cfn_nag?.rules_to_suppress) { 27 | resource.cfnOptions.metadata?.cfn_nag.rules_to_suppress.push(...rules); // NOSONAR - pushing to array 28 | } else { 29 | resource.addMetadata('cfn_nag', { 30 | rules_to_suppress: rules 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/infrastructure/test/framework/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM 'amazon/aws-stepfunctions-local' -------------------------------------------------------------------------------- /source/infrastructure/test/framework/sfn-mock-config-validator.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { readFileSync } from 'fs'; 5 | import { resolve } from 'path'; 6 | 7 | interface validMockConfig { 8 | StateMachines: Object; 9 | MockedResponses: Object; 10 | } 11 | 12 | export class SFNMockConfigValidator { 13 | mockConfig: validMockConfig; 14 | mockFilePath: string; 15 | constructor(mockConfigFile: string) { 16 | try { 17 | this.mockFilePath = resolve(mockConfigFile); 18 | this.mockConfig = JSON.parse(readFileSync(this.mockFilePath, 'utf-8')); 19 | } catch (error) { 20 | console.error(`Unable to read file: '${resolve(mockConfigFile)}'`); 21 | throw error; 22 | } 23 | } 24 | validate(): boolean { 25 | return this.requiredKeysExist() && this.validStateMachineNames(); 26 | } 27 | 28 | private validStateMachineNames(): boolean { 29 | const stateMachineNames = Object.keys(this.mockConfig.StateMachines).sort(); 30 | const isValid = stateMachineNames.every((name, idx) => name === `StateMachine${idx + 1}`); 31 | if (!isValid) { 32 | console.warn('StateMachine name(s) is invalid'); 33 | } 34 | return isValid; 35 | } 36 | 37 | private requiredKeysExist(): boolean { 38 | const requriedKeys = ['StateMachines', 'MockedResponses']; 39 | const mockConfigKeys = Object.keys(this.mockConfig); 40 | const isValid = requriedKeys.every((reqKey) => mockConfigKeys.includes(reqKey)); 41 | if (!isValid) { 42 | console.warn('MockConfig file is missing required keys'); 43 | } 44 | return isValid; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/.gitignore: -------------------------------------------------------------------------------- 1 | **.dist-info 2 | dist/ -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/infrastructure/test/mock-lambda-func/python-lambda/poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. 2 | package = [] 3 | 4 | [metadata] 5 | lock-version = "2.0" 6 | python-versions = "^3.11" 7 | content-hash = "81b2fa642d7f2d1219cf80112ace12d689d053d81be7f7addb98144d56fc0fb2" 8 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/infrastructure/test/mock-lambda-func/python-lambda/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "mock-python-lambda" 3 | version = "1.1.16" 4 | authors = [ "Amazon Web Services" ] 5 | description = "Mock function for infra testing" 6 | packages = [ 7 | { include = "*.toml" }, 8 | { include = "*.lock" }, 9 | ] 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.11" 13 | 14 | [build-system] 15 | requires = [ "poetry-core>=1.9.0" ] 16 | build-backend = "poetry.core.masonry.api" -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/java-lambda/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/node-lambda/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-lambda", 3 | "version": "1.1.16", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "node-lambda", 9 | "version": "1.1.16", 10 | "license": "Apache-2.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/node-lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-lambda", 3 | "version": "1.1.16", 4 | "description": "A mock lambda implementation for CDK infrastructure unit", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 11 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 12 | "code-linter": "npm run code-linter-ts && npm run code-linter-js", 13 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config .prettierrc.yml '**/*.ts' '**/*.js' --write" 14 | }, 15 | "author": { 16 | "name": "Amazon Web Services", 17 | "url": "https://aws.amazon.com/solutions" 18 | }, 19 | "license": "Apache-2.0" 20 | } 21 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/python-lambda/.gitignore: -------------------------------------------------------------------------------- 1 | **.dist-info 2 | dist/ -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/python-lambda/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/python-lambda/function.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import logging 6 | 7 | 8 | def handler(event, _): 9 | logging.debug("Mock lambda implementation for Python runtime") 10 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/python-lambda/poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. 2 | package = [] 3 | 4 | [metadata] 5 | lock-version = "2.1" 6 | python-versions = "^3.11" 7 | content-hash = "81b2fa642d7f2d1219cf80112ace12d689d053d81be7f7addb98144d56fc0fb2" 8 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-lambda-func/python-lambda/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "mock-python-lambda" 3 | version = "1.1.16" 4 | authors = [ "Amazon Web Services" ] 5 | description = "Mock function for infra testing" 6 | packages = [ 7 | { include = "*.toml" }, 8 | { include = "*.lock" }, 9 | ] 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.11" 13 | 14 | [build-system] 15 | requires = [ "poetry-core>=1.9.0" ] 16 | build-backend = "poetry.core.masonry.api" -------------------------------------------------------------------------------- /source/infrastructure/test/mock-ui/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-react-app", 3 | "version": "1.1.16", 4 | "description": "Mock Reactjs app used for unit testing constructs", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "react-scripts build", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "dependencies": { 11 | "react": "^18.3.1" 12 | }, 13 | "devDependencies": { 14 | "react-scripts": "^5.0.1" 15 | }, 16 | "overrides": { 17 | "nth-check": "^2.0.1", 18 | "@babel/traverse": "^7.23.2", 19 | "postcss": "^8.4.31", 20 | "rollup": "^4.22.4", 21 | "cross-spawn": "^7.0.5", 22 | "serialize-javascript": "^6.0.2" 23 | }, 24 | "license": "Apache-2.0", 25 | "author": { 26 | "name": "Amazon Web Services", 27 | "url": "https://aws.amazon.com/solutions" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/infrastructure/test/mock-ui/public/index.html: -------------------------------------------------------------------------------- 1 | Blank test file -------------------------------------------------------------------------------- /source/infrastructure/test/vpc/vpc-stack.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 { Capture, Template } from 'aws-cdk-lib/assertions'; 6 | import { DusStack } from '../../lib/dus-stack'; 7 | import * as rawCdkJson from '../../cdk.json'; 8 | import { VpcStack } from '../../lib/vpc/vpc-stack'; 9 | 10 | describe('When VPC stack is enabled and deployed', () => { 11 | let template: Template; 12 | let app: cdk.App; 13 | let stack: DusStack; 14 | let vpcStack: VpcStack; 15 | 16 | beforeAll(() => { 17 | app = new cdk.App({ 18 | context: rawCdkJson.context 19 | }); 20 | 21 | stack = new DusStack(app, 'TestStack', { 22 | solutionID: rawCdkJson.context.solution_id, 23 | solutionName: rawCdkJson.context.solution_name, 24 | solutionVersion: rawCdkJson.context.solution_version, 25 | appNamespace: rawCdkJson.context.app_namespace, 26 | applicationTrademarkName: rawCdkJson.context.application_trademark_name 27 | }); 28 | 29 | vpcStack = new VpcStack(stack, 'TestVpcStack'); 30 | template = Template.fromStack(vpcStack); 31 | }); 32 | 33 | it('should create VPC when deployed', () => { 34 | template.resourceCountIs('AWS::EC2::VPC', 1); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /source/infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018", "dom"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"], 21 | "esModuleInterop": true, 22 | "resolveJsonModule": true, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/lambda/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2021 5 | }, 6 | env: { 7 | node: true, 8 | jest: true, 9 | es6: true 10 | }, 11 | extends: ['eslint:recommended'], 12 | rules: { 13 | indent: ['error', 4], 14 | quotes: ['warn', 'single'] 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /source/lambda/create-presigned-url/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | const SharedLib = require('common-node-lib'); 6 | const { getDocumentUrl } = require('./utils/generate-signed-url'); 7 | const { checkAllEnvSetup } = require('./utils/env-setup'); 8 | 9 | exports.handler = async (event, context) => { 10 | try { 11 | checkAllEnvSetup(); 12 | 13 | const requestAccountId = SharedLib.getAccountIdFromLambdaContext(context); 14 | const userId = SharedLib.getUserIdFromEvent(event); 15 | 16 | const s3Key = event.queryStringParameters.key; 17 | const presignedUrl = await getDocumentUrl({ 18 | key: s3Key, 19 | userId: userId, 20 | expectedBucketOwner: requestAccountId 21 | }); 22 | 23 | return SharedLib.formatResponse({ downloadUrl: presignedUrl }); 24 | } catch (error) { 25 | console.error(`Error generating presigned url. Event is: ${JSON.stringify(event)}`); 26 | return SharedLib.formatError(error); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /source/lambda/create-presigned-url/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/create-presigned-url/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-presigned-url", 3 | "version": "1.1.16", 4 | "description": "This lambda has minimal permissions required to create downloadable presigned URLs for documents.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "clean-dev": "rm -rf node_modules && npm i --omit=dev", 11 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 12 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 13 | "code-linter": "npm run code-linter-ts && npm run code-linter-js", 14 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config .prettierrc.yml '**/*.ts' '**/*.js' --write" 15 | }, 16 | "overrides": { 17 | "cross-spawn": "^7.0.5" 18 | }, 19 | "author": { 20 | "name": "Amazon Web Services", 21 | "url": "https://aws.amazon.com/solutions" 22 | }, 23 | "license": "Apache-2.0", 24 | "devDependencies": { 25 | "aws-sdk-mock": "^5.8.0", 26 | "eslint": "^9.15.0", 27 | "jest": "^29.7.0", 28 | "prettier": "^3.3.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/lambda/create-presigned-url/utils/env-setup.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | let UPLOAD_DOCS_BUCKET_NAME; 7 | let S3_UPLOAD_PREFIX; 8 | 9 | function checkS3BucketNameEnvSetup() { 10 | if (process.env.UPLOAD_DOCS_BUCKET_NAME) { 11 | UPLOAD_DOCS_BUCKET_NAME = process.env.UPLOAD_DOCS_BUCKET_NAME; 12 | console.debug(`UPLOAD_DOCS_BUCKET_NAME is: ${UPLOAD_DOCS_BUCKET_NAME}`); 13 | } else { 14 | throw new Error('UPLOAD_DOCS_BUCKET_NAME Lambda Environment variable not set.'); 15 | } 16 | } 17 | 18 | function checkS3KeyPrefixEnvSetup() { 19 | if (process.env.S3_UPLOAD_PREFIX) { 20 | S3_UPLOAD_PREFIX = process.env.S3_UPLOAD_PREFIX; 21 | console.debug(`S3_UPLOAD_PREFIX is: ${S3_UPLOAD_PREFIX}`); 22 | } else { 23 | throw new Error('S3_UPLOAD_PREFIX Lambda Environment variable not set.'); 24 | } 25 | } 26 | 27 | function checkAllEnvSetup() { 28 | checkS3BucketNameEnvSetup(); 29 | checkS3KeyPrefixEnvSetup(); 30 | } 31 | 32 | module.exports = { checkAllEnvSetup, checkS3BucketNameEnvSetup, checkS3KeyPrefixEnvSetup }; 33 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | test/* 4 | .venv-*/* 5 | */__init__.py 6 | aws_lambda_powertools/* 7 | aws_xray_sdk/* 8 | boto3/* 9 | botocore/* 10 | dateutil/* 11 | jmespath/* 12 | s3transfer/* 13 | urllib*/* 14 | wrapt/* 15 | six.py 16 | typing_extensions.py 17 | source = 18 | . -------------------------------------------------------------------------------- /source/lambda/custom-resource/.gitignore: -------------------------------------------------------------------------------- 1 | aws_lambda_power_tools/* 2 | aws_xray_sdk/* 3 | botocore/* 4 | dateutil/* 5 | jmesopath/* 6 | urllib* 7 | wrapt/* 8 | boto3/* 9 | .coverage 10 | s3transfer/* 11 | *.dist-info 12 | jp.py 13 | THIRD_PARTY-LICENSES 14 | dist/ 15 | 16 | 17 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/lambda_ops_metrics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | import os 5 | 6 | from aws_lambda_powertools import Logger, Metrics, Tracer 7 | from custom_config import DEFAULT_APP_NAME 8 | from utils.constants import PUBLISH_METRICS_HOURS, STACK_UUID_ENV_VAR 9 | from utils.data import BuilderMetrics 10 | from utils.metrics import push_builder_metrics, verify_env_setup 11 | from utils.metrics_payload import get_metrics_payload 12 | 13 | logger = Logger(utc=True) 14 | tracer = Tracer() 15 | metrics = Metrics(namespace=os.environ.get("STACK_NAME", DEFAULT_APP_NAME)) 16 | STACK_UUID = os.getenv(STACK_UUID_ENV_VAR) 17 | 18 | 19 | @metrics.log_metrics(capture_cold_start_metric=True) # type: ignore 20 | @tracer.capture_lambda_handler 21 | @logger.inject_lambda_context(log_event=True) 22 | def handler(*_): 23 | try: 24 | verify_env_setup() 25 | metric_data = get_metrics_payload(PUBLISH_METRICS_HOURS) 26 | builder_metrics = BuilderMetrics( 27 | os.environ["SOLUTION_ID"], os.environ["SOLUTION_VERSION"], metric_data, STACK_UUID 28 | ) 29 | push_builder_metrics(builder_metrics) 30 | except Exception as ex: 31 | logger.error(f"Error occurred when sending cloudwatch anonymous metrics, Error is {ex}") 32 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/operations/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/operations/operation_types.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | 6 | # list of operation names as constants 7 | COPY_TEMPLATE = "COPY_TEMPLATE" 8 | COPY_SAMPLE_DOCUMENTS = "COPY_SAMPLE_DOCUMENTS" 9 | GEN_UUID = "GEN_UUID" 10 | ANONYMOUS_METRIC = "ANONYMOUS_METRIC" 11 | CW_LOGGROUP_POLICY = "CW_LOG_POLICY" 12 | COPY_WORKFLOW_CONFIG = "COPY_WORKFLOW_CONFIG" 13 | WEBCONFIG = "WEBCONFIG" 14 | COPY_WEB_UI = "COPY_WEB_UI" 15 | UPDATE_BUCKET_POLICY = "UPDATE_BUCKET_POLICY" 16 | 17 | 18 | # additional constants 19 | RESOURCE_PROPERTIES = "ResourceProperties" 20 | PHYSICAL_RESOURCE_ID = "PhysicalResourceId" 21 | RESOURCE = "Resource" 22 | 23 | # status constants 24 | SUCCESS = "SUCCESS" 25 | FAILED = "FAILED" 26 | 27 | # S3 copy constants 28 | SOURCE_BUCKET_NAME = "SOURCE_BUCKET_NAME" 29 | SOURCE_PREFIX = "SOURCE_PREFIX" 30 | LOGGING_BUCKET_NAME = "LOGGING_BUCKET_NAME" 31 | 32 | # temporary fs provided by the lambda environment 33 | TMP = "/tmp" # NOSONAR (python:S5443) using lambda's fs storage /tmp # nosec B108 34 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "custom-resource" 3 | version = "1.1.16" 4 | authors = [ "Amazon Web Services" ] 5 | description = "Perform specific operations triggered by AWS CloudFormation events" 6 | packages = [ 7 | { include = "*.py" }, 8 | { include = "./operations/*.py" }, 9 | { include = "./utils/*.py" } 10 | ] 11 | classifiers = [ 12 | "Programming Language :: Python :: 3", 13 | "License :: Apache-2.0", 14 | ] 15 | license = "Apache-2.0" 16 | 17 | [tool.poetry.dependencies] 18 | python = "^3.11" 19 | 20 | [tool.poetry.group.test.dependencies] 21 | freezegun = "1.4.0" 22 | mock = "5.1.0" 23 | moto = "5.0.20" 24 | pytest = "8.1.1" 25 | pytest-cov = "5.0.0" 26 | pytest-env = "1.1.3" 27 | PyYAML = "6.0.1" 28 | setuptools = "80.8.0" 29 | boto3-layer = { path = "../layers/aws_boto3/", develop = true } 30 | custom_boto3_init = { path = "../layers/custom_boto3_init", develop = true } 31 | 32 | [tool.black] 33 | line-length = 120 34 | 35 | [tool.isort] 36 | multi_line_output = 3 37 | include_trailing_comma = true 38 | force_grid_wrap = 0 39 | line_length = 120 40 | profile = "black" 41 | 42 | [build-system] 43 | requires = [ "poetry-core>=1.9.0" ] 44 | build-backend = "poetry.core.masonry.api" -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/fixtures/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/fixtures/anonymous_metrics_events.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import os 6 | from copy import copy 7 | 8 | import pytest 9 | from operations import operation_types 10 | from operations.operation_types import RESOURCE, RESOURCE_PROPERTIES, PHYSICAL_RESOURCE_ID 11 | 12 | 13 | @pytest.fixture 14 | def lambda_events(aws_credentials, custom_resource_event): 15 | events_list = [] 16 | payloads = [ 17 | { 18 | RESOURCE: operation_types.ANONYMOUS_METRIC, 19 | "SolutionId": "SO0999", 20 | "Version": "v9.9.9", 21 | "ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:fakefunction:1", 22 | "DeployKendraIndex": "Yes", 23 | "WorkflowConfigName": "default", 24 | }, 25 | { 26 | RESOURCE: operation_types.ANONYMOUS_METRIC, 27 | "SolutionId": "SO0999", 28 | "Version": "v9.9.9", 29 | "ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:fakefunction:1", 30 | "DeployKendraIndex": "No", 31 | "WorkflowConfigName": "default", 32 | }, 33 | { 34 | RESOURCE: operation_types.ANONYMOUS_METRIC, 35 | "SolutionId": "SO0999", 36 | "Version": "v9.9.9", 37 | "ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:fakefunction:1", 38 | }, 39 | ] 40 | custom_resource_event[PHYSICAL_RESOURCE_ID] = "fake_physical_resource_id" 41 | 42 | for payload_item in payloads: 43 | custom_resource_event[RESOURCE_PROPERTIES] = payload_item 44 | events_list.append(copy(custom_resource_event)) 45 | 46 | yield events_list 47 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/fixtures/cw_loggroup_policy_events.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import os 6 | 7 | import pytest 8 | from operations import operation_types 9 | from operations.operation_types import RESOURCE, RESOURCE_PROPERTIES, PHYSICAL_RESOURCE_ID 10 | 11 | 12 | @pytest.fixture 13 | def lambda_event(aws_credentials, custom_resource_event): 14 | custom_resource_event[RESOURCE_PROPERTIES] = { 15 | RESOURCE: operation_types.CW_LOGGROUP_POLICY, 16 | "ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:fakefunction:1", 17 | "CWLOG_ARN": "arn:aws:logs:us-east-1:123456789012:log-group:/fake-loggroup-ABC1234:*", 18 | "CWLOG_NAME": "fake-loggroup-ABC1234", 19 | "SERVICE_PRINCIPAL": "es.amazonaws.com", 20 | } 21 | custom_resource_event[PHYSICAL_RESOURCE_ID] = "fake_physical_resource_id" 22 | 23 | yield custom_resource_event 24 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/fixtures/gen_uuid_events.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import os 6 | 7 | import pytest 8 | from operations import operation_types 9 | from operations.operation_types import RESOURCE, RESOURCE_PROPERTIES, PHYSICAL_RESOURCE_ID 10 | 11 | 12 | @pytest.fixture 13 | def lambda_event(aws_credentials, custom_resource_event): 14 | custom_resource_event[RESOURCE_PROPERTIES] = {RESOURCE: operation_types.GEN_UUID} 15 | custom_resource_event[PHYSICAL_RESOURCE_ID] = "fake_physical_resource_id" 16 | 17 | yield custom_resource_event 18 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/operations/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/test_lambda_ops_metrics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import os 6 | from contextlib import nullcontext as does_not_raise 7 | 8 | import mock 9 | from lambda_ops_metrics import handler 10 | 11 | 12 | @mock.patch("lambda_ops_metrics.get_metrics_payload", {"fake-metric-1": 5, "fake-metric-2": 10}) 13 | @mock.patch("lambda_ops_metrics.push_builder_metrics", None) 14 | def test_lambda_handler_success(mock_lambda_context, monkeypatch): 15 | envs = { 16 | "UNIT_TEST_ENV": "yes", 17 | "POWERTOOLS_SERVICE_NAME": "ANONYMOUS-CW-METRICS", 18 | "LOG_LEVEL": "DEBUG", 19 | "SOLUTION_ID": "SO0999", 20 | "SOLUTION_VERSION": "v99.99.99", 21 | } 22 | monkeypatch.setattr(os, "environ", envs) 23 | assert handler({}, mock_lambda_context) == None 24 | 25 | 26 | @mock.patch("lambda_ops_metrics.get_metrics_payload", return_value={"fake-metric-1": 5, "fake-metric-2": 10}) 27 | @mock.patch("lambda_ops_metrics.push_builder_metrics", return_value=None) 28 | def test_lambda_handler_on_exception(mock_lambda_context, monkeypatch): 29 | # When error is thrown for env variables not set, it doesn't raise Exception 30 | envs = {} 31 | monkeypatch.setattr(os, "environ", envs) 32 | with does_not_raise(): 33 | assert handler({}, mock_lambda_context) == None 34 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/test/utils/test_lambda_context_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | from utils.lambda_context_parser import get_invocation_account_id 5 | 6 | 7 | def test_get_invocation_account_id(mock_lambda_context): 8 | account_id = get_invocation_account_id(mock_lambda_context) 9 | assert account_id == "123456789012" 10 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/lambda/custom-resource/utils/__init__.py -------------------------------------------------------------------------------- /source/lambda/custom-resource/utils/data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | import uuid 5 | from dataclasses import dataclass 6 | from datetime import datetime, timezone 7 | 8 | import urllib3 9 | from aws_lambda_powertools import Logger, Tracer 10 | 11 | logger = Logger(utc=True) 12 | tracer = Tracer() 13 | 14 | http = urllib3.PoolManager() 15 | UUID_VERSION = 4 16 | 17 | 18 | @dataclass 19 | class BuilderMetrics: 20 | solution_id: str 21 | version: str 22 | data: dict 23 | timestamp: datetime 24 | uuid: uuid 25 | 26 | def __init__(self, solution_id: str, version: str, data: dict = None, uuid: uuid = None): 27 | self.solution_id = solution_id 28 | self.version = version 29 | self.data = data if data else {} 30 | self.timestamp = datetime.now(timezone.utc).isoformat() 31 | self.uuid = uuid 32 | 33 | def __post_init__(self): 34 | if not isinstance(self.solution_id, str): 35 | raise TypeError(f"Expected {self.solution_id} to be a str") 36 | 37 | if not isinstance(self.version, str): 38 | raise TypeError(f"Expected {self.version} to be a str") 39 | 40 | if not isinstance(self.data, dict): 41 | raise TypeError(f"Expected {self.data} to be a dict") 42 | 43 | try: 44 | if self.uuid is not None: 45 | uuid.UUID(self.uuid, version=UUID_VERSION) 46 | except ValueError: 47 | raise TypeError(f"Expected {self.uuid} to be a UUID") 48 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/utils/lambda_context_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | from aws_lambda_powertools import Logger, Tracer 6 | 7 | logger = Logger(utc=True) 8 | tracer = Tracer() 9 | 10 | ARN_ACCOUNT_ID_INDEX = 4 11 | 12 | 13 | @tracer.capture_method 14 | def get_invocation_account_id(lambda_context): 15 | """Parses the account id from the lambda context. 16 | 17 | Returns: 18 | str: the account id 19 | """ 20 | return lambda_context.invoked_function_arn.split(":")[ARN_ACCOUNT_ID_INDEX] 21 | -------------------------------------------------------------------------------- /source/lambda/custom-resource/utils/metrics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | import json 5 | import os 6 | from datetime import datetime 7 | 8 | import urllib3 9 | from aws_lambda_powertools import Logger, Tracer 10 | 11 | from utils.constants import METRICS_ENDPOINT 12 | from utils.data import BuilderMetrics 13 | 14 | logger = Logger(utc=True) 15 | tracer = Tracer() 16 | 17 | http = urllib3.PoolManager() 18 | 19 | 20 | @tracer.capture_method 21 | def verify_env_setup(): 22 | """ 23 | This method verifies the mandatory environment variables 'SOLUTION_ID' and 'SOLUTION_VERSION'. 24 | """ 25 | if os.getenv("SOLUTION_ID") is None: 26 | err_msg = "SOLUTION_ID Lambda Environment variable not set." 27 | raise ValueError(err_msg) 28 | 29 | if os.getenv("SOLUTION_VERSION") is None: 30 | err_msg = "SOLUTION_VERSION Lambda Environment variable not set." 31 | raise ValueError(err_msg) 32 | 33 | 34 | @tracer.capture_method 35 | def push_builder_metrics(builder_metrics: BuilderMetrics): 36 | try: 37 | headers = {"Content-Type": "application/json"} 38 | payload = json.dumps( 39 | { 40 | "Solution": builder_metrics.solution_id, 41 | "Version": builder_metrics.version, 42 | "TimeStamp": datetime.utcnow().isoformat(), 43 | "Data": builder_metrics.data, 44 | "UUID": builder_metrics.uuid, 45 | } 46 | ) 47 | logger.info(f"Publishing anonymous metrics: {payload}") 48 | http.request(method="POST", url=METRICS_ENDPOINT, headers=headers, body=payload) 49 | except Exception as ex: 50 | logger.error(f"Error occurred when making the http request to the metrics endpoint, Error is {ex}") 51 | raise ex 52 | -------------------------------------------------------------------------------- /source/lambda/entity-detection/entity-detection-sync.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const SharedLib = require('common-node-lib'); 7 | const comprehendSync = require('./utils/sync'); 8 | 9 | exports.handler = async (event) => { 10 | console.debug('Entity Detection Sync - Lambda invoked. Now check for Lambda environment variables'); 11 | this.checkEnvSetup(); 12 | 13 | await SharedLib.processRecordsSync(event.Records, comprehendSync.runSyncEntityDetection); 14 | }; 15 | 16 | exports.checkEnvSetup = () => { 17 | comprehendSync.checkComprehendSyncEnvSetup(); 18 | }; 19 | -------------------------------------------------------------------------------- /source/lambda/entity-detection/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const ENTITY_DETECTION_STAGE_NAME = 'ENTITY-DETECTION'; 7 | const SharedLib = require('common-node-lib'); 8 | const { runSyncEntityDetection } = require('./util/sync'); 9 | 10 | module.exports = { 11 | SharedLib, 12 | ENTITY_DETECTION_STAGE_NAME, 13 | runSyncEntityDetection 14 | }; 15 | -------------------------------------------------------------------------------- /source/lambda/entity-detection/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/entity-detection/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "entity-detection", 3 | "version": "1.1.16", 4 | "description": "This lambda function detects entities within Text", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "clean-dev": "rm -fr node_modules && npm i --omit=dev", 11 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 12 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 13 | "code-linter": "npm run code-linter-ts && npm run code-linter-js", 14 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config .prettierrc.yml '**/*.ts' '**/*.js' --write" 15 | }, 16 | "author": { 17 | "name": "Amazon Web Services", 18 | "url": "https://aws.amazon.com/solutions" 19 | }, 20 | "license": "Apache-2.0", 21 | "devDependencies": { 22 | "aws-sdk-mock": "^5.7.0", 23 | "eslint": "^9.15.0", 24 | "jest": "^29.7.0", 25 | "prettier": "^3.3.3" 26 | }, 27 | "overrides": { 28 | "cross-spawn": "^7.0.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/lambda/entity-detection/test/entity-detection-sync.spec.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | const lambda = require('../entity-detection-sync'); 6 | const SharedLib = require('common-node-lib'); 7 | const syncUtils = require('../utils/sync'); 8 | const utils = require('../utils/generic'); 9 | 10 | const { sqsMessage, expectedSyncComprehendResponse } = require('./event-test-data'); 11 | 12 | jest.mock('../utils/sync'); 13 | jest.mock('common-node-lib'); 14 | 15 | describe('When provided with proper inputs', () => { 16 | beforeAll(() => { 17 | process.env.AWS_REGION = 'fake-region'; 18 | process.env.AWS_SDK_USER_AGENT = '{ "customUserAgent": "AwsSolution/SO0999/v9.9.9" }'; 19 | }); 20 | 21 | afterEach(() => { 22 | delete process.env.ENTITY_DETECTION_JOB_TYPE; 23 | delete process.env.CUSTOM_COMPREHEND_ARN; 24 | delete process.env.ENTITY_DETECTION_LANGUAGE; 25 | jest.clearAllMocks(); 26 | }); 27 | 28 | afterAll(() => { 29 | delete process.env.AWS_REGION; 30 | delete process.env.AWS_SDK_USER_AGENT; 31 | }); 32 | 33 | it('should invoke the lambda function successfully for medical', async () => { 34 | process.env.ENTITY_DETECTION_JOB_TYPE = utils.jobTypes.MEDICAL; 35 | const response = await lambda.handler(sqsMessage); 36 | expect(SharedLib.processRecordsSync).toHaveBeenCalled(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /source/lambda/entity-detection/utils/entity/entity-context.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | /** 6 | * This class is the context for the Comprehend Strategy pattern. It is the 7 | * container for the strategy object and the common operations that are 8 | * common to all supported versions of Comprehend. 9 | */ 10 | class EntityContext { 11 | constructor() { 12 | this.jobType = null; 13 | } 14 | /** 15 | * This acts as the Strategy interface that declares operations common to 16 | * all supported versions of entity 17 | */ 18 | setComprehendType(jobType) { 19 | this.jobType = jobType; 20 | } 21 | 22 | async getComprehendResult(params) { 23 | return await this.jobType.getComprehendResult(params); 24 | } 25 | 26 | addEntityLocations(params) { 27 | this.jobType.addEntityLocations(params); 28 | } 29 | } 30 | 31 | module.exports = { EntityContext }; 32 | -------------------------------------------------------------------------------- /source/lambda/fetch-records/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/fetch-records/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fetch-records", 3 | "version": "1.1.16", 4 | "description": "This lambda backs the ApiGateway REST endpoint to process record and document fetch requests", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "clean-dev": "rm -rf node_modules && npm i --omit=dev", 11 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 12 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 13 | "code-linter": "npm run code-linter-ts && npm run code-linter-js", 14 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config .prettierrc.yml '**/*.ts' '**/*.js' --write" 15 | }, 16 | "author": { 17 | "name": "Amazon Web Services", 18 | "url": "https://aws.amazon.com/solutions" 19 | }, 20 | "license": "Apache-2.0", 21 | "dependencies": { 22 | "uuid": "^9.0.1" 23 | }, 24 | "devDependencies": { 25 | "aws-sdk-mock": "^5.8.0", 26 | "eslint": "^9.15.0", 27 | "jest": "^29.7.0", 28 | "prettier": "^3.3.3" 29 | }, 30 | "overrides": { 31 | "cross-spawn": "^7.0.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/lambda/get-inferences/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/get-inferences/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-inferences", 3 | "version": "1.1.16", 4 | "description": "This lambda function is used to get inference results by backing the rest-endpoints", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config ../../.prettierrc.yml '**/*.{js,json,css,md}' !package*.json --write", 11 | "code-linter": "./node_modules/eslint/bin/eslint.js . -c ../.eslintrc.js --ext .js" 12 | }, 13 | "author": { 14 | "name": "Amazon Web Services", 15 | "url": "https://aws.amazon.com/solutions" 16 | }, 17 | "license": "Apache-2.0", 18 | "devDependencies": { 19 | "aws-sdk-mock": "^5.8.0", 20 | "eslint": "^9.15.0", 21 | "jest": "^29.7.0", 22 | "prettier": "^3.0.0" 23 | }, 24 | "overrides": { 25 | "cross-spawn": "^7.0.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/lambda/layers/aws-node-user-agent-config/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const USER_AGENT = 7 | '^(AwsSolution)\\/SO(\\d+)([a-zA-Z]*)\\/v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$'; 8 | 9 | const usrAgentRegex = new RegExp(USER_AGENT); 10 | 11 | exports.customAwsConfig = function () { 12 | checkEnvSetup(); 13 | 14 | return { 15 | ...JSON.parse(process.env.AWS_SDK_USER_AGENT), 16 | region: process.env.AWS_REGION, 17 | maxRetries: 5 18 | }; 19 | }; 20 | 21 | const checkEnvSetup = () => { 22 | if (!process.env.AWS_SDK_USER_AGENT) { 23 | throw new Error('User-agent for SDK not set as environment variables'); 24 | } 25 | 26 | const jsonUsrAgent = JSON.parse(process.env.AWS_SDK_USER_AGENT); 27 | 28 | if (!jsonUsrAgent.hasOwnProperty('customUserAgent')) { 29 | throw new Error('The environment variable JSON string does not have key "customUserAgent"'); 30 | } 31 | 32 | if (!usrAgentRegex.test(jsonUsrAgent.customUserAgent)) { 33 | throw new Error( 34 | 'User-agent for SDK does not meet the required format. The format should be "AwsSolution/SO/v", where id is the numeric id of the solution and version is the semver version number format' 35 | ); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /source/lambda/layers/aws-node-user-agent-config/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | testEnvironment: 'node', 6 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 7 | collectCoverage: true, 8 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 9 | coverageReporters: ['text', ['lcov', { 'projectRoot': '../../../../' }]] 10 | }; 11 | -------------------------------------------------------------------------------- /source/lambda/layers/aws-node-user-agent-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-nodesdk-custom-config", 3 | "version": "1.1.16", 4 | "description": "AWS Nodejs SDK Config intialization layer", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config ../../../.prettierrc.yml '**/*.{js,json,css,md}' !package*.json --write", 11 | "code-linter": "./node_modules/eslint/bin/eslint.js . -c ../../.eslintrc.js --ext .js" 12 | }, 13 | "devDependencies": { 14 | "eslint": "^8.56.0", 15 | "jest": "^29.7.0", 16 | "prettier": "^3.1.1" 17 | }, 18 | "overrides": { 19 | "@babel/traverse": "^7.23.2", 20 | "cross-spawn": "^7.0.5" 21 | }, 22 | "author": { 23 | "name": "Amazon Web Services", 24 | "url": "https://aws.amazon.com/solutions" 25 | }, 26 | "license": "Apache-2.0" 27 | } 28 | -------------------------------------------------------------------------------- /source/lambda/layers/aws-sdk-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-sdk-layer", 3 | "version": "1.1.16", 4 | "description": "AWS Nodejs SDK layer", 5 | "dependencies": { 6 | "aws-sdk": "^2.1613.0" 7 | }, 8 | "author": { 9 | "name": "Amazon Web Services", 10 | "url": "https://aws.amazon.com/solutions" 11 | }, 12 | "license": "Apache-2.0" 13 | } 14 | -------------------------------------------------------------------------------- /source/lambda/layers/aws_boto3/.gitignore: -------------------------------------------------------------------------------- 1 | *.dist-info* 2 | jp.py 3 | dist/ -------------------------------------------------------------------------------- /source/lambda/layers/aws_boto3/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "boto3-layer" 3 | version = "1.1.16" 4 | authors = [ "Amazon Web Services" ] 5 | description = "Layer for AWS Boto3 python SDK" 6 | packages = [ 7 | { include = "*.toml" }, 8 | { include = "*.lock" }, 9 | ] 10 | 11 | [tool.poetry.dependencies] 12 | botocore = "1.35.63" 13 | boto3 = "1.35.63" 14 | python = "^3.11" 15 | 16 | [build-system] 17 | requires = [ "poetry-core>=1.9.0" ] 18 | build-backend = "poetry.core.masonry.api" -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/cognito/decode-jwt-token.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const jwt_decode = require('jwt-decode').jwtDecode; 7 | 8 | /** 9 | * Uses the Authorization header to decode the JWT token 10 | * @param {Object} event - lambda event object 11 | * @returns decoded JWT token 12 | */ 13 | exports.decodeJwtToken = (jwtToken) => { 14 | try { 15 | return jwt_decode(jwtToken); 16 | } catch (error) { 17 | console.error(error); 18 | throw error; 19 | } 20 | }; 21 | 22 | /** 23 | * Uses the Authorization header to decode the JWT token and return the cognito user id 24 | * @param {Object} event - lambda event object 25 | * @returns cognito user id 26 | */ 27 | exports.getUserIdFromEvent = (event) => { 28 | const decodedJwt = this.decodeJwtToken(event.headers.Authorization); 29 | const cognitoUserId = decodedJwt['cognito:username']; 30 | return cognitoUserId; 31 | }; 32 | 33 | /** 34 | * Uses the Authorization header to decode the JWT token and return the cognito user pool name 35 | * and/or group name. 36 | * @param {String} authToken Cognito auth token 37 | * @returns Object containing cognito user id and groups 38 | */ 39 | exports.getCognitoEntityFromAuthToken = (authToken) => { 40 | const decodedJwt = this.decodeJwtToken(authToken); 41 | return Object.assign( 42 | {}, 43 | 'cognito:username' in decodedJwt ? { 'cognito:username': decodedJwt['cognito:username'] } : {}, 44 | 'cognito:groups' in decodedJwt ? { 'cognito:groups': decodedJwt['cognito:groups'] } : {} 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/ddb/ddb-get-case.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const AWS = require('aws-sdk'); 7 | const CustomConfig = require('aws-node-user-agent-config'); 8 | 9 | /** 10 | * Retrieves all items from the table with a given caseId. If a caseId is 11 | * not found in the table then an Error is thrown. By default all attributes 12 | * are returned. 13 | * 14 | * @param {Object} params.caseId JSON object containing the caseId 15 | * @param {Object} params.ddbTableName The name of the table to query 16 | * 17 | * @returns ddb response object 18 | */ 19 | const getCase = async (params) => { 20 | const awsCustomConfig = CustomConfig.customAwsConfig(); 21 | const dynamoDB = new AWS.DynamoDB(awsCustomConfig); 22 | 23 | const caseManagerTable = params.ddbTableName; 24 | const caseId = params.caseId; 25 | 26 | try { 27 | if (!caseManagerTable) { 28 | throw new Error('Table name is required'); 29 | } 30 | 31 | const ddbParams = { 32 | TableName: caseManagerTable, 33 | KeyConditionExpression: 'CASE_ID = :c', 34 | ExpressionAttributeValues: { 35 | ':c': { 36 | S: caseId 37 | } 38 | } 39 | }; 40 | 41 | const response = await dynamoDB.query(ddbParams).promise(); 42 | if (!response.Count) { 43 | console.error(`CaseId::${caseId} NOT found in Cases table.`); 44 | throw new Error('Incorrect CaseId'); 45 | } 46 | 47 | return response; 48 | } catch (error) { 49 | console.error(`Error retrieving caseId: ${caseId} \n`, error); 50 | throw error; 51 | } 52 | }; 53 | 54 | module.exports = { getCase }; 55 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/event-bridge/event-dispatcher.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const AWS = require('aws-sdk'); 7 | const CustomConfig = require('aws-node-user-agent-config'); 8 | 9 | const { EventSources } = require('./../constants'); 10 | 11 | /** 12 | * Publish an event to the the custom event bus with the ARN as defined with 13 | * the `EVENT_BUS_ARN` environment variable. 14 | * 15 | * @param {Object} eventDetail an object which will be stringified and put in the event 16 | * @param {string} eventDetailType the detailType, used by listeners to determine which events to pick up 17 | * 18 | */ 19 | const publishEvent = async (eventDetail, eventDetailType) => { 20 | const awsCustomConfig = CustomConfig.customAwsConfig(); 21 | const eventBridge = new AWS.EventBridge(awsCustomConfig); 22 | const eventString = JSON.stringify(eventDetail); 23 | 24 | const putEventsParams = { 25 | Entries: [ 26 | { 27 | Detail: eventString, 28 | DetailType: eventDetailType, 29 | EventBusName: process.env.EVENT_BUS_ARN, 30 | Source: `${EventSources.WORKFLOW_ORCHESTRATOR}.${process.env.APP_NAMESPACE}` 31 | } 32 | ] 33 | }; 34 | 35 | const response = await eventBridge.putEvents(putEventsParams).promise(); 36 | return response; 37 | }; 38 | 39 | module.exports = { publishEvent }; 40 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../../layers/', '/../../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { 'projectRoot': '../../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/metrics/documents.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | const { MetricNames } = require('../constants'); 5 | const { sendCloudWatchMetrics } = require('./utils/send-metrics'); 6 | 7 | /** 8 | * Publishes metrics for documents uploaded. 9 | * @param {String} namespace passed implicitly from CloudWatchContext to reflect the namespace that the metrics are published to. 10 | * @return {Integer} docsUploadedCount optional param for count of documents uploaded. 11 | */ 12 | class DocumentMetrics { 13 | async publishMetricsData(namespace, docsUploadedCount = 1) { 14 | const params = { 15 | MetricData: [ 16 | { 17 | MetricName: MetricNames.DOCUMENTS, 18 | Dimensions: [ 19 | { Name: 'Documents', Value: 'Upload' }, 20 | { Name: 'serviceName', Value: `eDUS-${process.env.UUID}` } 21 | ], 22 | Timestamp: new Date(), 23 | Unit: 'Count', 24 | Value: docsUploadedCount 25 | } 26 | ], 27 | Namespace: namespace 28 | }; 29 | const data = await sendCloudWatchMetrics(namespace, params); 30 | return data; 31 | } 32 | } 33 | 34 | module.exports = { DocumentMetrics }; 35 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/metrics/utils/send-metrics.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const AWS = require('aws-sdk'); 5 | const UserAgentConfig = require('aws-node-user-agent-config'); 6 | 7 | /** 8 | * Makes the call to cloudwatch for the provided params to send the metrics 9 | * 10 | * @param {Object} params Contains the parameters to be passed on to the cloudwatch metrics call. 11 | */ 12 | exports.sendCloudWatchMetrics = async (namespace, params) => { 13 | console.log(`Publishing cw metrics with params: ${JSON.stringify(params)}`); 14 | try { 15 | const cloudWatchClient = new AWS.CloudWatch(UserAgentConfig.customAwsConfig()); 16 | const data = await cloudWatchClient.putMetricData(params).promise(); 17 | console.log(`Published cw metrics to ${namespace}.`); 18 | return data; 19 | } catch (error) { 20 | console.error(`Failed to publish cw metrics with params: ${JSON.stringify(params)}. Error: ${error.message}`); 21 | return null; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "common-node-lib", 3 | "version": "1.1.16", 4 | "description": "Common libraries shared by all lambda node modules", 5 | "scripts": { 6 | "test": "jest --coverage --silent --verbose", 7 | "test-debug": "jest --coverage", 8 | "clean": "rm -rf node_modules", 9 | "clean-dev": "rm -rf node_modules && npm i --omit=dev", 10 | "code-formatter": "./node_modules/prettier/bin-prettier.js . --config ../../../.prettierrc.yml '**/*.{js,json,css,md}' !package*.json --write", 11 | "code-linter": "./node_modules/eslint/bin/eslint.js . -c ../../.eslintrc.js --ext .js" 12 | }, 13 | "devDependencies": { 14 | "@elastic/elasticsearch": "^8.13.1", 15 | "@elastic/elasticsearch-mock": "^2.0.0", 16 | "aws-sdk-mock": "^5.8.0", 17 | "eslint": "^8.56.0", 18 | "jest": "^29.7.0", 19 | "prettier": "^3.1.1" 20 | }, 21 | "author": { 22 | "name": "Amazon Web Services", 23 | "url": "https://aws.amazon.com/solutions" 24 | }, 25 | "license": "Apache-2.0", 26 | "dependencies": { 27 | "@aws-sdk/credential-provider-node": "^3.569.0", 28 | "@opensearch-project/opensearch": "^2.7.0", 29 | "jwt-decode": "^4.0.0", 30 | "lodash": "^4.17.21", 31 | "pdf-lib": "1.17.1", 32 | "uuid": "^9.0.1" 33 | }, 34 | "overrides": { 35 | "@babel/traverse": "^7.23.2", 36 | "find-my-way": "^8.2.2", 37 | "cross-spawn": "^7.0.5", 38 | "undici": "^5.28.5", 39 | "@aws-sdk/core": { 40 | "fast-xml-parser": "^4.4.1" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/response-formatter/error-response.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | /** 6 | * Formats a error object into a HTTP response with an error status code. 7 | * If error is a string, it is converted to a Object with parameter key `message` 8 | * @param {Error} error 9 | * @returns 10 | */ 11 | function formatError(error) { 12 | // if error is a string then convert to a Object with key message 13 | if (typeof error === 'string') { 14 | error = { 15 | message: error 16 | }; 17 | } 18 | 19 | error.statusCode = error.statusCode ?? '400'; 20 | error.code = error.code ?? 'CustomExecutionError'; 21 | const allowOrigin = process.env.ALLOW_ORIGIN || "*"; 22 | 23 | let response = { 24 | 'statusCode': error.statusCode, 25 | 'headers': { 26 | "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept", 27 | "Access-Control-Allow-Methods": "OPTIONS,POST,GET", 28 | "Access-Control-Allow-Origin": `${allowOrigin}`, 29 | 'Content-Type': 'text/plain', 30 | 'x-amzn-ErrorType': error.code, 31 | 'Access-Control-Allow-Origin': '*' // NOSONAR - javascript:S5122 - Domain not known at this point. 32 | }, 33 | 'isBase64Encoded': false, 34 | 'body': error.code + ': ' + error.message 35 | }; 36 | return response; 37 | } 38 | 39 | module.exports = { formatError }; 40 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/response-formatter/success-response.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | /** 6 | * Utility function to convert any success response into a Http 200 response with the 7 | * proper formatting and headers. 8 | * 9 | * @param {any} body Response message. This will be strigified and inserted into 'body' 10 | * @param {[key: string]: string} extraHeaders any extra headers to include in response. 11 | * any key in extraHeaders will override any header in the defaultHeaders with the same key. 12 | * @returns 13 | */ 14 | function formatResponse(body, extraHeaders) { 15 | const defaultHeaders = { 16 | 'Content-Type': 'application/json', 17 | 'Access-Control-Allow-Headers': 'Origin,X-Requested-With,Content-Type,Accept', 18 | 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET', 19 | 'Access-Control-Allow-Credentials': true, 20 | 'Access-Control-Allow-Origin': '*' // NOSONAR - javascript:S5122 - Domain not known at this point. 21 | }; 22 | const headers = typeof extraHeaders === 'undefined' ? defaultHeaders : { ...defaultHeaders, ...extraHeaders }; 23 | body = typeof body === 'string' ? body : JSON.stringify(body); 24 | let response = { 25 | 'statusCode': 200, 26 | 'headers': headers, 27 | 'isBase64Encoded': false, 28 | 'body': body 29 | }; 30 | return response; 31 | } 32 | 33 | module.exports = { formatResponse }; 34 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/s3/get-request-account-id.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | /** 6 | * Get the account ID from a lambda event 7 | * @param event 8 | * @returns {*} 9 | */ 10 | const getAccountIdFromEvent = (event) => { 11 | if (!event.requestContext.accountId) { 12 | console.error('No request context account ID'); 13 | throw new Error('No request context account ID'); 14 | } 15 | 16 | return event.requestContext.accountId; 17 | }; 18 | 19 | const getAccountIdFromLambdaContext = (context) => { 20 | if (!context) { 21 | console.error('Request context is missing'); 22 | throw new Error('Request context is missing'); 23 | } 24 | if (!context.invokedFunctionArn) { 25 | console.error('No request context invokedFunctionArn'); 26 | throw new Error('No request context invokedFunctionArn'); 27 | } 28 | 29 | const accountId = context.invokedFunctionArn.split(':')[4]; 30 | return accountId; 31 | }; 32 | 33 | module.exports = { getAccountIdFromEvent, getAccountIdFromLambdaContext }; 34 | -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/s3/s3-get-head-object.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const AWS = require('aws-sdk'); 7 | const UserAgentConfig = require('aws-node-user-agent-config'); 8 | 9 | let DOCUMENT_BUCKET_NAME; 10 | 11 | /** 12 | * Check the Lambda environment variables for S3. It sets: 13 | * 14 | * `DOCUMENT_BUCKET_NAME`: This value sets the name of the S3 bucket that stores the documents while Comprehend 15 | * processes them. 16 | * If not set, it will throw an error. 17 | * 18 | */ 19 | function checkS3EnvSetup() { 20 | if (process.env.DOCUMENT_BUCKET_NAME) { 21 | DOCUMENT_BUCKET_NAME = process.env.DOCUMENT_BUCKET_NAME; 22 | } else { 23 | throw new Error('DOCUMENT_BUCKET_NAME Lambda Environment variable not set.'); 24 | } 25 | } 26 | 27 | 28 | async function getHeadObjectFromS3(params) { 29 | const s3Client = new AWS.S3(UserAgentConfig.customAwsConfig()); 30 | checkS3EnvSetup(); 31 | 32 | const s3Params = { 33 | Bucket: params.Bucket ?? DOCUMENT_BUCKET_NAME, 34 | Key: params.Key, 35 | ...params.otherParams 36 | } 37 | try { 38 | 39 | return await s3Client.headObject(s3Params).promise(); 40 | } catch (error) { 41 | console.error(`Error retrieving object with params: ${JSON.stringify(s3Params)}`); 42 | throw error; 43 | } 44 | } 45 | 46 | 47 | module.exports = { 48 | getHeadObjectFromS3 49 | } -------------------------------------------------------------------------------- /source/lambda/layers/common-node-lib/utils/validate-case-access.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | /** 7 | * Validate user to case association. 8 | * @param {string} caseId - case id in the form `{userId}:{caseUUID}` 9 | * @param {Object} requestContext - lambda event request context 10 | * @returns true/ false 11 | */ 12 | function validateUserToCaseAssociation(caseId, requestContext) { 13 | if (caseId === undefined || requestContext === undefined) { 14 | return false; 15 | } 16 | 17 | const userIdFromCase = caseId.split(':')[0]; 18 | const userIdFromAuthClaim = requestContext.authorizer.claims['cognito:username']; 19 | return (userIdFromCase === userIdFromAuthClaim) 20 | } 21 | 22 | module.exports = { validateUserToCaseAssociation }; 23 | -------------------------------------------------------------------------------- /source/lambda/layers/custom-java-sdk-config/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /source/lambda/layers/custom-java-sdk-config/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /source/lambda/layers/custom-java-sdk-config/src/test/java/com/builder/config/CustomUserAgentConfigTest.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.config; 5 | 6 | import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; 7 | import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | 14 | import com.builder.config.CustomUserAgentConfig; 15 | 16 | import uk.org.webcompere.systemstubs.jupiter.SystemStub; 17 | import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; 18 | import org.junit.jupiter.api.Test; 19 | import org.junit.jupiter.api.DisplayName; 20 | 21 | @ExtendWith(SystemStubsExtension.class) 22 | public class CustomUserAgentConfigTest { 23 | CustomUserAgentConfig customConfig; 24 | 25 | @SystemStub 26 | private EnvironmentVariables environmentVariables = new EnvironmentVariables("AWS_SDK_USER_AGENT", 27 | "AwsSolution/SO999/v9.9.9"); 28 | 29 | @Test 30 | @DisplayName("When testing environment variable setup") 31 | void testEnvSetup() { 32 | assertEquals("AwsSolution/SO999/v9.9.9", System.getenv("AWS_SDK_USER_AGENT")); 33 | } 34 | 35 | @Test 36 | @DisplayName("When custom ClientConfiguration is successfully returned") 37 | void testCustomConfig() { 38 | ClientOverrideConfiguration config = CustomUserAgentConfig.getCustomClientConfiguration(); 39 | // assertEquals(config.useTcpKeepAlive(), Boolean.TRUE); 40 | assertEquals(config.advancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX).get(), 41 | "AwsSolution/SO999/v9.9.9"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | **/__init__.py 4 | __init__.py 5 | test/* 6 | .venv*/* 7 | aws_lambda_powertools/* 8 | aws_xray_sdk/* 9 | botocore/* 10 | dateutil/* 11 | jmespath/* 12 | urllib*/* 13 | wrapt/* 14 | setup.py 15 | six.py 16 | typing_extensions.py 17 | s3transfer/* 18 | boto3/** 19 | source = 20 | . -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | aws_lambda_powertools/ 3 | aws_xray_sdk/ 4 | botocore/ 5 | dateutil/ 6 | jmespath/ 7 | urllib*/ 8 | wrapt/ 9 | boto3/ 10 | six.py 11 | python_dateutil/ 12 | typing_extensions.py 13 | **/THIRD-PARTY-LICENSES 14 | *.dist-info 15 | *.egg-info 16 | jp.py 17 | s3transfer/* 18 | dist/ 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | from helper import get_service_client, get_service_resource 5 | from custom_config import custom_usr_agent_config 6 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | 6 | import boto3 7 | from custom_config import custom_usr_agent_config 8 | from aws_lambda_powertools import Logger, Tracer 9 | 10 | logger = Logger(utc=True) 11 | tracer = Tracer() 12 | 13 | _helpers_service_clients = dict() 14 | _helpers_service_resources = dict() 15 | _session = None 16 | 17 | 18 | @tracer.capture_method 19 | def get_session(): 20 | global _session 21 | if not _session: 22 | _session = boto3.session.Session() 23 | return _session 24 | 25 | 26 | @tracer.capture_method 27 | def get_service_client(service_name): 28 | global _helpers_service_clients 29 | session = get_session() 30 | 31 | if service_name not in _helpers_service_clients: 32 | logger.debug(f"Cache miss for {service_name}. Creating a new one and cache it") 33 | _helpers_service_clients[service_name] = session.client(service_name, config=custom_usr_agent_config()) 34 | 35 | return _helpers_service_clients[service_name] 36 | 37 | 38 | @tracer.capture_method 39 | def get_service_resource(service_name): 40 | global _helpers_service_resources 41 | session = get_session() 42 | 43 | if service_name not in _helpers_service_resources: 44 | logger.debug(f"Cache miss for {service_name}. Creating a new one and cache it") 45 | _helpers_service_resources[service_name] = session.resource(service_name, config=custom_usr_agent_config()) 46 | return _helpers_service_resources[service_name] 47 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "custom_boto3_init" 3 | version = "1.1.16" 4 | authors = [ "Amazon Web Services" ] 5 | description = "Initialize boto config for AWS Python SDK with custom configuration" 6 | packages = [ 7 | { include = "*.py" } 8 | ] 9 | classifiers = [ 10 | "Programming Language :: Python :: 3", 11 | "License :: Apache-2.0", 12 | ] 13 | license = "Apache-2.0" 14 | 15 | [tool.poetry.dependencies] 16 | aws-lambda-powertools = "2.43.1" 17 | aws-xray-sdk = "2.14.0" 18 | python = "^3.11" 19 | 20 | [tool.poetry.group.test.dependencies] 21 | coverage = "7.4.4" 22 | pytest = "8.1.1" 23 | pytest-cov = "5.0.0" 24 | mock = "5.1.0" 25 | boto3-layer = { path = "../aws_boto3/", develop = true } 26 | 27 | [tool.black] 28 | line-length = 120 29 | 30 | [tool.isort] 31 | multi_line_output = 3 32 | include_trailing_comma = true 33 | force_grid_wrap = 0 34 | line_length = 120 35 | profile = "black" 36 | 37 | [build-system] 38 | requires = [ "poetry-core>=1.9.0" ] 39 | build-backend = "poetry.core.masonry.api" 40 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | from setuptools import find_packages, setup 5 | 6 | setup( 7 | name="custom_boto3_init", 8 | version="1.1.16", 9 | description="Initialize boto config for AWS Python SDK with custom configuration", 10 | url="https://github.com/aws-solutions/enhanced-document-understanding-on-aws", 11 | author="Amazon Web Services", 12 | license="Apache 2.0", 13 | packages=find_packages(), 14 | install_requires=["aws-lambda-powertools>=2.30.2", "aws-xray-sdk>=2.12.1", "typing-extensions==4.9.0"], 15 | include_package_data=True, 16 | python_requires=">=3.11", 17 | ) 18 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /source/lambda/layers/custom_boto3_init/test/conftest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import pytest 6 | import os 7 | 8 | 9 | @pytest.fixture(autouse=True) 10 | def aws_credentials(): 11 | """Mocked AWS Credentials and general environment variables as required by python based lambda functions""" 12 | os.environ["AWS_ACCESS_KEY_ID"] = "fakeId" 13 | os.environ["AWS_SECRET_ACCESS_KEY"] = "fakeAccessKey" # nosec B105 14 | os.environ["AWS_REGION"] = "us-east-1" # must be a valid region 15 | -------------------------------------------------------------------------------- /source/lambda/redact-content/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /source/lambda/redact-content/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/ApiRequestBody.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Class representing the body of the API Gateway request for redaction. This 13 | * body is composed of 2 main sections: First the "entities" field contains a 14 | * nested structure describing the entities to be redacted, and second is the 15 | * "phrases" field which contains an array of objects indicating specific 16 | * phrases to be redacted. 17 | */ 18 | public class ApiRequestBody { 19 | private Map>>> entities; 20 | private ArrayList phrases; 21 | 22 | public Map>>> getEntities() { 23 | if (entities == null) { 24 | entities = new HashMap<>(); 25 | } 26 | return entities; 27 | } 28 | 29 | public List getPhrases() { 30 | if (phrases == null) { 31 | phrases = new ArrayList<>(); 32 | } 33 | return phrases; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object obj) { 38 | if (obj == null) { 39 | return false; 40 | } 41 | 42 | if (obj.getClass() != this.getClass()) { 43 | return false; 44 | } 45 | 46 | final ApiRequestBody other = (ApiRequestBody) obj; 47 | return phrases.equals(other.phrases) && entities.equals(other.entities); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return phrases.hashCode() + entities.hashCode(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/EntityDetails.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Representing the "EntityDetails" of a single entity instance in an 13 | * -locations inference (as computed by the entity-detection 14 | * lambda). 15 | */ 16 | public class EntityDetails { 17 | @SerializedName("Score") 18 | private double score; 19 | @SerializedName("BoundingBoxes") 20 | private ArrayList boundingBoxes; 21 | 22 | // add getters 23 | public double getScore() { 24 | return score; 25 | } 26 | 27 | public List getBoundingBoxes() { 28 | return boundingBoxes; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | if (obj == null) { 34 | return false; 35 | } 36 | 37 | if (obj.getClass() != this.getClass()) { 38 | return false; 39 | } 40 | 41 | final EntityDetails other = (EntityDetails) obj; 42 | return other.score == this.score && other.boundingBoxes.equals(this.boundingBoxes); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return (int) (score * 100) + boundingBoxes.hashCode(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/EventDataBody.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | /** 7 | * Data class representing the json-encoded content in the "body" field of the 8 | * incoming SQS message. References nested classes. 9 | */ 10 | public class EventDataBody { 11 | private String taskToken; 12 | private EventDataInput input; 13 | 14 | public String getTaskToken() { 15 | return taskToken; 16 | } 17 | 18 | public EventDataInput getInput() { 19 | return input; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object obj) { 24 | if (obj == null) { 25 | return false; 26 | } 27 | 28 | if (obj.getClass() != this.getClass()) { 29 | return false; 30 | } 31 | 32 | final EventDataBody other = (EventDataBody) obj; 33 | return taskToken.equals(other.taskToken) && input.equals(other.input); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return (taskToken + input).hashCode(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/EventDataInput.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Data class representing the input field from the parsed json received via the 11 | * "body" field in the SQS payload. 12 | */ 13 | public class EventDataInput { 14 | private String stage; 15 | private EventDataDocument document; 16 | private HashMap inferences; 17 | 18 | // getters 19 | public String getStage() { 20 | return stage; 21 | } 22 | 23 | public EventDataDocument getDocument() { 24 | return document; 25 | } 26 | 27 | public Map getInferences() { 28 | return inferences; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | if (obj == null) { 34 | return false; 35 | } 36 | 37 | if (obj.getClass() != this.getClass()) { 38 | return false; 39 | } 40 | 41 | final EventDataInput other = (EventDataInput) obj; 42 | return stage.equals(other.stage) && document.equals(other.document) && inferences.equals(other.inferences); 43 | } 44 | 45 | /** 46 | * Needed when equals is overridden. Generated definition. 47 | */ 48 | @Override 49 | public int hashCode() { 50 | int hash = 7; 51 | hash = 31 * hash + stage.hashCode(); 52 | hash = 31 * hash + document.hashCode(); 53 | hash = 31 * hash + inferences.hashCode(); 54 | return hash; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/FileType.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | /** 7 | * This enum file defines different types of input documents as constants. 8 | * The redaction lambda will only process these type of files, throws error 9 | * otherwise 10 | */ 11 | public enum FileType { 12 | PDF, 13 | JPEG, 14 | JPG, 15 | PNG; 16 | } 17 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/PhraseRedaction.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Represents phrases to be redacted as requested from the API gateway input. 11 | * Specifies some text to be redacted, and which pages to apply that redaction 12 | * to. 13 | */ 14 | public class PhraseRedaction { 15 | private String text; 16 | private ArrayList pages; 17 | 18 | public String getText() { 19 | return text; 20 | } 21 | 22 | public List getPages() { 23 | return pages; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object obj) { 28 | if (obj == null) { 29 | return false; 30 | } 31 | 32 | if (obj.getClass() != this.getClass()) { 33 | return false; 34 | } 35 | 36 | final PhraseRedaction other = (PhraseRedaction) obj; 37 | return text.equals(other.text) && pages.equals(other.pages); 38 | } 39 | 40 | /** 41 | * Needed when equals is overridden. Generated definition. 42 | */ 43 | @Override 44 | public int hashCode() { 45 | return text.hashCode() + pages.hashCode(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/TextractGeometry.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | public class TextractGeometry { 9 | @SerializedName("BoundingBox") 10 | private BoundingBox boundingBox; 11 | 12 | public BoundingBox getBoundingBox() { 13 | return boundingBox; 14 | } 15 | 16 | @Override 17 | public boolean equals(Object obj) { 18 | if (obj == null) { 19 | return false; 20 | } 21 | 22 | if (obj.getClass() != this.getClass()) { 23 | return false; 24 | } 25 | 26 | final TextractGeometry other = (TextractGeometry) obj; 27 | return this.boundingBox.equals(other.boundingBox); 28 | } 29 | 30 | /** 31 | * Needed when equals is overridden. Generated definition. 32 | */ 33 | @Override 34 | public int hashCode() { 35 | int hash = 7; 36 | hash = 31 * hash + (this.boundingBox != null ? this.boundingBox.hashCode() : 0); 37 | return hash; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/model/TextractRelationship.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import com.google.gson.annotations.SerializedName; 9 | 10 | public class TextractRelationship { 11 | @SerializedName("Type") 12 | private String type; 13 | 14 | @SerializedName("Ids") 15 | private ArrayList ids; 16 | 17 | public String getType() { 18 | return type; 19 | } 20 | 21 | public List getIds() { 22 | return ids; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object obj) { 27 | if (obj == null) { 28 | return false; 29 | } 30 | 31 | if (obj.getClass() != this.getClass()) { 32 | return false; 33 | } 34 | 35 | final TextractRelationship other = (TextractRelationship) obj; 36 | 37 | return type.equals(other.type) && ids.equals(other.ids); 38 | } 39 | 40 | /** 41 | * Needed when equals is overridden. Generated definition. 42 | */ 43 | @Override 44 | public int hashCode() { 45 | return (type + ids).hashCode(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/utils/LambdaContextParser.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.utils; 5 | 6 | import com.amazonaws.services.lambda.runtime.Context; 7 | 8 | import software.amazon.lambda.powertools.logging.Logging; 9 | 10 | public class LambdaContextParser { 11 | 12 | private static final Integer ACCOUNT_ID_INDEX = 4; 13 | private final Context lambdaContext; 14 | 15 | public LambdaContextParser(final Context context) { 16 | this.lambdaContext = context; 17 | } 18 | 19 | public Context getLambdaContext() { 20 | return this.lambdaContext; 21 | } 22 | 23 | /** 24 | * Get the account id by parsing the invocation function arn from 25 | * a given lambda context 26 | * 27 | * @return aws account id 28 | */ 29 | @Logging 30 | public String getInvocationAccountId() { 31 | String invocationFunctionArn = lambdaContext.getInvokedFunctionArn(); 32 | String[] arnParts = invocationFunctionArn.split(":"); 33 | return arnParts[ACCOUNT_ID_INDEX]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/main/java/com/builder/lambda/utils/Redactor.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.utils; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import com.builder.lambda.model.BoundingBox; 12 | import com.builder.lambda.model.Document; 13 | 14 | /** 15 | * This is the base interface for redaction. Any type of Redaction class 16 | * implements this interface 17 | */ 18 | public interface Redactor { 19 | /** 20 | * This method will be called to apply redaction on a document. 21 | * 22 | * @param document to be used to apply redactions on 23 | * @param boundingBoxesByPage to be used to identify the texts to be reacted on 24 | * the document 25 | * @return redacted document as a stream 26 | * @throws IOException if processing the document goes wrong 27 | */ 28 | ByteArrayOutputStream processDocument( 29 | Document document, 30 | Map> boundingBoxesByPage) throws IOException; 31 | } 32 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/com/builder/lambda/model/BoundingBoxTest.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertFalse; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | class BoundingBoxTest { 13 | @Test 14 | public void testMergeBoundingBox() { 15 | BoundingBox boundingBox = new BoundingBox(5, 5, 0, 0); 16 | BoundingBox anotherBoundingBox = new BoundingBox(10, 5, 2, 2); 17 | boundingBox.merge(anotherBoundingBox); 18 | 19 | assertEquals(0, boundingBox.getLeft()); 20 | assertEquals(0, boundingBox.getTop()); 21 | assertEquals(7.0, boundingBox.getHeight()); 22 | assertEquals(12.0, boundingBox.getWidth()); 23 | } 24 | 25 | @Test 26 | public void testEqualsAndHashCode() { 27 | BoundingBox boundingBox = new BoundingBox(5, 5, 0, 0); 28 | assertFalse(boundingBox.equals(null)); 29 | assertFalse(boundingBox.equals(new Object())); 30 | 31 | BoundingBox anotherBoundingBox = new BoundingBox(5, 5, 0, 0); 32 | assertTrue(boundingBox.equals(anotherBoundingBox) && anotherBoundingBox.equals(boundingBox)); 33 | assertEquals(boundingBox.hashCode(), anotherBoundingBox.hashCode()); 34 | } 35 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/com/builder/lambda/model/EventDataBodyTest.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.model; 5 | 6 | import com.google.gson.Gson; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.assertFalse; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | 16 | class EventDataBodyTest { 17 | @Test 18 | public void testEqualsAndHashCode() throws Exception { 19 | EventDataBody eventDataBody = new EventDataBody(); 20 | assertFalse(eventDataBody.equals(null)); 21 | assertFalse(eventDataBody.equals(new Object())); 22 | 23 | Path filePath = Path.of("src/test/java/resources/eventBody1.json"); 24 | String eventBody = Files.readString(filePath); 25 | eventDataBody = new Gson().fromJson(eventBody, EventDataBody.class); 26 | EventDataBody anotherEventDataBody = new Gson().fromJson(eventBody, EventDataBody.class); 27 | 28 | assertTrue(eventDataBody.equals(anotherEventDataBody) && anotherEventDataBody.equals(eventDataBody)); 29 | assertEquals(eventDataBody.hashCode(), anotherEventDataBody.hashCode()); 30 | } 31 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/com/builder/lambda/utils/FileUtilsTest.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.utils; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | import static org.junit.jupiter.api.Assertions.assertThrows; 8 | 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.builder.lambda.model.FileType; 12 | 13 | public class FileUtilsTest { 14 | @Test 15 | public void testGetFileTypeSuccess() throws Exception { 16 | String[] fileNamesToCheck = { "test.jpg", "test.jpeg", "test.png", "test.pdf", "test.jpg.pdf" }; 17 | FileType[] expectedTypes = { FileType.JPG, FileType.JPEG, FileType.PNG, FileType.PDF, FileType.PDF }; 18 | for (int i = 0; i < fileNamesToCheck.length; i++) { 19 | FileType type = FileUtils.getFileType(fileNamesToCheck[i]); 20 | assertEquals(type, expectedTypes[i]); 21 | } 22 | } 23 | 24 | @Test 25 | public void testGetFileTypeFailureBadExtension() throws Exception { 26 | assertThrows(IllegalArgumentException.class, () -> FileUtils.getFileType("test.bad")); 27 | } 28 | 29 | @Test 30 | public void testGetFileTypeFailureNoExtension() throws Exception { 31 | assertThrows(IllegalArgumentException.class, () -> FileUtils.getFileType("no extension")); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/com/builder/lambda/utils/LambdaContextParserTest.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.builder.lambda.utils; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | import static org.mockito.Mockito.*; 9 | 10 | import org.junit.jupiter.api.Test; 11 | 12 | import com.amazonaws.services.lambda.runtime.Context; 13 | 14 | public class LambdaContextParserTest { 15 | 16 | @Test 17 | // test for getInvocationAccountId method 18 | public void testGetInvocationAccountId() { 19 | 20 | Context context = mock(Context.class); 21 | when(context.getInvokedFunctionArn()).thenReturn("arn:aws:lambda:us-east-1:123456789012:function:my-function"); 22 | 23 | LambdaContextParser lamdbaContextParser = new LambdaContextParser(context); 24 | assertEquals("123456789012", lamdbaContextParser.getInvocationAccountId()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/apiRequestBody-entities.json: -------------------------------------------------------------------------------- 1 | { 2 | "entities": { 3 | "entity-standard": { 4 | "OTHER": { 5 | "John Doe": [ 6 | 1, 7 | 2, 8 | 3 9 | ], 10 | "type 2": [ 11 | 2 12 | ] 13 | }, 14 | "DATE": { 15 | "10/23/20, 3:28 PM": [ 16 | 1, 17 | 2 18 | ], 19 | "December 25, 2025": [ 20 | 2 21 | ] 22 | } 23 | }, 24 | "entity-medical": { 25 | "EntityType1": { 26 | "ActualEntityText": [ 27 | 1, 28 | 2, 29 | 3 30 | ] 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/apiRequestBody-phrases.json: -------------------------------------------------------------------------------- 1 | { 2 | "phrases": [ 3 | { 4 | "text": "to blenders Seattle", 5 | "pages": [ 6 | 1, 7 | 2 8 | ] 9 | }, 10 | { 11 | "text": "Seattle", 12 | "pages": [ 13 | 1 14 | ] 15 | }, 16 | { 17 | "text": "not a phrase", 18 | "pages": [ 19 | 1 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/apiRequestBody.json: -------------------------------------------------------------------------------- 1 | { 2 | "entities": { 3 | "entity-standard": { 4 | "OTHER": { 5 | "John Doe": [ 6 | 1, 7 | 2, 8 | 3 9 | ], 10 | "type 2": [ 11 | 2 12 | ] 13 | }, 14 | "DATE": { 15 | "10/23/20, 3:28 PM": [ 16 | 1, 17 | 2 18 | ], 19 | "December 25, 2025": [ 20 | 2 21 | ] 22 | } 23 | }, 24 | "entity-medical": { 25 | "EntityType1": { 26 | "ActualEntityText": [ 27 | 1, 28 | 2, 29 | 3 30 | ] 31 | } 32 | } 33 | }, 34 | "phrases": [ 35 | { 36 | "text": "to blenders Seattle", 37 | "pages": [ 38 | 1, 39 | 2 40 | ] 41 | }, 42 | { 43 | "text": "Seattle", 44 | "pages": [ 45 | 1 46 | ] 47 | }, 48 | { 49 | "text": "not a phrase", 50 | "pages": [ 51 | 1 52 | ] 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/badEventBody1.json: -------------------------------------------------------------------------------- 1 | { 2 | "taskToken": "fakeToken", 3 | "input": { 4 | "stage": "entity", 5 | "document": { 6 | "caseId": "fakeCaseId", 7 | "id": "fakeDocId2", 8 | "selfCertifiedDocType": "Paystub", 9 | "s3Prefix": "s3: //fake-s3uri/fake-bucket/file1.bad", 10 | "piiFlag": false, 11 | "processingType": "sync" 12 | }, 13 | "inferences": { 14 | "textract-detectText": "fake-s3-key3", 15 | "textract-detectText-locations": "fake-s3-key4" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/eventBody1.json: -------------------------------------------------------------------------------- 1 | { 2 | "taskToken": "fakeToken", 3 | "input": { 4 | "stage": "entity", 5 | "document": { 6 | "caseId": "fakeCaseId", 7 | "id": "fakeDocId1", 8 | "selfCertifiedDocType": "Paystub", 9 | "s3Bucket": "fake-bucket", 10 | "s3Prefix": "fake-prefix/file1.jpg", 11 | "piiFlag": false, 12 | "processingType": "sync", 13 | "runTextractAnalyzeAction": true, 14 | "documentWorkflow": [ 15 | "textract", 16 | "entity-standard", 17 | "entity-pii", 18 | "entity-medical", 19 | "redaction" 20 | ], 21 | "uploadedFileExtension": ".jpg", 22 | "uploadedFileName": "file1.jpg" 23 | }, 24 | "inferences": { 25 | "textract-detectText": "fake-s3-key1", 26 | "textract-detectText-locations": "fake-user-id/fake-doc-id/textract-detectText-locations.json" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/eventBody2.json: -------------------------------------------------------------------------------- 1 | { 2 | "taskToken": "fakeToken", 3 | "input": { 4 | "stage": "entity", 5 | "document": { 6 | "caseId": "fakeCaseId", 7 | "id": "fakeDocId2", 8 | "selfCertifiedDocType": "Paystub", 9 | "s3Bucket": "", 10 | "s3Prefix": "fake-prefix/file1.pdf", 11 | "piiFlag": false, 12 | "processingType": "sync", 13 | "runTextractAnalyzeAction": true, 14 | "documentWorkflow": [ 15 | "textract", 16 | "entity-standard", 17 | "entity-pii", 18 | "entity-medical", 19 | "redaction" 20 | ], 21 | "uploadedFileExtension": ".pdf", 22 | "uploadedFileName": "file1.pdf" 23 | }, 24 | "inferences": { 25 | "textract-detectText": "fake-s3-key3", 26 | "textract-detectText-locations": "fake-user-id/fake-doc-id/textract-detectText-locations.json" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /source/lambda/redact-content/src/test/java/resources/patient_intake_form_sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/lambda/redact-content/src/test/java/resources/patient_intake_form_sample.jpg -------------------------------------------------------------------------------- /source/lambda/search/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "search", 3 | "version": "1.1.16", 4 | "description": "This lambda function is used to search for queries in the kendra index backing the rest-endpoints", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "zip": "rm -rf package-lock.json && zip -rq --exclude=*test* search.zip .", 10 | "clean": "rm -rf node_modules", 11 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config ../../.prettierrc.yml '**/*.{js,json,css,md}' !package*.json --write", 12 | "code-linter": "./node_modules/eslint/bin/eslint.js . -c ../.eslintrc.js --ext .js" 13 | }, 14 | "author": { 15 | "name": "Amazon Web Services", 16 | "url": "https://aws.amazon.com/solutions" 17 | }, 18 | "license": "Apache-2.0", 19 | "devDependencies": { 20 | "aws-sdk-mock": "^5.8.0", 21 | "eslint": "^9.15.0", 22 | "jest": "^29.7.0", 23 | "prettier": "^3.3.3" 24 | }, 25 | "overrides": { 26 | "cross-spawn": "^7.0.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/lambda/search/test/event-test-data.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | exports.mockedOpenSearchResponse = [ 7 | { 8 | 'case_id': 'id-1', 9 | 'user_id': 'user', 10 | 'doc_type': 'generic', 11 | 'document_id':'id-1', 12 | 'file_name': 'example.jpg', 13 | 'file_type': '.jpg', 14 | 'lines': [ 15 | 'some-content' 16 | ] 17 | } 18 | ]; 19 | -------------------------------------------------------------------------------- /source/lambda/search/utils/env-setup.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | let KENDRA_INDEX_ID; 6 | let REGION; 7 | let ENDPOINT; 8 | 9 | function checkKendraIndexIdEnvSetup() { 10 | let checkEnv = false; 11 | if (process.env.KENDRA_INDEX_ID) { 12 | KENDRA_INDEX_ID = process.env.KENDRA_INDEX_ID; 13 | checkEnv = true; 14 | } 15 | 16 | return checkEnv; 17 | } 18 | 19 | /** 20 | * Check the Lambda environment variables for OpenSearch - search documents. It sets: 21 | * 22 | * `AWS_REGION`: This value sets the region of the where OpenSearch client will be calling to. 23 | * If not set, it will throw an error. 24 | * 25 | * `OS_COLLECTION_ENDPOINT`: This value sets the endpoint of the OpenSearch serverless cluster. 26 | * If not set, it will throw an error. 27 | */ 28 | function checkOpenSearchEnvSetup() { 29 | let checkEnv = false; 30 | if (process.env.AWS_REGION && process.env.OS_COLLECTION_ENDPOINT) { 31 | REGION = process.env.AWS_REGION; 32 | ENDPOINT = process.env.OS_COLLECTION_ENDPOINT; 33 | checkEnv = true; 34 | } 35 | 36 | return checkEnv; 37 | } 38 | 39 | function checkAllEnvSetup() { 40 | const checkKendra = checkKendraIndexIdEnvSetup(); 41 | const checkOpenSearch = checkOpenSearchEnvSetup(); 42 | 43 | if (!(checkKendra || checkOpenSearch)) { 44 | throw new Error( 45 | 'Either KENDRA_INDEX_ID Lambda Environment variable is not set or AWS_REGION and OS_COLLECTION_ENDPOINT is not set' 46 | ); 47 | } 48 | } 49 | 50 | module.exports = { 51 | checkKendraIndexIdEnvSetup, 52 | checkOpenSearchEnvSetup, 53 | checkAllEnvSetup 54 | }; 55 | -------------------------------------------------------------------------------- /source/lambda/send-notification/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/send-notification/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "send-notification", 3 | "version": "1.1.16", 4 | "description": "This lambda function sends notifications about document processing updates", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 11 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 12 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config ../../.prettierrc.yml '**/*.{js,json,css,md}' !package*.json --write", 13 | "code-linter": "./node_modules/eslint/bin/eslint.js . -c ../.eslintrc.js --ext .js" 14 | }, 15 | "author": { 16 | "name": "Amazon Web Services", 17 | "url": "https://aws.amazon.com/solutions" 18 | }, 19 | "license": "Apache-2.0", 20 | "devDependencies": { 21 | "aws-sdk-mock": "^5.7.0", 22 | "eslint": "^9.15.0", 23 | "jest": "^29.7.0", 24 | "prettier": "^3.3.3" 25 | }, 26 | "overrides": { 27 | "cross-spawn": "^7.0.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/lambda/send-notification/utils/sns-send-notification.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const AWS = require('aws-sdk'); 7 | const UserAgentConfig = require('aws-node-user-agent-config'); 8 | 9 | let SNS_TOPIC_ARN; 10 | 11 | /** 12 | * Check the Lambda environment variables for SNS. It sets: 13 | * 14 | * `SNS_TOPIC_ARN`: This value sets the name of the topic that the notification is published to 15 | * If not set, it will throw an error. 16 | * 17 | */ 18 | exports.checkSNSEnvSetup = async () => { 19 | if (process.env.SNS_TOPIC_ARN) { 20 | SNS_TOPIC_ARN = process.env.SNS_TOPIC_ARN; 21 | console.debug(`SNS_TOPIC_ARN is: ${SNS_TOPIC_ARN}`); 22 | } else { 23 | throw new Error('SNS_TOPIC_ARN Lambda Environment variable not set.'); 24 | } 25 | }; 26 | 27 | /** 28 | * Method to send the SNS notification. 29 | * This method should be called after the @checkSNSEnvSetup method has been called. 30 | * 31 | * @param {*} message 32 | * @param {*} subject 33 | * @param {*} topicArn 34 | * 35 | * @returns 36 | */ 37 | exports.sendSNSNotification = async (message, subject = 'Document Processing Update', topicArn = SNS_TOPIC_ARN) => { 38 | if (!SNS_TOPIC_ARN) { 39 | await this.checkSNSEnvSetup(); 40 | } 41 | const sns = new AWS.SNS(UserAgentConfig.customAwsConfig()); 42 | 43 | const params = { 44 | Message: message, 45 | Subject: subject, 46 | TopicArn: topicArn 47 | }; 48 | return sns.publish(params).promise(); 49 | }; 50 | -------------------------------------------------------------------------------- /source/lambda/text-extract/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const TEXTRACT_WORKFLOW_STAGE = 'DOCUMENT_TEXTRACT'; 7 | const { runSyncTextractJob, startSyncTextDetectionJob } = require('./utils/sync'); 8 | const { 9 | extractS3UriComponents, 10 | supportedImageTypes, 11 | documentTypes, 12 | docTypeMapping, 13 | getTextractApiType 14 | } = require('./utils/generic'); 15 | 16 | module.exports = { 17 | TEXTRACT_WORKFLOW_STAGE, 18 | runSyncTextractJob, 19 | startSyncTextDetectionJob, 20 | extractS3UriComponents, 21 | supportedImageTypes, 22 | documentTypes, 23 | docTypeMapping, 24 | getTextractApiType 25 | }; 26 | -------------------------------------------------------------------------------- /source/lambda/text-extract/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: [ 6 | '/../layers/', 7 | '/../layers/aws-sdk-lib/node_modules/', 8 | '/../layers/common-node-lib/node_modules/' 9 | ], 10 | testEnvironment: 'node', 11 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 12 | collectCoverage: true, 13 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 14 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 15 | }; 16 | -------------------------------------------------------------------------------- /source/lambda/text-extract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "text-extract", 3 | "version": "1.1.16", 4 | "description": "This lambda function extracts text from a document like ID or Paystub, and also detects texts.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "clean-dev": "rm -rf node_modules && npm i --omit=dev", 11 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config ../../.prettierrc.yml '**/*.{js,json,css,md}' !package*.json --write", 12 | "code-linter": "./node_modules/eslint/bin/eslint.js . -c ../.eslintrc.js --ext .js" 13 | }, 14 | "author": { 15 | "name": "Amazon Web Services", 16 | "url": "https://aws.amazon.com/solutions" 17 | }, 18 | "license": "Apache-2.0", 19 | "devDependencies": { 20 | "aws-sdk-mock": "^5.8.0", 21 | "eslint": "^9.15.0", 22 | "jest": "^29.7.0", 23 | "prettier": "^3.3.3" 24 | }, 25 | "overrides": { 26 | "cross-spawn": "^7.0.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/lambda/text-extract/test/index.spec.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | 5 | describe('Textract: When provided with correct inputs', () => { 6 | beforeAll(() => { 7 | process.env.AWS_SDK_USER_AGENT = '{ "customUserAgent": "AwsSolution/SO0999/v9.9.9" }'; 8 | }); 9 | 10 | it('The Textract Lambda should return Successfully', async () => { 11 | console.log('passed'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /source/lambda/text-extract/test/utils/generic.spec.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const utils = require('../../utils/generic'); 7 | 8 | describe('getTextractApiType: When provided with correct inputs', () => { 9 | it('receives Document type input', async () => { 10 | let actualResponse = utils.getTextractApiType('Paystub'); 11 | expect(actualResponse).toBe('Document'); 12 | }); 13 | 14 | it('receives expense document', async () => { 15 | let actualResponse = utils.getTextractApiType('Invoice'); 16 | expect(actualResponse).toBe('Expense'); 17 | }); 18 | 19 | it('receives ID document', async () => { 20 | let actualResponse = utils.getTextractApiType('Passport'); 21 | expect(actualResponse).toBe('ID'); 22 | }); 23 | }); 24 | 25 | describe('getTextractApiType: When provided with incorrect inputs', () => { 26 | it('throws an error in getTextractApiType due to unsupported document uploaded', async () => { 27 | let docType = 'Bank Statement'; 28 | expect(() => { 29 | utils.getTextractApiType(docType); 30 | }).toThrow(`The document type (${docType}) is not valid for Textract processing.`); 31 | }); 32 | }); 33 | 34 | describe('When extracting S3Uri components', () => { 35 | it('Should correctly parse the S3Prefix obtained frm ddb record', () => { 36 | const s3Prefix = 'test-case-id/initial/doc-test-id.jpg'; 37 | const [key, fileName, fileExtension] = utils.extractS3UriComponents(s3Prefix); 38 | 39 | expect(key).toEqual('test-case-id/initial/doc-test-id.jpg'); 40 | expect(fileName).toEqual('doc-test-id.jpg'); 41 | expect(fileExtension).toEqual('jpg'); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /source/lambda/text-extract/textract-sync.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const utils = require('./index'); 7 | const SharedLib = require('common-node-lib'); 8 | const { checkTextractSyncEnvSetup } = require('./utils/sync'); 9 | 10 | exports.handler = async (event, context) => { 11 | this.checkEnvSetup(); 12 | 13 | const requestAccountId = SharedLib.getAccountIdFromLambdaContext(context); 14 | await SharedLib.processRecordsSync(event.Records, utils.runSyncTextractJob, requestAccountId); 15 | }; 16 | 17 | exports.checkEnvSetup = () => { 18 | checkTextractSyncEnvSetup(); 19 | }; 20 | -------------------------------------------------------------------------------- /source/lambda/upload-document/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: ['/../layers/', '/../layers/aws-sdk-lib/node_modules/'], 6 | testEnvironment: 'node', 7 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 8 | collectCoverage: true, 9 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 10 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 11 | }; 12 | -------------------------------------------------------------------------------- /source/lambda/upload-document/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upload-document", 3 | "version": "1.1.16", 4 | "description": "This lambda function to upload documents", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "clean": "rm -rf node_modules", 10 | "clean-dev": "rm -rf node_modules && npm i --omit=dev", 11 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 12 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 13 | "code-linter": "npm run code-linter-ts && npm run code-linter-js", 14 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config .prettierrc.yml '**/*.ts' '**/*.js' --write" 15 | }, 16 | "author": { 17 | "name": "Amazon Web Services", 18 | "url": "https://aws.amazon.com/solutions" 19 | }, 20 | "license": "Apache-2.0", 21 | "dependencies": { 22 | "lodash": "4.17.21" 23 | }, 24 | "devDependencies": { 25 | "aws-sdk-mock": "^5.8.0", 26 | "eslint": "^9.15.0", 27 | "jest": "^29.7.0", 28 | "prettier": "^3.1.1" 29 | }, 30 | "overrides": { 31 | "cross-spawn": "^7.0.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/lambda/upload-document/utils/fetch-case.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 'use strict'; 4 | const AWS = require('aws-sdk'); 5 | const CustomConfig = require('aws-node-user-agent-config'); 6 | 7 | /** 8 | * Retrieves all items from the table with a given caseId. If a caseId is 9 | * not found in the table then an Error is thrown. By default all attributes 10 | * are returned. 11 | * @param {Object} params JSON object containing the caseId 12 | * @returns 13 | */ 14 | const getCase = async (params) => { 15 | const awsCustomConfig = CustomConfig.customAwsConfig(); 16 | const dynamoDB = new AWS.DynamoDB(awsCustomConfig); 17 | 18 | const caseId = params.caseId; 19 | const ddbParams = { 20 | TableName: process.env.CASE_DDB_TABLE_NAME, 21 | KeyConditionExpression: 'CASE_ID = :c', 22 | ExpressionAttributeValues: { 23 | ':c': { 24 | S: caseId 25 | } 26 | } 27 | }; 28 | try { 29 | const response = await dynamoDB.query(ddbParams).promise(); 30 | console.debug(`RESPONSE from dynamoDB.query: ${JSON.stringify(response)}`); 31 | if (!response.Count) { 32 | console.error(`CaseId::${caseId} NOT found in Cases table.`); 33 | throw new Error('Incorrect CaseId'); 34 | } 35 | 36 | return response; 37 | } catch (error) { 38 | console.error(`Error retrieving caseId: ${caseId} \n`, error); 39 | throw error; 40 | } 41 | }; 42 | 43 | module.exports = { getCase }; 44 | -------------------------------------------------------------------------------- /source/lambda/workflow-orchestrator/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | modulePaths: [ 6 | '/../layers/', 7 | '/../layers/aws-sdk-lib/node_modules/', 8 | '/../layers/common-node-lib/node_modules/' 9 | ], 10 | testEnvironment: 'node', 11 | testMatch: ['test/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], 12 | collectCoverage: true, 13 | collectCoverageFrom: ['**.js', '!coverage/**', '!test/*.js', '!jest.config.js'], 14 | coverageReporters: ['text', ['lcov', { projectRoot: '../../../' }]] 15 | }; 16 | -------------------------------------------------------------------------------- /source/lambda/workflow-orchestrator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workflow-orchestrator", 3 | "version": "1.1.16", 4 | "description": "This lambda validates if required documents for a workflow has been uploaded", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage --silent --verbose", 8 | "test-debug": "jest --coverage", 9 | "zip": "rm -rf package-lock.json && zip -rq --exclude=*test* workflow-orchestrator.zip .", 10 | "clean": "rm -rf node_modules", 11 | "clean-dev": "rm -rf node_modules && npm i --omit=dev", 12 | "code-linter-js": "./node_modules/eslint/bin/eslint.js lambda --ext .js", 13 | "code-linter-ts": "./node_modules/eslint/bin/eslint.js bin lib --ext .ts", 14 | "code-linter": "npm run code-linter-ts && npm run code-linter-js", 15 | "code-formatter": "./node_modules/prettier/bin-prettier.js --config .prettierrc.yml '**/*.ts' '**/*.js' --write" 16 | }, 17 | "author": { 18 | "name": "Amazon Web Services", 19 | "url": "https://aws.amazon.com/solutions" 20 | }, 21 | "license": "Apache-2.0", 22 | "dependencies": { 23 | "lodash": "4.17.21" 24 | }, 25 | "devDependencies": { 26 | "aws-sdk-mock": "^5.9.0", 27 | "eslint": "^9.15.0", 28 | "jest": "^29.7.0", 29 | "prettier": "^3.1.1" 30 | }, 31 | "overrides": { 32 | "cross-spawn": "^7.0.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "source", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /source/pre-build-jars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | [ "$DEBUG" == 'true' ] && set -x 6 | set -e 7 | 8 | echo "------------------------------------------" 9 | echo "Script executing from: ${PWD}" 10 | echo "------------------------------------------" 11 | 12 | # Write custom instructions to build jars for lambda layers to use in lambda functions on Java runtime 13 | 14 | execution_dir="$PWD" 15 | common_java_sdk_config_layer="$execution_dir/../lambda/layers/custom-java-sdk-config" 16 | 17 | build_layer() { 18 | layer_folder=$1 19 | artifact_jar_name=$2 20 | 21 | echo "-----------------------------------------" 22 | echo "Running maven build for $layer_folder" 23 | echo "-----------------------------------------" 24 | 25 | cd $layer_folder 26 | 27 | echo "-----------------------------------------" 28 | echo "Current directory is: ${PWD}". Running build 29 | echo "-----------------------------------------" 30 | 31 | mvn clean install -DskipTests --quiet --no-transfer-progress 32 | 33 | echo "-----------------------------------------" 34 | echo "Build complete" 35 | echo "-----------------------------------------" 36 | } 37 | 38 | build_layer $common_java_sdk_config_layer custom-java-sdk-config 39 | -------------------------------------------------------------------------------- /source/sample-documents/Finance/Lacey city bonds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Finance/Lacey city bonds.png -------------------------------------------------------------------------------- /source/sample-documents/Finance/Spokane accounting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Finance/Spokane accounting.png -------------------------------------------------------------------------------- /source/sample-documents/Finance/USDC balance sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Finance/USDC balance sheet.png -------------------------------------------------------------------------------- /source/sample-documents/Finance/USDE balance sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Finance/USDE balance sheet.png -------------------------------------------------------------------------------- /source/sample-documents/Medical/Causes_of_Chronic_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Causes_of_Chronic_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Chronic_Kidney_Disease_Tests_Diagnosis__NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Chronic_Kidney_Disease_Tests_Diagnosis__NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Diabetes_Diet_Eating_and_Physical_Activity_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Diabetes_Diet_Eating_and_Physical_Activity_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Diabetes_Gum_Disease_and_Other_Dental_Problems_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Diabetes_Gum_Disease_and_Other_Dental_Problems_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Diabetes_Heart_Disease_and_Stroke_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Diabetes_Heart_Disease_and_Stroke_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Diabetes_Tests_and_Diagnosis_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Diabetes_Tests_and_Diagnosis_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Eating_Right_for_Chronic_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Eating_Right_for_Chronic_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/HIPAA Release Form.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/HIPAA Release Form.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/High_Blood_Pressure_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/High_Blood_Pressure_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Identify_Evaluate_Patients_with_Chronic_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Identify_Evaluate_Patients_with_Chronic_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Insulin_Medicines_and_Other_Diabetes_Treatments_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Insulin_Medicines_and_Other_Diabetes_Treatments_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Kidney_Transplant_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Kidney_Transplant_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Managing_Chronic_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Managing_Chronic_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Managing_Diabetes_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Managing_Diabetes_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Medical History Form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Medical History Form.png -------------------------------------------------------------------------------- /source/sample-documents/Medical/Medical Insurance Claim Form.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Medical Insurance Claim Form.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Medical Progress Tracker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Medical Progress Tracker.png -------------------------------------------------------------------------------- /source/sample-documents/Medical/Nutrition_for_Advanced_Chronic_Kidney_Disease_in_Adults_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Nutrition_for_Advanced_Chronic_Kidney_Disease_in_Adults_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/PersonaSpecific/GeneralPublic/pdf/Creatine_Kinase_-_MedlinePlus_Medical_Test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/PersonaSpecific/GeneralPublic/pdf/Creatine_Kinase_-_MedlinePlus_Medical_Test.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/PersonaSpecific/HealthcareProvider/pdf/Muscular_Dystrophy_NIH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/PersonaSpecific/HealthcareProvider/pdf/Muscular_Dystrophy_NIH.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/PersonaSpecific/HealthcareProvider/pdf/What_is_Muscular_Dystrophy_-_CDC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/PersonaSpecific/HealthcareProvider/pdf/What_is_Muscular_Dystrophy_-_CDC.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/PersonaSpecific/Scientist/pdf/How_is_muscular_dystrophy_diagnosed__NICHD.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/PersonaSpecific/Scientist/pdf/How_is_muscular_dystrophy_diagnosed__NICHD.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/PersonaSpecific/Scientist/pdf/Muscular_Dystrophy_Information_Page_National_Institute_of_Neurological_Disorders_and_Stroke.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/PersonaSpecific/Scientist/pdf/Muscular_Dystrophy_Information_Page_National_Institute_of_Neurological_Disorders_and_Stroke.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/PersonaSpecific/Scientist/pdf/Muscular_Dystrophy__Hope_Through_Research_-_National_Institute_Of_Neurological_Disorders_And_Stroke.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/PersonaSpecific/Scientist/pdf/Muscular_Dystrophy__Hope_Through_Research_-_National_Institute_Of_Neurological_Disorders_And_Stroke.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Pregnancy_if_You_Have_Diabetes_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Pregnancy_if_You_Have_Diabetes_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Preventing_Chronic_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Preventing_Chronic_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Preventing_Diabetes_Problems_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Preventing_Diabetes_Problems_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/Preventing_Type_2_Diabetes_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/Preventing_Type_2_Diabetes_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/The_A1C_Test_Diabetes_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/The_A1C_Test_Diabetes_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/What_If_My_Kidneys_Fail_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/What_If_My_Kidneys_Fail_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/What_Is_Chronic_Kidney_Disease_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/What_Is_Chronic_Kidney_Disease_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Medical/What_is_Diabetes_NIDDK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Medical/What_is_Diabetes_NIDDK.pdf -------------------------------------------------------------------------------- /source/sample-documents/Misc/employmentapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Misc/employmentapp.png -------------------------------------------------------------------------------- /source/sample-documents/Misc/expense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Misc/expense.png -------------------------------------------------------------------------------- /source/sample-documents/Misc/management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Misc/management.png -------------------------------------------------------------------------------- /source/sample-documents/Research/employmentapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/sample-documents/Research/employmentapp.png -------------------------------------------------------------------------------- /source/ui/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | public/runtimeConfig.json 3 | 4 | 5 | -------------------------------------------------------------------------------- /source/ui/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = { 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | }, 8 | testEnvironmentOptions: { 9 | customExportConditions: [''] 10 | }, 11 | testMatch: ['**/*.test.tsx', '**/*.test.ts'], 12 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], 13 | resolver: '/src/resolver.js', 14 | moduleNameMapper: { 15 | '\\.(css|scss)$': 'identity-obj-proxy' 16 | }, 17 | preset: '@cloudscape-design/jest-preset', 18 | testEnvironment: 'jsdom', 19 | setupFilesAfterEnv: ['/src/jest.setup.ts'], 20 | collectCoverageFrom: [ 21 | 'src/**/*.js', 22 | 'src/**/*.tsx', 23 | 'src/**/*.ts', 24 | 'src/**/*.jsx', 25 | 26 | '!**/__test__/**', 27 | '!coverage/**', 28 | '!test/*.js', 29 | '!src/resolver.js', 30 | '!jest.config.js', 31 | '!src/App.js', 32 | '!src/index.js', 33 | // Because this folder holds all the reducers for redux, We don't need to test them via this documentation provided by Redux. https://redux.js.org/usage/writing-tests 34 | '!src/app/reducers/*', 35 | '!src/mock/**', 36 | '!src/test_data/**', 37 | '!node_modules' 38 | ], 39 | coverageDirectory: './coverage/', 40 | collectCoverage: true, 41 | coverageReporters: ['text', ['lcov', { projectRoot: '../../' }]] 42 | }; 43 | -------------------------------------------------------------------------------- /source/ui/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/ui/public/favicon.png -------------------------------------------------------------------------------- /source/ui/public/index.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Enhanced Document Understanding on AWS 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /source/ui/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/ui/public/logo192.png -------------------------------------------------------------------------------- /source/ui/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/enhanced-document-understanding-on-aws/5c3fab21b4713556920c12e756084fa1c7415aad/source/ui/public/logo512.png -------------------------------------------------------------------------------- /source/ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "EDUonAWS", 3 | "name": "Enhanced Document Understanding on AWS", 4 | "icons": [ 5 | { 6 | "src": "favicon.png", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } -------------------------------------------------------------------------------- /source/ui/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /source/ui/src/App.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | .amplify-tabs { 4 | display: none; 5 | } 6 | 7 | .signout-button { 8 | position:absolute; 9 | top: 17px; 10 | right: 20px; 11 | } 12 | -------------------------------------------------------------------------------- /source/ui/src/__test__/KendraDocumentResults.test.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import '@testing-library/jest-dom'; 4 | import { render, screen } from '@testing-library/react'; 5 | import { MemoryRouter } from 'react-router-dom'; 6 | import KendraDocumentResults from '../components/SearchView/Kendra/KendraDocumentResults'; 7 | 8 | describe('KendraDocumentResults', () => { 9 | const results = [ 10 | { Id: 1, DocumentId: 'doc1', DocumentExcerpt: 'Document excerpt 1' }, 11 | { Id: 2, DocumentId: 'doc2', DocumentExcerpt: 'Document excerpt 2' } 12 | ]; 13 | const casesList = [ 14 | { 15 | caseDocuments: [{ name: 'doc1' }], 16 | caseId: 1, 17 | caseName: 'Case 1' 18 | }, 19 | { 20 | caseDocuments: [{ name: 'doc3' }], 21 | caseId: 2, 22 | caseName: 'Case 2' 23 | } 24 | ]; 25 | const setSelectedCaseId = jest.fn(); 26 | const setSelectedDocumentId = jest.fn(); 27 | const setSelectedDocumentFileType = jest.fn(); 28 | it('renders the KendraResultTitle and KendraHighlightedText for each result', () => { 29 | render( 30 | 31 | 38 | 39 | ); 40 | expect(screen.getAllByTestId('kendra-result-title')).toHaveLength(2); 41 | expect(screen.getAllByTestId('kendra-highlighted-text')).toHaveLength(2); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /source/ui/src/__test__/KendraHighlightedText.test.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import '@testing-library/jest-dom'; 4 | import { render, screen } from '@testing-library/react'; 5 | import KendraHighlightedText from '../components/SearchView/Kendra/KendraHighlightedText'; 6 | 7 | describe('KendraHighlightedText', () => { 8 | it('renders text without highlights', () => { 9 | render(); 10 | expect(screen.getByText('Hello world')).toBeInTheDocument(); 11 | }); 12 | 13 | it('renders highlighted text', () => { 14 | const highlights = [ 15 | { BeginOffset: 0, EndOffset: 5 }, 16 | { BeginOffset: 6, EndOffset: 11 } 17 | ]; 18 | render(); 19 | expect(screen.getByText('Hello')).toHaveStyle('background-color: yellow'); 20 | expect(screen.getByText('world')).toHaveStyle('background-color: yellow'); 21 | }); 22 | 23 | it('merges overlapping highlights', () => { 24 | const highlights = [ 25 | { BeginOffset: 0, EndOffset: 5 }, 26 | { BeginOffset: 4, EndOffset: 11 } 27 | ]; 28 | render(); 29 | expect(screen.getByText('Hello world')).toHaveStyle('background-color: yellow'); 30 | }); 31 | 32 | it('handles empty highlights array', () => { 33 | render(); 34 | expect(screen.getByText('Hello world')).toBeInTheDocument(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /source/ui/src/__test__/KeyValueList.test.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { StatusIndicatorProps } from '@cloudscape-design/components'; 5 | import '@testing-library/jest-dom'; 6 | import { render, screen } from '@testing-library/react'; 7 | import { kvPairs } from './test_data'; 8 | import KeyValueList from '../components/KeyValueList'; 9 | 10 | const kvProps = { 11 | currentPageNumber: 1, 12 | documentPageCount: 4, 13 | kvPairs: kvPairs, 14 | switchPage: jest.fn(), 15 | currentStatus: 'success' as StatusIndicatorProps.Type 16 | }; 17 | 18 | test('Renders key-value pairs with count and page separation', async () => { 19 | render(); 20 | expect(await screen.findByText('Key-Value Pairs: 2 Found')).toBeInTheDocument(); 21 | expect(await screen.findByText('Page 1')).toBeInTheDocument(); 22 | expect(await screen.findByText('first name')).toBeInTheDocument(); 23 | expect(await screen.findByText('fake-name')).toBeInTheDocument(); 24 | expect(await screen.findByText('Page 2')).toBeInTheDocument(); 25 | expect(await screen.findByText('last name')).toBeInTheDocument(); 26 | expect(await screen.findByText('fake-last-name')).toBeInTheDocument(); 27 | }); 28 | 29 | test('calls switchPage when a line is clicked', async () => { 30 | render(); 31 | const line = await screen.findByText('last name'); 32 | line.click(); 33 | expect(kvProps.switchPage).toHaveBeenCalledWith(2); 34 | }); 35 | -------------------------------------------------------------------------------- /source/ui/src/__test__/__snapshots__/TableResults.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Renders without tables 1`] = ` 4 |
5 |

8 | No Tables detected 9 |

10 |
11 | `; 12 | -------------------------------------------------------------------------------- /source/ui/src/__test__/internal.test.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { FileSize } from '../components/FileUpload/interfaces'; 4 | import { formatFileSize } from '../components/FileUpload/internal'; 5 | 6 | describe('internal', () => { 7 | it('should convert bytes to file size', () => { 8 | expect(formatFileSize(5, { size: FileSize.BYTES })).toEqual('5.00 bytes'); 9 | // Decimal 10 | expect(formatFileSize(5000, { size: FileSize.KB })).toEqual('5.00 KB'); 11 | expect(formatFileSize(5000000, { size: FileSize.MB })).toEqual('5.00 MB'); 12 | expect(formatFileSize(5000000000, { size: FileSize.GB })).toEqual('5.00 GB'); 13 | // Binary 14 | expect(formatFileSize(5120, { size: FileSize.KIB })).toEqual('5.00 KiB'); 15 | expect(formatFileSize(5243000, { size: FileSize.MIB })).toEqual('5.00 MiB'); 16 | expect(formatFileSize(5369000000, { size: FileSize.GIB })).toEqual('5.00 GiB'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /source/ui/src/__test__/mocks/App.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const App = () =>
Mocked App
; 5 | 6 | export default App; 7 | -------------------------------------------------------------------------------- /source/ui/src/__test__/utils/tesUtils.tsx: -------------------------------------------------------------------------------- 1 | import { RenderOptions, render } from '@testing-library/react'; 2 | import { RootState } from '../../store/reducers/rootReducer'; 3 | import { AppStore, setupStore } from '../../store/store'; 4 | import { PropsWithChildren } from 'react'; 5 | import { Provider } from 'react-redux'; 6 | import userEvent from '@testing-library/user-event'; 7 | import { setupListeners } from '@reduxjs/toolkit/query'; 8 | import { BrowserRouter } from 'react-router-dom'; 9 | 10 | interface ExtendedRenderOptions extends Omit { 11 | preloadedState?: Partial; 12 | store?: AppStore; 13 | mode?: 'deep' | 'shallow'; 14 | routerProvided?: boolean; 15 | } 16 | 17 | export function renderWithProviders( 18 | ui: React.ReactElement, 19 | { 20 | preloadedState = {}, 21 | store = setupStore(preloadedState), 22 | mode = 'deep', 23 | routerProvided = true, 24 | ...renderOptions 25 | }: ExtendedRenderOptions = {} 26 | ) { 27 | setupListeners(store.dispatch); 28 | function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element { 29 | if (routerProvided) { 30 | return {children}; 31 | } else { 32 | return ( 33 | 34 | {children} 35 | 36 | ); 37 | } 38 | } 39 | 40 | return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) }; 41 | } 42 | 43 | export function renderWithProvidersAndUserSetup( 44 | ui: React.ReactElement, 45 | extendedRenderOptions: ExtendedRenderOptions = {} 46 | ) { 47 | return { 48 | user: userEvent.setup(), 49 | ...renderWithProviders(ui, extendedRenderOptions) 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentRenderer/DocumentRenderer.css: -------------------------------------------------------------------------------- 1 | 2 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 */ 4 | 5 | .image { 6 | width: 100%; 7 | } -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/common-components.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { Box, Button, SpaceBetween } from '@cloudscape-design/components'; 4 | 5 | export const TableNoMatchState = (props: any) => ( 6 | 7 | 8 |
9 | No matches 10 | 11 | We can't find a match. 12 | 13 |
14 | 15 |
16 |
17 | ); 18 | 19 | export const TableEmptyState = ({ resourceName, buttonOnClick }: any) => ( 20 | 21 | 22 |
23 | No {resourceName.toLowerCase()}s 24 | 25 | No {resourceName.toLowerCase()}s associated with this resource. 26 | 27 |
28 | 29 |
30 |
31 | ); 32 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/i18n-strings/app-layout.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { AppLayoutProps } from '@cloudscape-design/components'; 4 | 5 | export const appLayoutAriaLabels: AppLayoutProps.Labels = { 6 | navigation: 'Side navigation', 7 | navigationToggle: 'Open side navigation', 8 | navigationClose: 'Close side navigation', 9 | notifications: 'Notifications', 10 | tools: 'Help panel', 11 | toolsToggle: 'Open help panel', 12 | toolsClose: 'Close help panel' 13 | }; 14 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/i18n-strings/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | export * from './app-layout'; 4 | export * from './pagination'; 5 | export * from './split-panel'; 6 | export * from './text-filter'; 7 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/i18n-strings/pagination.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { PaginationProps } from '@cloudscape-design/components'; 4 | 5 | export const paginationAriaLabels: PaginationProps.Labels = { 6 | nextPageLabel: 'Next page', 7 | previousPageLabel: 'Previous page', 8 | pageLabel: (pageNumber) => `Page ${pageNumber} of all pages` 9 | }; 10 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/i18n-strings/split-panel.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { SplitPanelProps } from '@cloudscape-design/components'; 4 | 5 | export const splitPanelI18nStrings: SplitPanelProps.I18nStrings = { 6 | preferencesTitle: 'Split panel preferences', 7 | preferencesPositionLabel: 'Split panel position', 8 | preferencesPositionDescription: 'Choose the default split panel position for the service.', 9 | preferencesPositionSide: 'Side', 10 | preferencesPositionBottom: 'Bottom', 11 | preferencesConfirm: 'Confirm', 12 | preferencesCancel: 'Cancel', 13 | closeButtonAriaLabel: 'Close panel', 14 | openButtonAriaLabel: 'Open panel', 15 | resizeHandleAriaLabel: 'Resize split panel' 16 | }; 17 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/i18n-strings/text-filter.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const getTextFilterCounterText = (count: number) => `${count} ${count === 1 ? 'match' : 'matches'}`; 5 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/info-link.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import Link, { LinkProps } from '@cloudscape-design/components/link'; 5 | 6 | interface InfoLinkProps { 7 | id?: string; 8 | ariaLabel?: string; 9 | onFollow: LinkProps['onFollow']; 10 | } 11 | export const InfoLink = (props: InfoLinkProps) => ( 12 | 13 | Info 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/use-collection.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { UseCollectionOptions, UseCollectionResult } from './interfaces'; 4 | export declare function useCollection( 5 | allItems: ReadonlyArray, 6 | options: UseCollectionOptions 7 | ): UseCollectionResult; 8 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentTable/utils.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import { useEffect, useState } from 'react'; 4 | 5 | export const EMPTY_PANEL_CONTENT = { 6 | header: 'No case selected', 7 | body: 'Select a case to see its documents.' 8 | }; 9 | 10 | export const useSplitPanel = (selectedItems: any) => { 11 | const [splitPanelSize, setSplitPanelSize] = useState(500); 12 | const [splitPanelOpen, setSplitPanelOpen] = useState(false); 13 | const [hasManuallyClosedOnce, setHasManuallyClosedOnce] = useState(false); 14 | 15 | const onSplitPanelResize = ({ detail: { size } }: any) => { 16 | setSplitPanelSize(size); 17 | }; 18 | 19 | const onSplitPanelToggle = ({ detail: { open } }: any) => { 20 | setSplitPanelOpen(open); 21 | 22 | if (!open) { 23 | setHasManuallyClosedOnce(true); 24 | } 25 | }; 26 | 27 | useEffect(() => { 28 | if (selectedItems.length && !hasManuallyClosedOnce) { 29 | setSplitPanelOpen(true); 30 | } 31 | }, [selectedItems.length, hasManuallyClosedOnce]); 32 | 33 | return { 34 | splitPanelOpen, 35 | onSplitPanelToggle, 36 | splitPanelSize, 37 | onSplitPanelResize 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /source/ui/src/components/DocumentView/DocumentView.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | .react-tabs__tab { 5 | cursor: pointer; 6 | } 7 | 8 | .tab-box { 9 | cursor: pointer; 10 | display: inline-block; 11 | width: 20%; 12 | padding: 10px; 13 | text-align: center; 14 | border: 1px solid #ccc; 15 | } 16 | 17 | .document-view-box { 18 | padding-top: 3%; 19 | padding-left: 1%; 20 | padding-right: 1%; 21 | } 22 | 23 | .document-view-header { 24 | padding-top: 1%; 25 | padding-left: 20%; 26 | padding-bottom: -10%; 27 | } 28 | -------------------------------------------------------------------------------- /source/ui/src/components/EntitiesList.css: -------------------------------------------------------------------------------- 1 | .ObjectDisplay { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: space-between; 5 | } 6 | 7 | .ColumnHeader { 8 | font-weight: bold; 9 | } 10 | 11 | .ObjectRow { 12 | display: flex; 13 | flex-direction: row; 14 | justify-content: space-between; 15 | } 16 | 17 | .ObjectCell { 18 | margin: 5px; 19 | } 20 | -------------------------------------------------------------------------------- /source/ui/src/components/FileUpload/SelectedFile/SelectedFile.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | .file { 4 | min-width: 0; 5 | display: 'flex'; 6 | flex-flow: 'row nowrap'; 7 | align-items: 'flex-start'; 8 | gap: '8px'; 9 | } 10 | .fileThumb { 11 | max-width: '60px'; 12 | } 13 | .fileThumbImg { 14 | width: '100%'; 15 | height: 'auto'; 16 | } 17 | 18 | #fileThumbImg { 19 | width: '100%'; 20 | height: 'auto'; 21 | } 22 | .fileMetadata { 23 | overflow: 'hidden'; 24 | } 25 | .fileName { 26 | text-overflow: 'ellipsis'; 27 | white-space: 'nowrap'; 28 | overflow: 'hidden'; 29 | } 30 | .fileType { 31 | margin-left: '5px'; 32 | } -------------------------------------------------------------------------------- /source/ui/src/components/FileUpload/SelectedFileList/SelectedFileList.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | .token { 4 | align-items: 'flex-start'; 5 | /* background-color: colorBackgroundItemSelected; */ 6 | border-radius: '2px'; 7 | /* border: `1px solid ${colorBorderItemSelected}`; */ 8 | box-sizing: 'border-box'; 9 | /* color: colorTextBodyDefault; */ 10 | display: 'flex'; 11 | height: '100%'; 12 | padding: '4px 4px 4px 8px'; 13 | } 14 | .dismissButton { 15 | background-color: 'initial'; 16 | border: '1px solid #0000'; 17 | /* color: colorTextBodySecondary; */ 18 | margin-left: 'auto'; 19 | padding: '0 4px'; 20 | } -------------------------------------------------------------------------------- /source/ui/src/components/RawTextLines/RawTextLines.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | .page-separator { 5 | font-size: .8em; 6 | text-transform: uppercase; 7 | padding-top: 2em; 8 | } 9 | 10 | ul { 11 | padding: 0; 12 | } -------------------------------------------------------------------------------- /source/ui/src/components/SearchView/Kendra/KendraResultPage.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useMemo } from 'react'; 5 | 6 | import KendraDocumentResults from './KendraDocumentResults'; 7 | import KendraTopResults from './KendraTopResults'; 8 | 9 | type KendraResultPageProps = { 10 | title?: string | null; 11 | results: any[]; 12 | casesList: any[]; 13 | setSelectedCaseId: Function; 14 | setSelectedDocumentId: Function; 15 | setSelectedDocumentFileType: Function; 16 | }; 17 | 18 | export default function KendraResultPage(props: KendraResultPageProps) { 19 | const topResults = useMemo(() => props.results.filter((res: any) => res.Type === 'ANSWER'), [props.results]); 20 | const docResults = useMemo(() => props.results.filter((res: any) => res.Type === 'DOCUMENT'), [props.results]); 21 | 22 | return ( 23 |
24 | {props.title &&

{props.title}

} 25 | 31 | 38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /source/ui/src/components/SearchView/Kendra/KendraResults.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import KendraResultPage from './KendraResultPage'; 5 | 6 | import { SpaceBetween } from '@cloudscape-design/components'; 7 | import { MIN_SEARCH_QUERY_LENGTH } from '../../../utils/constants'; 8 | 9 | type KendraResultsProps = { 10 | results: any[]; 11 | searchQuery: string; 12 | casesList: any[]; 13 | setSelectedCaseId: Function; 14 | setSelectedDocumentId: Function; 15 | setSelectedDocumentFileType: Function; 16 | }; 17 | 18 | export default function KendraResults(props: KendraResultsProps) { 19 | const isQueryLongEnough = props.searchQuery && props.searchQuery.length >= MIN_SEARCH_QUERY_LENGTH; 20 | if (!props.searchQuery) return null; 21 | 22 | return ( 23 |
24 | 25 |
26 |

Amazon Kendra Results

27 |
28 | 29 |
30 | {isQueryLongEnough && props.results.length > 0 && ( 31 | 38 | )} 39 | {props.results.length < 1 &&
No results found
} 40 |
41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /source/ui/src/components/SearchView/OpenSearch/OpenSearchResultPage.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { useMemo } from 'react'; 5 | 6 | import OpenSearchQueryResults from './OpenSearchQueryResults'; 7 | 8 | type OpenSearchResultPageProps = { 9 | title?: string | null; 10 | results: any[]; 11 | casesList: any[]; 12 | searchQuery: string; 13 | setSelectedCaseId: Function; 14 | setSelectedDocumentId: Function; 15 | setSelectedDocumentFileType: Function; 16 | }; 17 | 18 | export default function OpenSearchResultPage(props: OpenSearchResultPageProps) { 19 | const queryResults = useMemo(() => props.results, [props.results]); 20 | 21 | return ( 22 |
23 | {props.title &&

{props.title}

} 24 | 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /source/ui/src/components/TableResults/TableResults.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | 4 | .page-separator { 5 | font-size: .8em; 6 | text-transform: uppercase; 7 | padding-top: 2em; 8 | } -------------------------------------------------------------------------------- /source/ui/src/components/buttons/StartJobButton.tsx: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { Button, ButtonProps, SpaceBetween, Spinner } from '@cloudscape-design/components'; 5 | import { PropsWithChildren, useState } from 'react'; 6 | import { useStartJobMutation } from '../../store/reducers/caseApiSlice'; 7 | 8 | export interface StartJobButtonProps extends PropsWithChildren { 9 | caseId: string; 10 | variant?: ButtonProps.Variant; 11 | disabled: boolean; 12 | } 13 | export default function StartJobButton(props: StartJobButtonProps) { 14 | const [statusEnabled, setStatusEnabled] = useState(false); 15 | const [startJob] = useStartJobMutation(); 16 | 17 | const handleClick = async () => { 18 | setStatusEnabled(true); 19 | await startJob({ 20 | caseId: props.caseId 21 | }).unwrap(); 22 | setStatusEnabled(false); 23 | }; 24 | return ( 25 | <> 26 | 27 | {statusEnabled && } 28 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /source/ui/src/components/entityUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { EntityTypes } from '../utils/constants'; 5 | 6 | /** 7 | * Get the entities to process based on the selected type 8 | * 9 | * @param entityType 10 | * @param props 11 | * @returns 12 | */ 13 | export function getEntitiesToProcess(entityType: string, props: any) { 14 | let entities: any; 15 | 16 | switch (entityType) { 17 | case EntityTypes.ENTITY_STANDARD: { 18 | entities = props.standardEntities; 19 | break; 20 | } 21 | case EntityTypes.MEDICAL_ENTITY: { 22 | entities = props.medicalEntities; 23 | break; 24 | } 25 | case EntityTypes.PII: { 26 | entities = props.piiEntities; 27 | } 28 | } 29 | return entities; 30 | } 31 | -------------------------------------------------------------------------------- /source/ui/src/index.css: -------------------------------------------------------------------------------- 1 | /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | SPDX-License-Identifier: Apache-2.0 */ 3 | body { 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 6 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 7 | sans-serif; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | 12 | code { 13 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 14 | monospace; 15 | } 16 | 17 | html { 18 | font-family: sans-serif; 19 | font-size: 14px; 20 | } 21 | 22 | table { 23 | border: 1px solid lightgray; 24 | } 25 | 26 | tbody { 27 | border-bottom: 1px solid lightgray; 28 | } 29 | 30 | th { 31 | border-bottom: 1px solid lightgray; 32 | border-right: 1px solid lightgray; 33 | padding: 2px 4px; 34 | } 35 | 36 | tfoot { 37 | color: gray; 38 | } 39 | 40 | tfoot th { 41 | font-weight: normal; 42 | } -------------------------------------------------------------------------------- /source/ui/src/jest.setup.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | import '@testing-library/jest-dom'; 4 | import '@testing-library/react'; 5 | import '@testing-library/user-event'; 6 | 7 | import { server } from './mock/api/server'; 8 | 9 | const { TextDecoder, TextEncoder, ReadableStream } = require('node:util'); 10 | Object.defineProperties(globalThis, { 11 | TextDecoder: { value: TextDecoder }, 12 | TextEncoder: { value: TextEncoder }, 13 | ReadableStream: { value: ReadableStream } 14 | }); 15 | 16 | import { fetch, Headers, Request, Response } from 'cross-fetch'; 17 | 18 | Object.defineProperties(globalThis, { 19 | fetch: { value: fetch, writable: true }, 20 | Headers: { value: Headers }, 21 | Request: { value: Request }, 22 | Response: { value: Response } 23 | }); 24 | 25 | if (typeof window.URL.createObjectURL === 'undefined') { 26 | window.URL.createObjectURL = jest.fn(); 27 | } 28 | 29 | beforeAll(() => { 30 | server.listen(); 31 | }); 32 | 33 | afterAll(() => { 34 | server.restoreHandlers(); 35 | }); 36 | 37 | afterEach(() => server.resetHandlers()); 38 | 39 | afterAll(() => server.close()); 40 | -------------------------------------------------------------------------------- /source/ui/src/mock/api/server.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { handlers } from "./handler"; 5 | import { setupServer } from "msw/node"; 6 | export const server = setupServer(...handlers); -------------------------------------------------------------------------------- /source/ui/src/models/requests/baseRequest.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export interface BaseRequest { 5 | caseId: string; 6 | documentId: string; 7 | } -------------------------------------------------------------------------------- /source/ui/src/models/requests/getDocumentRequest.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { BaseRequest } from "./baseRequest"; 5 | 6 | 7 | export interface GetDocumentRequest extends BaseRequest { 8 | redacted: boolean 9 | } -------------------------------------------------------------------------------- /source/ui/src/models/requests/inferenceRequest.ts: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | import {InferenceName} from '../../utils/constants'; 6 | import { BaseRequest } from './baseRequest'; 7 | 8 | export interface InferenceRequest extends BaseRequest{ 9 | 10 | inferenceType?: InferenceName 11 | } -------------------------------------------------------------------------------- /source/ui/src/models/requests/uploadDocumentRequest.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | export interface UploadDocumentRequest { 6 | userId?: string, 7 | caseId?: string, 8 | caseName?: string, 9 | fileName: string, 10 | fileExtension: string, 11 | documentType:string 12 | } -------------------------------------------------------------------------------- /source/ui/src/resolver.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = (path, options) => { 5 | // Call the defaultResolver, so we leverage its cache, error handling, etc. 6 | return options.defaultResolver(path, { 7 | ...options, 8 | // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb) 9 | packageFilter: pkg => { 10 | // This is a workaround for https://github.com/uuidjs/uuid/pull/616 11 | // 12 | // jest-environment-jsdom 28+ tries to use browser exports instead of default exports, 13 | // but uuid only offers an ESM browser export and not a CommonJS one. Jest does not yet 14 | // support ESM modules natively, so this causes a Jest error related to trying to parse 15 | // "export" syntax. 16 | // 17 | // This workaround prevents Jest from considering uuid's module-based exports at all; 18 | // it falls back to uuid's CommonJS+node "main" property. 19 | // 20 | // Once we're able to migrate our Jest config to ESM and a browser crypto 21 | // implementation is available for the browser+ESM version of uuid to use (eg, via 22 | // https://github.com/jsdom/jsdom/pull/3352 or a similar polyfill), this can go away. 23 | if (pkg.name === "uuid") { 24 | delete pkg["exports"]; 25 | delete pkg["module"]; 26 | } 27 | return pkg; 28 | } 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /source/ui/src/store/api/api.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { BaseQueryFn, createApi, FetchArgs, fetchBaseQuery, FetchBaseQueryError } from '@reduxjs/toolkit/query/react'; 5 | import { generateToken, getRuntimeConfig } from '../../utils/apiHelper'; 6 | 7 | export const dynamicBaseQuery: BaseQueryFn = async ( 8 | args: any, 9 | api: any, 10 | extraOptions: any 11 | ) => { 12 | const runtimeConfig = await getRuntimeConfig(); 13 | const rawBaseQuery = fetchBaseQuery({ 14 | baseUrl: runtimeConfig.ApiEndpoint, 15 | prepareHeaders: async (headers: any) => { 16 | if (process.env.NODE_ENV !== 'test') { 17 | const token = await generateToken(); 18 | headers.set('Authorization', `${token}`); 19 | } 20 | return headers; 21 | } 22 | }); 23 | return rawBaseQuery(args, api, extraOptions); 24 | }; 25 | export const api = createApi({ 26 | reducerPath: 'api', 27 | baseQuery: dynamicBaseQuery, 28 | tagTypes: ['Cases'], 29 | endpoints: () => ({ 30 | 31 | }), 32 | refetchOnMountOrArgChange: true 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /source/ui/src/store/hooks/hooks.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; 5 | import { RootState } from "../reducers/rootReducer"; 6 | import { AppDispatch } from "../store"; 7 | 8 | export const useAppDispatch: () => AppDispatch = useDispatch; 9 | export const useAppSelector: TypedUseSelectorHook = useSelector; 10 | -------------------------------------------------------------------------------- /source/ui/src/store/reducers/documentApiSlice.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { GetDocumentRequest } from '../../models/requests/getDocumentRequest'; 5 | import { UploadDocumentRequest } from '../../models/requests/uploadDocumentRequest'; 6 | import { api } from '../api/api'; 7 | 8 | export const documentApiSlice = api.injectEndpoints({ 9 | endpoints: (builder) => ({ 10 | uploadDocument: builder.mutation({ 11 | query: (body: UploadDocumentRequest) => ({ 12 | url: 'document', 13 | method: 'POST', 14 | body 15 | }), 16 | invalidatesTags: ['Cases'] 17 | }), 18 | documentToDownload: builder.query({ 19 | query: (key: Record) => ({ 20 | url: 'document/download', 21 | params: key 22 | }), 23 | }), 24 | 25 | getDocumentByCaseAndDocumentId: builder.query({ 26 | query: (params: GetDocumentRequest) => ({ 27 | url: `document/${params.caseId}/${params.documentId}`, 28 | params: {redacted: params.redacted} 29 | }) 30 | }) 31 | }) 32 | }); 33 | 34 | export const { useUploadDocumentMutation, useLazyGetDocumentByCaseAndDocumentIdQuery, useLazyDocumentToDownloadQuery } = documentApiSlice; 35 | -------------------------------------------------------------------------------- /source/ui/src/store/reducers/entitySlice.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { StatusIndicatorProps } from "@cloudscape-design/components"; 5 | import { createSlice } from "@reduxjs/toolkit"; 6 | import { RootState } from "./rootReducer"; 7 | 8 | interface EntityState { 9 | status?: StatusIndicatorProps.Type 10 | } 11 | 12 | export const entityInitialState: EntityState = { 13 | status: undefined 14 | } 15 | 16 | export const entitySlice = createSlice({ 17 | name: 'entity', 18 | initialState: entityInitialState, 19 | reducers: ({ 20 | setStatus: (state: EntityState, action) => { 21 | state.status = action.payload 22 | } 23 | }) 24 | }) 25 | 26 | export const {setStatus} = entitySlice.actions 27 | 28 | export const selectEntityStatus = (state: RootState) => state.entity.status -------------------------------------------------------------------------------- /source/ui/src/store/reducers/redactApiSlice.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { api } from "../api/api"; 5 | 6 | export const redactApiSlice = api.injectEndpoints({ 7 | endpoints: (builder) => ({ 8 | redact: builder.mutation({ 9 | query: (params) => ({ 10 | url: `redact/${params.caseId}/${params.documentId}`, 11 | body: params.body, 12 | method: 'POST' 13 | }) 14 | }) 15 | }) 16 | }) 17 | 18 | export const {useRedactMutation} = redactApiSlice -------------------------------------------------------------------------------- /source/ui/src/store/reducers/rootReducer.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { combineReducers } from "@reduxjs/toolkit"; 5 | import { api } from "../api/api"; 6 | import { entitySlice } from "./entitySlice"; 7 | import { documentSlice } from "./documentSlice"; 8 | 9 | const rootReducer = combineReducers({ 10 | [api.reducerPath]: api.reducer, 11 | [entitySlice.name]: entitySlice.reducer, 12 | [documentSlice.name]: documentSlice.reducer 13 | }); 14 | 15 | // Infer the `RootState` and `AppDispatch` types from the store itself 16 | export type RootState = ReturnType; 17 | export default rootReducer; 18 | -------------------------------------------------------------------------------- /source/ui/src/store/reducers/searchApiSlice.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { api } from "../api/api"; 5 | 6 | export const searchApiSlice = api.injectEndpoints({ 7 | endpoints: (builder) => ({ 8 | queryOpenSearch: builder.query({ 9 | query: (params) => ({ 10 | url: `search/opensearch/${params.searchValue}`, 11 | params: params.params 12 | }) 13 | }), 14 | queryKendra: builder.query({ 15 | query: (params) => ({ 16 | url: `search/kendra/${params.searchValue}`, 17 | params: params.params 18 | }) 19 | }) 20 | }) 21 | }) 22 | 23 | export const {useLazyQueryOpenSearchQuery, useLazyQueryKendraQuery} = searchApiSlice -------------------------------------------------------------------------------- /source/ui/src/store/store.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { configureStore, } from "@reduxjs/toolkit"; 5 | import { api } from "./api/api"; 6 | import rootReducer, { RootState } from "./reducers/rootReducer"; 7 | 8 | export const setupStore = (preloadedState: Partial) => { 9 | return configureStore({ 10 | reducer: rootReducer, 11 | preloadedState, 12 | middleware: getDefaultMiddleware => 13 | getDefaultMiddleware({ 14 | immutableCheck: false, 15 | serializableCheck: false 16 | }).concat(api.middleware) 17 | }); 18 | }; 19 | 20 | // Inferred type: {} 21 | export type AppStore = ReturnType; 22 | export type AppDispatch = AppStore["dispatch"]; 23 | -------------------------------------------------------------------------------- /source/ui/src/utils/apiHelper.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { Auth } from "aws-amplify"; 5 | 6 | let runtimeConfig: any | undefined; 7 | 8 | /** 9 | * 10 | */ 11 | export async function generateToken() { 12 | try { 13 | const user = await Auth.currentAuthenticatedUser(); 14 | const token = user.getSignInUserSession().getIdToken().getJwtToken(); 15 | return token; 16 | } catch (error) { 17 | console.error('error REST API:', error); 18 | } 19 | } 20 | 21 | /** 22 | * Gets token from cognito 23 | */ 24 | 25 | export const getRuntimeConfig = async () => { 26 | if(runtimeConfig === undefined) { 27 | runtimeConfig = (await fetch("/runtimeConfig.json")).json(); 28 | } 29 | return await runtimeConfig; 30 | }; 31 | 32 | export async function signOut() { 33 | await Auth.signOut(); 34 | } 35 | -------------------------------------------------------------------------------- /source/ui/src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const API_NAME = 'api'; 5 | 6 | export const COMPREHEND_SERVICE = 'COMPREHEND'; 7 | export const COMPREHEND_MEDICAL_SERVICE = 'COMPREHEND_MEDICAL'; 8 | export const TEXTRACT_RAW_TEXT = 'TEXTRACT_RAW_TEXT'; 9 | export const TEXTRACT_KEY_VALUE_PAIRS = 'TEXTRACT_KEY_VALUE_PAIRS'; 10 | export const TEXTRACT_TABLES = 'TEXTRACT_TABLES'; 11 | export const MIN_SEARCH_QUERY_LENGTH = 1; 12 | export const CASE_PLACEHOLDER_DOCUMENT_ID = '0000'; 13 | export const MIN_CASE_NAME_LENGTH = 3; 14 | export const MAX_CASE_NAME_LENGTH = 50; 15 | export const PREVIEW_REDACTION_ON = 'PREVIEW_REDACTION_ON'; 16 | export const ENTITIES = 'entities'; 17 | export const MAX_UPLOAD_FILE_SIZE = 5000000; 18 | 19 | export const EntityTypes = { 20 | ENTITY_STANDARD: 'entity-standard', 21 | PII: 'entity-pii', 22 | MEDICAL_ENTITY: 'entity-medical' 23 | }; 24 | 25 | export enum InferenceName { 26 | TEXTRACT_DETECT_TEXT= 'textract-detectText', 27 | TEXTRACT_ANALYZE_TEXT= 'textract-analyzeDoc', 28 | COMPREHEND_GENERIC= 'entity-standard-locations', 29 | COMPREHEND_PII= 'entity-pii-locations', 30 | COMPREHEND_MEDICAL= 'entity-medical-locations' 31 | }; 32 | 33 | export const FacetDocumentAttributeKey = { 34 | 'file_type': 'File type', 35 | 'doc_type': 'Document type' 36 | }; 37 | 38 | /** 39 | * Maps case status from backend to a friendly string for display 40 | */ 41 | export const CaseStatusDisplayTextMapping: Record = { 42 | 'initiate': 'Created', 43 | 'in-process': 'Processing', 44 | 'failure': 'Failed', 45 | 'success': 'Complete' 46 | }; 47 | 48 | export const DISPLAY_DATE_FORMAT = 'DD-MMM-YYYY, hh:mm:ss A'; 49 | -------------------------------------------------------------------------------- /source/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react-jsx", 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } -------------------------------------------------------------------------------- /source/workflow-config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "default", 3 | "WorkflowSequence": ["textract", "entity-standard", "entity-pii", "entity-medical", "redaction"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "generic", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-pii", "entity-medical", "redaction"], 10 | "RunTextractAnalyzeAction": true, 11 | "NumDocuments": "1" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /source/workflow-config/multi-doc-textract-entity-medical-pii.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "multi-doc-textract-entity-medical-pii", 3 | "WorkflowSequence": ["textract", "entity-standard", "entity-medical", "entity-pii"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "health-insurance-card", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-medical", "entity-pii"], 10 | "RunTextractAnalyzeAction": true, 11 | "NumDocuments": "1" 12 | }, 13 | { 14 | "DocumentType": "vaccination-card", 15 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 16 | "MaxSize": "5", 17 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-medical", "entity-pii"], 18 | "RunTextractAnalyzeAction": true, 19 | "NumDocuments": "1" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /source/workflow-config/multi-doc-textract-entity-pii.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "multi-doc-textract-entity-pii", 3 | "WorkflowSequence": ["textract", "entity-standard", "entity-pii"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "paystub", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-pii"], 10 | "RunTextractAnalyzeAction": true, 11 | "NumDocuments": "1" 12 | }, 13 | { 14 | "DocumentType": "loan-information", 15 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 16 | "MaxSize": "5", 17 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-pii"], 18 | "RunTextractAnalyzeAction": true, 19 | "NumDocuments": "2" 20 | }, 21 | { 22 | "DocumentType": "driving-license", 23 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 24 | "MaxSize": "5", 25 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-pii"], 26 | "RunTextractAnalyzeAction": true, 27 | "NumDocuments": "1" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /source/workflow-config/multi-doc-textract-entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "multi-doc-textract-entity", 3 | "WorkflowSequence": [ 4 | "textract", 5 | "entity-standard" 6 | ], 7 | "MinRequiredDocuments": [ 8 | { 9 | "DocumentType": "generic", 10 | "FileTypes": [ 11 | ".pdf", 12 | ".png", 13 | ".jpeg", 14 | ".jpg" 15 | ], 16 | "MaxSize": "5", 17 | "WorkflowsToProcess": [ 18 | "textract" 19 | ], 20 | "NumDocuments": "1" 21 | }, 22 | { 23 | "DocumentType": "receipt", 24 | "FileTypes": [ 25 | ".pdf", 26 | ".png", 27 | ".jpeg", 28 | ".jpg" 29 | ], 30 | "MaxSize": "5", 31 | "WorkflowsToProcess": [ 32 | "textract", 33 | "entity-standard" 34 | ], 35 | "NumDocuments": "1" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /source/workflow-config/multi-doc-textract-with-feature-type.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "multi-doc-textract-with-feature-type", 3 | "WorkflowSequence": ["textract"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "generic", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract"], 10 | "RunTextractAnalyzeAction": true, 11 | "AnalyzeDocFeatureType": ["TABLES", "FORMS", "SIGNATURES"], 12 | "NumDocuments": "3" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /source/workflow-config/single-doc-entity-detection.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-entity-detection", 3 | "WorkflowSequence": ["textract","entity-standard"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "Passport", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract", "entity-standard"], 10 | "PiiFlag": true, 11 | "NumDocuments": "1" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /source/workflow-config/single-doc-entity-redaction.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-entity-detection", 3 | "WorkflowSequence": [ 4 | "textract", 5 | "entity-standard", 6 | "redaction" 7 | ], 8 | "MinRequiredDocuments": [ 9 | { 10 | "DocumentType": "generic", 11 | "FileTypes": [ 12 | ".pdf", 13 | ".png", 14 | ".jpeg", 15 | ".jpg" 16 | ], 17 | "MaxSize": "5", 18 | "WorkflowsToProcess": [ 19 | "textract", 20 | "entity-standard", 21 | "redaction" 22 | ], 23 | "NumDocuments": "1" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /source/workflow-config/single-doc-textract-entitty-pii.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-textract-entity-pii", 3 | "WorkflowSequence": ["textract", "entity-standard", "entity-pii"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "passport", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract", "entity-standard", "entity-pii"], 10 | "RunTextractAnalyzeAction": true, 11 | "NumDocuments": "1" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /source/workflow-config/single-doc-textract-entity-medical.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-textract-entity-medical", 3 | "WorkflowSequence": [ 4 | "textract", 5 | "entity-medical" 6 | ], 7 | "MinRequiredDocuments": [ 8 | { 9 | "DocumentType": "generic", 10 | "FileTypes": [ 11 | ".pdf", 12 | ".png", 13 | ".jpeg", 14 | ".jpg" 15 | ], 16 | "MaxSize": "5", 17 | "WorkflowsToProcess": [ 18 | "textract", 19 | "entity-medical" 20 | ], 21 | "NumDocuments": "1" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /source/workflow-config/single-doc-textract-entity-pii.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-textract-entity-pii", 3 | "WorkflowSequence": [ 4 | "textract", 5 | "entity-pii" 6 | ], 7 | "MinRequiredDocuments": [ 8 | { 9 | "DocumentType": "generic", 10 | "FileTypes": [ 11 | ".pdf", 12 | ".png", 13 | ".jpeg", 14 | ".jpg" 15 | ], 16 | "MaxSize": "5", 17 | "WorkflowsToProcess": [ 18 | "textract", 19 | "entity-pii" 20 | ], 21 | "NumDocuments": "1" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /source/workflow-config/single-doc-textract-entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-textract-entity", 3 | "WorkflowSequence": [ 4 | "textract", 5 | "entity-standard" 6 | ], 7 | "MinRequiredDocuments": [ 8 | { 9 | "DocumentType": "generic", 10 | "FileTypes": [ 11 | ".pdf", 12 | ".png", 13 | ".jpeg", 14 | ".jpg" 15 | ], 16 | "MaxSize": "5", 17 | "WorkflowsToProcess": [ 18 | "textract", 19 | "entity-standard" 20 | ], 21 | "NumDocuments": "1" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /source/workflow-config/single-doc-textract.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "single-doc-textract", 3 | "WorkflowSequence": ["textract"], 4 | "MinRequiredDocuments": [ 5 | { 6 | "DocumentType": "Passport", 7 | "FileTypes": [".pdf", ".png", ".jpeg", ".jpg"], 8 | "MaxSize": "5", 9 | "WorkflowsToProcess": ["textract"], 10 | "NumDocuments": "1" 11 | } 12 | ] 13 | } 14 | --------------------------------------------------------------------------------