From 721022aeaad77309c179d6cc3054f732689cff93 Mon Sep 17 00:00:00 2001 From: usingtechnology <39388115+usingtechnology@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:03:36 -0800 Subject: [PATCH] Event Stream Service (#1444) * Event Stream Service Signed-off-by: Jason Sherman * event stream service api for ux (in progress) Signed-off-by: Jason Sherman * UX code to support Event Stream Service configuration Signed-off-by: Jason Sherman * Add appStore to all tests that use formStore (there is now a dependency to check for features). Signed-off-by: Jason Sherman * lint fix. Signed-off-by: Jason Sherman * add rate limits to routes Signed-off-by: Jason Sherman * sigh - cut/paste error... fix Signed-off-by: Jason Sherman * tweak ux when config is new Signed-off-by: Jason Sherman * helm charts for event stream service updates to connect via websockets Signed-off-by: Jason Sherman * merge fix Signed-off-by: Jason Sherman * add event stream config notes, add to deployment Signed-off-by: Jason Sherman * string instead of bool Signed-off-by: Jason Sherman * naming consistency Signed-off-by: Jason Sherman * add cicd param files for dev/test/prod Signed-off-by: Jason Sherman * add WEBSOCKETS flag, allow to connect to either NATS protocol or WS Signed-off-by: Jason Sherman * update values-dev.yaml for deployment to dev namespace with more resources Signed-off-by: Jason Sherman * updating packages so i can merge Signed-off-by: Jason Sherman * rename migration file to move past others added to main Signed-off-by: Jason Sherman * FORMS-1431 - feature flags tests Signed-off-by: Jason Sherman * Event Service Config unit tests, remove unused code Signed-off-by: Jason Sherman * import computed after merge.. Signed-off-by: Jason Sherman * FORMS-1429: tests for encryption keys some file clean up and merge fix. Signed-off-by: Jason Sherman * add event stream service tests Signed-off-by: Jason Sherman * FORMS-1428: event stream service tests Signed-off-by: Jason Sherman * rename migration file. Signed-off-by: Jason Sherman * FORMS-1427: add tests to form service for event stream messages Signed-off-by: Jason Sherman * Frontend tests Signed-off-by: Jason Sherman * FORMS-1561: add form metadata to event stream message Signed-off-by: Jason Sherman * Forms-1548: update open api spec Signed-off-by: Jason Sherman * changes to event stream pullConsumer example Signed-off-by: Jason Sherman * review changes -paths * fix for adding eventstreamconfig to existing forms. * reset the probeId interval to 10s * remove feature flags allow event stream service to run without connection add flags to enable form level event stream add flags to send event stream meta over webhook * event stream supporting webhook Signed-off-by: Jason Sherman * remove rate limiter Signed-off-by: Jason Sherman * Update app/src/docs/v1.api-spec.yaml Co-authored-by: Walter Moar * remove api access from encryptionkey routes Signed-off-by: Jason Sherman * remove features from config map Signed-off-by: Jason Sherman * put reduced limits on chefs stream * changes from review * Add stream configurations to deployment config map Add specific user for consuming messages Enforce a size on messages. * update params files for openshift. * openshift can't automatically marshall int to string? * logging/info on event stream configuration * remove defaults use param files * need param file included in PR oc command * updated example, updated chart values * ESS helm charts - preserve secret CHEFS - update stream configuration only if needed * Update event-stream-service/charts/event-stream-service/values-test.yaml Co-authored-by: Walter Moar * Update event-stream-service/charts/event-stream-service/values.yaml Co-authored-by: Walter Moar * ess - add axios update secrets * update nginx config, add hpa update resource allocation for prod * connection changes for publishing from CHEFS chart changes to remove NGINX --------- Signed-off-by: Jason Sherman Co-authored-by: Walter Moar --- .devcontainer/chefs_local/local.json.sample | 17 +- .devcontainer/chefs_local/test.json | 14 + .../actions/deploy-to-environment/action.yaml | 9 + .vscode/launch.json | 8 +- app/app.js | 32 +- app/config/custom-environment-variables.json | 15 + app/config/default.json | 15 + .../src/components/designer/FormDesigner.vue | 1 + .../src/components/designer/FormSettings.vue | 6 +- .../settings/FormEventStreamSettings.vue | 291 ++ .../components/forms/manage/ManageForm.vue | 3 +- .../trans/chefs/en/en.json | 57 +- .../src/services/encryptionKeyService.js | 31 + .../src/services/eventStreamConfigService.js | 18 + app/frontend/src/services/index.js | 2 + app/frontend/src/store/form.js | 46 + app/frontend/src/utils/constants.js | 2 + .../unit/components/base/BaseDialog.spec.js | 3 + .../unit/components/base/BaseFilter.spec.js | 3 + .../base/BaseInternationalization.spec.js | 3 + .../components/designer/FormViewer.spec.js | 2 +- .../designer/profile/FormAPIProfile.spec.js | 3 + .../profile/FormDeploymentProfile.spec.js | 3 + .../designer/profile/FormLabelProfile.spec.js | 3 + .../profile/FormUseCaseProfile.spec.js | 4 +- .../settings/FormAccessSettings.spec.js | 3 + .../settings/FormEventStreamSettings.spec.js | 128 + .../FormFunctionalitySettings.spec.js | 3 + .../settings/FormScheduleSettings.spec.js | 3 + .../settings/FormSubmissionSettings.spec.js | 3 + .../forms/ExportSubmissions.spec.js | 7 +- .../components/forms/SubmissionsTable.spec.js | 5 +- .../components/forms/manage/ApiKey.spec.js | 3 + .../forms/manage/DocumentTemplate.spec.js | 5 +- .../forms/manage/EmailTemplate.spec.js | 4 +- .../forms/manage/ManageForm.spec.js | 3 + .../forms/manage/ManageFormActions.spec.js | 3 + .../forms/manage/ManageLayout.spec.js | 3 + .../forms/manage/ManageVersions.spec.js | 3 + .../components/forms/manage/ShareForm.spec.js | 3 + .../forms/manage/Subscription.spec.js | 3 + .../forms/manage/TeamManagement.spec.js | 4 + .../forms/submission/DeleteSubmission.spec.js | 2 +- .../submission/ManageSubmissionUsers.spec.js | 3 + .../submission/MySubmissionsActions.spec.js | 4 + .../submission/MySubmissionsTable.spec.js | 3 + .../forms/submission/NotesPanel.spec.js | 3 + .../forms/submission/StatusPanel.spec.js | 3 + .../forms/submission/StatusTable.spec.js | 3 + .../UserDuplicateSubmission.spec.js | 3 + .../forms/submission/UserSubmission.spec.js | 3 + .../ProactiveHelpPreviewDialog.spec.js | 13 + .../services/encryptionKeyService.spec.js | 41 + .../services/eventStreamConfigService.spec.js | 32 + .../unit/store/modules/auth.actions.spec.js | 1 + .../unit/store/modules/form.actions.spec.js | 3 + .../tests/unit/utils/constants.spec.js | 2 + .../tests/unit/views/file/Download.spec.js | 3 + .../tests/unit/views/form/Create.spec.js | 3 + .../tests/unit/views/form/Design.spec.js | 3 + app/package-lock.json | 2653 ++++++++++------- app/package.json | 3 + app/src/components/eventStreamService.js | 508 ++++ ...20241031164117_060_event_stream_service.js | 38 + .../20241031164117_061_event_flags.js | 35 + app/src/docs/v1.api-spec.yaml | 216 +- .../common/middleware/validateParameter.js | 26 + app/src/forms/common/models/index.js | 3 + .../common/models/tables/formEncryptionKey.js | 48 + .../models/tables/formEventStreamConfig.js | 59 + .../common/models/tables/formSubscription.js | 1 + app/src/forms/event/eventService.js | 2 +- .../forms/form/encryptionKey/controller.js | 20 + app/src/forms/form/encryptionKey/routes.js | 21 + app/src/forms/form/encryptionKey/service.js | 99 + .../form/eventStreamConfig/controller.js | 12 + .../forms/form/eventStreamConfig/routes.js | 16 + .../forms/form/eventStreamConfig/service.js | 78 + app/src/forms/form/index.js | 4 +- app/src/forms/form/service.js | 39 +- app/src/forms/submission/service.js | 18 +- .../components/eventStreamService.spec.js | 405 +++ .../form/encryptionKey/controller.spec.js | 108 + .../forms/form/encryptionKey/routes.spec.js | 134 + .../forms/form/encryptionKey/service.spec.js | 239 ++ .../form/eventStreamConfig/controller.spec.js | 75 + .../form/eventStreamConfig/routes.spec.js | 74 + .../form/eventStreamConfig/service.spec.js | 161 + app/tests/unit/forms/form/service.spec.js | 225 ++ event-stream-service/.gitignore | 1 + event-stream-service/README.md | 12 + .../charts/event-stream-service/.helmignore | 23 + .../charts/event-stream-service/Chart.lock | 9 + .../charts/event-stream-service/Chart.yaml | 34 + .../charts/event-stream-service/README.md | 55 + .../charts/common-2.22.0.tgz | Bin 0 -> 16228 bytes .../charts/nats-1.2.6.tgz | Bin 0 -> 19633 bytes .../event-stream-service/templates/NOTES.txt | 5 + .../templates/_helpers.tpl | 7 + .../templates/nats-route.yaml | 22 + .../templates/nats-secrets.yaml | 19 + .../event-stream-service/values-dev.yaml | 13 + .../event-stream-service/values-prod.yaml | 22 + .../event-stream-service/values-test.yaml | 17 + .../charts/event-stream-service/values.yaml | 254 ++ event-stream-service/config/jetstream.conf | 121 + event-stream-service/consumer.js | 112 + event-stream-service/docker-compose.yml | 65 + event-stream-service/package-lock.json | 406 +++ event-stream-service/package.json | 17 + event-stream-service/publisher.js | 75 + event-stream-service/pullConsumer.js | 176 ++ event-stream-service/subscriber.js | 59 + openshift/README.md | 16 + openshift/app.dc.yaml | 22 + openshift/ess.cm.yaml | 75 + openshift/ess.dev.param | 9 + openshift/ess.prod.param | 9 + openshift/ess.test.param | 9 + 119 files changed, 6689 insertions(+), 1200 deletions(-) create mode 100644 app/frontend/src/components/designer/settings/FormEventStreamSettings.vue create mode 100644 app/frontend/src/services/encryptionKeyService.js create mode 100644 app/frontend/src/services/eventStreamConfigService.js create mode 100644 app/frontend/tests/unit/components/designer/settings/FormEventStreamSettings.spec.js create mode 100644 app/frontend/tests/unit/services/encryptionKeyService.spec.js create mode 100644 app/frontend/tests/unit/services/eventStreamConfigService.spec.js create mode 100644 app/src/components/eventStreamService.js create mode 100644 app/src/db/migrations/20241031164117_060_event_stream_service.js create mode 100644 app/src/db/migrations/20241031164117_061_event_flags.js create mode 100644 app/src/forms/common/models/tables/formEncryptionKey.js create mode 100644 app/src/forms/common/models/tables/formEventStreamConfig.js create mode 100644 app/src/forms/form/encryptionKey/controller.js create mode 100644 app/src/forms/form/encryptionKey/routes.js create mode 100644 app/src/forms/form/encryptionKey/service.js create mode 100644 app/src/forms/form/eventStreamConfig/controller.js create mode 100644 app/src/forms/form/eventStreamConfig/routes.js create mode 100644 app/src/forms/form/eventStreamConfig/service.js create mode 100644 app/tests/unit/components/eventStreamService.spec.js create mode 100644 app/tests/unit/forms/form/encryptionKey/controller.spec.js create mode 100644 app/tests/unit/forms/form/encryptionKey/routes.spec.js create mode 100644 app/tests/unit/forms/form/encryptionKey/service.spec.js create mode 100644 app/tests/unit/forms/form/eventStreamConfig/controller.spec.js create mode 100644 app/tests/unit/forms/form/eventStreamConfig/routes.spec.js create mode 100644 app/tests/unit/forms/form/eventStreamConfig/service.spec.js create mode 100644 event-stream-service/.gitignore create mode 100644 event-stream-service/README.md create mode 100644 event-stream-service/charts/event-stream-service/.helmignore create mode 100644 event-stream-service/charts/event-stream-service/Chart.lock create mode 100644 event-stream-service/charts/event-stream-service/Chart.yaml create mode 100644 event-stream-service/charts/event-stream-service/README.md create mode 100644 event-stream-service/charts/event-stream-service/charts/common-2.22.0.tgz create mode 100644 event-stream-service/charts/event-stream-service/charts/nats-1.2.6.tgz create mode 100644 event-stream-service/charts/event-stream-service/templates/NOTES.txt create mode 100644 event-stream-service/charts/event-stream-service/templates/_helpers.tpl create mode 100644 event-stream-service/charts/event-stream-service/templates/nats-route.yaml create mode 100644 event-stream-service/charts/event-stream-service/templates/nats-secrets.yaml create mode 100644 event-stream-service/charts/event-stream-service/values-dev.yaml create mode 100644 event-stream-service/charts/event-stream-service/values-prod.yaml create mode 100644 event-stream-service/charts/event-stream-service/values-test.yaml create mode 100644 event-stream-service/charts/event-stream-service/values.yaml create mode 100644 event-stream-service/config/jetstream.conf create mode 100644 event-stream-service/consumer.js create mode 100644 event-stream-service/docker-compose.yml create mode 100644 event-stream-service/package-lock.json create mode 100644 event-stream-service/package.json create mode 100644 event-stream-service/publisher.js create mode 100644 event-stream-service/pullConsumer.js create mode 100644 event-stream-service/subscriber.js create mode 100644 openshift/ess.cm.yaml create mode 100644 openshift/ess.dev.param create mode 100644 openshift/ess.prod.param create mode 100644 openshift/ess.test.param diff --git a/.devcontainer/chefs_local/local.json.sample b/.devcontainer/chefs_local/local.json.sample index 345c3b721..ae53fd993 100644 --- a/.devcontainer/chefs_local/local.json.sample +++ b/.devcontainer/chefs_local/local.json.sample @@ -55,13 +55,28 @@ "public": { "limitApiKey": "120", "limitFrontend": "500", - "windowMs": "60000", + "windowMs": "60000" } }, "encryption": { "proxy": "352f7c24819086bf3df5a38c1a40586045f73e0007440c9d27d59ee8560e3fe7" } }, + "eventStreamService": { + "servers": "localhost:4222,localhost:4223,localhost:4224", + "websockets": "false", + "streamName": "CHEFS", + "source": "chefs-local", + "domain": "forms", + "username": "chefs", + "password": "password", + "maxAge": "300000", + "maxBytes": "1048576", + "maxMsgs": "10", + "maxMsgSize": "51200", + "duplicateWindow": "60000", + "numReplicas": "3" + }, "serviceClient": { "commonServices": { "ches": { diff --git a/.devcontainer/chefs_local/test.json b/.devcontainer/chefs_local/test.json index 5928abdca..bf781161d 100644 --- a/.devcontainer/chefs_local/test.json +++ b/.devcontainer/chefs_local/test.json @@ -61,6 +61,20 @@ "proxy": "5fb2054478353fd8d514056d1745b3a9eef066deadda4b90967af7ca65ce6505" } }, + "eventStreamService": { + "servers": "localhost:4222,localhost:4223,localhost:4224", + "streamName": "CHEFS", + "source": "chefs", + "domain": "forms", + "username": "chefs", + "password": "password", + "maxAge": "900000", + "maxBytes": "26214400", + "maxMsgs": "500", + "maxMsgSize": "50000", + "duplicateWindow": "60000", + "numReplicas": "3" + }, "serviceClient": { "commonServices": { "ches": { diff --git a/.github/actions/deploy-to-environment/action.yaml b/.github/actions/deploy-to-environment/action.yaml index 6d83f3348..6c0a4f12a 100644 --- a/.github/actions/deploy-to-environment/action.yaml +++ b/.github/actions/deploy-to-environment/action.yaml @@ -94,6 +94,15 @@ runs: run: | oc process --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f openshift/app.cm.yaml -p NAMESPACE=${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -p APP_NAME=${{ inputs.acronym }} -p JOB_NAME=${{ inputs.job_name }} -p SERVER_HOST=${{ inputs.server_host }} -o yaml | oc apply --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f - + - name: Deploy event stream ConfigMaps + shell: bash + run: | + if [[ "${{ inputs.job_name }}" == pr-* ]]; then + oc process --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f openshift/ess.cm.yaml -p APP_NAME=${{ inputs.acronym }} -p JOB_NAME=${{ inputs.job_name }} --param-file=openshift/ess.dev.param -p SOURCE=${{ inputs.job_name }} -o yaml | oc apply --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f - + else + oc process --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f openshift/ess.cm.yaml -p APP_NAME=${{ inputs.acronym }} -p JOB_NAME=${{ inputs.job_name }} --param-file=openshift/ess.${{ inputs.namespace_environment }}.param -o yaml | oc apply --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f - + fi + - name: Deploy App shell: bash run: | diff --git a/.vscode/launch.json b/.vscode/launch.json index c5d7ac6e2..9f5e0d26b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -48,7 +48,7 @@ "type": "node", "request": "launch", "name": "Jest: current file", - //"env": { "NODE_ENV": "test" }, + "env": { "NODE_ENV": "test" }, "program": "${workspaceFolder}/app/node_modules/.bin/jest", "args": [ "${file}", @@ -77,6 +77,12 @@ ], "smartStep": true, "console": "integratedTerminal" + }, + { + "type": "node", + "request": "launch", + "name": "Node.js - Debug Current File", + "program": "${file}" } ], "version": "0.2.0" diff --git a/app/app.js b/app/app.js index c9f41e61a..09158d655 100644 --- a/app/app.js +++ b/app/app.js @@ -13,14 +13,18 @@ const v1Router = require('./src/routes/v1'); const DataConnection = require('./src/db/dataConnection'); const dataConnection = new DataConnection(); +const { eventStreamService } = require('./src/components/eventStreamService'); + const apiRouter = express.Router(); const state = { connections: { data: false, + eventStreamService: false, }, ready: false, shutdown: false, }; + let probeId; const app = express(); app.use(compression()); @@ -62,6 +66,14 @@ apiRouter.use('/config', (_req, res, next) => { // we will need to pass const uploads = config.get('files.uploads'); const feConfig = { ...frontend, uploads: uploads }; + let ess = config.util.cloneDeep(config.get('eventStreamService')); + if (ess) { + delete ess['username']; + delete ess['password']; + feConfig['eventStreamService'] = { + ...ess, + }; + } res.status(200).json(feConfig); } catch (err) { next(err); @@ -160,6 +172,7 @@ function cleanup() { log.info('Cleaning up...', { function: 'cleanup' }); clearInterval(probeId); + eventStreamService.closeConnection(); dataConnection.close(() => process.exit()); // Wait 10 seconds max before hard exiting @@ -173,7 +186,7 @@ function cleanup() { */ function initializeConnections() { // Initialize connections and exit if unsuccessful - const tasks = [dataConnection.checkAll()]; + const tasks = [dataConnection.checkAll(), eventStreamService.checkConnection()]; Promise.all(tasks) .then((results) => { @@ -183,9 +196,16 @@ function initializeConnections() { log.info('DataConnection Reachable', { function: 'initializeConnections', }); + + state.connections.eventStreamService = results[1]; + const reachable = state.connections.eventStreamService ? 'Reachable' : 'Unreachable'; + log.info(`EventStreamService ${reachable}`, { + function: 'initializeConnections', + }); }) .catch((error) => { log.error(`Initialization failed: Database OK = ${state.connections.data}`, { function: 'initializeConnections' }); + log.error(`Initialization failed: EventStreamService OK = ${state.connections.eventStreamService}`, { function: 'initializeConnections' }); log.error('Connection initialization failure', error.message, { function: 'initializeConnections', }); @@ -195,7 +215,7 @@ function initializeConnections() { } }) .finally(() => { - state.ready = Object.values(state.connections).every((x) => x); + state.ready = state.connections.data; // only need db running if (state.ready) { log.info('Service ready to accept traffic', { function: 'initializeConnections', @@ -214,17 +234,21 @@ function initializeConnections() { function checkConnections() { const wasReady = state.ready; if (!state.shutdown) { - const tasks = [dataConnection.checkConnection()]; + const tasks = [dataConnection.checkConnection(), eventStreamService.checkConnection()]; Promise.all(tasks).then((results) => { state.connections.data = results[0]; - state.ready = Object.values(state.connections).every((x) => x); + state.connections.eventStreamService = results[1]; + + state.ready = state.connections.data; // only want no db to halt application if (!wasReady && state.ready) log.info('Service ready to accept traffic', { function: 'checkConnections', }); log.verbose(state); if (!state.ready) { + log.error(`Database connected = ${state.connections.data}`, { function: 'checkConnections' }); + log.error(`EventStreamService connected = ${state.connections.eventStreamService}`, { function: 'checkConnections' }); process.exitCode = 1; shutdown(); } diff --git a/app/config/custom-environment-variables.json b/app/config/custom-environment-variables.json index 8b2ef444f..e9a8b34ab 100755 --- a/app/config/custom-environment-variables.json +++ b/app/config/custom-environment-variables.json @@ -58,6 +58,21 @@ "proxy": "SERVER_ENCRYPTION_PROXY" } }, + "eventStreamService": { + "servers": "EVENTSTREAMSERVICE_SERVERS", + "websockets": "EVENTSTREAMSERVICE_WEBSOCKETS", + "streamName": "EVENTSTREAMSERVICE_STREAMNAME", + "source": "EVENTSTREAMSERVICE_SOURCE", + "domain": "EVENTSTREAMSERVICE_DOMAIN", + "username": "EVENTSTREAMSERVICE_USERNAME", + "password": "EVENTSTREAMSERVICE_PASSWORD", + "maxAge": "EVENTSTREAMSERVICE_MAXAGE", + "maxBytes": "EVENTSTREAMSERVICE_MAXBYTES", + "maxMsgs": "EVENTSTREAMSERVICE_MAXMSGS", + "maxMsgSize": "EVENTSTREAMSERVICE_MAXMSGSIZE", + "duplicateWindow": "EVENTSTREAMSERVICE_DUPLICATEWINDOW", + "numReplicas": "EVENTSTREAMSERVICE_NUMREPLICAS" + }, "serviceClient": { "commonServices": { "ches": { diff --git a/app/config/default.json b/app/config/default.json index d2f8eb24f..0cb75ba33 100644 --- a/app/config/default.json +++ b/app/config/default.json @@ -62,6 +62,21 @@ "proxy": "352f7c24819086bf3df5a38c1a40586045f73e0007440c9d27d59ee8560e3fe7" } }, + "eventStreamService": { + "servers": "localhost:4222,localhost:4223,localhost:4224", + "websockets": "false", + "streamName": "CHEFS", + "source": "chefs-local", + "domain": "forms", + "username": "chefs", + "password": "password", + "maxAge": "900000", + "maxBytes": "966367641", + "maxMsgs": "1000", + "maxMsgSize": "966367", + "duplicateWindow": "60000", + "numReplicas": "3" + }, "serviceClient": { "commonServices": { "ches": { diff --git a/app/frontend/src/components/designer/FormDesigner.vue b/app/frontend/src/components/designer/FormDesigner.vue index 17df2cba2..909ffa092 100644 --- a/app/frontend/src/components/designer/FormDesigner.vue +++ b/app/frontend/src/components/designer/FormDesigner.vue @@ -406,6 +406,7 @@ async function schemaCreateNew() { useCase: form.value.useCase, labels: form.value.labels, formMetadata: form.value.formMetadata, + eventStreamConfig: form.value.eventStreamConfig, }); // update user labels with any new added labels if ( diff --git a/app/frontend/src/components/designer/FormSettings.vue b/app/frontend/src/components/designer/FormSettings.vue index 478b3bd7e..6106c0633 100644 --- a/app/frontend/src/components/designer/FormSettings.vue +++ b/app/frontend/src/components/designer/FormSettings.vue @@ -1,12 +1,13 @@ + + diff --git a/app/frontend/src/components/forms/manage/ManageForm.vue b/app/frontend/src/components/forms/manage/ManageForm.vue index e6d454edd..013b388f1 100644 --- a/app/frontend/src/components/forms/manage/ManageForm.vue +++ b/app/frontend/src/components/forms/manage/ManageForm.vue @@ -99,7 +99,8 @@ function enableSettingsEdit() { async function updateSettings() { try { - if (settingsForm.value.validate()) { + const { valid } = await settingsForm.value.validate(); + if (valid) { await formStore.updateForm(); formSettingsDisabled.value = true; notificationStore.addNotification({ diff --git a/app/frontend/src/internationalization/trans/chefs/en/en.json b/app/frontend/src/internationalization/trans/chefs/en/en.json index fc76f0b3a..20e3d81b2 100644 --- a/app/frontend/src/internationalization/trans/chefs/en/en.json +++ b/app/frontend/src/internationalization/trans/chefs/en/en.json @@ -67,7 +67,8 @@ "cancel": "Cancel", "eventSubscription": "Event Subscription", "cdogsTemplate": "CDOGS Template", - "externalAPIs": "External APIs" + "externalAPIs": "External APIs", + "eventStreamConfig": "Event Stream Configuration" }, "documentTemplate": { "uploadTemplate": "Upload CDOGS Template", @@ -84,6 +85,39 @@ "info": "Upload a template to use the Common Document Generation Service (CDOGS)", "fileSizeError": "File size must be less than 25MB." }, + "eventStreamConfig": { + "info": "Configure Event Stream for your form.", + "create": "Create new Event Stream Configuration", + "createError": "Event Stream Configuration could not be created.", + "createSuccess": "Event Stream Configuration created successfully.", + "createTitle": "New Event Stream Configuration", + "delete": "Delete", + "deleteSuccess": "Event Stream Configuration deleted successfully.", + "deleteError": "An error occurred while deleting the Event Stream Configuration.", + "edit": "Edit", + "editError": "Event Stream Configuration could not be updated.", + "editSuccess": "Event Stream Configuration updated successfully.", + "editTitle": "Edit Event Stream Configuration", + "fetchError": "An error occurred while fetching the Event Stream Configuration.", + "save": "Save" + }, + "encryptionKey": { + "info": "Configure Encryption Keys for your form.", + "create": "Create new Encryption Key", + "createError": "Encryption Key could not be created.", + "createSuccess": "Encryption Key created successfully.", + "createTitle": "New Encryption Key Configuration", + "delete": "Delete", + "deleteSuccess": "Encryption Key deleted successfully.", + "deleteError": "An error occurred while deleting the Encryption Key.", + "edit": "Edit", + "editError": "Encryption Key could not be updated.", + "editSuccess": "Encryption Key updated successfully.", + "editTitle": "Edit Encryption Key", + "fetchError": "An error occurred while fetching the Encryption Key.", + "fetchListError": "An error occurred while fetching the Encryption Key list.", + "save": "Save" + }, "externalAPI": { "info": "Configure External APIs for use in your form.", "create": "Create new External API.", @@ -202,7 +236,26 @@ "wideFormLayout": "Wide Form Layout", "formMetadataTitle": "Form Metadata", "formMetadataMessage": "Structured information to describe or explain this form to external systems. Calls to external systems will include this metadata in their payloads.", - "formMetadataJsonError": "Form metadata must be valid JSON. Use double-quotes around attributes and values." + "formMetadataJsonError": "Form metadata must be valid JSON. Use double-quotes around attributes and values.", + "eventStreamTitle": "Event Stream Settings", + "eventStreamMessage": "Event Stream Service will publish notifications about form publishing and submissions. The underlying technology is NATS.io which allows consumers to subscribe or pull the event messages and process them in external systems. Private messages will contain encrypted payloads with the key configured here.", + "natsConfiguration": "NATS Configuration and Message Metadata", + "publishConfiguration": "Event Publishing Configuration", + "enablePublicStream": "Enable Public Stream", + "enablePrivateStream": "Enable Private Stream", + "encryptionKeyAlgorithm": "Encryption Key Algorithm", + "encryptionKey": "Encryption Key", + "encryptionKeyReq": "Encryption Key is required for Private Streams", + "fetchEncryptionAlgorithmListError": "Error fetching Encryption Algorithm list.", + "serversLabel": "Servers", + "streamNameLabel": "Stream Name", + "sourceLabel": "Source", + "domainLabel": "Domain", + "eventStreamUpdatedBy": "Event Stream Settings Updated By", + "encryptionKeyUpdatedBy": "Encryption Key Updated By", + "encryptionKeyCopySnackbar": "Encryption Key copied to clipboard", + "encryptionKeyCopyTooltip": "Copy Encryption Key to clipboard", + "encryptionKeyGenerate": "Generate Encryption Key" }, "formProfile": { "message": "The CHEFS team is collecting and organizing information to serve as crucial input for crafting comprehensive business cases. These cases will play a pivotal role in guiding the strategic operation and ongoing improvement of CHEFS in the coming years. This initiative to gather data is essential for informing critical decisions and molding the trajectory of CHEFS, ensuring its adaptability and effectiveness in addressing evolving needs and challenges.", diff --git a/app/frontend/src/services/encryptionKeyService.js b/app/frontend/src/services/encryptionKeyService.js new file mode 100644 index 000000000..c281d1bf8 --- /dev/null +++ b/app/frontend/src/services/encryptionKeyService.js @@ -0,0 +1,31 @@ +import { appAxios } from '~/services/interceptors'; +import { ApiRoutes } from '~/utils/constants'; + +export default { + /** + * @function listEncryptionAlgorithms + * Get the Encryption Key Algortithms supported + * @returns {Promise} An axios response + */ + listEncryptionAlgorithms(params = {}) { + return appAxios().get( + `${ApiRoutes.FORMS}/${ApiRoutes.ENCRYPTION_KEY}/algorithms`, + { params } + ); + }, + + /** + * @function getEncryptionKey + * Get the encryption key + * @param {string} formId The form uuid + * @param {string} formEncryptionKeyId The form encryption key uuid + * @param {Object} [params={}] The query parameters + * @returns {Promise} An axios response + */ + getEncryptionKey(formId, formEncryptionKeyId, params = {}) { + return appAxios().get( + `${ApiRoutes.FORMS}/${formId}${ApiRoutes.ENCRYPTION_KEY}/${formEncryptionKeyId}`, + { params } + ); + }, +}; diff --git a/app/frontend/src/services/eventStreamConfigService.js b/app/frontend/src/services/eventStreamConfigService.js new file mode 100644 index 000000000..3bdea26cc --- /dev/null +++ b/app/frontend/src/services/eventStreamConfigService.js @@ -0,0 +1,18 @@ +import { appAxios } from '~/services/interceptors'; +import { ApiRoutes } from '~/utils/constants'; + +export default { + /** + * @function getEventStreamConfig + * Get the event stream configuration + * @param {string} formId The form uuid + * @param {Object} [params={}] The query parameters + * @returns {Promise} An axios response + */ + getEventStreamConfig(formId, params = {}) { + return appAxios().get( + `${ApiRoutes.FORMS}/${formId}${ApiRoutes.EVENT_STREAM_CONFIG}`, + { params } + ); + }, +}; diff --git a/app/frontend/src/services/index.js b/app/frontend/src/services/index.js index a0f299000..5dd1ff563 100755 --- a/app/frontend/src/services/index.js +++ b/app/frontend/src/services/index.js @@ -6,3 +6,5 @@ export { default as roleService } from './roleService'; export { default as userService } from './userService'; export { default as fileService } from './fileService'; export { default as utilsService } from './utilsService'; +export { default as encryptionKeyService } from './encryptionKeyService'; +export { default as eventStreamConfigService } from './eventStreamConfigService'; diff --git a/app/frontend/src/store/form.js b/app/frontend/src/store/form.js index 3d8cf7d64..28843598a 100644 --- a/app/frontend/src/store/form.js +++ b/app/frontend/src/store/form.js @@ -11,6 +11,7 @@ import { import { useNotificationStore } from '~/store/notification'; import { IdentityMode, NotificationTypes } from '~/utils/constants'; import { generateIdps, parseIdps } from '~/utils/transformUtils'; +import { encryptionKeyService, eventStreamConfigService } from '~/services'; const genInitialSchedule = () => ({ enabled: null, @@ -59,6 +60,21 @@ const genInitialSubscribeDetails = () => ({ endpointToken: null, key: '', }); +const genInitialEncryptionKey = () => ({ + id: null, + name: null, + algorithm: null, + key: null, +}); +const genInitialEventStreamConfig = () => ({ + id: null, + formId: null, + enabled: false, + enablePublicStream: false, + enablePrivateStream: false, + encryptionKeyId: null, + encryptionKey: genInitialEncryptionKey(), +}); const genInitialFormMetadata = () => ({ id: null, formId: null, @@ -90,6 +106,7 @@ const genInitialForm = () => ({ useCase: null, wideFormLayout: false, formMetadata: genInitialFormMetadata(), + eventStreamConfig: genInitialEventStreamConfig(), }); export const useFormStore = defineStore('form', { @@ -317,6 +334,29 @@ export const useFormStore = defineStore('form', { }); } }, + async fetchEventStreamConfig(formId) { + try { + // populate the event service config object... + let resp = await eventStreamConfigService.getEventStreamConfig(formId); + const evntSrvCfg = resp.data; + let encKey = genInitialEncryptionKey(); + if (evntSrvCfg.encryptionKeyId) { + resp = await encryptionKeyService.getEncryptionKey( + formId, + evntSrvCfg.encryptionKeyId + ); + encKey = resp.data; + } + return { + ...evntSrvCfg, + encryptionKey: { + ...encKey, + }, + }; + } catch { + return genInitialEventStreamConfig(); + } + }, async fetchForm(formId) { try { this.apiKey = null; @@ -336,6 +376,9 @@ export const useFormStore = defineStore('form', { if (!data.formMetadata) { data.formMetadata = genInitialFormMetadata(); } + const evntSrvCfg = await this.fetchEventStreamConfig(formId); + data.eventStreamConfig = evntSrvCfg; + this.form = data; } catch (error) { const notificationStore = useNotificationStore(); @@ -424,7 +467,9 @@ export const useFormStore = defineStore('form', { const subscribe = this.form.subscribe.enabled ? this.form.subscribe : {}; + const formMetadata = this.form.formMetadata; + const eventStreamConfig = this.form.eventStreamConfig; await formService.updateForm(this.form.id, { name: this.form.name, description: this.form.description, @@ -453,6 +498,7 @@ export const useFormStore = defineStore('form', { ? this.form.enableCopyExistingSubmission : false, formMetadata: formMetadata, + eventStreamConfig: eventStreamConfig, }); // update user labels with any new added labels diff --git a/app/frontend/src/utils/constants.js b/app/frontend/src/utils/constants.js index 9c7ee6511..dbe4aff33 100755 --- a/app/frontend/src/utils/constants.js +++ b/app/frontend/src/utils/constants.js @@ -17,6 +17,8 @@ export const ApiRoutes = Object.freeze({ PROXY: '/proxy', EXTERNAL_APIS: '/externalAPIs', FORM_METADATA: '/formMetadata', + EVENT_STREAM_CONFIG: '/eventStreamConfig', + ENCRYPTION_KEY: '/encryptionKey', }); /** Roles a user can have on a form. These are defined in the DB and sent from the API */ diff --git a/app/frontend/tests/unit/components/base/BaseDialog.spec.js b/app/frontend/tests/unit/components/base/BaseDialog.spec.js index fe51b6699..d4f282486 100644 --- a/app/frontend/tests/unit/components/base/BaseDialog.spec.js +++ b/app/frontend/tests/unit/components/base/BaseDialog.spec.js @@ -6,14 +6,17 @@ import { createTestingPinia } from '@pinia/testing'; import BaseDialog from '~/components/base/BaseDialog.vue'; import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; describe('BaseDialog.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders with ok button', async () => { diff --git a/app/frontend/tests/unit/components/base/BaseFilter.spec.js b/app/frontend/tests/unit/components/base/BaseFilter.spec.js index 8293bd219..403983440 100644 --- a/app/frontend/tests/unit/components/base/BaseFilter.spec.js +++ b/app/frontend/tests/unit/components/base/BaseFilter.spec.js @@ -3,13 +3,16 @@ import { describe, it } from 'vitest'; import { createTestingPinia } from '@pinia/testing'; import BaseFilter from '~/components/base/BaseFilter.vue'; import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; describe('BaseFilter.vue', () => { const pinia = createTestingPinia(); const formStore = useFormStore(); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders', async () => { diff --git a/app/frontend/tests/unit/components/base/BaseInternationalization.spec.js b/app/frontend/tests/unit/components/base/BaseInternationalization.spec.js index 378fe02fd..2ace70ff4 100644 --- a/app/frontend/tests/unit/components/base/BaseInternationalization.spec.js +++ b/app/frontend/tests/unit/components/base/BaseInternationalization.spec.js @@ -4,14 +4,17 @@ import { beforeEach, describe, it } from 'vitest'; import BaseInternationalization from '~/components/base/BaseInternationalization.vue'; import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; describe('BaseInternationalization.vue', () => { const pinia = createPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); formStore.isRTL = false; }); diff --git a/app/frontend/tests/unit/components/designer/FormViewer.spec.js b/app/frontend/tests/unit/components/designer/FormViewer.spec.js index 6f9cabb9f..702c8ef28 100644 --- a/app/frontend/tests/unit/components/designer/FormViewer.spec.js +++ b/app/frontend/tests/unit/components/designer/FormViewer.spec.js @@ -51,7 +51,6 @@ describe('FormViewer.vue', () => { const authStore = useAuthStore(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); - const getProxyHeadersSpy = vi.spyOn(formService, 'getProxyHeaders'); const readVersionSpy = vi.spyOn(formService, 'readVersion'); const readDraftSpy = vi.spyOn(formService, 'readDraft'); @@ -69,6 +68,7 @@ describe('FormViewer.vue', () => { appStore.$reset(); authStore.$reset(); formStore.$reset(); + appStore.$reset(); authStore.authenticated = true; authStore.keycloak = { tokenParsed: { diff --git a/app/frontend/tests/unit/components/designer/profile/FormAPIProfile.spec.js b/app/frontend/tests/unit/components/designer/profile/FormAPIProfile.spec.js index bcbb6c94b..31d0ac25d 100644 --- a/app/frontend/tests/unit/components/designer/profile/FormAPIProfile.spec.js +++ b/app/frontend/tests/unit/components/designer/profile/FormAPIProfile.spec.js @@ -4,14 +4,17 @@ import { createTestingPinia } from '@pinia/testing'; import { setActivePinia } from 'pinia'; import { mount } from '@vue/test-utils'; import { nextTick } from 'vue'; +import { useAppStore } from '~/store/app'; describe('FormAPIProfile.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders properly', () => { diff --git a/app/frontend/tests/unit/components/designer/profile/FormDeploymentProfile.spec.js b/app/frontend/tests/unit/components/designer/profile/FormDeploymentProfile.spec.js index 4aed65007..7aa8a1992 100644 --- a/app/frontend/tests/unit/components/designer/profile/FormDeploymentProfile.spec.js +++ b/app/frontend/tests/unit/components/designer/profile/FormDeploymentProfile.spec.js @@ -5,14 +5,17 @@ import { setActivePinia } from 'pinia'; import { mount } from '@vue/test-utils'; import { FormProfileValues } from '~/utils/constants'; import { nextTick } from 'vue'; +import { useAppStore } from '~/store/app'; describe('FormDeploymentProfile.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders properly', () => { diff --git a/app/frontend/tests/unit/components/designer/profile/FormLabelProfile.spec.js b/app/frontend/tests/unit/components/designer/profile/FormLabelProfile.spec.js index d145f1888..1a014a118 100644 --- a/app/frontend/tests/unit/components/designer/profile/FormLabelProfile.spec.js +++ b/app/frontend/tests/unit/components/designer/profile/FormLabelProfile.spec.js @@ -7,18 +7,21 @@ import FormLabelProfile from '~/components/designer/profile/FormLabelProfile.vue import { userService } from '~/services'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; describe('FormLabelProfile.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); const getUserLabelsSpy = vi.spyOn(userService, 'getUserLabels'); const addNotificationSpy = vi.spyOn(notificationStore, 'addNotification'); beforeEach(() => { formStore.$reset(); + appStore.$reset(); notificationStore.$reset(); getUserLabelsSpy.mockReset(); }); diff --git a/app/frontend/tests/unit/components/designer/profile/FormUseCaseProfile.spec.js b/app/frontend/tests/unit/components/designer/profile/FormUseCaseProfile.spec.js index 7faa37120..0cc34028d 100644 --- a/app/frontend/tests/unit/components/designer/profile/FormUseCaseProfile.spec.js +++ b/app/frontend/tests/unit/components/designer/profile/FormUseCaseProfile.spec.js @@ -5,14 +5,17 @@ import { FormProfileValues } from '~/utils/constants'; import { createTestingPinia } from '@pinia/testing'; import { setActivePinia } from 'pinia'; import { mount } from '@vue/test-utils'; +import { useAppStore } from '~/store/app'; describe('FormUseCaseProfile.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders properly', () => { @@ -36,7 +39,6 @@ describe('FormUseCaseProfile.vue', () => { const select = wrapper.findComponent('[data-test="case-select"]'); const items = select.componentVM.items; - console.log(items); expect(items).toEqual(FormProfileValues.USE_CASE); }); }); diff --git a/app/frontend/tests/unit/components/designer/settings/FormAccessSettings.spec.js b/app/frontend/tests/unit/components/designer/settings/FormAccessSettings.spec.js index 98590f307..82f8b5bf3 100644 --- a/app/frontend/tests/unit/components/designer/settings/FormAccessSettings.spec.js +++ b/app/frontend/tests/unit/components/designer/settings/FormAccessSettings.spec.js @@ -7,15 +7,18 @@ import { nextTick, ref } from 'vue'; import { useFormStore } from '~/store/form'; import FormAccessSettings from '~/components/designer/settings/FormAccessSettings.vue'; import { IdentityMode } from '~/utils/constants'; +import { useAppStore } from '~/store/app'; describe('FormAccessSettings.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders and displays 3 radio buttons', () => { diff --git a/app/frontend/tests/unit/components/designer/settings/FormEventStreamSettings.spec.js b/app/frontend/tests/unit/components/designer/settings/FormEventStreamSettings.spec.js new file mode 100644 index 000000000..f52009267 --- /dev/null +++ b/app/frontend/tests/unit/components/designer/settings/FormEventStreamSettings.spec.js @@ -0,0 +1,128 @@ +import { createTestingPinia } from '@pinia/testing'; +import { mount } from '@vue/test-utils'; +import { setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { ref } from 'vue'; + +import { useFormStore } from '~/store/form'; +import FormEventStreamSettings from '~/components/designer/settings/FormEventStreamSettings.vue'; + +describe('FormEventStreamSettings.vue', () => { + const crypto = require('crypto').webcrypto; + // Shims the crypto property onto global + global.crypto = crypto; + + const pinia = createTestingPinia(); + setActivePinia(pinia); + + const formStore = useFormStore(pinia); + + beforeEach(() => { + formStore.$reset(); + }); + + it('generates an encryption key when it has an algorithm', async () => { + formStore.form = ref({ + eventStreamConfig: { + enablePrivateStream: true, + encryptionKey: { + algorithm: 'aes-256-gcm', + key: null, + }, + }, + }); + const wrapper = mount(FormEventStreamSettings, { + global: { + plugins: [pinia], + stubs: { + BasePanel: { + name: 'BasePanel', + template: '
', + }, + }, + }, + }); + + expect(formStore.form.eventStreamConfig.encryptionKey.key).toBe(null); + await wrapper.vm.generateKey(); + expect(formStore.form.eventStreamConfig.encryptionKey.key).toBeTruthy(); // populated + }); + + it('does not generate an encryption key without algorithm', async () => { + formStore.form = ref({ + eventStreamConfig: { + enablePrivateStream: true, + encryptionKey: { + algorithm: null, + key: null, + }, + }, + }); + const wrapper = mount(FormEventStreamSettings, { + global: { + plugins: [pinia], + stubs: { + BasePanel: { + name: 'BasePanel', + template: '
', + }, + }, + }, + }); + + expect(formStore.form.eventStreamConfig.encryptionKey.key).toBe(null); + await wrapper.vm.generateKey(); + expect(formStore.form.eventStreamConfig.encryptionKey.key).toBe(undefined); + }); + + it('requires algorithm and key when private stream enabled', async () => { + formStore.form = ref({ + eventStreamConfig: { + enablePublicStream: false, + enablePrivateStream: true, + encryptionKey: { + algorithm: null, + key: null, + }, + }, + }); + + const wrapper = mount(FormEventStreamSettings, { + global: { + plugins: [pinia], + stubs: { + BasePanel: { + name: 'BasePanel', + template: '
', + }, + }, + }, + }); + + expect( + wrapper.vm.encryptionKeyRules[0]( + formStore.form.eventStreamConfig.encryptionKey.algorithm + ) + ).toEqual('trans.formSettings.encryptionKeyReq'); + + expect( + wrapper.vm.encryptionKeyRules[0]( + formStore.form.eventStreamConfig.encryptionKey.key + ) + ).toEqual('trans.formSettings.encryptionKeyReq'); + + expect(wrapper.vm.encryptionKeyRules[0](null)).toEqual( + 'trans.formSettings.encryptionKeyReq' + ); + + expect(wrapper.vm.encryptionKeyRules[0](undefined)).toEqual( + 'trans.formSettings.encryptionKeyReq' + ); + + expect(wrapper.vm.encryptionKeyRules[0](' ')).toEqual( + 'trans.formSettings.encryptionKeyReq' + ); + + expect(wrapper.vm.encryptionKeyRules[0]('aes-256-gcm')).toEqual(true); // some value should pass + }); +}); diff --git a/app/frontend/tests/unit/components/designer/settings/FormFunctionalitySettings.spec.js b/app/frontend/tests/unit/components/designer/settings/FormFunctionalitySettings.spec.js index 4981b3489..8ab0c6a25 100644 --- a/app/frontend/tests/unit/components/designer/settings/FormFunctionalitySettings.spec.js +++ b/app/frontend/tests/unit/components/designer/settings/FormFunctionalitySettings.spec.js @@ -9,6 +9,7 @@ import { useFormStore } from '~/store/form'; import { useIdpStore } from '~/store/identityProviders'; import FormFunctionalitySettings from '~/components/designer/settings/FormFunctionalitySettings.vue'; import { FormRoleCodes, AppPermissions } from '~/utils/constants'; +import { useAppStore } from '~/store/app'; const IDIR = { active: true, @@ -109,11 +110,13 @@ describe('FormFunctionalitySettings.vue', () => { const authStore = useAuthStore(pinia); const formStore = useFormStore(pinia); const idpStore = useIdpStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { authStore.$reset(); formStore.$reset(); idpStore.$reset(); + appStore.$reset(); authStore.identityProvider = ref({ code: 'idir', diff --git a/app/frontend/tests/unit/components/designer/settings/FormScheduleSettings.spec.js b/app/frontend/tests/unit/components/designer/settings/FormScheduleSettings.spec.js index bb5a72f2b..f0a8845ae 100644 --- a/app/frontend/tests/unit/components/designer/settings/FormScheduleSettings.spec.js +++ b/app/frontend/tests/unit/components/designer/settings/FormScheduleSettings.spec.js @@ -11,15 +11,18 @@ import { useFormStore } from '~/store/form'; import FormScheduleSettings from '~/components/designer/settings/FormScheduleSettings.vue'; import { ScheduleType } from '~/utils/constants'; import { getSubmissionPeriodDates } from '~/utils/transformUtils'; +import { useAppStore } from '~/store/app'; describe('FormScheduleSettings.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); formStore.form = ref({ schedule: { enabled: null, diff --git a/app/frontend/tests/unit/components/designer/settings/FormSubmissionSettings.spec.js b/app/frontend/tests/unit/components/designer/settings/FormSubmissionSettings.spec.js index 9d323d0eb..acd2ea015 100644 --- a/app/frontend/tests/unit/components/designer/settings/FormSubmissionSettings.spec.js +++ b/app/frontend/tests/unit/components/designer/settings/FormSubmissionSettings.spec.js @@ -6,15 +6,18 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { useFormStore } from '~/store/form'; import FormSubmissionSettings from '~/components/designer/settings/FormSubmissionSettings.vue'; import { nextTick } from 'vue'; +import { useAppStore } from '~/store/app'; describe('FormSubmissionSettings.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders and tests default values', () => { diff --git a/app/frontend/tests/unit/components/forms/ExportSubmissions.spec.js b/app/frontend/tests/unit/components/forms/ExportSubmissions.spec.js index cc534dc0e..94c6de7ed 100644 --- a/app/frontend/tests/unit/components/forms/ExportSubmissions.spec.js +++ b/app/frontend/tests/unit/components/forms/ExportSubmissions.spec.js @@ -12,9 +12,12 @@ import ExportSubmissions from '~/components/forms/ExportSubmissions.vue'; import { formService } from '~/services'; import { useAuthStore } from '~/store/auth'; import { useFormStore } from '~/store/form'; + +import { useAppStore } from '~/store/app'; import { useNotificationStore } from '~/store/notification'; import { ExportLargeData } from '~/utils/constants'; + describe('ExportSubmissions.vue', () => { const formId = '123-456'; @@ -28,6 +31,8 @@ describe('ExportSubmissions.vue', () => { setActivePinia(pinia); const authStore = useAuthStore(pinia); const formStore = useFormStore(pinia); + + const appStore = useAppStore(pinia); const notificationStore = useNotificationStore(pinia); const addNotificationSpy = vi.spyOn(notificationStore, 'addNotification'); addNotificationSpy.mockImplementation(() => {}); @@ -35,7 +40,7 @@ describe('ExportSubmissions.vue', () => { beforeEach(() => { authStore.$reset(); formStore.$reset(); - + appStore.$reset(); addNotificationSpy.mockReset(); }); diff --git a/app/frontend/tests/unit/components/forms/SubmissionsTable.spec.js b/app/frontend/tests/unit/components/forms/SubmissionsTable.spec.js index 03a4c867d..3c94f627d 100644 --- a/app/frontend/tests/unit/components/forms/SubmissionsTable.spec.js +++ b/app/frontend/tests/unit/components/forms/SubmissionsTable.spec.js @@ -8,6 +8,8 @@ import getRouter from '~/router'; import SubmissionsTable from '~/components/forms/SubmissionsTable.vue'; import { useAuthStore } from '~/store/auth'; import { useFormStore } from '~/store/form'; + +import { useAppStore } from '~/store/app'; import { FormRoleCodes } from '~/utils/constants'; import { STUBS } from '../../stubs'; import moment from 'moment'; @@ -25,11 +27,12 @@ describe('SubmissionsTable.vue', () => { setActivePinia(pinia); const authStore = useAuthStore(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { authStore.$reset(); formStore.$reset(); - + appStore.$reset(); formStore.form = require('../../fixtures/form.json'); }); diff --git a/app/frontend/tests/unit/components/forms/manage/ApiKey.spec.js b/app/frontend/tests/unit/components/forms/manage/ApiKey.spec.js index e58d6a1ca..7e4b35809 100644 --- a/app/frontend/tests/unit/components/forms/manage/ApiKey.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/ApiKey.spec.js @@ -7,6 +7,7 @@ import { describe, beforeEach, vi } from 'vitest'; import ApiKey from '~/components/forms/manage/ApiKey.vue'; import { useFormStore } from '~/store/form'; import { FormPermissions } from '~/utils/constants'; +import { useAppStore } from '~/store/app'; const STUBS = { BaseCopyToClipboard: { @@ -32,9 +33,11 @@ describe('ApiKey.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders', () => { diff --git a/app/frontend/tests/unit/components/forms/manage/DocumentTemplate.spec.js b/app/frontend/tests/unit/components/forms/manage/DocumentTemplate.spec.js index 1207bcd6f..b78ddddfc 100644 --- a/app/frontend/tests/unit/components/forms/manage/DocumentTemplate.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/DocumentTemplate.spec.js @@ -15,6 +15,7 @@ import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; import getRouter from '~/router'; import { ref } from 'vue'; +import { useAppStore } from '~/store/app'; const STUBS = { VDataTableServer: { @@ -33,7 +34,7 @@ const STUBS = { }; describe('DocumentTemplate.vue', () => { - let router, pinia, formStore, notificationStore; + let router, pinia, formStore, notificationStore, appStore; let createObjectURLSpy = vi.spyOn(window.URL, 'createObjectURL'); beforeEach(() => { @@ -47,9 +48,11 @@ describe('DocumentTemplate.vue', () => { formStore = useFormStore(pinia); notificationStore = useNotificationStore(pinia); + appStore = useAppStore(pinia); formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); // Explicitly mock/spy on global functions createObjectURLSpy.mockImplementation(() => '#'); diff --git a/app/frontend/tests/unit/components/forms/manage/EmailTemplate.spec.js b/app/frontend/tests/unit/components/forms/manage/EmailTemplate.spec.js index a2df1ab67..7d225484b 100644 --- a/app/frontend/tests/unit/components/forms/manage/EmailTemplate.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/EmailTemplate.spec.js @@ -7,6 +7,7 @@ import { ref } from 'vue'; import EmailTemplate from '~/components/forms/manage/EmailTemplate.vue'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; const STUBS = {}; @@ -16,10 +17,11 @@ describe('EmailTemplate.vue', () => { const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); - + appStore.$reset(); formStore.emailTemplates = ref([ { body: 'Thank you for your {{ form.name }} submission. You can view your submission details by visiting the following links:', diff --git a/app/frontend/tests/unit/components/forms/manage/ManageForm.spec.js b/app/frontend/tests/unit/components/forms/manage/ManageForm.spec.js index ec93f2a50..d2701c888 100644 --- a/app/frontend/tests/unit/components/forms/manage/ManageForm.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/ManageForm.spec.js @@ -10,6 +10,7 @@ import getRouter from '~/router'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; import { FormPermissions } from '~/utils/constants'; +import { useAppStore } from '~/store/app'; const STUBS = { VDataTable: { @@ -42,12 +43,14 @@ describe('ManageForm.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); const readFormSpy = vi.spyOn(formStore, 'readFormSubscriptionData'); const addNotificationSpy = vi.spyOn(notificationStore, 'addNotification'); beforeEach(() => { formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); readFormSpy.mockReset(); addNotificationSpy.mockReset(); readFormSpy.mockImplementationOnce(async () => {}); diff --git a/app/frontend/tests/unit/components/forms/manage/ManageFormActions.spec.js b/app/frontend/tests/unit/components/forms/manage/ManageFormActions.spec.js index 7b7257561..3fa13bbfb 100644 --- a/app/frontend/tests/unit/components/forms/manage/ManageFormActions.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/ManageFormActions.spec.js @@ -8,6 +8,7 @@ import ManageFormActions from '~/components/forms/manage/ManageFormActions.vue'; import { useFormStore } from '~/store/form'; import { FormPermissions } from '~/utils/constants'; import { ref } from 'vue'; +import { useAppStore } from '~/store/app'; vi.mock('vue-router', () => ({ useRouter: vi.fn(() => ({ @@ -30,9 +31,11 @@ describe('ManageForm.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders', async () => { diff --git a/app/frontend/tests/unit/components/forms/manage/ManageLayout.spec.js b/app/frontend/tests/unit/components/forms/manage/ManageLayout.spec.js index ffa573558..9e274eaf4 100644 --- a/app/frontend/tests/unit/components/forms/manage/ManageLayout.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/ManageLayout.spec.js @@ -9,6 +9,7 @@ import ManageLayout from '~/components/forms/manage/ManageLayout.vue'; import { useFormStore } from '~/store/form'; import { FormPermissions } from '~/utils/constants'; import { ref } from 'vue'; +import { useAppStore } from '~/store/app'; describe('ManageLayout.vue', () => { const pinia = createTestingPinia(); @@ -19,9 +20,11 @@ describe('ManageLayout.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders', () => { diff --git a/app/frontend/tests/unit/components/forms/manage/ManageVersions.spec.js b/app/frontend/tests/unit/components/forms/manage/ManageVersions.spec.js index eef6a06cd..7796f8eb0 100644 --- a/app/frontend/tests/unit/components/forms/manage/ManageVersions.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/ManageVersions.spec.js @@ -13,6 +13,7 @@ import ManageVersions from '~/components/forms/manage/ManageVersions.vue'; import { formService } from '~/services'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; vi.mock('vue-router', () => ({ useRouter: vi.fn(() => ({ @@ -32,10 +33,12 @@ describe('ManageVersions.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); }); it('renders', async () => { diff --git a/app/frontend/tests/unit/components/forms/manage/ShareForm.spec.js b/app/frontend/tests/unit/components/forms/manage/ShareForm.spec.js index 4be51d4c7..adeb85427 100644 --- a/app/frontend/tests/unit/components/forms/manage/ShareForm.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/ShareForm.spec.js @@ -7,6 +7,7 @@ import { useRouter } from 'vue-router'; import ShareForm from '~/components/forms/manage/ShareForm.vue'; import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; const STUBS = { BaseCopyToClipboard: { @@ -41,9 +42,11 @@ describe('ShareForm.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('formLink resolves a URL and returns the href', async () => { diff --git a/app/frontend/tests/unit/components/forms/manage/Subscription.spec.js b/app/frontend/tests/unit/components/forms/manage/Subscription.spec.js index 03f882412..a4d645c38 100644 --- a/app/frontend/tests/unit/components/forms/manage/Subscription.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/Subscription.spec.js @@ -5,14 +5,17 @@ import { beforeEach, vi } from 'vitest'; import Subscription from '~/components/forms/manage/Subscription.vue'; import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; describe('Subscription.vue', () => { const pinia = createTestingPinia(); setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('showHideKey should toggle the value', async () => { diff --git a/app/frontend/tests/unit/components/forms/manage/TeamManagement.spec.js b/app/frontend/tests/unit/components/forms/manage/TeamManagement.spec.js index d29fb4e25..e19b531b0 100644 --- a/app/frontend/tests/unit/components/forms/manage/TeamManagement.spec.js +++ b/app/frontend/tests/unit/components/forms/manage/TeamManagement.spec.js @@ -11,6 +11,8 @@ import { useAuthStore } from '~/store/auth'; import { useFormStore } from '~/store/form'; import { useIdpStore } from '~/store/identityProviders'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; + import { AppPermissions, FormPermissions, @@ -279,6 +281,7 @@ describe('TeamManagement.vue', () => { const formStore = useFormStore(pinia); const idpStore = useIdpStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); const fetchFormSpy = vi.spyOn(formStore, 'fetchForm'); const listRolesSpy = vi.spyOn(roleService, 'list'); @@ -286,6 +289,7 @@ describe('TeamManagement.vue', () => { authStore.$reset(); formStore.$reset(); idpStore.$reset(); + appStore.$reset(); notificationStore.$reset(); fetchFormSpy.mockReset(); listRolesSpy.mockReset(); diff --git a/app/frontend/tests/unit/components/forms/submission/DeleteSubmission.spec.js b/app/frontend/tests/unit/components/forms/submission/DeleteSubmission.spec.js index 66b372dd7..c33bc1647 100644 --- a/app/frontend/tests/unit/components/forms/submission/DeleteSubmission.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/DeleteSubmission.spec.js @@ -2,7 +2,7 @@ import { createTestingPinia } from '@pinia/testing'; import { shallowMount } from '@vue/test-utils'; import { setActivePinia } from 'pinia'; import DeleteSubmission from '~/components/forms/submission/DeleteSubmission.vue'; -import { useFormStore } from '../../../../../src/store/form'; +import { useFormStore } from '~/store/form'; import { beforeEach, vi } from 'vitest'; describe('DeleteSubmission.vue', () => { diff --git a/app/frontend/tests/unit/components/forms/submission/ManageSubmissionUsers.spec.js b/app/frontend/tests/unit/components/forms/submission/ManageSubmissionUsers.spec.js index da414ba4c..8e81180e0 100644 --- a/app/frontend/tests/unit/components/forms/submission/ManageSubmissionUsers.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/ManageSubmissionUsers.spec.js @@ -11,6 +11,7 @@ import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; import { useIdpStore } from '~/store/identityProviders'; import { FormPermissions } from '~/utils/constants'; +import { useAppStore } from '~/store/app'; const providers = require('../../../fixtures/identityProviders.json'); @@ -34,6 +35,8 @@ describe('ManageSubmissionUsers.vue', () => { const pinia = createTestingPinia({ stubActions: false }); setActivePinia(pinia); const formStore = useFormStore(pinia); + useAppStore(pinia); + formStore.form.name = 'myForm'; getSubmissionUsersSpy.mockImplementation(() => ({ data: [] })); const wrapper = mount(ManageSubmissionUsers, { diff --git a/app/frontend/tests/unit/components/forms/submission/MySubmissionsActions.spec.js b/app/frontend/tests/unit/components/forms/submission/MySubmissionsActions.spec.js index 6a20c307e..0943aacf0 100644 --- a/app/frontend/tests/unit/components/forms/submission/MySubmissionsActions.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/MySubmissionsActions.spec.js @@ -8,6 +8,7 @@ import MySubmissionsActions from '~/components/forms/submission/MySubmissionsAct import getRouter from '~/router'; import { useFormStore } from '~/store/form'; import { FormPermissions } from '~/utils/constants'; +import { useAppStore } from '~/store/app'; const FORM_ID = '123'; const STUBS = { @@ -24,6 +25,8 @@ const STUBS = { describe('MySubmissionsActions', () => { const pinia = createTestingPinia(); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); + setActivePinia(pinia); const router = createRouter({ history: createWebHistory(), @@ -32,6 +35,7 @@ describe('MySubmissionsActions', () => { beforeEach(() => { formStore.$reset(); + appStore.$reset(); }); it('renders', () => { diff --git a/app/frontend/tests/unit/components/forms/submission/MySubmissionsTable.spec.js b/app/frontend/tests/unit/components/forms/submission/MySubmissionsTable.spec.js index f8b854c5c..af446e95f 100644 --- a/app/frontend/tests/unit/components/forms/submission/MySubmissionsTable.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/MySubmissionsTable.spec.js @@ -7,6 +7,7 @@ import { createRouter, createWebHistory } from 'vue-router'; import getRouter from '~/router'; import MySubmissionsTable from '~/components/forms/submission/MySubmissionsTable.vue'; import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; describe('MySubmissionsTable.vue', () => { const formId = '123-456'; @@ -20,6 +21,7 @@ describe('MySubmissionsTable.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); const fetchFormSpy = vi.spyOn(formStore, 'fetchForm').mockResolvedValue({}); const fetchFormFieldsSpy = vi @@ -30,6 +32,7 @@ describe('MySubmissionsTable.vue', () => { fetchFormSpy.mockReset(); fetchFormFieldsSpy.mockReset(); formStore.$reset(); + appStore.$reset(); formStore.form = { versions: [ diff --git a/app/frontend/tests/unit/components/forms/submission/NotesPanel.spec.js b/app/frontend/tests/unit/components/forms/submission/NotesPanel.spec.js index cf18090bf..e7c1449fa 100644 --- a/app/frontend/tests/unit/components/forms/submission/NotesPanel.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/NotesPanel.spec.js @@ -8,6 +8,7 @@ import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; import { formService } from '~/services'; import { rbacService } from '~/services'; +import { useAppStore } from '~/store/app'; const SUBMISSION_ID = 'submissionId'; const USER_ID = 'userId'; @@ -29,6 +30,7 @@ describe('NotesPanel', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); const addNotificationSpy = vi .spyOn(notificationStore, 'addNotification') @@ -37,6 +39,7 @@ describe('NotesPanel', () => { beforeEach(() => { formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); addNotificationSpy.mockReset(); }); diff --git a/app/frontend/tests/unit/components/forms/submission/StatusPanel.spec.js b/app/frontend/tests/unit/components/forms/submission/StatusPanel.spec.js index 2c6cc0139..16fa9f583 100644 --- a/app/frontend/tests/unit/components/forms/submission/StatusPanel.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/StatusPanel.spec.js @@ -10,6 +10,7 @@ import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; import { formService } from '~/services'; import { rbacService } from '~/services'; +import { useAppStore } from '~/store/app'; const FORM_ID = 'formId'; const SUBMISSION_ID = 'submissionId'; @@ -53,6 +54,7 @@ describe('StatusPanel', () => { const authStore = useAuthStore(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); const addNotificationSpy = vi .spyOn(notificationStore, 'addNotification') @@ -64,6 +66,7 @@ describe('StatusPanel', () => { authStore.$reset(); formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); addNotificationSpy.mockReset(); getFormUsersSpy.mockReset(); diff --git a/app/frontend/tests/unit/components/forms/submission/StatusTable.spec.js b/app/frontend/tests/unit/components/forms/submission/StatusTable.spec.js index 7ba8def34..2bda04208 100644 --- a/app/frontend/tests/unit/components/forms/submission/StatusTable.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/StatusTable.spec.js @@ -7,6 +7,7 @@ import { formService } from '~/services'; import StatusTable from '~/components/forms/submission/StatusTable.vue'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; describe('StatusTable.vue', () => { const submissionId = '123-456'; @@ -16,10 +17,12 @@ describe('StatusTable.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); }); it('renders', async () => { diff --git a/app/frontend/tests/unit/components/forms/submission/UserDuplicateSubmission.spec.js b/app/frontend/tests/unit/components/forms/submission/UserDuplicateSubmission.spec.js index 85531594e..620ca2dd3 100644 --- a/app/frontend/tests/unit/components/forms/submission/UserDuplicateSubmission.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/UserDuplicateSubmission.spec.js @@ -6,6 +6,7 @@ import { beforeEach, vi } from 'vitest'; import UserDuplicateSubmission from '~/components/forms/submission/UserDuplicateSubmission.vue'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; const STUBS = { VSkeletonLoader: { @@ -22,10 +23,12 @@ describe('UserDuplicateSubmission.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); }); it('renders', async () => { diff --git a/app/frontend/tests/unit/components/forms/submission/UserSubmission.spec.js b/app/frontend/tests/unit/components/forms/submission/UserSubmission.spec.js index 83f0038d3..2c6712567 100644 --- a/app/frontend/tests/unit/components/forms/submission/UserSubmission.spec.js +++ b/app/frontend/tests/unit/components/forms/submission/UserSubmission.spec.js @@ -7,6 +7,7 @@ import { useRouter } from 'vue-router'; import UserSubmission from '~/components/forms/submission/UserSubmission.vue'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; vi.mock('vue-router', () => ({ useRouter: vi.fn(() => ({ @@ -31,10 +32,12 @@ describe('UserSubmission.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); const notificationStore = useNotificationStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); notificationStore.$reset(); + appStore.$reset(); }); it('renders', async () => { diff --git a/app/frontend/tests/unit/components/infolinks/ProactiveHelpPreviewDialog.spec.js b/app/frontend/tests/unit/components/infolinks/ProactiveHelpPreviewDialog.spec.js index c82038574..188eabe55 100644 --- a/app/frontend/tests/unit/components/infolinks/ProactiveHelpPreviewDialog.spec.js +++ b/app/frontend/tests/unit/components/infolinks/ProactiveHelpPreviewDialog.spec.js @@ -7,6 +7,9 @@ import { expect } from 'vitest'; import getRouter from '~/router'; import ProactiveHelpPreviewDialog from '~/components/infolinks/ProactiveHelpPreviewDialog.vue'; +import { useFormStore } from '~/store/form'; +import { useAppStore } from '~/store/app'; + describe('ProactiveHelpPreviewDialog.vue', () => { const pinia = createTestingPinia(); const router = createRouter({ @@ -16,6 +19,16 @@ describe('ProactiveHelpPreviewDialog.vue', () => { setActivePinia(pinia); + const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); + + beforeEach(() => { + formStore.$reset(); + appStore.$reset(); + }); + + afterAll(() => {}); + it('renders', () => { const wrapper = mount(ProactiveHelpPreviewDialog, { props: { diff --git a/app/frontend/tests/unit/services/encryptionKeyService.spec.js b/app/frontend/tests/unit/services/encryptionKeyService.spec.js new file mode 100644 index 000000000..92e9be8fa --- /dev/null +++ b/app/frontend/tests/unit/services/encryptionKeyService.spec.js @@ -0,0 +1,41 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import service from '~/services/encryptionKeyService'; +import { ApiRoutes } from '~/utils/constants'; + +const mockInstance = axios.create(); +const mockAxios = new MockAdapter(mockInstance); + +const zeroUuid = '00000000-0000-0000-0000-000000000000'; + +vi.mock('~/services/interceptors', () => { + return { + appAxios: () => mockInstance, + }; +}); + +beforeEach(() => { + mockAxios.reset(); +}); + +describe('Encryption Key Service', () => { + it('calls get on GET list algorithms endpoint', async () => { + const endpoint = `${ApiRoutes.FORMS}/${ApiRoutes.ENCRYPTION_KEY}/algorithms`; + mockAxios.onGet(endpoint).reply(200); + + const result = await service.listEncryptionAlgorithms(); + expect(result).toBeTruthy(); + expect(mockAxios.history.get).toHaveLength(1); + }); + + it('calls get on GET form encryption key endpoint', async () => { + const endpoint = `${ApiRoutes.FORMS}/${zeroUuid}${ApiRoutes.ENCRYPTION_KEY}/${zeroUuid}`; + mockAxios.onGet(endpoint).reply(200); + + const result = await service.getEncryptionKey(zeroUuid, zeroUuid); + expect(result).toBeTruthy(); + expect(mockAxios.history.get).toHaveLength(1); + }); +}); diff --git a/app/frontend/tests/unit/services/eventStreamConfigService.spec.js b/app/frontend/tests/unit/services/eventStreamConfigService.spec.js new file mode 100644 index 000000000..2990880ba --- /dev/null +++ b/app/frontend/tests/unit/services/eventStreamConfigService.spec.js @@ -0,0 +1,32 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import service from '~/services/eventStreamConfigService'; +import { ApiRoutes } from '~/utils/constants'; + +const mockInstance = axios.create(); +const mockAxios = new MockAdapter(mockInstance); + +const zeroUuid = '00000000-0000-0000-0000-000000000000'; + +vi.mock('~/services/interceptors', () => { + return { + appAxios: () => mockInstance, + }; +}); + +beforeEach(() => { + mockAxios.reset(); +}); + +describe('Event Stream Config Service', () => { + it('calls get on GET form event stream config', async () => { + const endpoint = `${ApiRoutes.FORMS}/${zeroUuid}${ApiRoutes.EVENT_STREAM_CONFIG}`; + mockAxios.onGet(endpoint).reply(200); + + const result = await service.getEventStreamConfig(zeroUuid); + expect(result).toBeTruthy(); + expect(mockAxios.history.get).toHaveLength(1); + }); +}); diff --git a/app/frontend/tests/unit/store/modules/auth.actions.spec.js b/app/frontend/tests/unit/store/modules/auth.actions.spec.js index f3c87da42..03dbe633f 100644 --- a/app/frontend/tests/unit/store/modules/auth.actions.spec.js +++ b/app/frontend/tests/unit/store/modules/auth.actions.spec.js @@ -26,6 +26,7 @@ describe('auth actions', () => { beforeEach(() => { mockStore.$reset(); formStore.$reset(); + appStore.$reset(); mockStore.keycloak = { createLoginUrl: vi.fn(() => 'about:blank'), createLogoutUrl: vi.fn(() => 'about:blank'), diff --git a/app/frontend/tests/unit/store/modules/form.actions.spec.js b/app/frontend/tests/unit/store/modules/form.actions.spec.js index 555f6683e..35df8495c 100644 --- a/app/frontend/tests/unit/store/modules/form.actions.spec.js +++ b/app/frontend/tests/unit/store/modules/form.actions.spec.js @@ -9,12 +9,14 @@ import { } from '~/services'; import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; +import { useAppStore } from '~/store/app'; vi.mock('~/services'); describe('form actions', () => { setActivePinia(createPinia()); const mockStore = useFormStore(); + const appStore = useAppStore(); const notificationStore = useNotificationStore(); const addNotificationSpy = vi.spyOn(notificationStore, 'addNotification'); const listSubmissionsSpy = vi.spyOn(formService, 'listSubmissions'); @@ -24,6 +26,7 @@ describe('form actions', () => { beforeEach(() => { mockStore.$reset(); mockConsoleError.mockReset(); + appStore.$reset(); notificationStore.$reset(); addNotificationSpy.mockReset(); listSubmissionsSpy.mockReset(); diff --git a/app/frontend/tests/unit/utils/constants.spec.js b/app/frontend/tests/unit/utils/constants.spec.js index 1e48ccd6e..71dca6ed3 100644 --- a/app/frontend/tests/unit/utils/constants.spec.js +++ b/app/frontend/tests/unit/utils/constants.spec.js @@ -17,6 +17,8 @@ describe('Constants', () => { FILES_API_ACCESS: '/filesApiAccess', PROXY: '/proxy', FORM_METADATA: '/formMetadata', + ENCRYPTION_KEY: '/encryptionKey', + EVENT_STREAM_CONFIG: '/eventStreamConfig', }); }); diff --git a/app/frontend/tests/unit/views/file/Download.spec.js b/app/frontend/tests/unit/views/file/Download.spec.js index 33facc393..76b5165e8 100644 --- a/app/frontend/tests/unit/views/file/Download.spec.js +++ b/app/frontend/tests/unit/views/file/Download.spec.js @@ -10,6 +10,7 @@ import { useFormStore } from '~/store/form'; import { useNotificationStore } from '~/store/notification'; import Download from '~/views/file/Download.vue'; import * as transformUtils from '~/utils/transformUtils'; +import { useAppStore } from '~/store/app'; describe('Download.vue', () => { let pinia; @@ -37,6 +38,8 @@ describe('Download.vue', () => { formStore.$reset(); const notificationStore = useNotificationStore(); notificationStore.$reset(); + const appStore = useAppStore(pinia); + appStore.$reset(); }); it('renders and downloads json', async () => { diff --git a/app/frontend/tests/unit/views/form/Create.spec.js b/app/frontend/tests/unit/views/form/Create.spec.js index 600c2fb2b..1cbb27cc4 100644 --- a/app/frontend/tests/unit/views/form/Create.spec.js +++ b/app/frontend/tests/unit/views/form/Create.spec.js @@ -11,6 +11,7 @@ import { useFormStore } from '~/store/form'; import Create from '~/views/form/Create.vue'; import { IdentityMode } from '~/utils/constants'; import { nextTick } from 'vue'; +import { useAppStore } from '~/store/app'; vi.mock('vue-router', () => ({ ...vi.importActual('vue-router'), @@ -24,9 +25,11 @@ describe('Create.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(async () => { formStore.$reset(); + appStore.$reset(); mockWindowConfirm.mockReset(); }); diff --git a/app/frontend/tests/unit/views/form/Design.spec.js b/app/frontend/tests/unit/views/form/Design.spec.js index 5bf05f3ed..dc9debe77 100644 --- a/app/frontend/tests/unit/views/form/Design.spec.js +++ b/app/frontend/tests/unit/views/form/Design.spec.js @@ -8,6 +8,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { useFormStore } from '~/store/form'; import Design from '~/views/form/Design.vue'; +import { useAppStore } from '~/store/app'; vi.mock('vue-router', () => ({ ...vi.importActual('vue-router'), @@ -20,9 +21,11 @@ describe('Design.vue', () => { setActivePinia(pinia); const formStore = useFormStore(pinia); + const appStore = useAppStore(pinia); beforeEach(() => { formStore.$reset(); + appStore.$reset(); mockWindowConfirm.mockReset(); }); diff --git a/app/package-lock.json b/app/package-lock.json index 67843b26e..0abf6e2c7 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -36,10 +36,13 @@ "mime-types": "^2.1.35", "moment": "^2.29.4", "multer": "^1.4.5-lts.1", + "nats": "^2.28.0", + "nats.ws": "^1.29.2", "nested-objects-util": "^1.1.2", "objection": "^3.0.1", "pg": "^8.10.0", "uuid": "^8.3.2", + "websocket": "^1.0.35", "winston": "^3.8.2" }, "devDependencies": { @@ -300,67 +303,67 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/client-s3": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.645.0.tgz", - "integrity": "sha512-RjT/mfNv4yr1uv/+aEXgSIxC5EB+yHPSU7hH0KZOZrvZEFASLl0i4FeoHzbMEOH5KdKGAi0uu3zRP3D1y45sKg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.654.0.tgz", + "integrity": "sha512-EsyeZJhkZD2VMdZpNt4NhlQ3QUAF24gMC+5w2wpGg6Yw+Bv7VLdg1t3PkTQovriJX1KTJAYHcGAuy92OFmWIng==", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.645.0", - "@aws-sdk/client-sts": "3.645.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.645.0", - "@aws-sdk/middleware-bucket-endpoint": "3.620.0", - "@aws-sdk/middleware-expect-continue": "3.620.0", - "@aws-sdk/middleware-flexible-checksums": "3.620.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-location-constraint": "3.609.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@aws-sdk/xml-builder": "3.609.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-blob-browser": "^3.1.2", - "@smithy/hash-node": "^3.0.3", - "@smithy/hash-stream-node": "^3.1.2", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/client-sso-oidc": "3.654.0", + "@aws-sdk/client-sts": "3.654.0", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/credential-provider-node": "3.654.0", + "@aws-sdk/middleware-bucket-endpoint": "3.654.0", + "@aws-sdk/middleware-expect-continue": "3.654.0", + "@aws-sdk/middleware-flexible-checksums": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-location-constraint": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-sdk-s3": "3.654.0", + "@aws-sdk/middleware-ssec": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/signature-v4-multi-region": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@aws-sdk/xml-builder": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/eventstream-serde-browser": "^3.0.9", + "@smithy/eventstream-serde-config-resolver": "^3.0.6", + "@smithy/eventstream-serde-node": "^3.0.8", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-blob-browser": "^3.1.5", + "@smithy/hash-node": "^3.0.6", + "@smithy/hash-stream-node": "^3.1.5", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/md5-js": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-stream": "^3.1.6", "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.2", + "@smithy/util-waiter": "^3.1.5", "tslib": "^2.6.2" }, "engines": { @@ -373,46 +376,46 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/client-sso": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.645.0.tgz", - "integrity": "sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.654.0.tgz", + "integrity": "sha512-4kBxs2IzCDtj6a6lRXa/lXK5wWpMGzwKtb+HMXf/rJYVM6x7wYRzc1hYrOd3DYkFQ/sR3dUFj+0mTP0os3aAbA==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -421,47 +424,47 @@ } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.645.0.tgz", - "integrity": "sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.654.0.tgz", + "integrity": "sha512-gbHrKsEnaAtmkNCVQzLyiqMzpDaThV/bWl/ODEklI+t6stW3Pe3oDMstEHLfJ6JU5g8sYnx4VLuxlnJMtUkvPw==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.645.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/credential-provider-node": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -469,7 +472,7 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.645.0" + "@aws-sdk/client-sts": "^3.654.0" } }, "node_modules/@aws-sdk/client-sso-oidc/node_modules/tslib": { @@ -483,48 +486,48 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/client-sts": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.645.0.tgz", - "integrity": "sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.654.0.tgz", + "integrity": "sha512-tyHa8jsBy+/NQZFHm6Q2Q09Vi9p3EH4yPy6PU8yPewpi2klreObtrUd0anJa6nzjS9SSuqnlZWsRic3cQ4QwCg==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.645.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.645.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/client-sso-oidc": "3.654.0", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/credential-provider-node": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -538,18 +541,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/core": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", - "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", - "dependencies": { - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.654.0.tgz", + "integrity": "sha512-4Rwx7BVaNaFqmXBDmnOkMbyuIFFbpZ+ru4lr660p45zY1QoNNSalechfoRffcokLFOZO+VWEJkdcorPUUU993w==", + "dependencies": { + "@smithy/core": "^2.4.3", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, @@ -563,13 +566,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", - "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.654.0.tgz", + "integrity": "sha512-kogsx3Ql81JouHS7DkheCDU9MYAvK0AokxjcshDveGmf7BbgbWCA8Fnb9wjQyNDaOXNvkZu8Z8rgkX91z324/w==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -582,18 +585,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", - "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.654.0.tgz", + "integrity": "sha512-tgmAH4MBi/aDR882lfw48+tDV95ZH3GWc1Eoe6DpNLiM3GN2VfU/cZwuHmi6aq+vAbdIlswBHJ/+va0fOvlyjw==", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/util-stream": "^3.1.6", "tslib": "^2.6.2" }, "engines": { @@ -606,27 +609,27 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.645.0.tgz", - "integrity": "sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.645.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.654.0.tgz", + "integrity": "sha512-DKSdaNu2hwdmuvnm9KnA0NLqMWxxmxSOLWjSUSoFIm++wGXUjPrRMFYKvMktaXnPuyf5my8gF/yGbwzPZ8wlTg==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.654.0", + "@aws-sdk/credential-provider-http": "3.654.0", + "@aws-sdk/credential-provider-process": "3.654.0", + "@aws-sdk/credential-provider-sso": "3.654.0", + "@aws-sdk/credential-provider-web-identity": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.645.0" + "@aws-sdk/client-sts": "^3.654.0" } }, "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { @@ -635,21 +638,21 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.645.0.tgz", - "integrity": "sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-ini": "3.645.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.645.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.654.0.tgz", + "integrity": "sha512-wPV7CNYaXDEc+SS+3R0v8SZwkHRUE1z2k2j1d49tH5QBDT4tb/k2V/biXWkwSk3hbR+IMWXmuhJDv/5lybhIvg==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.654.0", + "@aws-sdk/credential-provider-http": "3.654.0", + "@aws-sdk/credential-provider-ini": "3.654.0", + "@aws-sdk/credential-provider-process": "3.654.0", + "@aws-sdk/credential-provider-sso": "3.654.0", + "@aws-sdk/credential-provider-web-identity": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -662,14 +665,14 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", - "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.654.0.tgz", + "integrity": "sha512-PmQoo8sZ9Q2Ow8OMzK++Z9lI7MsRUG7sNq3E72DVA215dhtTICTDQwGlXH2AAmIp7n+G9LLRds+4wo2ehG4mkg==", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -682,16 +685,16 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.645.0.tgz", - "integrity": "sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==", - "dependencies": { - "@aws-sdk/client-sso": "3.645.0", - "@aws-sdk/token-providers": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.654.0.tgz", + "integrity": "sha512-7GFme6fWEdA/XYKzZPOAdj/jS6fMBy1NdSIZsDXikS0v9jU+ZzHrAaWt13YLzHyjgxB9Sg9id9ncdY1IiubQXQ==", + "dependencies": { + "@aws-sdk/client-sso": "3.654.0", + "@aws-sdk/token-providers": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -704,20 +707,20 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.621.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", - "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.654.0.tgz", + "integrity": "sha512-6a2g9gMtZToqSu+CusjNK5zvbLJahQ9di7buO3iXgbizXpLXU1rnawCpWxwslMpT5fLgMSKDnKDrr6wdEk7jSw==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.621.0" + "@aws-sdk/client-sts": "^3.654.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { @@ -726,15 +729,15 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", - "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.654.0.tgz", + "integrity": "sha512-/lWkyeLESiK+rAB4+NCw1cVPle9RN7RW/v7B4b8ORiCn1FwZLUPmEiZSYzyh4in5oa3Mri+W/g+KafZDH6LCbA==", "dependencies": { - "@aws-sdk/types": "3.609.0", + "@aws-sdk/types": "3.654.0", "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", "tslib": "^2.6.2" }, @@ -748,13 +751,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", - "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.654.0.tgz", + "integrity": "sha512-S7fSlo8vdjkQTy9DmdF54ZsPwc+aA4z5Y9JVqAlGL9QiZe/fPtRE3GZ8BBbMICjBfMEa12tWjzhDz9su2c6PIA==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -767,16 +770,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", - "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.654.0.tgz", + "integrity": "sha512-ZSRC+Lf9WxyoDLuTkd7JrFRrBLPLXcTOZzX6tDsnHc6tgdneBNwV3/ZOYUwQ8bdwLLnzSaQUU+X5B2BkEFKIhQ==", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", - "@aws-sdk/types": "3.609.0", + "@aws-sdk/types": "3.654.0", "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -790,13 +795,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", - "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.654.0.tgz", + "integrity": "sha512-rxGgVHWKp8U2ubMv+t+vlIk7QYUaRCHaVpmUlJv0Wv6Q0KeO9a42T9FxHphjOTlCGQOLcjCreL9CF8Qhtb4mdQ==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -809,12 +814,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz", - "integrity": "sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.654.0.tgz", + "integrity": "sha512-Duvv5c4DEQ7P6c0YlcvEUW3xCJi6X2uktafNGjILhVDMQwShSF/aFqNv/ikWU/luQcmWHZ9DtDjTR9UKLh6eTA==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -827,12 +832,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", - "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.654.0.tgz", + "integrity": "sha512-OQYb+nWlmASyXfRb989pwkJ9EVUMP1CrKn2eyTk3usl20JZmKo2Vjis6I0tLUkMSxMhnBJJlQKyWkRpD/u1FVg==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -845,13 +850,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", - "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.654.0.tgz", + "integrity": "sha512-gKSomgltKVmsT8sC6W7CrADZ4GHwX9epk3GcH6QhebVO3LA9LRbkL3TwOPUXakxxOLLUTYdOZLIOtFf7iH00lg==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -864,22 +869,22 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", - "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.654.0.tgz", + "integrity": "sha512-6prq+GK6hLMAbxEb83tBMb1YiTWWK196fJhFO/7gE5TUPL1v756RhQZzKV/njbwB1fIBjRBTuhYLh5Bn98HhdA==", "dependencies": { - "@aws-sdk/core": "3.635.0", - "@aws-sdk/types": "3.609.0", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/types": "3.654.0", "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/core": "^2.4.3", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-stream": "^3.1.3", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-stream": "^3.1.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -893,12 +898,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", - "integrity": "sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.654.0.tgz", + "integrity": "sha512-k7hkQDJh4hcRJC7YojQ11kc37SY4foryen26Eafj5qYjeG2OGMW0oZTJDl1TVFJ7AcCjqIuMIo0Ho2US/2JspQ==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -911,14 +916,14 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.645.0.tgz", - "integrity": "sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.654.0.tgz", + "integrity": "sha512-liCcqPAyRsr53cy2tYu4qeH4MMN0eh9g6k56XzI5xd4SghXH5YWh4qOYAlQ8T66ZV4nPMtD8GLtLXGzsH8moFg==", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -931,15 +936,15 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", - "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.654.0.tgz", + "integrity": "sha512-ydGOrXJxj3x0sJhsXyTmvJVLAE0xxuTWFJihTl67RtaO7VRNtd82I3P3bwoMMaDn5WpmV5mPo8fEUDRlBm3fPg==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "tslib": "^2.6.2" }, "engines": { @@ -952,15 +957,15 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", - "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.654.0.tgz", + "integrity": "sha512-f8kyvbzgD3lSK1kFc3jsDCYjdutcqGO3tOzYO/QIK7BTl5lxc4rm6IKTcF2UYJsn8jiNqih7tVK8aVIGi8IF/w==", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -973,21 +978,21 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/token-providers": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", - "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.654.0.tgz", + "integrity": "sha512-D8GeJYmvbfWkQDtTB4owmIobSMexZel0fOoetwvgCQ/7L8VPph3Q2bn1TRRIXvH7wdt6DcDxA3tKMHPBkT3GlA==", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.614.0" + "@aws-sdk/client-sso-oidc": "^3.654.0" } }, "node_modules/@aws-sdk/token-providers/node_modules/tslib": { @@ -996,11 +1001,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/types": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", - "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.654.0.tgz", + "integrity": "sha512-VWvbED3SV+10QJIcmU/PKjsKilsTV16d1I7/on4bvD/jo1qGeMXqLDBSen3ks/tuvXZF/mFc7ZW/W2DiLVtO7A==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -1029,13 +1034,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.645.0.tgz", - "integrity": "sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.654.0.tgz", + "integrity": "sha512-i902fcBknHs0Irgdpi62+QMvzxE+bczvILXigYrlHL4+PiEnlMVpni5L5W1qCkNZXf8AaMrSBuR1NZAGp6UOUw==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "@smithy/util-endpoints": "^2.0.5", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "@smithy/util-endpoints": "^2.1.2", "tslib": "^2.6.2" }, "engines": { @@ -1064,12 +1069,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", - "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.654.0.tgz", + "integrity": "sha512-ykYAJqvnxLt7wfrqya28wuH3/7NdrwzfiFd7NqEVQf7dXVxL5RPEpD7DxjcyQo3DsHvvdUvGZVaQhozycn1pzA==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } @@ -1080,13 +1085,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", - "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.654.0.tgz", + "integrity": "sha512-a0ojjdBN6pqv6gB4H/QPPSfhs7mFtlVwnmKCM/QrTaFzN0U810PJ1BST3lBx5sa23I5jWHGaoFY+5q65C3clLQ==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -1107,11 +1112,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz", - "integrity": "sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.654.0.tgz", + "integrity": "sha512-qA2diK3d/ztC8HUb7NwPKbJRV01NpzTzxFn+L5G3HzJBNeKbjLcprQ/9uG9gp2UEx2Go782FI1ddrMNa0qBICA==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2493,11 +2498,11 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", - "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.4.tgz", + "integrity": "sha512-VupaALAQlXViW3/enTf/f5l5JZYSAxoJL7f0nanhNNKnww6DGCg1oYIuNP78KDugnkwthBO6iEcym16HhWV8RQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2537,14 +2542,14 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/config-resolver": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", - "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.8.tgz", + "integrity": "sha512-Tv1obAC18XOd2OnDAjSWmmthzx6Pdeh63FbLin8MlPiuJ2ATpKkq0NcNOJFr0dO+JmZXnwu8FQxKJ3TKJ3Hulw==", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "tslib": "^2.6.2" }, "engines": { @@ -2557,18 +2562,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", - "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.5.tgz", + "integrity": "sha512-Z0qlPXgZ0pouYgnu/cZTEYeRAvniiKZmVl4wIbZHX/nEMHkMDV9ao6KFArsU9KndE0TuhL149xcRx45wfw1YCA==", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.20", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -2582,14 +2587,14 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", - "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.3.tgz", + "integrity": "sha512-VoxMzSzdvkkjMJNE38yQgx4CfnmT+Z+5EUXkg4x7yag93eQkVQgZvN3XBSHC/ylfBbLbAtdu7flTCChX9I+mVg==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "tslib": "^2.6.2" }, "engines": { @@ -2602,12 +2607,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/eventstream-codec": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", - "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.5.tgz", + "integrity": "sha512-6pu+PT2r+5ZnWEV3vLV1DzyrpJ0TmehQlniIDCSpZg6+Ji2SfOI38EqUyQ+O8lotVElCrfVc9chKtSMe9cmCZQ==", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } @@ -2618,12 +2623,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", - "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.9.tgz", + "integrity": "sha512-PiQLo6OQmZAotJweIcObL1H44gkvuJACKMNqpBBe5Rf2Ax1DOcGi/28+feZI7yTe1ERHlQQaGnm8sSkyDUgsMg==", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-serde-universal": "^3.0.8", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2636,11 +2641,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz", - "integrity": "sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.6.tgz", + "integrity": "sha512-iew15It+c7WfnVowWkt2a7cdPp533LFJnpjDQgfZQcxv2QiOcyEcea31mnrk5PVbgo0nNH3VbYGq7myw2q/F6A==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2653,12 +2658,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", - "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.8.tgz", + "integrity": "sha512-6m+wI+fT0na+6oao6UqALVA38fsScCpoG5UO/A8ZSyGLnPM2i4MS1cFUhpuALgvLMxfYoTCh7qSeJa0aG4IWpQ==", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-serde-universal": "^3.0.8", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2671,12 +2676,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", - "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.8.tgz", + "integrity": "sha512-09tqzIQ6e+7jLqGvRji1yJoDbL/zob0OFhq75edgStWErGLf16+yI5hRc/o9/YAybOhUZs/swpW2SPn892G5Gg==", "dependencies": { - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-codec": "^3.1.5", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2689,13 +2694,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", - "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.8.tgz", + "integrity": "sha512-Lqe0B8F5RM7zkw//6avq1SJ8AfaRd3ubFUS1eVp5WszV7p6Ne5hQ4dSuMHDpNRPhgTvj4va9Kd/pcVigHEHRow==", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/querystring-builder": "^3.0.6", + "@smithy/types": "^3.4.2", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } @@ -2706,13 +2711,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/hash-blob-browser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz", - "integrity": "sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.5.tgz", + "integrity": "sha512-Vi3eoNCmao4iKglS80ktYnBOIqZhjbDDwa1IIbF/VaJ8PsHnZTQ5wSicicPrU7nTI4JPFn92/txzWkh4GlK18Q==", "dependencies": { "@smithy/chunked-blob-reader": "^3.0.0", "@smithy/chunked-blob-reader-native": "^3.0.0", - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" } }, @@ -2722,11 +2727,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/hash-node": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", - "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.6.tgz", + "integrity": "sha512-c/FHEdKK/7DU2z6ZE91L36ahyXWayR3B+FzELjnYq7wH5YqIseM24V+pWCS9kFn1Ln8OFGTf+pyYPiHZuX0s/Q==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -2741,11 +2746,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/hash-stream-node": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz", - "integrity": "sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.5.tgz", + "integrity": "sha512-61CyFCzqN3VBfcnGX7mof/rkzLb8oHjm4Lr6ZwBIRpBssBb8d09ChrZAqinP2rUrA915BRNkq9NpJz18N7+3hQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -2759,11 +2764,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", - "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.6.tgz", + "integrity": "sha512-czM7Ioq3s8pIXht7oD+vmgy4Wfb4XavU/k/irO8NdXFFOx7YAlsCCcKOh/lJD1mJSYQqiR7NmpZ9JviryD/7AQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" } }, @@ -2789,11 +2794,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/md5-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", - "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.6.tgz", + "integrity": "sha512-Ze690T8O3M5SVbb70WormwrKzVf9QQRtIuxtJDgpUQDkmt+PtdYDetBbyCbF9ryupxLw6tgzWKgwffAShhVIXQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } @@ -2804,12 +2809,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", - "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.8.tgz", + "integrity": "sha512-VuyszlSO49WKh3H9/kIO2kf07VUwGV80QRiaDxUfP8P8UKlokz381ETJvwLhwuypBYhLymCYyNhB3fLAGBX2og==", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2822,16 +2827,16 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", - "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", - "dependencies": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.3.tgz", + "integrity": "sha512-KeM/OrK8MVFUsoJsmCN0MZMVPjKKLudn13xpgwIMpGTYpA8QZB2Xq5tJ+RE6iu3A6NhOI4VajDTwBsm8pwwrhg==", + "dependencies": { + "@smithy/middleware-serde": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@smithy/util-middleware": "^3.0.6", "tslib": "^2.6.2" }, "engines": { @@ -2844,17 +2849,17 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", - "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.20.tgz", + "integrity": "sha512-HELCOVwYw5hFDBm69d+LmmGjBCjWnwp/t7SJiHmp+c4u9vgfIaCjdSeIdnlOsLrr5ic5jGTJXvJFUQnd987b/g==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/service-error-classification": "^3.0.6", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -2880,11 +2885,11 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", - "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.6.tgz", + "integrity": "sha512-KKTUSl1MzOM0MAjGbudeaVNtIDo+PpekTBkCNwvfZlKndodrnvRo+00USatiyLOc0ujjO9UydMRu3O9dYML7ag==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2897,11 +2902,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", - "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.6.tgz", + "integrity": "sha512-2c0eSYhTQ8xQqHMcRxLMpadFbTXg6Zla5l0mwNftFCZMQmuhI7EbAJMx6R5eqfuV3YbJ3QGyS3d5uSmrHV8Khg==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2914,13 +2919,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", - "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.7.tgz", + "integrity": "sha512-g3mfnC3Oo8pOI0dYuPXLtdW1WGVb3bR2tkV21GNkm0ZvQjLTtamXAwCWt/FCb0HGvKt3gHHmF1XerG0ICfalOg==", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2933,14 +2938,14 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/node-http-handler": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", - "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", - "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.3.tgz", + "integrity": "sha512-/gcm5DJ3k1b1zEInzBGAZC8ntJ+jwrz1NcSIu+9dSXd1FfG0G6QgkDI40tt8/WYUbHtLyo8fEqtm2v29koWo/w==", + "dependencies": { + "@smithy/abort-controller": "^3.1.4", + "@smithy/protocol-http": "^4.1.3", + "@smithy/querystring-builder": "^3.0.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2953,11 +2958,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/property-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", - "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.6.tgz", + "integrity": "sha512-NK3y/T7Q/Bw+Z8vsVs9MYIQ5v7gOX7clyrXcwhhIBQhbPgRl6JDrZbusO9qWDhcEus75Tg+VCxtIRfo3H76fpw==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2970,11 +2975,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/protocol-http": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", - "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.3.tgz", + "integrity": "sha512-GcbMmOYpH9iRqtC05RbRnc/0FssxSTHlmaNhYBTgSgNCYpdR3Kt88u5GAZTBmouzv+Zlj/VRv92J9ruuDeJuEw==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -2987,11 +2992,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", - "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.6.tgz", + "integrity": "sha512-sQe08RunoObe+Usujn9+R2zrLuQERi3CWvRO3BvnoWSYUaIrLKuAIeY7cMeDax6xGyfIP3x/yFWbEKSXvOnvVg==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -3005,11 +3010,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", - "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.6.tgz", + "integrity": "sha512-UJKw4LlEkytzz2Wq+uIdHf6qOtFfee/o7ruH0jF5I6UAuU+19r9QV7nU3P/uI0l6+oElRHmG/5cBBcGJrD7Ozg==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -3022,22 +3027,22 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", - "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.6.tgz", + "integrity": "sha512-53SpchU3+DUZrN7J6sBx9tBiCVGzsib2e4sc512Q7K9fpC5zkJKs6Z9s+qbMxSYrkEkle6hnMtrts7XNkMJJMg==", "dependencies": { - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.4.2" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", - "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.7.tgz", + "integrity": "sha512-IA4K2qTJYXkF5OfVN4vsY1hfnUZjaslEE8Fsr/gGFza4TAC2A9NfnZuSY2srQIbt9bwtjHiAayrRVgKse4Q7fA==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -3050,15 +3055,15 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/signature-v4": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", - "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.4.tgz", + "integrity": "sha512-72MiK7xYukNsnLJI9NqvUHqTu0ziEsfMsYNlWpiJfuGQnCTFKpckThlEatirvcA/LmT1h7rRO+pJD06PYsPu9Q==", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -3073,15 +3078,15 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/smithy-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", - "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.4.tgz", + "integrity": "sha512-NKw/2XxOW/Rg3rzB90HxsmGok5oS6vRzJgMh/JN4BHaOQQ4q5OuX999GmOGxEp730wbpIXIowfKZmIMXkG4v0Q==", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "@smithy/util-stream": "^3.1.8", "tslib": "^2.6.2" }, "engines": { @@ -3094,9 +3099,9 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", - "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.2.tgz", + "integrity": "sha512-tHiFcfcVedVBHpmHUEUHOCCih8iZbIAYn9NvPsNzaPm/237I3imdDdZoOC8c87H5HBAVEa06tTgb+OcSWV9g5w==", "dependencies": { "tslib": "^2.6.2" }, @@ -3110,12 +3115,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/url-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", - "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.6.tgz", + "integrity": "sha512-47Op/NU8Opt49KyGpHtVdnmmJMsp2hEwBdyjuFB9M2V5QVOwA7pBhhxKN5z6ztKGrMw76gd8MlbPuzzvaAncuQ==", "dependencies": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/querystring-parser": "^3.0.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" } }, @@ -3205,13 +3210,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", - "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.20.tgz", + "integrity": "sha512-HpYmCpEThQJpCKzwzrGrklhdegRfuXI9keHRrHidbyEMliCdgic6t38MikJeZEkdIcEMhO1g95HIYMzjUzB+xg==", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -3225,16 +3230,16 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", - "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", - "dependencies": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.20.tgz", + "integrity": "sha512-atdsHNtAX0rwTvRRGsrONU0C0XzapH6tI8T1y/OReOvWN7uBwXqqWRft6m8egU2DgeReU0xqT3PHdGCe5VRaaQ==", + "dependencies": { + "@smithy/config-resolver": "^3.0.8", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -3247,12 +3252,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-endpoints": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", - "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.2.tgz", + "integrity": "sha512-FEISzffb4H8DLzGq1g4MuDpcv6CIG15fXoQzDH9SjpRJv6h7J++1STFWWinilG0tQh9H1v2UKWG19Jjr2B16zQ==", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -3281,11 +3286,11 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", - "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.6.tgz", + "integrity": "sha512-BxbX4aBhI1O9p87/xM+zWy0GzT3CEVcXFPBRDoHAM+pV0eSW156pR+PSYEz0DQHDMYDsYAflC2bQNz2uaDBUZQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -3298,12 +3303,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-retry": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", - "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.6.tgz", + "integrity": "sha512-BRZiuF7IwDntAbevqMco67an0Sr9oLQJqqRCsSPZZHYRnehS0LHDAkJk/pSmI7Z8c/1Vet294H7fY2fWUgB+Rg==", "dependencies": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/service-error-classification": "^3.0.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -3316,13 +3321,13 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-stream": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", - "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.8.tgz", + "integrity": "sha512-hoKOqSmb8FD3WLObuB5hwbM7bNIWgcnvkThokTvVq7J5PKjlLUK5qQQcB9zWLHIoSaIlf3VIv2OxZY2wtQjcRQ==", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/types": "^3.4.2", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -3372,12 +3377,12 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@smithy/util-waiter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", - "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.5.tgz", + "integrity": "sha512-jYOSvM3H6sZe3CHjzD2VQNCjWBJs+4DbtwBMvUp9y5EnnwNa7NQxTeYeQw0CKCAdGGZ3QvVkyJmvbvs5M/B10A==", "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/types": "^3.3.0", + "@smithy/abort-controller": "^3.1.4", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -4392,6 +4397,18 @@ "node": ">=4" } }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4906,6 +4923,18 @@ "resolved": "https://registry.npmjs.org/cryptr/-/cryptr-6.3.0.tgz", "integrity": "sha512-TA4byAuorT8qooU9H8YJhBwnqD151i1rcauHfJ3Divg6HmukHB2AYMp0hmjv2873J2alr4t15QqC7zAnWFrtfQ==" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/db-errors": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/db-errors/-/db-errors-0.2.3.tgz", @@ -5229,6 +5258,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -7517,6 +7583,20 @@ "node": ">=6" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", @@ -7597,6 +7677,15 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -7806,6 +7895,14 @@ } ] }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -8970,6 +9067,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -10378,6 +10480,25 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nats": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.28.0.tgz", + "integrity": "sha512-zXWOTOZEizUQy8UO2lMYFAee0htGrZLmJ2sddfmsDI00nc+dsK5+gS7p7bt26O1omfdCLVU5xymlnAjaqYnOmw==", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nats.ws": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/nats.ws/-/nats.ws-1.29.2.tgz", + "integrity": "sha512-dJf7aWp+5+8LwWEhgoTMc3pvfz5JlhA0yWtXKcTMDxUe43mHvgpvDaPnLyHQNL2LoDpdkjgOG176i5IeHBDlqg==", + "optionalDependencies": { + "nkeys.js": "1.1.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10402,12 +10523,38 @@ "resolved": "https://registry.npmjs.org/nested-objects-util/-/nested-objects-util-1.1.2.tgz", "integrity": "sha512-jNUcnODTq1HpGGFFo9aDXP7JxrJ1ilLHSaUElSyW5Zw8EJHyYyujdBIdTwa0K4lBC/9M23dJyhExo9XyhO39LQ==" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -12410,6 +12557,16 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -12474,6 +12631,14 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -12580,6 +12745,18 @@ "punycode": "^2.1.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -12718,6 +12895,22 @@ "makeerror": "1.0.12" } }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -12962,6 +13155,14 @@ "node": ">=10" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -13228,67 +13429,67 @@ } }, "@aws-sdk/client-s3": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.645.0.tgz", - "integrity": "sha512-RjT/mfNv4yr1uv/+aEXgSIxC5EB+yHPSU7hH0KZOZrvZEFASLl0i4FeoHzbMEOH5KdKGAi0uu3zRP3D1y45sKg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.654.0.tgz", + "integrity": "sha512-EsyeZJhkZD2VMdZpNt4NhlQ3QUAF24gMC+5w2wpGg6Yw+Bv7VLdg1t3PkTQovriJX1KTJAYHcGAuy92OFmWIng==", "requires": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.645.0", - "@aws-sdk/client-sts": "3.645.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.645.0", - "@aws-sdk/middleware-bucket-endpoint": "3.620.0", - "@aws-sdk/middleware-expect-continue": "3.620.0", - "@aws-sdk/middleware-flexible-checksums": "3.620.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-location-constraint": "3.609.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@aws-sdk/xml-builder": "3.609.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-blob-browser": "^3.1.2", - "@smithy/hash-node": "^3.0.3", - "@smithy/hash-stream-node": "^3.1.2", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/client-sso-oidc": "3.654.0", + "@aws-sdk/client-sts": "3.654.0", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/credential-provider-node": "3.654.0", + "@aws-sdk/middleware-bucket-endpoint": "3.654.0", + "@aws-sdk/middleware-expect-continue": "3.654.0", + "@aws-sdk/middleware-flexible-checksums": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-location-constraint": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-sdk-s3": "3.654.0", + "@aws-sdk/middleware-ssec": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/signature-v4-multi-region": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@aws-sdk/xml-builder": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/eventstream-serde-browser": "^3.0.9", + "@smithy/eventstream-serde-config-resolver": "^3.0.6", + "@smithy/eventstream-serde-node": "^3.0.8", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-blob-browser": "^3.1.5", + "@smithy/hash-node": "^3.0.6", + "@smithy/hash-stream-node": "^3.1.5", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/md5-js": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-stream": "^3.1.6", "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.2", + "@smithy/util-waiter": "^3.1.5", "tslib": "^2.6.2" }, "dependencies": { @@ -13300,46 +13501,46 @@ } }, "@aws-sdk/client-sso": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.645.0.tgz", - "integrity": "sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.654.0.tgz", + "integrity": "sha512-4kBxs2IzCDtj6a6lRXa/lXK5wWpMGzwKtb+HMXf/rJYVM6x7wYRzc1hYrOd3DYkFQ/sR3dUFj+0mTP0os3aAbA==", "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -13352,47 +13553,47 @@ } }, "@aws-sdk/client-sso-oidc": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.645.0.tgz", - "integrity": "sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.654.0.tgz", + "integrity": "sha512-gbHrKsEnaAtmkNCVQzLyiqMzpDaThV/bWl/ODEklI+t6stW3Pe3oDMstEHLfJ6JU5g8sYnx4VLuxlnJMtUkvPw==", "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.645.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/credential-provider-node": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -13405,48 +13606,48 @@ } }, "@aws-sdk/client-sts": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.645.0.tgz", - "integrity": "sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.654.0.tgz", + "integrity": "sha512-tyHa8jsBy+/NQZFHm6Q2Q09Vi9p3EH4yPy6PU8yPewpi2klreObtrUd0anJa6nzjS9SSuqnlZWsRic3cQ4QwCg==", "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.645.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.645.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.645.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/client-sso-oidc": "3.654.0", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/credential-provider-node": "3.654.0", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.3", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.18", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.18", + "@smithy/util-defaults-mode-node": "^3.0.18", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -13459,18 +13660,18 @@ } }, "@aws-sdk/core": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", - "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", - "requires": { - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.654.0.tgz", + "integrity": "sha512-4Rwx7BVaNaFqmXBDmnOkMbyuIFFbpZ+ru4lr660p45zY1QoNNSalechfoRffcokLFOZO+VWEJkdcorPUUU993w==", + "requires": { + "@smithy/core": "^2.4.3", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, @@ -13483,13 +13684,13 @@ } }, "@aws-sdk/credential-provider-env": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", - "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.654.0.tgz", + "integrity": "sha512-kogsx3Ql81JouHS7DkheCDU9MYAvK0AokxjcshDveGmf7BbgbWCA8Fnb9wjQyNDaOXNvkZu8Z8rgkX91z324/w==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13501,18 +13702,18 @@ } }, "@aws-sdk/credential-provider-http": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", - "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.654.0.tgz", + "integrity": "sha512-tgmAH4MBi/aDR882lfw48+tDV95ZH3GWc1Eoe6DpNLiM3GN2VfU/cZwuHmi6aq+vAbdIlswBHJ/+va0fOvlyjw==", + "requires": { + "@aws-sdk/types": "3.654.0", + "@smithy/fetch-http-handler": "^3.2.7", + "@smithy/node-http-handler": "^3.2.2", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", + "@smithy/util-stream": "^3.1.6", "tslib": "^2.6.2" }, "dependencies": { @@ -13524,20 +13725,20 @@ } }, "@aws-sdk/credential-provider-ini": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.645.0.tgz", - "integrity": "sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==", - "requires": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.645.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.654.0.tgz", + "integrity": "sha512-DKSdaNu2hwdmuvnm9KnA0NLqMWxxmxSOLWjSUSoFIm++wGXUjPrRMFYKvMktaXnPuyf5my8gF/yGbwzPZ8wlTg==", + "requires": { + "@aws-sdk/credential-provider-env": "3.654.0", + "@aws-sdk/credential-provider-http": "3.654.0", + "@aws-sdk/credential-provider-process": "3.654.0", + "@aws-sdk/credential-provider-sso": "3.654.0", + "@aws-sdk/credential-provider-web-identity": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13549,21 +13750,21 @@ } }, "@aws-sdk/credential-provider-node": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.645.0.tgz", - "integrity": "sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==", - "requires": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-ini": "3.645.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.645.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.654.0.tgz", + "integrity": "sha512-wPV7CNYaXDEc+SS+3R0v8SZwkHRUE1z2k2j1d49tH5QBDT4tb/k2V/biXWkwSk3hbR+IMWXmuhJDv/5lybhIvg==", + "requires": { + "@aws-sdk/credential-provider-env": "3.654.0", + "@aws-sdk/credential-provider-http": "3.654.0", + "@aws-sdk/credential-provider-ini": "3.654.0", + "@aws-sdk/credential-provider-process": "3.654.0", + "@aws-sdk/credential-provider-sso": "3.654.0", + "@aws-sdk/credential-provider-web-identity": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13575,14 +13776,14 @@ } }, "@aws-sdk/credential-provider-process": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", - "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.654.0.tgz", + "integrity": "sha512-PmQoo8sZ9Q2Ow8OMzK++Z9lI7MsRUG7sNq3E72DVA215dhtTICTDQwGlXH2AAmIp7n+G9LLRds+4wo2ehG4mkg==", + "requires": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13594,16 +13795,16 @@ } }, "@aws-sdk/credential-provider-sso": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.645.0.tgz", - "integrity": "sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==", - "requires": { - "@aws-sdk/client-sso": "3.645.0", - "@aws-sdk/token-providers": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.654.0.tgz", + "integrity": "sha512-7GFme6fWEdA/XYKzZPOAdj/jS6fMBy1NdSIZsDXikS0v9jU+ZzHrAaWt13YLzHyjgxB9Sg9id9ncdY1IiubQXQ==", + "requires": { + "@aws-sdk/client-sso": "3.654.0", + "@aws-sdk/token-providers": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13615,13 +13816,13 @@ } }, "@aws-sdk/credential-provider-web-identity": { - "version": "3.621.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", - "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.654.0.tgz", + "integrity": "sha512-6a2g9gMtZToqSu+CusjNK5zvbLJahQ9di7buO3iXgbizXpLXU1rnawCpWxwslMpT5fLgMSKDnKDrr6wdEk7jSw==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13633,15 +13834,15 @@ } }, "@aws-sdk/middleware-bucket-endpoint": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", - "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.654.0.tgz", + "integrity": "sha512-/lWkyeLESiK+rAB4+NCw1cVPle9RN7RW/v7B4b8ORiCn1FwZLUPmEiZSYzyh4in5oa3Mri+W/g+KafZDH6LCbA==", "requires": { - "@aws-sdk/types": "3.609.0", + "@aws-sdk/types": "3.654.0", "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", "tslib": "^2.6.2" }, @@ -13654,13 +13855,13 @@ } }, "@aws-sdk/middleware-expect-continue": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", - "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.654.0.tgz", + "integrity": "sha512-S7fSlo8vdjkQTy9DmdF54ZsPwc+aA4z5Y9JVqAlGL9QiZe/fPtRE3GZ8BBbMICjBfMEa12tWjzhDz9su2c6PIA==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13672,16 +13873,18 @@ } }, "@aws-sdk/middleware-flexible-checksums": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", - "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.654.0.tgz", + "integrity": "sha512-ZSRC+Lf9WxyoDLuTkd7JrFRrBLPLXcTOZzX6tDsnHc6tgdneBNwV3/ZOYUwQ8bdwLLnzSaQUU+X5B2BkEFKIhQ==", "requires": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", - "@aws-sdk/types": "3.609.0", + "@aws-sdk/types": "3.654.0", "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -13694,13 +13897,13 @@ } }, "@aws-sdk/middleware-host-header": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", - "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.654.0.tgz", + "integrity": "sha512-rxGgVHWKp8U2ubMv+t+vlIk7QYUaRCHaVpmUlJv0Wv6Q0KeO9a42T9FxHphjOTlCGQOLcjCreL9CF8Qhtb4mdQ==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13712,12 +13915,12 @@ } }, "@aws-sdk/middleware-location-constraint": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz", - "integrity": "sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.654.0.tgz", + "integrity": "sha512-Duvv5c4DEQ7P6c0YlcvEUW3xCJi6X2uktafNGjILhVDMQwShSF/aFqNv/ikWU/luQcmWHZ9DtDjTR9UKLh6eTA==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13729,12 +13932,12 @@ } }, "@aws-sdk/middleware-logger": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", - "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.654.0.tgz", + "integrity": "sha512-OQYb+nWlmASyXfRb989pwkJ9EVUMP1CrKn2eyTk3usl20JZmKo2Vjis6I0tLUkMSxMhnBJJlQKyWkRpD/u1FVg==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13746,13 +13949,13 @@ } }, "@aws-sdk/middleware-recursion-detection": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", - "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.654.0.tgz", + "integrity": "sha512-gKSomgltKVmsT8sC6W7CrADZ4GHwX9epk3GcH6QhebVO3LA9LRbkL3TwOPUXakxxOLLUTYdOZLIOtFf7iH00lg==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13764,22 +13967,22 @@ } }, "@aws-sdk/middleware-sdk-s3": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", - "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.654.0.tgz", + "integrity": "sha512-6prq+GK6hLMAbxEb83tBMb1YiTWWK196fJhFO/7gE5TUPL1v756RhQZzKV/njbwB1fIBjRBTuhYLh5Bn98HhdA==", "requires": { - "@aws-sdk/core": "3.635.0", - "@aws-sdk/types": "3.609.0", + "@aws-sdk/core": "3.654.0", + "@aws-sdk/types": "3.654.0", "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/core": "^2.4.3", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.3", + "@smithy/smithy-client": "^3.3.2", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-stream": "^3.1.3", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-stream": "^3.1.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -13792,12 +13995,12 @@ } }, "@aws-sdk/middleware-ssec": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", - "integrity": "sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.654.0.tgz", + "integrity": "sha512-k7hkQDJh4hcRJC7YojQ11kc37SY4foryen26Eafj5qYjeG2OGMW0oZTJDl1TVFJ7AcCjqIuMIo0Ho2US/2JspQ==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13809,14 +14012,14 @@ } }, "@aws-sdk/middleware-user-agent": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.645.0.tgz", - "integrity": "sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.645.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.654.0.tgz", + "integrity": "sha512-liCcqPAyRsr53cy2tYu4qeH4MMN0eh9g6k56XzI5xd4SghXH5YWh4qOYAlQ8T66ZV4nPMtD8GLtLXGzsH8moFg==", + "requires": { + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13828,15 +14031,15 @@ } }, "@aws-sdk/region-config-resolver": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", - "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.654.0.tgz", + "integrity": "sha512-ydGOrXJxj3x0sJhsXyTmvJVLAE0xxuTWFJihTl67RtaO7VRNtd82I3P3bwoMMaDn5WpmV5mPo8fEUDRlBm3fPg==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "tslib": "^2.6.2" }, "dependencies": { @@ -13848,15 +14051,15 @@ } }, "@aws-sdk/signature-v4-multi-region": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", - "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", - "requires": { - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.654.0.tgz", + "integrity": "sha512-f8kyvbzgD3lSK1kFc3jsDCYjdutcqGO3tOzYO/QIK7BTl5lxc4rm6IKTcF2UYJsn8jiNqih7tVK8aVIGi8IF/w==", + "requires": { + "@aws-sdk/middleware-sdk-s3": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13868,14 +14071,14 @@ } }, "@aws-sdk/token-providers": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", - "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.654.0.tgz", + "integrity": "sha512-D8GeJYmvbfWkQDtTB4owmIobSMexZel0fOoetwvgCQ/7L8VPph3Q2bn1TRRIXvH7wdt6DcDxA3tKMHPBkT3GlA==", + "requires": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13887,11 +14090,11 @@ } }, "@aws-sdk/types": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", - "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.654.0.tgz", + "integrity": "sha512-VWvbED3SV+10QJIcmU/PKjsKilsTV16d1I7/on4bvD/jo1qGeMXqLDBSen3ks/tuvXZF/mFc7ZW/W2DiLVtO7A==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13918,13 +14121,13 @@ } }, "@aws-sdk/util-endpoints": { - "version": "3.645.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.645.0.tgz", - "integrity": "sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.654.0.tgz", + "integrity": "sha512-i902fcBknHs0Irgdpi62+QMvzxE+bczvILXigYrlHL4+PiEnlMVpni5L5W1qCkNZXf8AaMrSBuR1NZAGp6UOUw==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "@smithy/util-endpoints": "^2.0.5", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "@smithy/util-endpoints": "^2.1.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13951,12 +14154,12 @@ } }, "@aws-sdk/util-user-agent-browser": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", - "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.654.0.tgz", + "integrity": "sha512-ykYAJqvnxLt7wfrqya28wuH3/7NdrwzfiFd7NqEVQf7dXVxL5RPEpD7DxjcyQo3DsHvvdUvGZVaQhozycn1pzA==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -13969,13 +14172,13 @@ } }, "@aws-sdk/util-user-agent-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", - "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.654.0.tgz", + "integrity": "sha512-a0ojjdBN6pqv6gB4H/QPPSfhs7mFtlVwnmKCM/QrTaFzN0U810PJ1BST3lBx5sa23I5jWHGaoFY+5q65C3clLQ==", "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -13987,11 +14190,11 @@ } }, "@aws-sdk/xml-builder": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz", - "integrity": "sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==", + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.654.0.tgz", + "integrity": "sha512-qA2diK3d/ztC8HUb7NwPKbJRV01NpzTzxFn+L5G3HzJBNeKbjLcprQ/9uG9gp2UEx2Go782FI1ddrMNa0qBICA==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15062,11 +15265,11 @@ } }, "@smithy/abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", - "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.4.tgz", + "integrity": "sha512-VupaALAQlXViW3/enTf/f5l5JZYSAxoJL7f0nanhNNKnww6DGCg1oYIuNP78KDugnkwthBO6iEcym16HhWV8RQ==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15109,14 +15312,14 @@ } }, "@smithy/config-resolver": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", - "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.8.tgz", + "integrity": "sha512-Tv1obAC18XOd2OnDAjSWmmthzx6Pdeh63FbLin8MlPiuJ2ATpKkq0NcNOJFr0dO+JmZXnwu8FQxKJ3TKJ3Hulw==", "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "tslib": "^2.6.2" }, "dependencies": { @@ -15128,18 +15331,18 @@ } }, "@smithy/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", - "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", - "requires": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.5.tgz", + "integrity": "sha512-Z0qlPXgZ0pouYgnu/cZTEYeRAvniiKZmVl4wIbZHX/nEMHkMDV9ao6KFArsU9KndE0TuhL149xcRx45wfw1YCA==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.20", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -15152,14 +15355,14 @@ } }, "@smithy/credential-provider-imds": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", - "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", - "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.3.tgz", + "integrity": "sha512-VoxMzSzdvkkjMJNE38yQgx4CfnmT+Z+5EUXkg4x7yag93eQkVQgZvN3XBSHC/ylfBbLbAtdu7flTCChX9I+mVg==", + "requires": { + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", "tslib": "^2.6.2" }, "dependencies": { @@ -15171,12 +15374,12 @@ } }, "@smithy/eventstream-codec": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", - "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.5.tgz", + "integrity": "sha512-6pu+PT2r+5ZnWEV3vLV1DzyrpJ0TmehQlniIDCSpZg6+Ji2SfOI38EqUyQ+O8lotVElCrfVc9chKtSMe9cmCZQ==", "requires": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" }, @@ -15189,12 +15392,12 @@ } }, "@smithy/eventstream-serde-browser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", - "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.9.tgz", + "integrity": "sha512-PiQLo6OQmZAotJweIcObL1H44gkvuJACKMNqpBBe5Rf2Ax1DOcGi/28+feZI7yTe1ERHlQQaGnm8sSkyDUgsMg==", "requires": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-serde-universal": "^3.0.8", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15206,11 +15409,11 @@ } }, "@smithy/eventstream-serde-config-resolver": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz", - "integrity": "sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.6.tgz", + "integrity": "sha512-iew15It+c7WfnVowWkt2a7cdPp533LFJnpjDQgfZQcxv2QiOcyEcea31mnrk5PVbgo0nNH3VbYGq7myw2q/F6A==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15222,12 +15425,12 @@ } }, "@smithy/eventstream-serde-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", - "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.8.tgz", + "integrity": "sha512-6m+wI+fT0na+6oao6UqALVA38fsScCpoG5UO/A8ZSyGLnPM2i4MS1cFUhpuALgvLMxfYoTCh7qSeJa0aG4IWpQ==", "requires": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-serde-universal": "^3.0.8", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15239,12 +15442,12 @@ } }, "@smithy/eventstream-serde-universal": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", - "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.8.tgz", + "integrity": "sha512-09tqzIQ6e+7jLqGvRji1yJoDbL/zob0OFhq75edgStWErGLf16+yI5hRc/o9/YAybOhUZs/swpW2SPn892G5Gg==", "requires": { - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-codec": "^3.1.5", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15256,13 +15459,13 @@ } }, "@smithy/fetch-http-handler": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", - "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.8.tgz", + "integrity": "sha512-Lqe0B8F5RM7zkw//6avq1SJ8AfaRd3ubFUS1eVp5WszV7p6Ne5hQ4dSuMHDpNRPhgTvj4va9Kd/pcVigHEHRow==", "requires": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/querystring-builder": "^3.0.6", + "@smithy/types": "^3.4.2", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" }, @@ -15275,13 +15478,13 @@ } }, "@smithy/hash-blob-browser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz", - "integrity": "sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.5.tgz", + "integrity": "sha512-Vi3eoNCmao4iKglS80ktYnBOIqZhjbDDwa1IIbF/VaJ8PsHnZTQ5wSicicPrU7nTI4JPFn92/txzWkh4GlK18Q==", "requires": { "@smithy/chunked-blob-reader": "^3.0.0", "@smithy/chunked-blob-reader-native": "^3.0.0", - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15293,11 +15496,11 @@ } }, "@smithy/hash-node": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", - "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.6.tgz", + "integrity": "sha512-c/FHEdKK/7DU2z6ZE91L36ahyXWayR3B+FzELjnYq7wH5YqIseM24V+pWCS9kFn1Ln8OFGTf+pyYPiHZuX0s/Q==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -15311,11 +15514,11 @@ } }, "@smithy/hash-stream-node": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz", - "integrity": "sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.5.tgz", + "integrity": "sha512-61CyFCzqN3VBfcnGX7mof/rkzLb8oHjm4Lr6ZwBIRpBssBb8d09ChrZAqinP2rUrA915BRNkq9NpJz18N7+3hQ==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -15328,11 +15531,11 @@ } }, "@smithy/invalid-dependency": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", - "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.6.tgz", + "integrity": "sha512-czM7Ioq3s8pIXht7oD+vmgy4Wfb4XavU/k/irO8NdXFFOx7YAlsCCcKOh/lJD1mJSYQqiR7NmpZ9JviryD/7AQ==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15359,11 +15562,11 @@ } }, "@smithy/md5-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", - "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.6.tgz", + "integrity": "sha512-Ze690T8O3M5SVbb70WormwrKzVf9QQRtIuxtJDgpUQDkmt+PtdYDetBbyCbF9ryupxLw6tgzWKgwffAShhVIXQ==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -15376,12 +15579,12 @@ } }, "@smithy/middleware-content-length": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", - "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.8.tgz", + "integrity": "sha512-VuyszlSO49WKh3H9/kIO2kf07VUwGV80QRiaDxUfP8P8UKlokz381ETJvwLhwuypBYhLymCYyNhB3fLAGBX2og==", "requires": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15393,16 +15596,16 @@ } }, "@smithy/middleware-endpoint": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", - "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", - "requires": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.3.tgz", + "integrity": "sha512-KeM/OrK8MVFUsoJsmCN0MZMVPjKKLudn13xpgwIMpGTYpA8QZB2Xq5tJ+RE6iu3A6NhOI4VajDTwBsm8pwwrhg==", + "requires": { + "@smithy/middleware-serde": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@smithy/util-middleware": "^3.0.6", "tslib": "^2.6.2" }, "dependencies": { @@ -15414,17 +15617,17 @@ } }, "@smithy/middleware-retry": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", - "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", - "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.20.tgz", + "integrity": "sha512-HELCOVwYw5hFDBm69d+LmmGjBCjWnwp/t7SJiHmp+c4u9vgfIaCjdSeIdnlOsLrr5ic5jGTJXvJFUQnd987b/g==", + "requires": { + "@smithy/node-config-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.3", + "@smithy/service-error-classification": "^3.0.6", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -15442,11 +15645,11 @@ } }, "@smithy/middleware-serde": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", - "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.6.tgz", + "integrity": "sha512-KKTUSl1MzOM0MAjGbudeaVNtIDo+PpekTBkCNwvfZlKndodrnvRo+00USatiyLOc0ujjO9UydMRu3O9dYML7ag==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15458,11 +15661,11 @@ } }, "@smithy/middleware-stack": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", - "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.6.tgz", + "integrity": "sha512-2c0eSYhTQ8xQqHMcRxLMpadFbTXg6Zla5l0mwNftFCZMQmuhI7EbAJMx6R5eqfuV3YbJ3QGyS3d5uSmrHV8Khg==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15474,13 +15677,13 @@ } }, "@smithy/node-config-provider": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", - "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.7.tgz", + "integrity": "sha512-g3mfnC3Oo8pOI0dYuPXLtdW1WGVb3bR2tkV21GNkm0ZvQjLTtamXAwCWt/FCb0HGvKt3gHHmF1XerG0ICfalOg==", "requires": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15492,14 +15695,14 @@ } }, "@smithy/node-http-handler": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", - "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", - "requires": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.3.tgz", + "integrity": "sha512-/gcm5DJ3k1b1zEInzBGAZC8ntJ+jwrz1NcSIu+9dSXd1FfG0G6QgkDI40tt8/WYUbHtLyo8fEqtm2v29koWo/w==", + "requires": { + "@smithy/abort-controller": "^3.1.4", + "@smithy/protocol-http": "^4.1.3", + "@smithy/querystring-builder": "^3.0.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15511,11 +15714,11 @@ } }, "@smithy/property-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", - "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.6.tgz", + "integrity": "sha512-NK3y/T7Q/Bw+Z8vsVs9MYIQ5v7gOX7clyrXcwhhIBQhbPgRl6JDrZbusO9qWDhcEus75Tg+VCxtIRfo3H76fpw==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15527,11 +15730,11 @@ } }, "@smithy/protocol-http": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", - "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.3.tgz", + "integrity": "sha512-GcbMmOYpH9iRqtC05RbRnc/0FssxSTHlmaNhYBTgSgNCYpdR3Kt88u5GAZTBmouzv+Zlj/VRv92J9ruuDeJuEw==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15543,11 +15746,11 @@ } }, "@smithy/querystring-builder": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", - "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.6.tgz", + "integrity": "sha512-sQe08RunoObe+Usujn9+R2zrLuQERi3CWvRO3BvnoWSYUaIrLKuAIeY7cMeDax6xGyfIP3x/yFWbEKSXvOnvVg==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -15560,11 +15763,11 @@ } }, "@smithy/querystring-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", - "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.6.tgz", + "integrity": "sha512-UJKw4LlEkytzz2Wq+uIdHf6qOtFfee/o7ruH0jF5I6UAuU+19r9QV7nU3P/uI0l6+oElRHmG/5cBBcGJrD7Ozg==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15576,19 +15779,19 @@ } }, "@smithy/service-error-classification": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", - "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.6.tgz", + "integrity": "sha512-53SpchU3+DUZrN7J6sBx9tBiCVGzsib2e4sc512Q7K9fpC5zkJKs6Z9s+qbMxSYrkEkle6hnMtrts7XNkMJJMg==", "requires": { - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.4.2" } }, "@smithy/shared-ini-file-loader": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", - "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.7.tgz", + "integrity": "sha512-IA4K2qTJYXkF5OfVN4vsY1hfnUZjaslEE8Fsr/gGFza4TAC2A9NfnZuSY2srQIbt9bwtjHiAayrRVgKse4Q7fA==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15600,15 +15803,15 @@ } }, "@smithy/signature-v4": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", - "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.4.tgz", + "integrity": "sha512-72MiK7xYukNsnLJI9NqvUHqTu0ziEsfMsYNlWpiJfuGQnCTFKpckThlEatirvcA/LmT1h7rRO+pJD06PYsPu9Q==", "requires": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.6", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -15622,15 +15825,15 @@ } }, "@smithy/smithy-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", - "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", - "requires": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.4.tgz", + "integrity": "sha512-NKw/2XxOW/Rg3rzB90HxsmGok5oS6vRzJgMh/JN4BHaOQQ4q5OuX999GmOGxEp730wbpIXIowfKZmIMXkG4v0Q==", + "requires": { + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "@smithy/util-stream": "^3.1.8", "tslib": "^2.6.2" }, "dependencies": { @@ -15642,9 +15845,9 @@ } }, "@smithy/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", - "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.2.tgz", + "integrity": "sha512-tHiFcfcVedVBHpmHUEUHOCCih8iZbIAYn9NvPsNzaPm/237I3imdDdZoOC8c87H5HBAVEa06tTgb+OcSWV9g5w==", "requires": { "tslib": "^2.6.2" }, @@ -15657,12 +15860,12 @@ } }, "@smithy/url-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", - "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.6.tgz", + "integrity": "sha512-47Op/NU8Opt49KyGpHtVdnmmJMsp2hEwBdyjuFB9M2V5QVOwA7pBhhxKN5z6ztKGrMw76gd8MlbPuzzvaAncuQ==", "requires": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/querystring-parser": "^3.0.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15752,13 +15955,13 @@ } }, "@smithy/util-defaults-mode-browser": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", - "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.20.tgz", + "integrity": "sha512-HpYmCpEThQJpCKzwzrGrklhdegRfuXI9keHRrHidbyEMliCdgic6t38MikJeZEkdIcEMhO1g95HIYMzjUzB+xg==", "requires": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -15771,16 +15974,16 @@ } }, "@smithy/util-defaults-mode-node": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", - "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", - "requires": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.20.tgz", + "integrity": "sha512-atdsHNtAX0rwTvRRGsrONU0C0XzapH6tI8T1y/OReOvWN7uBwXqqWRft6m8egU2DgeReU0xqT3PHdGCe5VRaaQ==", + "requires": { + "@smithy/config-resolver": "^3.0.8", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/smithy-client": "^3.3.4", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15792,12 +15995,12 @@ } }, "@smithy/util-endpoints": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", - "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.2.tgz", + "integrity": "sha512-FEISzffb4H8DLzGq1g4MuDpcv6CIG15fXoQzDH9SjpRJv6h7J++1STFWWinilG0tQh9H1v2UKWG19Jjr2B16zQ==", "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15824,11 +16027,11 @@ } }, "@smithy/util-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", - "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.6.tgz", + "integrity": "sha512-BxbX4aBhI1O9p87/xM+zWy0GzT3CEVcXFPBRDoHAM+pV0eSW156pR+PSYEz0DQHDMYDsYAflC2bQNz2uaDBUZQ==", "requires": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15840,12 +16043,12 @@ } }, "@smithy/util-retry": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", - "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.6.tgz", + "integrity": "sha512-BRZiuF7IwDntAbevqMco67an0Sr9oLQJqqRCsSPZZHYRnehS0LHDAkJk/pSmI7Z8c/1Vet294H7fY2fWUgB+Rg==", "requires": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/service-error-classification": "^3.0.6", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -15857,13 +16060,13 @@ } }, "@smithy/util-stream": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", - "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.8.tgz", + "integrity": "sha512-hoKOqSmb8FD3WLObuB5hwbM7bNIWgcnvkThokTvVq7J5PKjlLUK5qQQcB9zWLHIoSaIlf3VIv2OxZY2wtQjcRQ==", "requires": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/types": "^3.4.2", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -15910,12 +16113,12 @@ } }, "@smithy/util-waiter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", - "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.5.tgz", + "integrity": "sha512-jYOSvM3H6sZe3CHjzD2VQNCjWBJs+4DbtwBMvUp9y5EnnwNa7NQxTeYeQw0CKCAdGGZ3QvVkyJmvbvs5M/B10A==", "requires": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/types": "^3.3.0", + "@smithy/abort-controller": "^3.1.4", + "@smithy/types": "^3.4.2", "tslib": "^2.6.2" }, "dependencies": { @@ -16709,6 +16912,14 @@ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" }, + "bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -17097,6 +17308,15 @@ "resolved": "https://registry.npmjs.org/cryptr/-/cryptr-6.3.0.tgz", "integrity": "sha512-TA4byAuorT8qooU9H8YJhBwnqD151i1rcauHfJ3Divg6HmukHB2AYMp0hmjv2873J2alr4t15QqC7zAnWFrtfQ==" }, + "d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "requires": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + } + }, "db-errors": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/db-errors/-/db-errors-0.2.3.tgz", @@ -17346,6 +17566,36 @@ "is-symbol": "^1.0.2" } }, + "es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "requires": { + "d": "^1.0.2", + "ext": "^1.7.0" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -19090,6 +19340,17 @@ "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + } + }, "espree": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", @@ -19142,6 +19403,15 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -19297,6 +19567,14 @@ } } }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + } + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -20105,6 +20383,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -21170,6 +21453,22 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nats": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.28.0.tgz", + "integrity": "sha512-zXWOTOZEizUQy8UO2lMYFAee0htGrZLmJ2sddfmsDI00nc+dsK5+gS7p7bt26O1omfdCLVU5xymlnAjaqYnOmw==", + "requires": { + "nkeys.js": "1.1.0" + } + }, + "nats.ws": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/nats.ws/-/nats.ws-1.29.2.tgz", + "integrity": "sha512-dJf7aWp+5+8LwWEhgoTMc3pvfz5JlhA0yWtXKcTMDxUe43mHvgpvDaPnLyHQNL2LoDpdkjgOG176i5IeHBDlqg==", + "requires": { + "nkeys.js": "1.1.0" + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -21191,12 +21490,30 @@ "resolved": "https://registry.npmjs.org/nested-objects-util/-/nested-objects-util-1.1.2.tgz", "integrity": "sha512-jNUcnODTq1HpGGFFo9aDXP7JxrJ1ilLHSaUElSyW5Zw8EJHyYyujdBIdTwa0K4lBC/9M23dJyhExo9XyhO39LQ==" }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "requires": { + "tweetnacl": "1.0.3" + } + }, + "node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -22673,6 +22990,16 @@ "tslib": "^1.8.1" } }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -22719,6 +23046,14 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -22783,6 +23118,14 @@ "punycode": "^2.1.0" } }, + "utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -22885,6 +23228,19 @@ "makeerror": "1.0.12" } }, + "websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "requires": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -23057,6 +23413,11 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" + }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/app/package.json b/app/package.json index d01ad2b55..15fbef877 100644 --- a/app/package.json +++ b/app/package.json @@ -74,10 +74,13 @@ "mime-types": "^2.1.35", "moment": "^2.29.4", "multer": "^1.4.5-lts.1", + "nats": "^2.28.0", + "nats.ws": "^1.29.2", "nested-objects-util": "^1.1.2", "objection": "^3.0.1", "pg": "^8.10.0", "uuid": "^8.3.2", + "websocket": "^1.0.35", "winston": "^3.8.2" }, "devDependencies": { diff --git a/app/src/components/eventStreamService.js b/app/src/components/eventStreamService.js new file mode 100644 index 000000000..37f3c9f88 --- /dev/null +++ b/app/src/components/eventStreamService.js @@ -0,0 +1,508 @@ +const config = require('config'); +const falsey = require('falsey'); +const { JSONCodec, nanos } = require('nats'); +const axios = require('axios'); + +// different connection libraries if we are using websockets or nats protocols. +const WEBSOCKETS = !falsey(config.get('eventStreamService.websockets')); + +const HEADERS_SIZE = 1024; // account for NATS headers in messages, use 1kb. + +let natsConnect; +if (WEBSOCKETS) { + // shim the websocket library + globalThis.WebSocket = require('websocket').w3cwebsocket; + const { connect } = require('nats.ws'); + natsConnect = connect; +} else { + const { connect } = require('nats'); + natsConnect = connect; +} + +const log = require('./log')(module.filename); + +const { FormVersion, Form, FormEventStreamConfig, FormSubscription } = require('../forms/common/models'); +const formMetadataService = require('../forms/form/formMetadata/service'); +const { encryptionService } = require('./encryptionService'); + +const SERVICE = 'EventStreamService'; + +const FORM_EVENT_TYPES = { + PUBLISHED: 'published', + UNPUBLISHED: 'unpublished', +}; + +const SUBMISSION_EVENT_TYPES = { + CREATED: 'created', + UPDATED: 'updated', + DELETED: 'deleted', +}; + +const jsonCodec = JSONCodec(); + +class EventStreamService { + constructor(cfg) { + if (!cfg || !cfg.servers || !cfg.streamName || !cfg.source || !cfg.domain || !cfg.username || !cfg.password) { + throw new Error('EventStreamService is not configured. Check configuration.'); + } + + if (!cfg.maxAge || !cfg.maxBytes || !cfg.maxMsgs || !cfg.maxMsgSize || !cfg.duplicateWindow || !cfg.numReplicas) { + throw new Error('EventStreamService is not configured (missing stream limits). Check configuration.'); + } + + this.servers = cfg.servers; + this.streamName = cfg.streamName; + this.source = cfg.source; + this.domain = cfg.domain; + this.username = cfg.username; + this.password = cfg.password; + + this.configured = false; + + // stream limits configuration + try { + this.maxAge = parseInt(cfg.maxAge); + this.maxBytes = parseInt(cfg.maxBytes); + this.maxMsgs = parseInt(cfg.maxMsgs); + this.maxMsgSize = parseInt(cfg.maxMsgSize); + this.duplicateWindow = parseInt(cfg.duplicateWindow); + + this.numReplicas = parseInt(cfg.numReplicas); + } catch (error) { + throw new Error('EventStreamService configuration, error parsing integers for stream limits. Check configuration.'); + } + + this.allowedMsgSize = this.maxMsgSize - HEADERS_SIZE; // the allowed message size is the stream limit less the nats headers. + + this.nc = null; // nats connection + this.js = null; // jet stream + this.jsm = null; // jet stream manager + this.natsOptions = { + servers: this.servers.split(','), + maxReconnectAttempts: 5, + name: this.streamName, + reconnectTimeWait: 5000, // wait 5 seconds before retrying... + waitOnFirstConnect: true, + pingInterval: 500, + user: this.username, + pass: this.password, + }; + + this.publicSubject = `PUBLIC.${this.domain}`; + this.privateSubject = `PRIVATE.${this.domain}`; + this.firstConnect = true; + } + + async checkConnection() { + // this is for health checks + // it will also open our connection on first check... + try { + await this.openConnection(); + return this.connected; + } catch (e) { + log.error(e.message, { function: 'checkConnection' }); + } + return false; + } + + async openConnection() { + try { + if (this.connected) return this.nc; + + const me = this; + const connectToNats = async function () { + // nats.connect will throw errors only if the server is running. + // nats.connect will NOT timeout if the server isn't reachable/not started. + // so, let's wait twice the reconnect time and create our own timeout. + let timeout; + // eslint-disable-next-line no-unused-vars + const timeoutPromise = new Promise((resolve, reject) => { + timeout = setTimeout(() => { + resolve(null); + }, me.natsOptions.reconnectTimeWait * 2); + }); + + // we either timeout or connect... + const result = await Promise.race([natsConnect(me.natsOptions), timeoutPromise]); + + if (timeout) { + clearTimeout(timeout); + } + return result; + }; + + this.nc = await connectToNats(); + if (this.connected) { + log.info(`Connected to server: ${this.nc.info.server_name}`, { function: 'openConnection' }); + this.js = this.nc.jetstream(); + this.jsm = await this.js.jetstreamManager(); + if (!this.configured) { + const cfg = { + name: this.streamName, + subjects: [`${this.publicSubject}.>`, `${this.privateSubject}.>`], + }; + let streamInfo; + try { + // this will throw an error if stream is not created. + streamInfo = await this.jsm.streams.info(cfg.name); + } catch (err) { + // catch the error and add the stream, it doesn't exist! + if (err.message === 'stream not found') { + log.info(`Stream: ${cfg.name} not found, creating stream...`, { function: 'openConnection' }); + Object.assign( + cfg, + { max_msgs: this.maxMsgs, max_bytes: this.maxBytes, max_msg_size: this.maxMsgSize, num_replicas: this.numReplicas }, + { max_age: nanos(this.maxAge), duplicate_window: nanos(this.duplicateWindow) } + ); + await this.jsm.streams.add(cfg); + log.info(`Stream: ${cfg.name} created.`, { function: 'openConnection' }); + } + } + try { + streamInfo = await this.jsm.streams.info(cfg.name); + const upd = {}; + if (streamInfo.config.max_msgs !== this.maxMsgs) { + upd['max_msgs'] = this.maxMsgs; + } + if (streamInfo.config.max_bytes !== this.maxBytes) { + upd['max_bytes'] = this.maxBytes; + } + if (streamInfo.config.max_msg_size !== this.maxMsgSize) { + upd['max_msg_size'] = this.maxMsgSize; + } + if (streamInfo.config.num_replicas !== this.numReplicas) { + upd['num_replicas'] = this.maxMsgs; + } + if (streamInfo.config.max_age !== nanos(this.maxAge)) { + upd['max_age'] = nanos(this.maxAge); + } + if (streamInfo.config.duplicate_window !== nanos(this.duplicateWindow)) { + upd['duplicate_window'] = nanos(this.duplicateWindow); + } + if (Object.keys(upd).length) { + log.info(`Stream: ${cfg.name} updating configuration...`, { function: 'openConnection' }); + await this.jsm.streams.update(cfg.name, upd); + await new Promise((r) => setTimeout(r, 1000)); + log.info(`Stream: ${cfg.name} configuration updated.`, { function: 'openConnection' }); + } + } catch (err) { + log.error(err.message, { function: 'openConnection' }); + } + this.configured = true; + } + + this.nc.closed().then((err) => { + if (err) { + log.warn(`the connection closed with an error ${err.message}`, { function: 'connection.closed' }); + } else { + log.info('the connection closed.', { function: 'connection.closed' }); + } + }); + // log will be littered with these messages... (every 10 seconds). + //} else { + // log.warn(`the connection to event stream service [${this.servers}] can not be established.`); + } + return this.nc; + } catch (e) { + log.error(e.message, { function: 'openConnection' }); + } + } + + closeConnection() { + if (this.connected) { + try { + // make this sync so we can use it in our app shutdown/clean up + this.nc.close().then(() => {}); + log.info('Disconnected', { function: 'closeConnection' }); + } catch (e) { + log.error(e.message, { function: 'closeConnection' }); + } + } + } + + get connected() { + try { + if (this.nc && this.nc.info != undefined) { + return true; + } + } catch (e) { + log.error(e.message, { function: 'connected' }); + } + return false; + } + + async _getForm(formId) { + return Form.query().findById(formId).allowGraph('[identityProviders]').withGraphFetched('identityProviders(orderDefault)').throwIfNotFound(); + } + + async _getFormVersion(formVersionId) { + return FormVersion.query().findById(formVersionId).throwIfNotFound(); + } + + async _getEventStreamConfig(formId) { + return FormEventStreamConfig.query().modify('filterFormId', formId).allowGraph('[encryptionKey]').withGraphFetched('encryptionKey').first(); + } + + async _getWebhook(formId) { + return FormSubscription.query().modify('filterFormId', formId).first(); + } + + _sizeCheck(msg) { + const size = Buffer.byteLength(JSON.stringify(msg)); + if (size > this.allowedMsgSize) { + // we need to remove the payload data and add metadata that this is too big. + msg.payload = { data: {} }; + msg.error = { code: 'MAX_MSG_SIZE', message: `Message is ${size} bytes. This exceeds maximum allowed of ${this.allowedMsgSize} bytes. Please download payload via API.` }; + } + return jsonCodec.encode(msg); + } + + async _onPublishEventStream(eventType, meta, formId, formVersionId) { + try { + await this.openConnection(); + if (this.connected) { + // fetch form (don't fetch all versions...) + const form = await this._getForm(formId); + // fetch version and place in form.versions[] + const formVersion = await this._getFormVersion(formVersionId); + form['versions'] = [formVersion]; + + // need to fetch the encryption key... + const evntStrmCfg = await this._getEventStreamConfig(formId); + + if (evntStrmCfg && evntStrmCfg.enabled) { + const sub = `schema.${eventType}.${formId}`; + const publicSubj = `${this.publicSubject}.${sub}`; + const privateSubj = `${this.privateSubject}.${sub}`; + + if (evntStrmCfg.enablePrivateStream) { + const encPayload = encryptionService.encryptExternal(evntStrmCfg.encryptionKey.algorithm, evntStrmCfg.encryptionKey.key, form); + const privMsg = { + meta: meta, + payload: { + data: encPayload, + }, + }; + const encodedPayload = this._sizeCheck(privMsg); + const ack = await this._publishToStream(privateSubj, encodedPayload); + if (ack) { + log.info(`form ${eventType} event (private) - formId: ${formId}, version: ${formVersion.version}, seq: ${ack.seq}`, { function: 'onPublish' }); + } else { + log.error(`failed: form ${eventType} event (private) - formId: ${formId}, version: ${formVersion.version}, seq: ${ack.seq}`, { function: 'onPublish' }); + } + } + if (evntStrmCfg.enablePublicStream) { + const pubMsg = { + meta: meta, + payload: {}, + }; + const encodedPayload = jsonCodec.encode(pubMsg); + const ack = await this._publishToStream(publicSubj, encodedPayload); + if (ack) { + log.info(`form ${eventType} event (public) - formId: ${formId}, version: ${formVersion.version}, seq: ${ack.seq}`, { function: 'onPublish' }); + } else { + log.error(`failed: form ${eventType} event (public) - formId: ${formId}, version: ${formVersion.version}, seq: ${ack.seq}`, { function: 'onPublish' }); + } + } + } else { + log.info(`formId '${formId}' has no event stream configuration (or it is not enabled); will not publish events.`); + } + } else { + // warn, error??? + log.warn(`${SERVICE} is not connected. Cannot publish (form) event. [event: form.'${eventType}', formId: ${formId}, versionId: ${formVersionId}]`, { + function: 'onPublish', + }); + } + } catch (e) { + log.error(`${SERVICE}._onPublishEventStream: ${e.message}`, e); + } + } + + async _onPublishWebhook(meta) { + try { + const cfg = await this._getWebhook(meta.formId); + if (cfg && cfg.endpointUrl && cfg.eventStreamNotifications) { + const axiosOptions = { timeout: 10000 }; + const axiosInstance = axios.create(axiosOptions); + axiosInstance.interceptors.request.use( + (cfg) => { + cfg.headers = { [cfg.key]: `${cfg.endpointToken}` }; + return Promise.resolve(cfg); + }, + (error) => { + return Promise.reject(error); + } + ); + axiosInstance.post(cfg.endpointUrl, meta); + } + } catch (e) { + log.error(`${SERVICE}._onPublishWebhook: ${e.message}`, e); + } + } + + async onPublish(formId, formVersionId, published) { + try { + const eventType = published ? FORM_EVENT_TYPES.PUBLISHED : FORM_EVENT_TYPES.UNPUBLISHED; + const meta = { + source: this.source, + domain: this.domain, + class: 'schema', + type: eventType, + formId: formId, + formVersionId: formVersionId, + timestamp: new Date(), + }; + await formMetadataService.addAttribute(formId, meta); + + const tasks = [this._onPublishEventStream(eventType, meta, formId, formVersionId, published), this._onPublishWebhook(meta)]; + + return Promise.all(tasks); + } catch (e) { + log.error(`${SERVICE}.onPublish: ${e.message}`, e); + } + } + + async _publishToStream(subject, payload) { + let conn; + let ack; + try { + conn = await natsConnect(this.natsOptions); + log.info(`Connected to server: ${conn.info.server_name}`, { function: '_publishToStream' }); + const js = conn.jetstream(); + ack = await js.publish(subject, payload); + } catch (e) { + log.error(`${e.message}, try again.`); + } finally { + try { + await conn.close(); + } catch (e) { + log.error(e.message, { function: '_publishToStream' }); + } + } + return ack; + } + + async _onSubmitEventStream(eventType, meta, submission, formVersion) { + try { + await this.openConnection(); + if (this.connected) { + // need to fetch the encryption key... + const evntStrmCfg = await this._getEventStreamConfig(formVersion.formId); + + if (evntStrmCfg && evntStrmCfg.enabled) { + const sub = `submission.${eventType}.${formVersion.formId}`; + const publicSubj = `${this.publicSubject}.${sub}`; + const privateSubj = `${this.privateSubject}.${sub}`; + + if (evntStrmCfg.enablePrivateStream) { + const encPayload = encryptionService.encryptExternal(evntStrmCfg.encryptionKey.algorithm, evntStrmCfg.encryptionKey.key, submission); + const privMsg = { + meta: meta, + payload: { + data: encPayload, + }, + }; + const encodedPayload = this._sizeCheck(privMsg); + const ack = await this._publishToStream(privateSubj, encodedPayload); + if (ack) { + log.info( + `submission ${eventType} event (private) - formId: ${formVersion.formId}, version: ${formVersion.version}, submissionId: ${submission.id}, seq: ${ack.seq}`, + { + function: 'onSubmit', + } + ); + } else { + log.error(`failed: submission ${eventType} event (private) - formId: ${formVersion.formId}, version: ${formVersion.version}, submissionId: ${submission.id}`, { + function: 'onSubmit', + }); + } + } + if (evntStrmCfg.enablePublicStream) { + const pubMsg = { + meta: meta, + payload: {}, + }; + const encodedPayload = jsonCodec.encode(pubMsg); + const ack = await this._publishToStream(publicSubj, encodedPayload); + if (ack) { + log.info(`submission ${eventType} event (public) - formId: ${formVersion.formId}, version: ${formVersion.version}, submissionId: ${submission.id}, seq: ${ack.seq}`, { + function: 'onSubmit', + }); + } else { + log.error( + `failed: submission ${eventType} event (public) - formId: ${formVersion.formId}, version: ${formVersion.version}, submissionId: ${submission.id}, seq: ${ack.seq}`, + { + function: 'onSubmit', + } + ); + } + } + } else { + log.info(`formId '${formVersion.formId}' has no event stream configuration (or it is not enabled); will not publish events.`); + } + } else { + // warn, error??? + log.warn(`${SERVICE} is not connected. Cannot publish (submission) event. [submission.event: '${eventType}', submissionId: ${submission.id}]`, { + function: 'onSubmit', + }); + } + } catch (e) { + log.error(`${SERVICE}._onSubmitEventStream: ${e.message}`, e); + } + } + + async _onSubmitWebhook(meta) { + try { + const cfg = await this._getWebhook(meta.formId); + if (cfg && cfg.endpointUrl && cfg.eventStreamNotifications) { + const axiosOptions = { timeout: 10000 }; + const axiosInstance = axios.create(axiosOptions); + axiosInstance.interceptors.request.use( + (cfg) => { + cfg.headers = { [cfg.key]: `${cfg.endpointToken}` }; + return Promise.resolve(cfg); + }, + (error) => { + return Promise.reject(error); + } + ); + axiosInstance.post(cfg.endpointUrl, meta); + } + } catch (e) { + log.error(`${SERVICE}._onSubmitWebhook: ${e.message}`, e); + } + } + + async onSubmit(eventType, submission, draft) { + try { + const formVersion = await this._getFormVersion(submission.formVersionId); + + const meta = { + source: this.source, + domain: this.domain, + class: 'submission', + type: eventType, + formId: formVersion.formId, + formVersionId: submission.formVersionId, + submissionId: submission.id, + draft: draft, + timestamp: new Date(), + }; + await formMetadataService.addAttribute(formVersion.formId, meta); + + const tasks = [this._onSubmitEventStream(eventType, meta, submission, formVersion), this._onSubmitWebhook(meta)]; + + return Promise.all(tasks); + } catch (e) { + log.error(`${SERVICE}.onSubmit: ${e.message}`, e); + } + } +} + +const eventStreamService = new EventStreamService(config.get('eventStreamService')); + +module.exports = { + eventStreamService: eventStreamService, + FORM_EVENT_TYPES: Object.freeze(FORM_EVENT_TYPES), + SUBMISSION_EVENT_TYPES: Object.freeze(SUBMISSION_EVENT_TYPES), +}; diff --git a/app/src/db/migrations/20241031164117_060_event_stream_service.js b/app/src/db/migrations/20241031164117_060_event_stream_service.js new file mode 100644 index 000000000..1d0f28902 --- /dev/null +++ b/app/src/db/migrations/20241031164117_060_event_stream_service.js @@ -0,0 +1,38 @@ +const stamps = require('../stamps'); +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return Promise.resolve() + .then(() => + knex.schema.createTable('form_encryption_key', (table) => { + table.uuid('id').primary(); + table.uuid('formId').references('id').inTable('form').notNullable().index(); + table.string('name', 255).notNullable(); + table.string('algorithm'); + table.string('key'); + stamps(knex, table); + }) + ) + .then(() => + knex.schema.createTable('form_event_stream_config', (table) => { + table.uuid('id').primary(); + table.uuid('formId').references('id').inTable('form').notNullable().index(); + table.boolean('enablePublicStream').defaultTo(false); + table.boolean('enablePrivateStream').defaultTo(false); + table.uuid('encryptionKeyId').references('id').inTable('form_encryption_key'); + stamps(knex, table); + }) + ); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return Promise.resolve() + .then(() => knex.schema.dropTableIfExists('form_event_stream_config')) + .then(() => knex.schema.dropTableIfExists('form_encryption_keys')); +}; diff --git a/app/src/db/migrations/20241031164117_061_event_flags.js b/app/src/db/migrations/20241031164117_061_event_flags.js new file mode 100644 index 000000000..e59821f02 --- /dev/null +++ b/app/src/db/migrations/20241031164117_061_event_flags.js @@ -0,0 +1,35 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return Promise.resolve() + .then(() => + knex.schema.alterTable('form_subscription', (table) => { + table.boolean('eventStreamNotifications').defaultTo(false); + }) + ) + .then(() => + knex.schema.alterTable('form_event_stream_config', (table) => { + table.boolean('enabled').defaultTo(false); + }) + ); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return Promise.resolve() + .then(() => + knex.schema.alterTable('form_subscription', (table) => { + table.dropColumn('eventStreamNotifications'); + }) + ) + .then(() => + knex.schema.alterTable('form_event_stream_config', (table) => { + table.dropColumn('enabled'); + }) + ); +}; diff --git a/app/src/docs/v1.api-spec.yaml b/app/src/docs/v1.api-spec.yaml index 867dd677b..0af2b303b 100755 --- a/app/src/docs/v1.api-spec.yaml +++ b/app/src/docs/v1.api-spec.yaml @@ -637,11 +637,13 @@ paths: name: columns schema: type: array - description: List of form level columns (Only Allowed `deleted`, `draft`, `updatedAt`) to be included, others will be ignored - example: - - deleted, - - draft, - - updatedAt + description: List of form level columns (Only Allowed `deleted`, `draft`, `updatedAt`) to be included, others will be ignored + example: + - deleted, + - draft, + - updatedAt + items: + type: string - in: query name: status schema: @@ -3455,6 +3457,116 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /forms/encryptionKey/algorithms: + get: + summary: Get the list of encryption algorithms + operationId: listEncryptionAlgorithms + security: + - BearerAuth: [] + OpenID: [] + tags: + - Form + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + type: object + properties: + code: + type: string + description: code for encryption Algorithm + display: + type: string + description: display for encryption Algorithm + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /forms/{formId}/encryptionKey/{formEncryptionKeyId}: + get: + summary: Get the encryption key for a form + operationId: readEncryptionKey + security: + - BearerAuth: [] + OpenID: [] + tags: + - Form + parameters: + - in: path + name: formId + schema: + type: string + example: bea3b705-1de5-4f4e-a4e6-0716b7674132 + description: form ID + required: true + - in: path + name: formEncryptionKeyId + schema: + type: string + example: bea3b705-1de5-4f4e-a4e6-0716b7674132 + description: form encryption key ID + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EncryptionKey' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /forms/{formId}/eventStreamConfig: + get: + summary: Get a form's event stream configuration + operationId: readEventStreamConfig + security: + - BearerAuth: [] + OpenID: [] + tags: + - Form + parameters: + - in: path + name: formId + schema: + type: string + example: bea3b705-1de5-4f4e-a4e6-0716b7674132 + description: form ID + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EventStreamConfig' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' components: headers: RateLimit: @@ -3651,6 +3763,34 @@ components: format: uuid description: Transaction ID returned by a messaging service example: 7d13b78b-21d2-4128-b4e8-02ad3da17d22 + EncryptionKey: + allOf: + - type: object + properties: + id: + type: string + format: uuid + description: ID of EncryptionKey. + example: 1223h78b-21d2-4128-b4e8-02ad3daipoi32 + formId: + type: string + format: uuid + description: Form ID this EncryptionKey belongs to. + example: 1223h78b-21d2-4128-b4e8-02ad3daipoi32 + name: + type: string + description: Name to identify this EncryptionKey to a user + example: 'private-event-stream' + algorithm: + type: string + description: Algorithm used for encryption + example: 'aes-256-gcm' + key: + type: string + format: sha256 hash + description: sha256 hash for encryption algorithm + example: ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985 + - $ref: '#/components/schemas/TimeStampUserData' Error: allOf: - $ref: '#/components/schemas/Problem' @@ -3662,6 +3802,34 @@ components: example: Internal Server Error type: example: https://httpstatuses.com/500 + EventStreamConfig: + allOf: + - type: object + properties: + id: + type: string + format: uuid + description: ID of EventStreamConfig. + example: 1223h78b-21d2-4128-b4e8-02ad3daipoi32 + formId: + type: string + format: uuid + description: Form ID this EventStreamConfig belongs to. + example: 1223h78b-21d2-4128-b4e8-02ad3daipoi32 + enablePublicStream: + type: boolean + description: When true, send messages on public stream + enablePrivateStream: + type: boolean + description: When true, send messages on private stream (requires EncryptionKey) + encryptionKeyId: + type: string + format: uuid + description: Id of EncryptionKey for private stream + example: 7d13b78b-21d2-4128-b4e8-02ad3da17d22 + encryptionKey: + $ref: '#/components/schemas/EncryptionKey' + - $ref: '#/components/schemas/TimeStampUserData' FileUpload: type: object properties: @@ -3863,6 +4031,8 @@ components: type: string description: This contains a value that makes apparent that use case of the created form example: feedback + eventStreamConfig: + $ref: '#/components/schemas/EventStreamConfig' FormCore: type: object properties: @@ -4112,6 +4282,8 @@ components: - oneRowPerLake.dataGrid.fishType, - oneRowPerLake.dataGrid.numberCaught, - oneRowPerLake.dataGrid.numberKept + items: + type: string emailExport: type: boolean description: This parameter should be set to true if the form submissions are large (e.g. above 500) or the form is big @@ -4143,23 +4315,23 @@ components: allOf: - type: array items: - - type: object - properties: - draft: - type: boolean - description: >- - Used to indicate if submission came from a draft version of a - form. - example: false - submission: - type: object - description: this object should contain one property named data as an array. This array named data should store all the multiple drafts as object in this array. - properties: - data: - type: array - items: - $ref: '#/components/schemas/FormSubmissionObjectForMultipleUpload' - description: this is an array of multiple objects, each object contain data of single the form submissions. + type: object + properties: + draft: + type: boolean + description: >- + Used to indicate if submission came from a draft version of a + form. + example: false + submission: + type: object + description: this object should contain one property named data as an array. This array named data should store all the multiple drafts as object in this array. + properties: + data: + type: array + items: + $ref: '#/components/schemas/FormSubmissionObjectForMultipleUpload' + description: this is an array of multiple objects, each object contain data of single the form submissions. FormSubmissionSummary: allOf: - type: object diff --git a/app/src/forms/common/middleware/validateParameter.js b/app/src/forms/common/middleware/validateParameter.js index af884f5a0..f9592ef5d 100644 --- a/app/src/forms/common/middleware/validateParameter.js +++ b/app/src/forms/common/middleware/validateParameter.js @@ -5,6 +5,7 @@ const constants = require('../../common/constants'); const externalApiService = require('../../form/externalApi/service'); const formService = require('../../form/service'); const submissionService = require('../../submission/service'); +const encryptionKeyService = require('../../form/encryptionKey/service'); /** * Throws a 400 problem if the parameter is not a valid UUID. @@ -273,7 +274,31 @@ const validateRoleCode = async (_req, _res, next, code) => { const validateUserId = async (_req, _res, next, userId) => { try { _validateUuid(userId, 'userId'); + next(); + } catch (error) { + next(error); + } +}; +/** + * Validates that the :formEncryptionKeyId route parameter exists and is a UUID. This + * validator requires that the :formId route parameter also exists. + * + * @param {*} req the Express object representing the HTTP request. + * @param {*} _res the Express object representing the HTTP response - unused. + * @param {*} next the Express chaining function. + * @param {*} formEncryptionKeyId the :formEncryptionKeyId value from the route. + */ +const validateFormEncryptionKeyId = async (req, _res, next, formEncryptionKeyId) => { + try { + _validateUuid(formEncryptionKeyId, 'formEncryptionKeyId'); + + const rec = await encryptionKeyService.readEncryptionKey(req.params.formId, formEncryptionKeyId); + if (!rec) { + throw new Problem(404, { + detail: 'formEncryptionKeyId does not exist on this form', + }); + } next(); } catch (error) { next(error); @@ -292,4 +317,5 @@ module.exports = { validatePermissionCode, validateRoleCode, validateUserId, + validateFormEncryptionKeyId, }; diff --git a/app/src/forms/common/models/index.js b/app/src/forms/common/models/index.js index ee96e239b..876416ac7 100644 --- a/app/src/forms/common/models/index.js +++ b/app/src/forms/common/models/index.js @@ -27,6 +27,9 @@ module.exports = { ExternalAPI: require('./tables/externalAPI'), ExternalAPIStatusCode: require('./tables/externalAPIStatusCode'), FormMetadata: require('./tables/formMetadata'), + FormEncryptionKey: require('./tables/formEncryptionKey'), + FormEventStreamConfig: require('./tables/formEventStreamConfig'), + // Views FormSubmissionUserPermissions: require('./views/formSubmissionUserPermissions'), PublicFormAccess: require('./views/publicFormAccess'), diff --git a/app/src/forms/common/models/tables/formEncryptionKey.js b/app/src/forms/common/models/tables/formEncryptionKey.js new file mode 100644 index 000000000..94492c0d6 --- /dev/null +++ b/app/src/forms/common/models/tables/formEncryptionKey.js @@ -0,0 +1,48 @@ +const { Model } = require('objection'); +const { Timestamps } = require('../mixins'); +const { Regex } = require('../../constants'); +const stamps = require('../jsonSchema').stamps; + +class FormEncryptionKey extends Timestamps(Model) { + static get tableName() { + return 'form_encryption_key'; + } + + static get modifiers() { + return { + filterFormId(query, value) { + if (value) { + query.where('formId', value); + } + }, + findByIdAndFormId(query, id, formId) { + if (id !== undefined && formId !== undefined) { + query.where('id', id).where('formId', formId); + } + }, + findByFormIdAndName(query, formId, name) { + if (name !== undefined && formId !== undefined) { + query.where('name', name).where('formId', formId); + } + }, + }; + } + + static get jsonSchema() { + return { + type: 'object', + required: ['formId', 'name', 'algorithm', 'key'], + properties: { + id: { type: 'string', pattern: Regex.UUID }, + formId: { type: 'string', pattern: Regex.UUID }, + name: { type: 'string', minLength: 1, maxLength: 255 }, + algorithm: { type: 'string' }, + key: { type: 'string' }, + ...stamps, + }, + additionalProperties: false, + }; + } +} + +module.exports = FormEncryptionKey; diff --git a/app/src/forms/common/models/tables/formEventStreamConfig.js b/app/src/forms/common/models/tables/formEventStreamConfig.js new file mode 100644 index 000000000..b25cfe7d2 --- /dev/null +++ b/app/src/forms/common/models/tables/formEventStreamConfig.js @@ -0,0 +1,59 @@ +const { Model } = require('objection'); +const { Timestamps } = require('../mixins'); +const { Regex } = require('../../constants'); +const stamps = require('../jsonSchema').stamps; + +class FormEventStreamConfig extends Timestamps(Model) { + static get tableName() { + return 'form_event_stream_config'; + } + + static get relationMappings() { + const FormEncryptionKey = require('./formEncryptionKey'); + + return { + encryptionKey: { + relation: Model.HasOneRelation, + modelClass: FormEncryptionKey, + join: { + from: 'form_event_stream_config.encryptionKeyId', + to: 'form_encryption_key.id', + }, + }, + }; + } + + static get modifiers() { + return { + filterFormId(query, value) { + if (value) { + query.where('formId', value); + } + }, + findByIdAndFormId(query, id, formId) { + if (id !== undefined && formId !== undefined) { + query.where('id', id).where('formId', formId); + } + }, + }; + } + + static get jsonSchema() { + return { + type: 'object', + required: ['formId'], + properties: { + id: { type: 'string', pattern: Regex.UUID }, + formId: { type: 'string', pattern: Regex.UUID }, + enablePublicStream: { type: 'boolean', default: false }, + enablePrivateStream: { type: 'boolean', default: false }, + encryptionKeyId: { type: ['string', 'null'], pattern: Regex.UUID }, + enabled: { type: 'boolean', default: false }, + ...stamps, + }, + additionalProperties: false, + }; + } +} + +module.exports = FormEventStreamConfig; diff --git a/app/src/forms/common/models/tables/formSubscription.js b/app/src/forms/common/models/tables/formSubscription.js index 947eeeb3b..6750250b3 100644 --- a/app/src/forms/common/models/tables/formSubscription.js +++ b/app/src/forms/common/models/tables/formSubscription.js @@ -29,6 +29,7 @@ class FormSubscription extends Timestamps(Model) { endpointUrl: { type: 'string' }, endpointToken: { type: 'string' }, key: { type: 'string' }, + eventStreamNotifications: { type: 'boolean', default: false }, ...stamps, }, additionalProperties: false, diff --git a/app/src/forms/event/eventService.js b/app/src/forms/event/eventService.js index 74b8b862c..7886c0050 100644 --- a/app/src/forms/event/eventService.js +++ b/app/src/forms/event/eventService.js @@ -48,7 +48,7 @@ const service = { postSubscriptionEvent: async (subscribe, formVersion, submissionId, subscriptionEvent) => { try { // Check if there are endpoints subscribed for form submission event - if (subscribe && subscribe.endpointUrl) { + if (subscribe && subscribe.endpointUrl && !subscribe.eventStreamNotifications) { const axiosOptions = { timeout: 10000 }; const axiosInstance = axios.create(axiosOptions); const jsonData = { formId: formVersion.formId, formVersion: formVersion.id, subscriptionEvent: subscriptionEvent }; diff --git a/app/src/forms/form/encryptionKey/controller.js b/app/src/forms/form/encryptionKey/controller.js new file mode 100644 index 000000000..fb18e5397 --- /dev/null +++ b/app/src/forms/form/encryptionKey/controller.js @@ -0,0 +1,20 @@ +const service = require('./service'); + +module.exports = { + listEncryptionAlgorithms: async (req, res, next) => { + try { + const response = await service.listEncryptionAlgorithms(); + res.status(200).json(response); + } catch (error) { + next(error); + } + }, + readEncryptionKey: async (req, res, next) => { + try { + const response = await service.readEncryptionKey(req.params.formId, req.params.formEncryptionKeyId); + res.status(200).json(response); + } catch (error) { + next(error); + } + }, +}; diff --git a/app/src/forms/form/encryptionKey/routes.js b/app/src/forms/form/encryptionKey/routes.js new file mode 100644 index 000000000..4db0a7a45 --- /dev/null +++ b/app/src/forms/form/encryptionKey/routes.js @@ -0,0 +1,21 @@ +const routes = require('express').Router(); +const { currentUser, hasFormPermissions } = require('../../auth/middleware/userAccess'); +const validateParameter = require('../../common/middleware/validateParameter'); +const P = require('../../common/constants').Permissions; + +const controller = require('./controller'); + +routes.use(currentUser); + +routes.param('formId', validateParameter.validateFormId); +routes.param('formEncryptionKeyId', validateParameter.validateFormEncryptionKeyId); + +routes.get('/encryptionKey/algorithms', async (req, res, next) => { + await controller.listEncryptionAlgorithms(req, res, next); +}); + +routes.get('/:formId/encryptionKey/:formEncryptionKeyId', hasFormPermissions([P.FORM_READ, P.FORM_UPDATE]), async (req, res, next) => { + await controller.readEncryptionKey(req, res, next); +}); + +module.exports = routes; diff --git a/app/src/forms/form/encryptionKey/service.js b/app/src/forms/form/encryptionKey/service.js new file mode 100644 index 000000000..f6db65222 --- /dev/null +++ b/app/src/forms/form/encryptionKey/service.js @@ -0,0 +1,99 @@ +const { v4: uuidv4 } = require('uuid'); + +const { FormEncryptionKey } = require('../../common/models'); + +const { ENCRYPTION_ALGORITHMS } = require('../../../components/encryptionService'); + +const PRIVATE_EVENT_STREAM_NAME = 'private-event-stream'; + +const service = { + listEncryptionAlgorithms: async () => { + return Object.values(ENCRYPTION_ALGORITHMS).map((x) => ({ + code: x, + display: x, + })); + }, + + _initModel: (formId, data) => { + data.id = uuidv4(); + data.formId = formId; + data.name = PRIVATE_EVENT_STREAM_NAME; + }, + + _update: async (existing, data, currentUser, transaction) => { + // only update if data has changed. + if (existing.algorithm != data.algorithm || existing.key != data.key) { + // yes... update. + await FormEncryptionKey.query(transaction) + .findById(existing.id) + .update({ + ...data, + updatedBy: currentUser.usernameIdp, + }); + } + }, + + _insert: async (data, currentUser, transaction) => { + if (data && data.algorithm && data.key) { + await FormEncryptionKey.query(transaction).insert({ + ...data, + createdBy: currentUser.usernameIdp, + updatedBy: currentUser.usernameIdp, + }); + return data.id; + } + }, + + upsertForEventStreamConfig: async (formId, data, currentUser, transaction) => { + // special case for forms. they have only one event stream configuration + // that requires an encryption key if it has private streams. + // NOTE: event stream config will remove this key if it is unneeded! + if (!data) return; + const externalTrx = transaction != undefined; + let trx; + let id; + try { + trx = externalTrx ? transaction : await FormEncryptionKey.startTransaction(); + const existing = await FormEncryptionKey.query(trx).modify('findByFormIdAndName', formId, PRIVATE_EVENT_STREAM_NAME).first(); + if (existing) { + id = existing.id; + await service._update(existing, data, currentUser, trx); + } else { + // add a new configuration. + service._initModel(formId, data); + id = await service._insert(data, currentUser, trx); //returns id if inserted. + } + if (!externalTrx) trx.commit(); + if (id) { + return FormEncryptionKey.query(trx).findById(id); + } + } catch (err) { + if (!externalTrx && trx) await trx.rollback(); + throw err; + } + }, + + remove: async (id, transaction) => { + const externalTrx = transaction != undefined; + let trx; + try { + trx = externalTrx ? transaction : await FormEncryptionKey.startTransaction(); + const existing = await FormEncryptionKey.query(trx).findById(id); + + if (existing) { + await FormEncryptionKey.query(trx).deleteById(id); + } + + if (!externalTrx) trx.commit(); + } catch (err) { + if (!externalTrx && trx) await trx.rollback(); + throw err; + } + }, + + readEncryptionKey: async (formId, formEncryptionKeyId) => { + return FormEncryptionKey.query().modify('findByIdAndFormId', formEncryptionKeyId, formId).first(); + }, +}; + +module.exports = service; diff --git a/app/src/forms/form/eventStreamConfig/controller.js b/app/src/forms/form/eventStreamConfig/controller.js new file mode 100644 index 000000000..79e0f622b --- /dev/null +++ b/app/src/forms/form/eventStreamConfig/controller.js @@ -0,0 +1,12 @@ +const service = require('./service'); + +module.exports = { + readEventStreamConfig: async (req, res, next) => { + try { + const response = await service.readEventStreamConfig(req.params.formId); + res.status(200).json(response); + } catch (error) { + next(error); + } + }, +}; diff --git a/app/src/forms/form/eventStreamConfig/routes.js b/app/src/forms/form/eventStreamConfig/routes.js new file mode 100644 index 000000000..81c8ac72b --- /dev/null +++ b/app/src/forms/form/eventStreamConfig/routes.js @@ -0,0 +1,16 @@ +const routes = require('express').Router(); +const { currentUser, hasFormPermissions } = require('../../auth/middleware/userAccess'); +const validateParameter = require('../../common/middleware/validateParameter'); +const P = require('../../common/constants').Permissions; + +const controller = require('./controller'); + +routes.use(currentUser); + +routes.param('formId', validateParameter.validateFormId); + +routes.get('/:formId/eventStreamConfig', hasFormPermissions([P.FORM_READ, P.FORM_UPDATE]), async (req, res, next) => { + await controller.readEventStreamConfig(req, res, next); +}); + +module.exports = routes; diff --git a/app/src/forms/form/eventStreamConfig/service.js b/app/src/forms/form/eventStreamConfig/service.js new file mode 100644 index 000000000..57f467fdd --- /dev/null +++ b/app/src/forms/form/eventStreamConfig/service.js @@ -0,0 +1,78 @@ +const Problem = require('api-problem'); + +const { v4: uuidv4 } = require('uuid'); + +const { FormEventStreamConfig } = require('../../common/models'); + +const encryptionKeyService = require('../encryptionKey/service'); + +const service = { + validateEventStreamConfig: (data) => { + if (!data) { + throw new Problem(422, `'EventStreamConfig record' cannot be empty.`); + } + }, + + upsert: async (formId, data, currentUser, transaction) => { + service.validateEventStreamConfig(data); + const externalTrx = transaction != undefined; + let trx; + try { + trx = externalTrx ? transaction : await FormEventStreamConfig.startTransaction(); + const existing = await FormEventStreamConfig.query(trx).modify('filterFormId', formId).first(); //only 1... + + // let's deal with encryption key + const encKey = await encryptionKeyService.upsertForEventStreamConfig(formId, data.encryptionKey, currentUser, transaction); + data.encryptionKeyId = encKey && data.enablePrivateStream ? encKey.id : null; + data.encryptionKey = null; // only want the id for config upsert + + if (existing) { + // do we need to update? + if ( + existing.enablePrivateStream != data.enablePrivateStream || + existing.enablePublicStream != data.enablePublicStream || + existing.encryptionKeyId != data.encryptionKeyId + ) { + // yes... update. + // except the enabled field... do not allow change via API (yet) + data.enabled = existing.enabled; + await FormEventStreamConfig.query(trx) + .findById(existing.id) + .update({ + ...data, + updatedBy: currentUser.usernameIdp, + }); + } + } else { + // add a new configuration. + data.id = uuidv4(); + data.formId = formId; + await FormEventStreamConfig.query(trx).insert({ + ...data, + createdBy: currentUser.usernameIdp, + }); + } + // finally, if we do not have private stream AND we have an encryption key, delete it... + if (!data.enablePrivateStream && encKey) { + await encryptionKeyService.remove(encKey.id, trx); + } + if (!externalTrx) trx.commit(); + } catch (err) { + if (!externalTrx && trx) await trx.rollback(); + throw err; + } + }, + + readEventStreamConfig: async (formId) => { + let result = await FormEventStreamConfig.query().modify('filterFormId', formId).first(); // there should be only one + if (!result) { + // let's create a default + const rec = new FormEventStreamConfig(); + rec.formId = formId; + return await service.upsert(formId, rec, { usernameIdp: 'systemdefault' }); + } + return result; + }, +}; + +module.exports = service; diff --git a/app/src/forms/form/index.js b/app/src/forms/form/index.js index 06c842dbe..e18f1c620 100644 --- a/app/src/forms/form/index.js +++ b/app/src/forms/form/index.js @@ -1,9 +1,11 @@ const routes = require('./routes'); const setupMount = require('../common/utils').setupMount; +const encryptionKeyRoutes = require('./encryptionKey/routes'); +const eventStreamConfigRoutes = require('./eventStreamConfig/routes'); const externalApiRoutes = require('./externalApi/routes'); module.exports.mount = (app) => { - const p = setupMount('forms', app, [routes, externalApiRoutes]); + const p = setupMount('forms', app, [routes, encryptionKeyRoutes, eventStreamConfigRoutes, externalApiRoutes]); return p; }; diff --git a/app/src/forms/form/service.js b/app/src/forms/form/service.js index 44f6652f7..8f9011da2 100644 --- a/app/src/forms/form/service.js +++ b/app/src/forms/form/service.js @@ -26,6 +26,8 @@ const { const { falsey, queryUtils, checkIsFormExpired, validateScheduleObject, typeUtils } = require('../common/utils'); const { Permissions, Roles, Statuses } = require('../common/constants'); const formMetadataService = require('./formMetadata/service'); +const { eventStreamService, SUBMISSION_EVENT_TYPES } = require('../../components/eventStreamService'); +const eventStreamConfigService = require('./eventStreamConfig/service'); const Rolenames = [Roles.OWNER, Roles.TEAM_MANAGER, Roles.FORM_DESIGNER, Roles.SUBMISSION_REVIEWER, Roles.FORM_SUBMITTER, Roles.SUBMISSION_APPROVER]; const service = { @@ -135,6 +137,7 @@ const service = { await FormStatusCode.query(trx).insert(defaultStatuses); await formMetadataService.upsert(obj.id, data.formMetadata, currentUser, trx); + await eventStreamConfigService.upsert(obj.id, data.eventStreamConfig, currentUser, trx); await trx.commit(); const result = await service.readForm(obj.id); @@ -193,10 +196,10 @@ const service = { if (fIdps && fIdps.length) await FormIdentityProvider.query(trx).insert(fIdps); await formMetadataService.upsert(obj.id, data.formMetadata, currentUser, trx); + await eventStreamConfigService.upsert(obj.id, data.eventStreamConfig, currentUser, trx); await trx.commit(); - const result = await service.readForm(obj.id); - return result; + return await service.readForm(obj.id); } catch (err) { if (trx) await trx.rollback(); throw err; @@ -463,9 +466,10 @@ const service = { publishVersion: async (formId, formVersionId, params = {}, currentUser) => { let trx; + let result; + // allow an unpublish if they pass in unpublish parameter with an affirmative + const publish = params.unpublish ? falsey(params.unpublish) : true; try { - // allow an unpublish if they pass in unpublish parameter with an affirmative - const publish = params.unpublish ? falsey(params.unpublish) : true; const form = await service.readForm(formId); trx = await FormVersion.startTransaction(); @@ -486,11 +490,15 @@ const service = { eventService.publishFormEvent(formId, formVersionId, publish); // return the published form/version... - return await service.readPublishedForm(formId); + result = await service.readPublishedForm(formId); } catch (err) { if (trx) await trx.rollback(); throw err; } + if (result) { + await eventStreamService.onPublish(formId, formVersionId, publish); + return result; + } }, readVersion: (formVersionId) => { @@ -529,6 +537,7 @@ const service = { }, createSubmission: async (formVersionId, data, currentUser) => { let trx; + let result; try { const formVersion = await service.readVersion(formVersionId); const { identityProviders } = await service.readForm(formVersion.formId); @@ -597,13 +606,15 @@ const service = { } await trx.commit(); - const result = await service.readSubmission(obj.id); - - return result; + result = await service.readSubmission(obj.id); } catch (err) { if (trx) await trx.rollback(); throw err; } + if (result) { + await eventStreamService.onSubmit(SUBMISSION_EVENT_TYPES.CREATED, result, data.draft); + return result; + } }, createMultiSubmission: async (formVersionId, data, currentUser) => { let trx; @@ -743,12 +754,14 @@ const service = { }, publishDraft: async (formId, formVersionDraftId, currentUser) => { let trx; + let result; + let version; try { const form = await service.readForm(formId); const draft = await service.readDraft(formVersionDraftId); trx = await FormVersionDraft.startTransaction(); - const version = { + version = { id: uuidv4(), formId: form.id, version: form.versions.length ? form.versions[0].version + 1 : 1, @@ -771,11 +784,15 @@ const service = { eventService.publishFormEvent(formId, version.id, version.published); // return the published version... - return await service.readVersion(version.id); + result = await service.readVersion(version.id); } catch (err) { if (trx) await trx.rollback(); throw err; } + if (result) { + await eventStreamService.onPublish(formId, version.id, version.published); + return result; + } }, getStatusCodes: async (formId) => { return FormStatusCode.query().withGraphFetched('statusCode').where('formId', formId); @@ -914,6 +931,8 @@ const service = { if (subscriptionDetails) { // Update new subscription settings for a form + // except the eventStreamNotifications - for now, no updates via API + subscriptionData.eventStreamNotifications = subscriptionDetails.eventStreamNotifications; await FormSubscription.query(trx) .modify('filterFormId', formId) .update({ diff --git a/app/src/forms/submission/service.js b/app/src/forms/submission/service.js index ae4f56301..5ef72ed34 100644 --- a/app/src/forms/submission/service.js +++ b/app/src/forms/submission/service.js @@ -9,6 +9,7 @@ const eventService = require('../event/eventService'); const fileService = require('../file/service'); const formService = require('../form/service'); const permissionService = require('../permission/service'); +const { eventStreamService, SUBMISSION_EVENT_TYPES } = require('../../components/eventStreamService'); const service = { // ------------------------------------------------------------------------------------------------------- @@ -103,11 +104,13 @@ const service = { update: async (formSubmissionId, data, currentUser, referrer, etrx = undefined) => { let trx; + let result; try { trx = etrx ? etrx : await FormSubmission.startTransaction(); + const restoring = data['deleted'] !== undefined && typeof data.deleted == 'boolean'; // If we're restoring a submission - if (data['deleted'] !== undefined && typeof data.deleted == 'boolean') { + if (restoring) { await FormSubmission.query(trx).patchAndFetchById(formSubmissionId, { deleted: data.deleted, updatedBy: currentUser.usernameIdp }); } else { const statuses = await FormSubmissionStatus.query().modify('filterSubmissionId', formSubmissionId).modify('orderDescending'); @@ -150,11 +153,15 @@ const service = { if (!etrx) await trx.commit(); - return service.read(formSubmissionId); + result = await service.read(formSubmissionId); } catch (err) { if (!etrx && trx) await trx.rollback(); throw err; } + if (result) { + await eventStreamService.onSubmit(SUBMISSION_EVENT_TYPES.UPDATED, result.submission, data.draft); + return result; + } }, /** @@ -187,6 +194,7 @@ const service = { delete: async (formSubmissionId, currentUser) => { let trx; + let result; try { trx = await FormSubmission.startTransaction(); await FormSubmission.query(trx).patchAndFetchById(formSubmissionId, { @@ -194,11 +202,15 @@ const service = { updatedBy: currentUser.usernameIdp, }); await trx.commit(); - return await service.read(formSubmissionId); + result = await service.read(formSubmissionId); } catch (err) { if (trx) await trx.rollback(); throw err; } + if (result) { + await eventStreamService.onSubmit(SUBMISSION_EVENT_TYPES.DELETED, result.submission, false); + return result; + } }, deleteMultipleSubmissions: async (submissionIds, currentUser) => { diff --git a/app/tests/unit/components/eventStreamService.spec.js b/app/tests/unit/components/eventStreamService.spec.js new file mode 100644 index 000000000..14a23982c --- /dev/null +++ b/app/tests/unit/components/eventStreamService.spec.js @@ -0,0 +1,405 @@ +const { MockModel } = require('../../common/dbHelper'); +const { JSONCodec } = require('nats'); +const crypto = require('crypto'); +const { Form, FormVersion, FormEventStreamConfig } = require('../../../src/forms/common/models'); + +const formMetadataService = require('../../../src/forms/form/formMetadata/service'); +const { eventStreamService, FORM_EVENT_TYPES, SUBMISSION_EVENT_TYPES } = require('../../../src/components/eventStreamService'); +const jsonCodec = JSONCodec(); +jest.mock('../../../src/forms/form/formMetadata/service'); + +// change these as appropriate after adding new default keys/algos... +const FORM_EVENT_TYPES_COUNT = 2; +const SUBMISSION_EVENT_TYPES_COUNT = 3; + +beforeEach(() => { + MockModel.mockReset(); + jest.resetModules(); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('eventStreamService', () => { + let service; + beforeEach(() => { + MockModel.mockReset(); + + Form.query = jest.fn().mockReturnThis(); + Form.where = jest.fn().mockReturnThis(); + Form.modify = jest.fn().mockReturnThis(); + Form.findById = jest.fn().mockReturnThis(); + Form.allowGraph = jest.fn().mockReturnThis(); + Form.withGraphFetched = jest.fn().mockReturnThis(); + Form.first = jest.fn().mockReturnThis(); + Form.throwIfNotFound = jest.fn().mockReturnThis(); + + FormVersion.query = jest.fn().mockReturnThis(); + FormVersion.where = jest.fn().mockReturnThis(); + FormVersion.modify = jest.fn().mockReturnThis(); + FormVersion.findById = jest.fn().mockReturnThis(); + FormVersion.allowGraph = jest.fn().mockReturnThis(); + FormVersion.withGraphFetched = jest.fn().mockReturnThis(); + FormVersion.first = jest.fn().mockReturnThis(); + FormVersion.throwIfNotFound = jest.fn().mockReturnThis(); + + FormEventStreamConfig.query = jest.fn().mockReturnThis(); + FormEventStreamConfig.where = jest.fn().mockReturnThis(); + FormEventStreamConfig.modify = jest.fn().mockReturnThis(); + FormEventStreamConfig.findById = jest.fn().mockReturnThis(); + FormEventStreamConfig.allowGraph = jest.fn().mockReturnThis(); + FormEventStreamConfig.withGraphFetched = jest.fn().mockReturnThis(); + FormEventStreamConfig.first = jest.fn().mockReturnThis(); + FormEventStreamConfig.throwIfNotFound = jest.fn().mockReturnThis(); + + formMetadataService.addAttribute = jest.fn().mockResolvedValueOnce({}); + service = eventStreamService; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should return a implemented service ', () => { + expect(service).toBeTruthy(); + expect(service.servers).toBeTruthy(); + expect(Object.entries(FORM_EVENT_TYPES)).toHaveLength(FORM_EVENT_TYPES_COUNT); + expect(Object.entries(SUBMISSION_EVENT_TYPES)).toHaveLength(SUBMISSION_EVENT_TYPES_COUNT); + }); + + it('onPublish should emit published private', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: false, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: true, + }); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onPublish('123', '456', true); + expect(service._publishToStream).toBeCalledTimes(1); // called once for private, no public stream enabled + expect(service._publishToStream).toBeCalledWith('PRIVATE.forms.schema.published.123', expect.anything()); // called once for private, no public stream enabled + }); + + it('onPublish should not emit when config not enabled', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: false, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: false, + }); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onPublish('123', '456', true); + expect(service._publishToStream).toBeCalledTimes(0); // config not enabled, js.publish not called + }); + + it('onPublish should emit published public', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: true, + enablePrivateStream: false, + enabled: true, + }); + await service.onPublish('123', '456', true); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + expect(service._publishToStream).toBeCalledTimes(1); // called once for private, no public stream enabled + expect(service._publishToStream).toBeCalledWith('PUBLIC.forms.schema.published.123', expect.anything()); // called once for private, no public stream enabled + }); + + it('onPublish should emit unpublished private', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: false, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: true, + }); + await service.onPublish('123', '456', false); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + expect(service._publishToStream).toBeCalledTimes(1); // called once for private, no public stream enabled + expect(service._publishToStream).toBeCalledWith('PRIVATE.forms.schema.unpublished.123', expect.anything()); // called once for private, no public stream enabled + }); + + it('onPublish should emit unpublished public', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: true, + enablePrivateStream: false, + enabled: true, + }); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onPublish('123', '456', false); + expect(service._publishToStream).toBeCalledTimes(1); // called once for private, no public stream enabled + expect(service._publishToStream).toBeCalledWith('PUBLIC.forms.schema.unpublished.123', expect.anything()); // called once for private, no public stream enabled + }); + + it('onPublish should emit private and public', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: true, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: true, + }); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onPublish('123', '456', true); + expect(service._publishToStream).toBeCalledTimes(2); + }); + + it('onSubmit should emit private', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: false, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: true, + }); + service._onSubmitWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onSubmit('created', { id: '345', formId: '123', formVersionId: '456' }); + expect(service._publishToStream).toBeCalledTimes(1); // called once for private, no public stream enabled + expect(service._publishToStream).toBeCalledWith('PRIVATE.forms.submission.created.123', expect.anything()); // called once for private, no public stream enabled + }); + + it('onSubmit should not emit when config not enabled', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: false, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: false, + }); + service._onSubmitWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onSubmit('created', { id: '345', formId: '123', formVersionId: '456' }); + expect(service._publishToStream).toBeCalledTimes(0); // config not enabled, no js.publish + }); + + it('onSubmit should emit public', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: true, + enablePrivateStream: false, + enabled: true, + }); + service._onSubmitWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onSubmit('created', { id: '345', formId: '123', formVersionId: '456' }); + expect(service._publishToStream).toBeCalledTimes(1); // called once for private, no public stream enabled + expect(service._publishToStream).toBeCalledWith('PUBLIC.forms.submission.created.123', expect.anything()); // called once for private, no public stream enabled + }); + + it('onSubmit should emit private and public', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockResolvedValueOnce({ id: '123' }); + service._getFormVersion = jest.fn().mockResolvedValueOnce({ id: '456', formId: '123', version: '1' }); + service._getEventStreamConfig = jest.fn().mockResolvedValueOnce({ + id: '789', + formId: '123', + enablePublicStream: true, + enablePrivateStream: true, + encryptionKeyId: '012', + encryptionKey: { + id: '012', + formId: '123', + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', + }, + enabled: true, + }); + service._onSubmitWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onSubmit('created', { id: '345', formId: '123', formVersionId: '456' }); + expect(service._publishToStream).toBeCalledTimes(2); + }); + + it('onPublish should not throw error', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + service._getFormVersion = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + service._getEventStreamConfig = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + service._onPublishWebhook = jest.fn().mockResolvedValueOnce({}); + + await service.onPublish('123', '456', true); + expect(service._publishToStream).toBeCalledTimes(0); + }); + + it('onSubmit should not throw error', async () => { + // mock out service properties and functions + service.openConnection = jest.fn().mockResolvedValueOnce(true); + service.nc = jest.fn().mockReturnThis(); + service.nc.info = jest.fn().mockReturnThis(); + service._publishToStream = jest.fn().mockResolvedValueOnce({ seq: 1 }); + // mock out model queries + service._getForm = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + service._getFormVersion = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + service._getEventStreamConfig = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + service._onSubmitWebhook = jest.fn().mockRejectedValueOnce(); + await service.onSubmit('created', { id: '345', formId: '123', formVersionId: '456' }); + expect(service._publishToStream).toBeCalledTimes(0); + }); + + it('_sizeCheck should handle large message', async () => { + // mock out service properties and functions + service.allowedMsgSize = 1000; // allow 1000 bytes max + const msg = { + meta: {}, + payload: { data: 'hi' }, + }; + + // small message: no error, payload same + let encoded = service._sizeCheck(msg); + let decoded = jsonCodec.decode(encoded); + expect(decoded.error).toBeFalsy(); + expect(decoded.payload.data).toEqual('hi'); + + // large message: error, payload removed + msg.payload.data = crypto.randomBytes(1000).toString('hex'); + encoded = service._sizeCheck(msg); + decoded = jsonCodec.decode(encoded); + expect(decoded.error).toBeTruthy(); + expect(decoded.payload.data).toEqual({}); + }); +}); diff --git a/app/tests/unit/forms/form/encryptionKey/controller.spec.js b/app/tests/unit/forms/form/encryptionKey/controller.spec.js new file mode 100644 index 000000000..552b68930 --- /dev/null +++ b/app/tests/unit/forms/form/encryptionKey/controller.spec.js @@ -0,0 +1,108 @@ +const { MockModel, MockTransaction } = require('../../../../common/dbHelper'); +const { getMockReq, getMockRes } = require('@jest-mock/express'); +const uuid = require('uuid'); + +const controller = require('../../../../../src/forms/form/encryptionKey/controller'); +const service = require('../../../../../src/forms/form/encryptionKey/service'); + +jest.mock('../../../../../src/forms/common/models/tables/formEncryptionKey', () => MockModel); + +const currentUser = { + usernameIdp: 'TESTER', +}; + +const formId = uuid.v4(); + +const validData = { + id: uuid.v4(), + formId: formId, + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', +}; + +const validRequest = { + body: { + ...validData, + }, + currentUser: currentUser, + params: { + formId: formId, + formEncryptionKeyId: validData.id, + }, +}; + +const error = new Error('error'); + +beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('readEncryptionKey', () => { + describe('error response when', () => { + it('has an unsuccessful service call', async () => { + service.readEncryptionKey = jest.fn().mockRejectedValueOnce(error); + const req = getMockReq(validRequest); + const { res, next } = getMockRes(); + + await controller.readEncryptionKey(req, res, next); + + expect(service.readEncryptionKey).toBeCalledWith(validRequest.params.formId, validRequest.params.formEncryptionKeyId); + expect(res.json).not.toBeCalled(); + expect(res.status).not.toBeCalled(); + expect(next).toBeCalledWith(error); + }); + }); + + describe('200 response when', () => { + it('has a successful service call', async () => { + service.readEncryptionKey = jest.fn().mockResolvedValue(validData); + const req = getMockReq(validRequest); + const { res, next } = getMockRes(); + + await controller.readEncryptionKey(req, res, next); + + expect(service.readEncryptionKey).toBeCalledWith(validRequest.params.formId, validRequest.params.formEncryptionKeyId); + expect(res.json).toBeCalledWith(validData); + expect(res.status).toBeCalledWith(200); + expect(next).not.toBeCalled(); + }); + }); +}); + +describe('listEncryptionAlgorithms', () => { + describe('error response when', () => { + it('has an unsuccessful service call', async () => { + service.listEncryptionAlgorithms = jest.fn().mockRejectedValueOnce(error); + const req = getMockReq(validRequest); + const { res, next } = getMockRes(); + + await controller.listEncryptionAlgorithms(req, res, next); + + expect(service.listEncryptionAlgorithms).toBeCalledWith(); + expect(res.json).not.toBeCalled(); + expect(res.status).not.toBeCalled(); + expect(next).toBeCalledWith(error); + }); + }); + + describe('200 response when', () => { + it('has a successful service call', async () => { + service.listEncryptionAlgorithms = jest.fn().mockResolvedValue([{ code: 'abc', display: 'ABC' }]); + const req = getMockReq(validRequest); + const { res, next } = getMockRes(); + + await controller.listEncryptionAlgorithms(req, res, next); + + expect(service.listEncryptionAlgorithms).toBeCalledWith(); + expect(res.json).toBeCalledWith([{ code: 'abc', display: 'ABC' }]); + expect(res.status).toBeCalledWith(200); + expect(next).not.toBeCalled(); + }); + }); +}); diff --git a/app/tests/unit/forms/form/encryptionKey/routes.spec.js b/app/tests/unit/forms/form/encryptionKey/routes.spec.js new file mode 100644 index 000000000..9cdea0b83 --- /dev/null +++ b/app/tests/unit/forms/form/encryptionKey/routes.spec.js @@ -0,0 +1,134 @@ +const request = require('supertest'); +const uuid = require('uuid'); + +const { expressHelper } = require('../../../../common/helper'); + +const jwtService = require('../../../../../src/components/jwtService'); +const apiAccess = require('../../../../../src/forms/auth/middleware/apiAccess'); +const userAccess = require('../../../../../src/forms/auth/middleware/userAccess'); +const validateParameter = require('../../../../../src/forms/common/middleware/validateParameter'); +const controller = require('../../../../../src/forms/form/encryptionKey/controller'); + +// +// Mock out all the middleware - we're testing that the routes are set up +// correctly, not the functionality of the middleware. +// + +jest.mock('../../../../../src/forms/auth/middleware/apiAccess'); +apiAccess.mockImplementation( + jest.fn((_req, _res, next) => { + next(); + }) +); + +jwtService.protect = jest.fn(() => { + return jest.fn((_req, _res, next) => { + next(); + }); +}); + +const hasFormPermissionsMock = jest.fn((_req, _res, next) => { + next(); +}); +userAccess.currentUser = jest.fn((_req, _res, next) => { + next(); +}); +userAccess.hasFormPermissions = jest.fn(() => { + return hasFormPermissionsMock; +}); + +validateParameter.validateFormId = jest.fn((_req, _res, next) => { + next(); +}); + +validateParameter.validateFormEncryptionKeyId = jest.fn((_req, _res, next) => { + next(); +}); + +// +// Create the router and a simple Express server. +// + +const router = require('../../../../../src/forms/form/encryptionKey/routes'); +const basePath = '/form'; +const app = expressHelper(basePath, router); +const appRequest = request(app); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe(`${basePath}/encryptionKey/algorithms`, () => { + const path = `${basePath}/encryptionKey/algorithms`; + + it('should have correct middleware for GET', async () => { + controller.listEncryptionAlgorithms = jest.fn((_req, res) => { + res.sendStatus(200); + }); + + await appRequest.get(path); + + expect(apiAccess).toBeCalledTimes(0); + expect(controller.listEncryptionAlgorithms).toBeCalledTimes(1); + expect(hasFormPermissionsMock).toBeCalledTimes(0); // no form id, no permissions check + expect(userAccess.currentUser).toBeCalledTimes(1); + expect(validateParameter.validateFormId).toBeCalledTimes(0); // no form id, no validation call + }); + + it('should return 404 for DELETE', async () => { + const response = await appRequest.delete(path); + + expect(response.statusCode).toBe(404); + }); + + it('should return 404 for POST', async () => { + const response = await appRequest.post(path); + + expect(response.statusCode).toBe(404); + }); + + it('should return 404 for PUT', async () => { + const response = await appRequest.put(path); + + expect(response.statusCode).toBe(404); + }); +}); + +describe(`${basePath}/:formId/encryptionKey/:formEncryptionKeyId`, () => { + const formId = uuid.v4(); + const formEncryptionKeyId = uuid.v4(); + const path = `${basePath}/${formId}/encryptionKey/${formEncryptionKeyId}`; + + it('should have correct middleware for GET', async () => { + controller.readEncryptionKey = jest.fn((_req, res) => { + res.sendStatus(200); + }); + + await appRequest.get(path); + + expect(apiAccess).toBeCalledTimes(0); + expect(controller.readEncryptionKey).toBeCalledTimes(1); + expect(hasFormPermissionsMock).toBeCalledTimes(1); + expect(userAccess.currentUser).toBeCalledTimes(1); + expect(validateParameter.validateFormId).toBeCalledTimes(1); + expect(validateParameter.validateFormEncryptionKeyId).toBeCalledTimes(1); + }); + + it('should return 404 for DELETE', async () => { + const response = await appRequest.delete(path); + + expect(response.statusCode).toBe(404); + }); + + it('should return 404 for POST', async () => { + const response = await appRequest.post(path); + + expect(response.statusCode).toBe(404); + }); + + it('should return 404 for PUT', async () => { + const response = await appRequest.put(path); + + expect(response.statusCode).toBe(404); + }); +}); diff --git a/app/tests/unit/forms/form/encryptionKey/service.spec.js b/app/tests/unit/forms/form/encryptionKey/service.spec.js new file mode 100644 index 000000000..430d0bfb4 --- /dev/null +++ b/app/tests/unit/forms/form/encryptionKey/service.spec.js @@ -0,0 +1,239 @@ +const { MockModel, MockTransaction } = require('../../../../common/dbHelper'); + +const uuid = require('uuid'); + +const service = require('../../../../../src/forms/form/encryptionKey/service'); +const { ENCRYPTION_ALGORITHMS } = require('../../../../../src/components/encryptionService'); + +jest.mock('../../../../../src/forms/common/models/tables/formEncryptionKey', () => MockModel); + +const user = { + usernameIdp: 'TESTER', +}; + +const formId = uuid.v4(); + +const validData = { + id: uuid.v4(), + formId: formId, + name: 'test', + algorithm: 'aes-256-gcm', + key: 'ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985', +}; + +beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('listEncryptionAlgorithms', () => { + it('should return valid data', async () => { + const res = await service.listEncryptionAlgorithms(); + expect(res.length).toEqual(Object.keys(ENCRYPTION_ALGORITHMS).length); + expect(Object.keys(res[0]).includes('code')).toBeTruthy(); + expect(Object.keys(res[0]).includes('display')).toBeTruthy(); + }); +}); + +describe('readEncryptionKey', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should return valid data', async () => { + MockModel.first = jest.fn().mockResolvedValueOnce(validData); + const res = await service.readEncryptionKey(validData.formId, validData.formEncryptionKeyId); + expect(MockModel.first).toBeCalledTimes(1); + expect(res).toEqual(validData); + }); +}); + +describe('remove', () => { + beforeEach(() => { + MockModel.mockClear(); + MockModel.mockReset(); + MockTransaction.mockReset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should remove valid id', async () => { + MockModel.findById = jest.fn().mockResolvedValue(Object.assign({}, validData)); + + await service.remove(validData.id); + expect(MockModel.deleteById).toBeCalledTimes(1); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); + + it('should not remove invalid id', async () => { + MockModel.findById = jest.fn().mockResolvedValue(null); + + await service.remove(validData.id); + expect(MockModel.deleteById).toBeCalledTimes(0); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); + + it('should raise errors on failed delete', async () => { + MockModel.findById = jest.fn().mockResolvedValue(Object.assign({}, validData)); + MockModel.deleteById = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + await expect(service.remove(validData.id)).rejects.toThrow(); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); + + it('should use provided transaction', async () => { + MockModel.findById = jest.fn().mockResolvedValue(Object.assign({}, validData)); + const xact = jest.fn().mockResolvedValue(MockTransaction); + await service.remove(validData.id, xact); + expect(MockModel.deleteById).toBeCalledTimes(1); + expect(MockModel.startTransaction).toBeCalledTimes(0); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); +}); +describe('_insert', () => { + beforeEach(() => { + MockModel.mockClear(); + MockModel.mockReset(); + MockTransaction.mockReset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should create with good data', async () => { + const xact = jest.fn().mockResolvedValue(MockTransaction); + let id = await service._insert(validData, user, xact); + expect(MockModel.insert).toBeCalledTimes(1); + expect(MockModel.insert).toBeCalledWith({ + createdBy: user.usernameIdp, + updatedBy: user.usernameIdp, + ...validData, + }); + expect(id).toBeTruthy(); + }); + + it('should not create without algorithm', async () => { + const xact = jest.fn().mockResolvedValue(MockTransaction); + let data = Object.assign({}, validData); + data.algorithm = null; + let id = await service._insert(data, user, xact); + expect(MockModel.insert).toBeCalledTimes(0); + expect(id).toBeFalsy(); + }); + + it('should not create without key', async () => { + const xact = jest.fn().mockResolvedValue(MockTransaction); + let data = Object.assign({}, validData); + data.key = null; + let id = await service._insert(data, user, xact); + expect(MockModel.insert).toBeCalledTimes(0); + expect(id).toBeFalsy(); + }); + + it('should raise errors on failed insert', async () => { + MockModel.insert = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + const xact = jest.fn().mockResolvedValue(MockTransaction); + await expect(service._insert(validData, user, xact)).rejects.toThrow(); + }); +}); + +describe('upsertForEventStreamConfig', () => { + beforeEach(() => { + MockModel.mockClear(); + MockModel.mockReset(); + MockTransaction.mockReset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should update valid data', async () => { + MockModel.first = jest.fn().mockResolvedValue(Object.assign({}, validData)); + MockModel.findById = jest.fn().mockReturnThis(); + + let data = Object.assign({}, validData); + data.key = 'differentkey'; + + await service.upsertForEventStreamConfig(validData.formId, data, user); + expect(MockModel.update).toBeCalledTimes(1); + expect(MockModel.update).toBeCalledWith({ + updatedBy: user.usernameIdp, + ...data, + }); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); + + it('should create when not found', async () => { + MockModel.first = jest.fn().mockResolvedValueOnce(null); + service._initModel = jest.fn().mockReturnValue(validData); + await service.upsertForEventStreamConfig(validData.formId, validData, user); + expect(MockModel.insert).toBeCalledTimes(1); + expect(MockModel.insert).toBeCalledWith({ + createdBy: user.usernameIdp, + updatedBy: user.usernameIdp, + ...validData, + }); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); + + it('should raise errors on failed update', async () => { + MockModel.first = jest.fn().mockResolvedValue(Object.assign({}, validData)); + MockModel.update = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + + let data = Object.assign({}, validData); + data.key = 'differentkey'; + + await expect(service.upsertForEventStreamConfig(validData.formId, data, user)).rejects.toThrow(); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); + + it('should raise errors on failed insert', async () => { + MockModel.first = jest.fn().mockResolvedValueOnce(null); + MockModel.insert = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + await expect(service.upsertForEventStreamConfig(validData.formId, validData, user)).rejects.toThrow(); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); + + it('should use provided transaction', async () => { + MockModel.first = jest.fn().mockResolvedValueOnce(null); + service._initModel = jest.fn().mockReturnValue(validData); + const xact = jest.fn().mockResolvedValue(MockTransaction); + await service.upsertForEventStreamConfig(validData.formId, validData, user, xact); + expect(MockModel.insert).toBeCalledTimes(1); + expect(MockModel.insert).toBeCalledWith({ + createdBy: user.usernameIdp, + updatedBy: user.usernameIdp, + ...validData, + }); + expect(MockModel.startTransaction).toBeCalledTimes(0); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); +}); diff --git a/app/tests/unit/forms/form/eventStreamConfig/controller.spec.js b/app/tests/unit/forms/form/eventStreamConfig/controller.spec.js new file mode 100644 index 000000000..6f0af5ddc --- /dev/null +++ b/app/tests/unit/forms/form/eventStreamConfig/controller.spec.js @@ -0,0 +1,75 @@ +const { MockModel, MockTransaction } = require('../../../../common/dbHelper'); +const { getMockReq, getMockRes } = require('@jest-mock/express'); +const uuid = require('uuid'); + +const controller = require('../../../../../src/forms/form/eventStreamConfig/controller'); +const service = require('../../../../../src/forms/form/eventStreamConfig/service'); + +jest.mock('../../../../../src/forms/common/models/tables/formEventStreamConfig', () => MockModel); + +const currentUser = { + usernameIdp: 'TESTER', +}; + +const formId = uuid.v4(); + +const validData = { + id: uuid.v4(), + formId: formId, + enablePublicStream: true, + enablePrivateStream: true, + encryptionKeyId: uuid.v4(), +}; + +const validRequest = { + body: { + ...validData, + }, + currentUser: currentUser, + params: { + formId: formId, + }, +}; + +const error = new Error('error'); + +beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('readEventStreamConfig', () => { + describe('error response when', () => { + it('has an unsuccessful service call', async () => { + service.readEventStreamConfig = jest.fn().mockRejectedValueOnce(error); + const req = getMockReq(validRequest); + const { res, next } = getMockRes(); + + await controller.readEventStreamConfig(req, res, next); + + expect(service.readEventStreamConfig).toBeCalledWith(validRequest.params.formId); + expect(res.json).not.toBeCalled(); + expect(res.status).not.toBeCalled(); + expect(next).toBeCalledWith(error); + }); + }); + + describe('200 response when', () => { + it('has a successful service call', async () => { + service.readEventStreamConfig = jest.fn().mockResolvedValue(validData); + const req = getMockReq(validRequest); + const { res, next } = getMockRes(); + + await controller.readEventStreamConfig(req, res, next); + + expect(service.readEventStreamConfig).toBeCalledWith(validRequest.params.formId); + expect(res.json).toBeCalledWith(validData); + expect(res.status).toBeCalledWith(200); + expect(next).not.toBeCalled(); + }); + }); +}); diff --git a/app/tests/unit/forms/form/eventStreamConfig/routes.spec.js b/app/tests/unit/forms/form/eventStreamConfig/routes.spec.js new file mode 100644 index 000000000..e4400ee1e --- /dev/null +++ b/app/tests/unit/forms/form/eventStreamConfig/routes.spec.js @@ -0,0 +1,74 @@ +const request = require('supertest'); +const uuid = require('uuid'); + +const { expressHelper } = require('../../../../common/helper'); + +const jwtService = require('../../../../../src/components/jwtService'); +const apiAccess = require('../../../../../src/forms/auth/middleware/apiAccess'); +const userAccess = require('../../../../../src/forms/auth/middleware/userAccess'); +const validateParameter = require('../../../../../src/forms/common/middleware/validateParameter'); +const controller = require('../../../../../src/forms/form/eventStreamConfig/controller'); + +// +// Mock out all the middleware - we're testing that the routes are set up +// correctly, not the functionality of the middleware. +// + +jest.mock('../../../../../src/forms/auth/middleware/apiAccess'); +apiAccess.mockImplementation( + jest.fn((_req, _res, next) => { + next(); + }) +); + +jwtService.protect = jest.fn(() => { + return jest.fn((_req, _res, next) => { + next(); + }); +}); + +const hasFormPermissionsMock = jest.fn((_req, _res, next) => { + next(); +}); +userAccess.currentUser = jest.fn((_req, _res, next) => { + next(); +}); +userAccess.hasFormPermissions = jest.fn(() => { + return hasFormPermissionsMock; +}); + +validateParameter.validateFormId = jest.fn((_req, _res, next) => { + next(); +}); + +// +// Create the router and a simple Express server. +// + +const router = require('../../../../../src/forms/form/eventStreamConfig/routes'); +const basePath = '/form'; +const app = expressHelper(basePath, router); +const appRequest = request(app); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe(`${basePath}/:formId/eventStreamConfig`, () => { + const formId = uuid.v4(); + const path = `${basePath}/${formId}/eventStreamConfig`; + + it('should have correct middleware for GET', async () => { + controller.readEventStreamConfig = jest.fn((_req, res) => { + res.sendStatus(200); + }); + + await appRequest.get(path); + + expect(apiAccess).toBeCalledTimes(0); + expect(controller.readEventStreamConfig).toBeCalledTimes(1); + expect(hasFormPermissionsMock).toBeCalledTimes(1); + expect(userAccess.currentUser).toBeCalledTimes(1); + expect(validateParameter.validateFormId).toBeCalledTimes(1); + }); +}); diff --git a/app/tests/unit/forms/form/eventStreamConfig/service.spec.js b/app/tests/unit/forms/form/eventStreamConfig/service.spec.js new file mode 100644 index 000000000..01bd932e3 --- /dev/null +++ b/app/tests/unit/forms/form/eventStreamConfig/service.spec.js @@ -0,0 +1,161 @@ +const { MockModel, MockTransaction } = require('../../../../common/dbHelper'); + +const uuid = require('uuid'); + +const service = require('../../../../../src/forms/form/eventStreamConfig/service'); +const encryptionKeyService = require('../../../../../src/forms/form/encryptionKey/service'); + +jest.mock('../../../../../src/forms/common/models/tables/formEventStreamConfig', () => MockModel); +jest.mock('../../../../../src/forms/common/models/tables/formEncryptionKey', () => MockModel); + +const currentUser = { + usernameIdp: 'TESTER', +}; + +const formId = uuid.v4(); +const encryptionKeyId = uuid.v4(); + +const validData = { + id: uuid.v4(), + formId: formId, + enablePublicStream: true, + enablePrivateStream: true, + encryptionKeyId: encryptionKeyId, +}; + +beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('validate', () => { + it('should not throw errors with valid data', () => { + service.validateEventStreamConfig(validData); + }); + + it('should throw 422 with no data', () => { + expect(() => service.validateEventStreamConfig(undefined)).toThrow(); + }); +}); + +describe('readEventStreamConfig', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should return valid data', async () => { + MockModel.first = jest.fn().mockResolvedValueOnce(validData); + const res = await service.readEventStreamConfig(validData.formId, currentUser); + expect(MockModel.first).toBeCalledTimes(1); + expect(res).toEqual(validData); + }); +}); + +describe('upsert', () => { + beforeEach(() => { + MockModel.mockClear(); + MockModel.mockReset(); + MockTransaction.mockReset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should update valid data', async () => { + encryptionKeyService.upsertForEventStreamConfig = jest.fn().mockResolvedValueOnce({ id: encryptionKeyId }); + MockModel.first = jest.fn().mockResolvedValue(Object.assign({}, validData)); + let data = Object.assign({}, validData); + data.enablePublicStream = false; // make a change, force the update + await service.upsert(validData.formId, data, currentUser); + expect(MockModel.update).toBeCalledTimes(1); + expect(MockModel.update).toBeCalledWith({ + updatedBy: currentUser.usernameIdp, + ...data, + }); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); + + it('should create when not found', async () => { + encryptionKeyService.upsertForEventStreamConfig = jest.fn().mockResolvedValueOnce({ id: encryptionKeyId }); + MockModel.first = jest.fn().mockResolvedValueOnce(null); + service.initModel = jest.fn().mockReturnValue(validData); + await service.upsert(validData.formId, validData, currentUser); + expect(MockModel.insert).toBeCalledTimes(1); + expect(MockModel.insert).toBeCalledWith({ + createdBy: currentUser.usernameIdp, + ...validData, + }); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); + + it('should raise errors on failed update', async () => { + encryptionKeyService.upsertForEventStreamConfig = jest.fn().mockResolvedValueOnce({ id: encryptionKeyId }); + MockModel.first = jest.fn().mockResolvedValue(Object.assign({}, validData)); + MockModel.update = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + let data = Object.assign({}, validData); + data.enablePublicStream = false; // make a change, force the update + await expect(service.upsert(validData.formId, data, currentUser)).rejects.toThrow(); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); + + it('should raise errors on failed insert', async () => { + encryptionKeyService.upsertForEventStreamConfig = jest.fn().mockResolvedValueOnce({ id: encryptionKeyId }); + MockModel.first = jest.fn().mockResolvedValueOnce(null); + MockModel.insert = jest.fn().mockRejectedValueOnce(new Error('SQL Error')); + await expect(service.upsert(validData.formId, validData, currentUser)).rejects.toThrow(); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); + + it('should use provided transaction', async () => { + encryptionKeyService.upsertForEventStreamConfig = jest.fn().mockResolvedValueOnce({ id: encryptionKeyId }); + MockModel.first = jest.fn().mockResolvedValueOnce(null); + const xact = jest.fn().mockResolvedValue(MockTransaction); + await service.upsert(validData.formId, validData, currentUser, xact); + expect(MockModel.insert).toBeCalledTimes(1); + expect(MockModel.insert).toBeCalledWith({ + createdBy: currentUser.usernameIdp, + ...validData, + }); + expect(MockModel.startTransaction).toBeCalledTimes(0); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(0); + }); + + it('should remove encryption key when no private stream', async () => { + encryptionKeyService.upsertForEventStreamConfig = jest.fn().mockResolvedValueOnce({ id: encryptionKeyId }); + encryptionKeyService.remove = jest.fn().mockResolvedValueOnce(); + MockModel.first = jest.fn().mockResolvedValue(Object.assign({}, validData)); + let data = Object.assign({}, validData); + data.enablePrivateStream = false; // make a change, force the update + await service.upsert(validData.formId, data, currentUser); + // should remove the encryption key + data.encryptionKeyId = null; + expect(MockModel.update).toBeCalledTimes(1); + expect(MockModel.update).toBeCalledWith({ + updatedBy: currentUser.usernameIdp, + ...data, + }); + expect(MockModel.startTransaction).toBeCalledTimes(1); + expect(MockTransaction.rollback).toBeCalledTimes(0); + expect(MockTransaction.commit).toBeCalledTimes(1); + expect(encryptionKeyService.remove).toBeCalledTimes(1); + }); +}); diff --git a/app/tests/unit/forms/form/service.spec.js b/app/tests/unit/forms/form/service.spec.js index 9ae660f54..afe5440fa 100644 --- a/app/tests/unit/forms/form/service.spec.js +++ b/app/tests/unit/forms/form/service.spec.js @@ -9,11 +9,30 @@ jest.mock('../../../../src/forms/common/models/tables/documentTemplate', () => M jest.mock('../../../../src/forms/common/models/tables/formEmailTemplate', () => MockModel); jest.mock('../../../../src/forms/common/models/views/submissionMetadata', () => MockModel); +const { eventStreamService } = require('../../../../src/components/eventStreamService'); +const formMetadataService = require('../../../../src/forms/form/formMetadata/service'); +const eventStreamConfigService = require('../../../../src/forms/form/eventStreamConfig/service'); +const eventService = require('../../../../src/forms//event/eventService'); + +const { + Form, + FormIdentityProvider, + FormRoleUser, + FormStatusCode, + IdentityProvider, + FormVersionDraft, + FormVersion, + FormSubmission, + FormSubmissionUser, + FormSubmissionStatus, +} = require('../../../../src/forms/common/models'); + const documentTemplateId = uuid.v4(); const formId = uuid.v4(); const currentUser = { usernameIdp: 'TESTER', + id: uuid.v4(), }; const documentTemplate = { @@ -39,9 +58,82 @@ const emailTemplate = { type: EmailTypes.SUBMISSION_CONFIRMATION, }; +function resetModels() { + Form.query = jest.fn().mockReturnThis(); + Form.where = jest.fn().mockReturnThis(); + Form.modify = jest.fn().mockReturnThis(); + Form.first = jest.fn().mockReturnThis(); + Form.insert = jest.fn().mockReturnThis(); + Form.startTransaction = jest.fn().mockResolvedValue(MockTransaction); + Form.patchAndFetchById = jest.fn().mockReturnThis(); + + FormVersion.query = jest.fn().mockReturnThis(); + FormVersion.where = jest.fn().mockReturnThis(); + FormVersion.modify = jest.fn().mockReturnThis(); + FormVersion.first = jest.fn().mockReturnThis(); + FormVersion.insert = jest.fn().mockReturnThis(); + FormVersion.patch = jest.fn().mockReturnThis(); + FormVersion.findById = jest.fn().mockReturnThis(); + FormVersion.startTransaction = jest.fn().mockResolvedValue(MockTransaction); + + IdentityProvider.query = jest.fn().mockReturnThis(); + IdentityProvider.where = jest.fn().mockReturnThis(); + IdentityProvider.modify = jest.fn().mockReturnThis(); + IdentityProvider.first = jest.fn().mockReturnThis(); + IdentityProvider.insert = jest.fn().mockReturnThis(); + + FormIdentityProvider.query = jest.fn().mockReturnThis(); + FormIdentityProvider.where = jest.fn().mockReturnThis(); + FormIdentityProvider.modify = jest.fn().mockReturnThis(); + FormIdentityProvider.first = jest.fn().mockReturnThis(); + FormIdentityProvider.insert = jest.fn().mockReturnThis(); + FormIdentityProvider.delete = jest.fn().mockReturnThis(); + + FormRoleUser.query = jest.fn().mockReturnThis(); + FormRoleUser.where = jest.fn().mockReturnThis(); + FormRoleUser.modify = jest.fn().mockReturnThis(); + FormRoleUser.first = jest.fn().mockReturnThis(); + FormRoleUser.insert = jest.fn().mockReturnThis(); + + FormStatusCode.query = jest.fn().mockReturnThis(); + FormStatusCode.where = jest.fn().mockReturnThis(); + FormStatusCode.modify = jest.fn().mockReturnThis(); + FormStatusCode.first = jest.fn().mockReturnThis(); + FormStatusCode.insert = jest.fn().mockReturnThis(); + + FormVersionDraft.query = jest.fn().mockReturnThis(); + FormVersionDraft.where = jest.fn().mockReturnThis(); + FormVersionDraft.modify = jest.fn().mockReturnThis(); + FormVersionDraft.first = jest.fn().mockReturnThis(); + FormVersionDraft.insert = jest.fn().mockReturnThis(); + FormVersionDraft.startTransaction = jest.fn().mockResolvedValue(MockTransaction); + FormVersionDraft.deleteById = jest.fn().mockResolvedValue(MockTransaction); + + FormSubmission.query = jest.fn().mockReturnThis(); + FormSubmission.where = jest.fn().mockReturnThis(); + FormSubmission.modify = jest.fn().mockReturnThis(); + FormSubmission.first = jest.fn().mockReturnThis(); + FormSubmission.insert = jest.fn().mockReturnThis(); + FormSubmission.startTransaction = jest.fn().mockResolvedValue(MockTransaction); + FormSubmission.deleteById = jest.fn().mockResolvedValue(MockTransaction); + + FormSubmissionUser.query = jest.fn().mockReturnThis(); + FormSubmissionUser.where = jest.fn().mockReturnThis(); + FormSubmissionUser.modify = jest.fn().mockReturnThis(); + FormSubmissionUser.first = jest.fn().mockReturnThis(); + FormSubmissionUser.insert = jest.fn().mockReturnThis(); + + FormSubmissionStatus.query = jest.fn().mockReturnThis(); + FormSubmissionStatus.where = jest.fn().mockReturnThis(); + FormSubmissionStatus.modify = jest.fn().mockReturnThis(); + FormSubmissionStatus.first = jest.fn().mockReturnThis(); + FormSubmissionStatus.insert = jest.fn().mockReturnThis(); +} + beforeEach(() => { MockModel.mockReset(); MockTransaction.mockReset(); + resetModels(); }); afterEach(() => { @@ -816,3 +908,136 @@ describe('createOrUpdateEmailTemplates', () => { expect(MockTransaction.rollback).toBeCalledTimes(1); }); }); + +describe('createForm', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + resetModels(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should upsert event stream configuration and form metadata', async () => { + service.validateScheduleObject = jest.fn().mockReturnValueOnce({ status: 'success' }); + service.readForm = jest.fn().mockReturnValueOnce({}); + formMetadataService.upsert = jest.fn().mockResolvedValueOnce(); + eventStreamConfigService.upsert = jest.fn().mockResolvedValueOnce(); + + const data = { identityProviders: [{ code: 'test' }] }; + await service.createForm(data, currentUser); + + expect(formMetadataService.upsert).toBeCalledTimes(1); + expect(eventStreamConfigService.upsert).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); +}); + +describe('updateForm', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + resetModels(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should upsert event stream configuration and form metadata', async () => { + service.validateScheduleObject = jest.fn().mockReturnValueOnce({ status: 'success' }); + service.readForm = jest.fn().mockReturnValueOnce({}); + formMetadataService.upsert = jest.fn().mockResolvedValueOnce(); + eventStreamConfigService.upsert = jest.fn().mockResolvedValueOnce(); + + const data = { identityProviders: [{ code: 'test' }] }; + await service.updateForm(formId, data, currentUser); + + expect(formMetadataService.upsert).toBeCalledTimes(1); + expect(eventStreamConfigService.upsert).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); +}); + +describe('publishVersion', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + resetModels(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should trigger event notifications', async () => { + service.validateScheduleObject = jest.fn().mockReturnValueOnce({ status: 'success' }); + service.readForm = jest.fn().mockReturnValueOnce({}); + service.readPublishedForm = jest.fn().mockReturnValueOnce({}); + eventService.publishFormEvent = jest.fn().mockResolvedValueOnce(); + eventStreamService.onPublish = jest.fn().mockResolvedValueOnce(); + + await service.publishVersion(formId, '123', {}, currentUser); + + expect(eventService.publishFormEvent).toBeCalledTimes(1); + expect(eventStreamService.onPublish).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); +}); + +describe('publishDraft', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + resetModels(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should trigger event notifications', async () => { + service.validateScheduleObject = jest.fn().mockReturnValueOnce({ status: 'success' }); + service.readForm = jest.fn().mockReturnValueOnce({ id: formId, versions: [{ version: 1 }] }); + service.readDraft = jest.fn().mockReturnValueOnce({}); + service.readVersion = jest.fn().mockReturnValueOnce({}); + eventService.publishFormEvent = jest.fn().mockResolvedValueOnce(); + eventStreamService.onPublish = jest.fn().mockResolvedValueOnce(); + + await service.publishDraft(formId, '123', currentUser); + + expect(eventService.publishFormEvent).toBeCalledTimes(1); + expect(eventStreamService.onPublish).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); +}); + +describe('createSubmission', () => { + beforeEach(() => { + MockModel.mockReset(); + MockTransaction.mockReset(); + resetModels(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should trigger event notifications', async () => { + service.validateScheduleObject = jest.fn().mockReturnValueOnce({ status: 'success' }); + service.readForm = jest.fn().mockReturnValueOnce({ id: formId, versions: [{ version: 1 }], identityProviders: [] }); + service.readSubmission = jest.fn().mockReturnValueOnce({}); + service.readVersion = jest.fn().mockReturnValueOnce({ id: '123', formId: formId, schema: {} }); + eventService.formSubmissionEventReceived = jest.fn().mockReturnValueOnce(); + eventStreamService.onSubmit = jest.fn().mockResolvedValueOnce(); + + const data = { draft: false, submission: { data: {} } }; + await service.createSubmission('123', data, currentUser); + + expect(eventService.formSubmissionEventReceived).toBeCalledTimes(1); + expect(eventStreamService.onSubmit).toBeCalledTimes(1); + expect(MockTransaction.commit).toBeCalledTimes(1); + }); +}); diff --git a/event-stream-service/.gitignore b/event-stream-service/.gitignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/event-stream-service/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/event-stream-service/README.md b/event-stream-service/README.md new file mode 100644 index 000000000..cbe74d929 --- /dev/null +++ b/event-stream-service/README.md @@ -0,0 +1,12 @@ +**not official documentation yet - just rough notes** + +To use `natsbox` in the devcontainer: + +- terminal into `natsbox` +- run `nats context add nats --server nats://n1:4222 --description "N1" --select`. + +This will allow you to run `nats` command line commands against the local nats server. + +Get information about the `CHEFS` stream: + +- run `nats stream info CHEFS` diff --git a/event-stream-service/charts/event-stream-service/.helmignore b/event-stream-service/charts/event-stream-service/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/event-stream-service/charts/event-stream-service/Chart.lock b/event-stream-service/charts/event-stream-service/Chart.lock new file mode 100644 index 000000000..2fc691a77 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: common + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.22.0 +- name: nats + repository: https://nats-io.github.io/k8s/helm/charts/ + version: 1.2.6 +digest: sha256:bc1f650dd08952cb93d8584e991562b99ac7b688db94f0b21ccabc629ff768d5 +generated: "2024-11-27T10:49:45.816335-08:00" diff --git a/event-stream-service/charts/event-stream-service/Chart.yaml b/event-stream-service/charts/event-stream-service/Chart.yaml new file mode 100644 index 000000000..d3bf2c309 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/Chart.yaml @@ -0,0 +1,34 @@ +apiVersion: v2 +name: event-stream-service +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +dependencies: + - name: common + version: 2.22.0 + repository: oci://registry-1.docker.io/bitnamicharts + alias: common + - name: nats + repository: https://nats-io.github.io/k8s/helm/charts/ + version: 1.2.6 + alias: nats diff --git a/event-stream-service/charts/event-stream-service/README.md b/event-stream-service/charts/event-stream-service/README.md new file mode 100644 index 000000000..b22dd5fe7 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/README.md @@ -0,0 +1,55 @@ +# Event Stream Service Installation + +This current documentation will be very simple and make assumptions: + +- you are installing on BC Gov Openshift. +- you have installed `oc` command line tools on your workstation that work on BC Gov Openshift. +- you know how to get your Openshift token for running your `oc` command line tools. +- you have installed [helm](https://helm.sh) on your workstation. +- you have a basic understanding of helm and values files overrides. +- you have access/permissions in your Openshift namespace to run `helm` and `oc` commands. + +**September 3, 2024** - we are using the `a191b5` namespaces to host the proof of concepts. These spaces have minimal resources (we can request more as we learn about the requirements). + +## Basic Instructions + +1. get your Openshift token +2. use oc login to your namespace +3. navigate to the `/event-stream-service` +4. run the `helm` install / upgrade command + +``` +oc login --token=sha256~yk5BCjn0syJV0qXEyPk12s09v-RIdmTeLVdQmQrQEBc --server=https://api.silver.devops.gov.bc.ca:6443 +helm upgrade --install event-stream-service ./charts/event-stream-service -f ./charts/event-stream-service/values.yaml +``` + +To set up a CHEFS instance to use this installation of Event Stream Service, you will need to know the server name and you will need the generated secret for the `chefs` account. + +Find the `ess-nginx-route` and note the location. The Event Stream Service server will be the host (so no `https://` and no path). +Find the `ess-nats-auth` secret and copy the value for `chefs_pwd`. + +### To remove + +1. get your Openshift token +2. use oc login to your namespace +3. run the `helm` uninstall command +4. if wanting to do a clean install later, then run the `oc delete pvc` command to remove the persistent storage +5. if permanently deleting, then run the `oc delete secret` command to remove the secret + +``` +oc login --token=sha256~yk5BCjn0syJV0qXEyPk12s09v-RIdmTeLVdQmQrQEBc --server=https://api.silver.devops.gov.bc.ca:6443 +helm uninstall event-stream-service +oc delete pvc -l 'app.kubernetes.io/instance=event-stream-service' +oc delete secret -l 'app.kubernetes.io/instance=event-stream-service' +``` + +## Future + +We will need to create different param override (values) files for each instance. Each namespace and instance will have different resource allocation that we need to tune. +You can specify the '--values'/'-f' flag multiple times. The priority will be given to the last (right-most) file specified. + +``` +helm upgrade --install event-stream-service ./charts/event-stream-service -f ./charts/event-stream-service/values.yaml -f ./charts/event-stream-service/values-prod.yaml +``` + +This would apply our default values file (`values.yaml`) with any overrides found in `values-prod.yaml` taking priority. diff --git a/event-stream-service/charts/event-stream-service/charts/common-2.22.0.tgz b/event-stream-service/charts/event-stream-service/charts/common-2.22.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..17f5674c04b9dbef078ea4223b663f88ba8e08f3 GIT binary patch literal 16228 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 OKmQ*kMarxIk^unXfVsB- literal 0 HcmV?d00001 diff --git a/event-stream-service/charts/event-stream-service/charts/nats-1.2.6.tgz b/event-stream-service/charts/event-stream-service/charts/nats-1.2.6.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5e57f295dda27502d14197d2b349534653e9e8b2 GIT binary patch literal 19633 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 literal 0 HcmV?d00001 diff --git a/event-stream-service/charts/event-stream-service/templates/NOTES.txt b/event-stream-service/charts/event-stream-service/templates/NOTES.txt new file mode 100644 index 000000000..ffd55e71d --- /dev/null +++ b/event-stream-service/charts/event-stream-service/templates/NOTES.txt @@ -0,0 +1,5 @@ +You have deployed the following: +Release: {{ include "common.names.fullname" . }} +Namespace: {{ include "common.names.namespace" . | quote}} + + diff --git a/event-stream-service/charts/event-stream-service/templates/_helpers.tpl b/event-stream-service/charts/event-stream-service/templates/_helpers.tpl new file mode 100644 index 000000000..6c63f89fc --- /dev/null +++ b/event-stream-service/charts/event-stream-service/templates/_helpers.tpl @@ -0,0 +1,7 @@ +{{- define "nats.openshift.route.tls" -}} +{{- if (.Values.ess.nats.route.tls.enabled) -}} +tls: + insecureEdgeTerminationPolicy: {{ .Values.ess.nats.route.tls.insecureEdgeTerminationPolicy }} + termination: {{ .Values.ess.nats.route.tls.termination }} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/event-stream-service/charts/event-stream-service/templates/nats-route.yaml b/event-stream-service/charts/event-stream-service/templates/nats-route.yaml new file mode 100644 index 000000000..6487c9a2b --- /dev/null +++ b/event-stream-service/charts/event-stream-service/templates/nats-route.yaml @@ -0,0 +1,22 @@ +{{- if .Values.ess.nats.route.enabled -}} +{{- $routeName := (.Values.ess.nats.route.name) }} +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: {{ $routeName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + annotations: + haproxy.router.openshift.io/timeout: {{ .Values.ess.nats.route.timeout }} +spec: + host: {{ .Values.ess.nats.route.ingressPrefix }}{{ .Values.ess.nats.route.ingressSuffix }} + path: {{ .Values.ess.nats.route.path }} + to: + kind: Service + name: {{ .Values.ess.nats.route.serviceName }} + weight: 100 + port: + targetPort: {{ .Values.ess.nats.route.targetPort }} + wildcardPolicy: {{ .Values.ess.nats.route.wildcardPolicy }} +{{ include "nats.openshift.route.tls" . | indent 2}} +{{- end -}} \ No newline at end of file diff --git a/event-stream-service/charts/event-stream-service/templates/nats-secrets.yaml b/event-stream-service/charts/event-stream-service/templates/nats-secrets.yaml new file mode 100644 index 000000000..a68b81f20 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/templates/nats-secrets.yaml @@ -0,0 +1,19 @@ +{{- $secretName := (.Values.ess.nats.secret.name) }} +{{- $sysadminPwdValue := randAlphaNum 32 | b64enc }} +{{- $adminPwdValue := randAlphaNum 32 | b64enc }} +{{- $chefsPwdValue := randAlphaNum 32 | b64enc }} +{{- $chefsconsumerPwdValue := randAlphaNum 32 | b64enc }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + annotations: + helm.sh/resource-policy: keep +data: + sysadmin_pwd: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "sysadmin_pwd" "defaultValue" $sysadminPwdValue "context" $) }} + admin_pwd: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "admin_pwd" "defaultValue" $adminPwdValue "context" $) }} + chefs_pwd: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "chefs_pwd" "defaultValue" $chefsPwdValue "context" $) }} + chefsconsumer_pwd: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "chefsconsumer_pwd" "defaultValue" $chefsconsumerPwdValue "context" $) }} + \ No newline at end of file diff --git a/event-stream-service/charts/event-stream-service/values-dev.yaml b/event-stream-service/charts/event-stream-service/values-dev.yaml new file mode 100644 index 000000000..d60a70b07 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/values-dev.yaml @@ -0,0 +1,13 @@ +ess: + nats: + route: + enabled: true + ingressPrefix: stream-dev + ingressSuffix: .apps.silver.devops.gov.bc.ca + +nats: + config: + jetstream: + fileStore: + pvc: + size: 1Gi diff --git a/event-stream-service/charts/event-stream-service/values-prod.yaml b/event-stream-service/charts/event-stream-service/values-prod.yaml new file mode 100644 index 000000000..a81eaa19a --- /dev/null +++ b/event-stream-service/charts/event-stream-service/values-prod.yaml @@ -0,0 +1,22 @@ +ess: + nats: + route: + enabled: false + ingressPrefix: stream + ingressSuffix: .apps.silver.devops.gov.bc.ca + +nats: + config: + jetstream: + fileStore: + pvc: + size: 1Gi + container: + merge: + resources: + limits: + cpu: 250m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi diff --git a/event-stream-service/charts/event-stream-service/values-test.yaml b/event-stream-service/charts/event-stream-service/values-test.yaml new file mode 100644 index 000000000..c8ca34361 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/values-test.yaml @@ -0,0 +1,17 @@ +ess: + nats: + route: + enabled: true + ingressPrefix: stream-test + ingressSuffix: .apps.silver.devops.gov.bc.ca + +nats: + container: + merge: + resources: + limits: + cpu: 20m + memory: 256Mi + requests: + cpu: 10m + memory: 32Mi diff --git a/event-stream-service/charts/event-stream-service/values.yaml b/event-stream-service/charts/event-stream-service/values.yaml new file mode 100644 index 000000000..7297c15f9 --- /dev/null +++ b/event-stream-service/charts/event-stream-service/values.yaml @@ -0,0 +1,254 @@ +ess: + nats: + secret: + name: ess-nats-auth + certSecret: tls-stream-secret + route: + name: ess-route + enabled: false + ingressPrefix: stream-dev + ingressSuffix: .apps.silver.devops.gov.bc.ca + path: "" + serviceName: ess-nats + targetPort: websocket + timeout: 2m + tls: + enabled: true + insecureEdgeTerminationPolicy: None + termination: edge + wildcardPolicy: None + +nats: + fullnameOverride: ess-nats + config: + cluster: + enabled: true + replicas: 3 + jetstream: + enabled: true + fileStore: + enabled: true + pvc: + enabled: true + storageClassName: netapp-block-standard + size: 250Mi + websocket: + enabled: true + port: 8888 + merge: + debug: false + trace: false + accounts: + $SYS: + users: + - user: "sysadmin" + password: << $SYSADMIN_PWD >> + authorization: + default_permissions: + publish: + [ + "SANDBOX.*", + "$JS.API.INFO", + "$JS.API.CONSUMER.CREATE.*", + "$JS.API.CONSUMER.CREATE.*.>", + "$JS.API.CONSUMER.DURABLE.CREATE.*.>", + "$JS.API.CONSUMER.DELETE.*.>", + "$JS.API.CONSUMER.INFO.*.>", + "$JS.API.CONSUMER.LIST.*", + "$JS.API.CONSUMER.NAMES.*", + "$JS.API.CONSUMER.MSG.NEXT.*.>", + "$JS.API.CONSUMER.MSG.NEXT.*.NEW", + "$JS.API.STREAM.MSG.GET.*", + "$JS.API.STREAM.INFO.*", + "$JS.API.STREAM.LIST", + "$JS.API.STREAM.NAMES", + "$JS.ACK.*", + "$JS.ACK.*.>", + ] + subscribe: ["PUBLIC.>", "PRIVATE.>", "_INBOX.>"] + users: + - user: "chefsConsumer" + password: << $CHEFSCONSUMER_PWD >> + permissions: + publish: + [ + "SANDBOX.*", + "$JS.API.INFO", + "$JS.API.CONSUMER.CREATE.CHEFS", + "$JS.API.CONSUMER.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DURABLE.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DELETE.CHEFS.>", + "$JS.API.CONSUMER.INFO.CHEFS.>", + "$JS.API.CONSUMER.LIST.CHEFS", + "$JS.API.CONSUMER.NAMES.CHEFS", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.>", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.NEW", + "$JS.API.STREAM.MSG.GET.CHEFS", + "$JS.API.STREAM.INFO.CHEFS", + "$JS.API.STREAM.LIST", + "$JS.API.STREAM.NAMES", + "$JS.ACK.CHEFS", + "$JS.ACK.CHEFS.>", + ] + subscribe: ["PUBLIC.forms.>", "PRIVATE.forms.>", "_INBOX.>"] + - user: "admin" + password: << $ADMIN_PWD >> + permissions: + publish: [">"] + subscribe: [">"] + - user: "chefs" + password: << $CHEFS_PWD >> + permissions: + publish: + [ + "$JS.API.INFO", + "$JS.API.STREAM.CREATE.CHEFS", + "$JS.API.STREAM.UPDATE.CHEFS", + "$JS.API.STREAM.DELETE.CHEFS", + "$JS.API.STREAM.INFO.CHEFS", + "$JS.API.STREAM.PURGE.CHEFS", + "$JS.API.STREAM.LIST", + "$JS.API.STREAM.NAMES", + "$JS.API.STREAM.MSG.DELETE.CHEFS", + "$JS.API.STREAM.MSG.GET.CHEFS", + "$JS.API.STREAM.SNAPSHOT.CHEFS", + "$JS.API.STREAM.RESTORE.CHEFS", + "$JS.API.CONSUMER.CREATE.CHEFS", + "$JS.API.CONSUMER.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DURABLE.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DELETE.CHEFS.>", + "$JS.API.CONSUMER.INFO.CHEFS.>", + "$JS.API.CONSUMER.LIST.CHEFS", + "$JS.API.CONSUMER.NAMES.CHEFS", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.>", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.NEW", + "$JS.API.STREAM.MSG.GET.CHEFS", + "$JS.ACK.CHEFS.>", + "PUBLIC.forms.>", + "PRIVATE.forms.>", + ] + subscribe: ["_INBOX.>"] + container: + env: + SYSADMIN_PWD: + valueFrom: + secretKeyRef: + name: ess-nats-auth + key: sysadmin_pwd + ADMIN_PWD: + valueFrom: + secretKeyRef: + name: ess-nats-auth + key: admin_pwd + CHEFS_PWD: + valueFrom: + secretKeyRef: + name: ess-nats-auth + key: chefs_pwd + CHEFSCONSUMER_PWD: + valueFrom: + secretKeyRef: + name: ess-nats-auth + key: chefsconsumer_pwd + merge: + # Each pod combines these resources with the reloader (2 containers, 1 pod) + # so add the below to the reloader for total resources per pod. + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 10m + memory: 32Mi + reloader: + merge: + resources: + limits: + cpu: 20m + memory: 12Mi + requests: + cpu: 10m + memory: 8Mi + service: + ports: + nats: + enabled: true + leafnodes: + enabled: false + websocket: + enabled: true + mqtt: + enabled: false + cluster: + enabled: true + gateway: + enabled: false + monitor: + enabled: false + profiling: + enabled: false + podTemplate: + topologySpreadConstraints: + kubernetes.io/hostname: + maxSkew: 1 + whenUnsatisfiable: DoNotSchedule + natsBox: + enabled: false + container: + image: + tag: nonroot + merge: + resources: + limits: + cpu: 50m + memory: 128Mi + requests: + cpu: 25m + memory: 64Mi + +nginx: + fullnameOverride: ess-nginx + readinessProbe: + enabled: true + path: /health + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + service: + type: ClusterIP + ports: + http: 8080 + https: 8443 + extraPorts: + tls: + enabled: true + serverBlock: |- + server { + listen 8080; + listen [::]:8080; + server_name localhost; + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + + location / { + proxy_pass http://ess-nats:8888; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /health { + default_type application/json; + return 200 '{"message": "healthy"}'; + } + + } diff --git a/event-stream-service/config/jetstream.conf b/event-stream-service/config/jetstream.conf new file mode 100644 index 000000000..b58d1de5b --- /dev/null +++ b/event-stream-service/config/jetstream.conf @@ -0,0 +1,121 @@ +debug: true +trace: false + +# Each server can connect to clients on the internal port 4222 +# (mapped to external ports in our docker-compose) +port: 4222 + +# Persistent JetStream data store +jetstream: { + # Each server persists messages within the docker container + # at /data/nats-server (mounted as ./persistent-data/server-n… + # in our docker-compose) + store_dir: "/data/nats-server/" +} + +# Cluster formation +cluster: { + name: "JSC" + listen: "0.0.0.0:4245" + + # Servers can connect to one another at + # the following routes + routes: [ + "nats://n1:4245" + "nats://n2:4245" + "nats://n3:4245" + ] + +} + +authorization: { + default_permissions = { + publish = ["SANDBOX.*", + "$JS.API.INFO", + "$JS.API.CONSUMER.CREATE.*", + "$JS.API.CONSUMER.CREATE.*.>", + "$JS.API.CONSUMER.DURABLE.CREATE.*.>", + "$JS.API.CONSUMER.DELETE.*.>", + "$JS.API.CONSUMER.INFO.*.>", + "$JS.API.CONSUMER.LIST.*", + "$JS.API.CONSUMER.NAMES.*", + "$JS.API.CONSUMER.MSG.NEXT.*.>", + "$JS.API.CONSUMER.MSG.NEXT.*.NEW", + "$JS.API.STREAM.MSG.GET.*", + "$JS.API.STREAM.INFO.*", + "$JS.API.STREAM.LIST", + "$JS.API.STREAM.NAMES", + "$JS.ACK.*", + "$JS.ACK.*.>"] + subscribe = [ + "PUBLIC.>", + "PRIVATE.>", + "_INBOX.>"] + } + ADMIN = { + publish = ">" + subscribe = ">" + } + CHEFS = { + publish = [ + "$JS.API.INFO", + "$JS.API.STREAM.CREATE.CHEFS", + "$JS.API.STREAM.UPDATE.CHEFS", + "$JS.API.STREAM.DELETE.CHEFS", + "$JS.API.STREAM.INFO.CHEFS", + "$JS.API.STREAM.PURGE.CHEFS", + "$JS.API.STREAM.LIST", + "$JS.API.STREAM.NAMES", + "$JS.API.STREAM.MSG.DELETE.CHEFS", + "$JS.API.STREAM.MSG.GET.CHEFS", + "$JS.API.STREAM.SNAPSHOT.CHEFS", + "$JS.API.STREAM.RESTORE.CHEFS", + + + "$JS.API.CONSUMER.CREATE.CHEFS", + "$JS.API.CONSUMER.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DURABLE.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DELETE.CHEFS.>", + "$JS.API.CONSUMER.INFO.CHEFS.>", + "$JS.API.CONSUMER.LIST.CHEFS", + "$JS.API.CONSUMER.NAMES.CHEFS", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.>", + + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.NEW", + "$JS.API.STREAM.MSG.GET.CHEFS", + + "$JS.ACK.CHEFS.>" + + "PUBLIC.forms.>", + "PRIVATE.forms.>"] + subscribe = "_INBOX.>" + } + CHEFS_CONSUMER = { + publish = ["SANDBOX.*", + "$JS.API.INFO", + "$JS.API.CONSUMER.CREATE.CHEFS", + "$JS.API.CONSUMER.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DURABLE.CREATE.CHEFS.>", + "$JS.API.CONSUMER.DELETE.CHEFS.>", + "$JS.API.CONSUMER.INFO.CHEFS.>", + "$JS.API.CONSUMER.LIST.CHEFS", + "$JS.API.CONSUMER.NAMES.CHEFS", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.>", + "$JS.API.CONSUMER.MSG.NEXT.CHEFS.NEW", + "$JS.API.STREAM.MSG.GET.CHEFS", + "$JS.API.STREAM.INFO.CHEFS", + "$JS.API.STREAM.LIST", + "$JS.API.STREAM.NAMES", + "$JS.ACK.CHEFS", + "$JS.ACK.CHEFS.>"] + subscribe = [ + "PUBLIC.forms.>", + "PRIVATE.forms.>", + "_INBOX.>"] + } + users = [ + {user: admin, password: password, permissions: $ADMIN} + {user: chefs, password: password, permissions: $CHEFS} + {user: chefsConsumer, password: password, permissions: $CHEFS_CONSUMER} + ] +} \ No newline at end of file diff --git a/event-stream-service/consumer.js b/event-stream-service/consumer.js new file mode 100644 index 000000000..5c1b4011e --- /dev/null +++ b/event-stream-service/consumer.js @@ -0,0 +1,112 @@ +const { AckPolicy, connect, millis } = require("nats"); +// connection info +const servers = ["localhost:4222", "localhost:4223", "localhost:4224"]; + +let nc = undefined; // nats connection +let js = undefined; // jet stream +let jsm = undefined; // jet stream manager + +// stream info +const name = "CHEFS"; + +const printMsg = (m) => { + try { + const ts = new Date(m.info.timestampNanos / 1000000).toISOString(); + console.log( + `msg seq: ${m.seq}, subject: ${m.subject}, timestamp: ${ts}, streamSequence: ${m.info.streamSequence}, deliverySequence: ${m.info.deliverySequence}` + ); + console.log(JSON.stringify(m.json(), null, 2)); + } catch (e) { + console.error(`Error printing message: ${e.message}`); + } +}; + +const consume = async () => { + console.log(`connect to nats server(s) ${servers} as 'anonymous'...`); + nc = await connect({ + servers: servers, + }); + + console.log("access jetstream..."); + js = nc.jetstream(); + console.log("get jetstream manager..."); + jsm = await js.jetstreamManager(); + + // The new consumer API is a pull consumer + // Let's create an ephemeral consumer. An ephemeral consumer + // will be reaped by the server when inactive for some time + console.log( + `\nadd ephemeral consumer of stream '${name}' to jetstream manager...` + ); + let ci = await jsm.consumers.add(name, { ack_policy: AckPolicy.None }); + console.log(`get consumer by name '${ci.name}'...`); + const c = await js.consumers.get(name, ci.name); + console.log( + "ephemeral consumer will live until inactivity of ", + millis((await c.info(true)).config.inactive_threshold), + "millis" + ); + + // you can retrieve messages one at time with next(): + console.log("retrieve messages by calling next on consumer..."); + let m = await c.next(); + if (!m) { + // no messages, publisher may have cleared the stream... + console.log("No messages, publisher may have cleared the stream..."); + console.log("drain nats connection..."); + await nc.drain(); + return; + } + printMsg(m); + + m = await c.next(); + printMsg(m); + + m = await c.next(); + printMsg(m); + + // Let's create another consumer, this time well use fetch + // we'll make this a durable + console.log("\nadd consumer with durable_name 'A'"); + await jsm.consumers.add(name, { + ack_policy: AckPolicy.Explicit, + durable_name: "A", + }); + console.log("get consumer by name 'A'..."); + const c2 = await js.consumers.get(name, "A"); + console.log("fetch 3 messages (max)..."); + let iter = await c2.fetch({ max_messages: 3 }); + console.log("iterate over results and ack each one..."); + for await (const m of iter) { + printMsg(m); + m.ack(); + } + // if you know you don't need to save the state of the consumer, you can + // delete it: + console.log("delete consumer..."); + await c2.delete(); + + // Lastly we'll create another one but this time use consume + // this consumer will be an ordered consumer - this one is an ephemeral + // that guarantees that messages are delivered in order + // These have a special shortcut, we only need the name of the stream + // the underlying consumer is managed under the covers + console.log("\nget ordered consumer (ephemeral)..."); + const c3 = await js.consumers.get(name); + console.log("consume 3 messages (max)..."); + iter = await c3.consume({ max_messages: 3 }); + for await (const m of iter) { + printMsg(m); + // if we don't break, consume would keep waiting for messages + // we know when we have seen all messages when no more are pending + if (m.info.pending === 0) { + console.log("no more messages, break."); + break; + } + } + + console.log("drain nats connection..."); + await nc.drain(); +}; + +consume(); diff --git a/event-stream-service/docker-compose.yml b/event-stream-service/docker-compose.yml new file mode 100644 index 000000000..f9e8af2a5 --- /dev/null +++ b/event-stream-service/docker-compose.yml @@ -0,0 +1,65 @@ +services: + n1: + container_name: n1 + image: nats:2.10.12 + entrypoint: /nats-server + command: "--config /config/jetstream.conf --server_name S1" + networks: + - nats + ports: + - 4222:4222 + volumes: + - ./config:/config + - n1-data:/data/nats-server/jetstream + + n2: + container_name: n2 + image: nats:2.10.12 + entrypoint: /nats-server + command: "--config /config/jetstream.conf --server_name S2" + networks: + - nats + ports: + - 4223:4222 + volumes: + - ./config:/config + - n2-data:/data/nats-server/jetstream + + n3: + container_name: n3 + image: nats:2.10.12 + entrypoint: /nats-server + command: "--config /config/jetstream.conf --server_name S3" + networks: + - nats + ports: + - 4224:4222 + volumes: + - ./config:/config + - n3-data:/data/nats-server/jetstream + + natsbox: + container_name: natsbox + image: natsio/nats-box:latest + tty: true + stdin_open: true + command: sh + networks: + - nats + +networks: + nats: + driver: bridge + ipam: + driver: default + config: + - subnet: "192.168.0.0/24" + gateway: "192.168.0.1" + +volumes: + n1-data: + driver: local + n2-data: + driver: local + n3-data: + driver: local diff --git a/event-stream-service/package-lock.json b/event-stream-service/package-lock.json new file mode 100644 index 000000000..3f951c27c --- /dev/null +++ b/event-stream-service/package-lock.json @@ -0,0 +1,406 @@ +{ + "name": "event-stream-service-demos", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "event-stream-service-demos", + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "axios": "^1.7.7", + "command-line-args": "^6.0.1", + "cryptr": "^6.3.0", + "falsey": "^1.0.0", + "nats": "^2.28.0", + "nats.ws": "^1.29.2", + "uuid": "^10.0.0", + "websocket": "^1.0.35" + } + }, + "node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-line-args": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.2.0" + }, + "engines": { + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/cryptr": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cryptr/-/cryptr-6.3.0.tgz", + "integrity": "sha512-TA4byAuorT8qooU9H8YJhBwnqD151i1rcauHfJ3Divg6HmukHB2AYMp0hmjv2873J2alr4t15QqC7zAnWFrtfQ==" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/falsey": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/falsey/-/falsey-1.0.0.tgz", + "integrity": "sha512-zMDNZ/Ipd8MY0+346CPvhzP1AsiVyNfTOayJza4reAIWf72xbkuFUDcJNxSAsQE1b9Bu0wijKb8Ngnh/a7fI5w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/nats": { + "version": "2.28.2", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.28.2.tgz", + "integrity": "sha512-02cvR8EPach+0BfVaQjPgsbPFn6uMjEQAuvXS2ppg8jiWEm2KYdfmeFmtshiU9b2+kFh3LSEKMEaIfRgk3K8tw==", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nats.ws": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/nats.ws/-/nats.ws-1.29.2.tgz", + "integrity": "sha512-dJf7aWp+5+8LwWEhgoTMc3pvfz5JlhA0yWtXKcTMDxUe43mHvgpvDaPnLyHQNL2LoDpdkjgOG176i5IeHBDlqg==", + "optionalDependencies": { + "nkeys.js": "1.1.0" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + } + } +} diff --git a/event-stream-service/package.json b/event-stream-service/package.json new file mode 100644 index 000000000..c5abc0257 --- /dev/null +++ b/event-stream-service/package.json @@ -0,0 +1,17 @@ +{ + "name": "event-stream-service-demos", + "version": "0.0.0", + "private": true, + "license": "Apache-2.0", + "scripts": {}, + "dependencies": { + "axios": "^1.7.7", + "command-line-args": "^6.0.1", + "cryptr": "^6.3.0", + "falsey": "^1.0.0", + "nats": "^2.28.0", + "nats.ws": "^1.29.2", + "uuid": "^10.0.0", + "websocket": "^1.0.35" + } +} diff --git a/event-stream-service/publisher.js b/event-stream-service/publisher.js new file mode 100644 index 000000000..88baf4d10 --- /dev/null +++ b/event-stream-service/publisher.js @@ -0,0 +1,75 @@ +const { connect } = require("nats"); + +// connection info +const servers = ["localhost:4222", "localhost:4223", "localhost:4224"]; +const username = "chefs"; +const password = "password"; + +let nc = undefined; // nats connection +let js = undefined; // jet stream +let jsm = undefined; // jet stream manager + +// stream info +const name = "CHEFS"; +const subj = "PUBLIC.forms"; +const privatesubj = "PRIVATE.forms"; + +const publish = async () => { + console.log(`connect to nats server(s) ${servers} as '${username}'...`); + nc = await connect({ + servers: servers, + user: username, + pass: password, + }); + + console.log("access jetstream..."); + js = nc.jetstream(); + console.log("get jetstream manager..."); + jsm = await js.jetstreamManager(); + + console.log(`add stream '${name}' to jetstream manager...`); + + await jsm.streams.add({ + name, + subjects: [`${subj}.>`, `${privatesubj}.>`], + }); + + console.log("publish 3 messages..."); + let ack = await js.publish( + `${subj}.1`, + JSON.stringify({ meta: { id: 1 }, payload: { data: "one" } }) + ); + console.log(`ack stream: ${ack.stream}, seq: ${ack.seq}`); + ack = await js.publish( + `${subj}.2`, + JSON.stringify({ meta: { id: 2 }, payload: { data: "two" } }) + ); + console.log(`ack stream: ${ack.stream}, seq: ${ack.seq}`); + ack = await js.publish( + `${subj}.3`, + JSON.stringify({ meta: { id: 3 }, payload: { data: "three" } }) + ); + console.log(`ack stream: ${ack.stream}, seq: ${ack.seq}`); + + // find a stream that stores a specific subject: + const sname = await jsm.streams.find(`${subj}.1`); + // retrieve info about the stream by its name + const si = await jsm.streams.info(sname); + console.log( + `stream messages: ${si.state.messages}, first_seq: ${si.state.first_seq}, last_seq: ${si.state.last_seq}, last_ts: ${si.state.last_ts}` + ); + + console.log("CTRL-C to close."); +}; + +publish(); + +const close = async () => { + console.log(`\npurge messages from '${name}' stream`); + await jsm.streams.purge(name); + console.log("drain nats connection..."); + await nc.drain(); + console.log("done."); +}; + +process.on("SIGINT", close); diff --git a/event-stream-service/pullConsumer.js b/event-stream-service/pullConsumer.js new file mode 100644 index 000000000..83d64ae3b --- /dev/null +++ b/event-stream-service/pullConsumer.js @@ -0,0 +1,176 @@ +const { AckPolicy, JSONCodec } = require("nats"); +const Cryptr = require("cryptr"); +const falsey = require("falsey"); +const { v4: uuidv4 } = require("uuid"); +/* + command line pass in environment variables for: + SERVERS - which nats instance to connect to (default is local from docker-compose) + DURABLE_NAME - name for the consumer, makes a persistant/durable consumer that will remain listening through periods of inactivity (default is generated uuid) + WEBSOCKET - connect via nats protocol or websockets. true/false (default false, connect via nats) + ENCRYPTION_KEY - what encryption key to decrypt (Cryptr - aes-256-gcm) private payloads + SOURCE - filter messages based on meta.source (default is none) + USERNAME/PASSWORD - user and pass to connect to the stream (default is chefsConsumer/password) + Examples: + SERVERS=stream-dev.apps.silver.devops.gov.bc.ca WEBSOCKETS=true DURABLE_NAME=pullConsumer ENCRYPTION_KEY=ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985 SOURCE=pr-1444 node pullConsumer.js + SERVERS=stream-dev.apps.silver.devops.gov.bc.ca WEBSOCKETS=true ENCRYPTION_KEY=ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985 SOURCE=pr-1444 PASSWORD= node pullConsumer.js + SERVERS=stream-dev.apps.silver.devops.gov.bc.ca WEBSOCKETS=true ENCRYPTION_KEY=ad5520469720325d1694c87511afda28a0432dd974cb77b5b4b9f946a5af6985 node pullConsumer.js + + // runs with all defaults against localhost + node pullConsumer.js +*/ + +// different connection libraries if we are using websockets or nats protocols. +const WEBSOCKETS = !falsey(process.env.WEBSOCKETS); + +let natsConnect; +if (WEBSOCKETS) { + console.log("connect via ws"); + // shim the websocket library + globalThis.WebSocket = require("websocket").w3cwebsocket; + const { connect } = require("nats.ws"); + natsConnect = connect; +} else { + console.log("connect via nats"); + const { connect } = require("nats"); + natsConnect = connect; +} + +// connection info +let servers = []; +if (process.env.SERVERS) { + servers = process.env.SERVERS.split(","); +} else { + // running locally + servers = "localhost:4222,localhost:4223,localhost:4224".split(","); +} + +let nc = undefined; // nats connection +let js = undefined; // jet stream +let jsm = undefined; // jet stream manager +let consumer = undefined; // pull consumer (ordered, ephemeral) + +// stream info +const STREAM_NAME = "CHEFS"; +const FILTER_SUBJECTS = ["PUBLIC.forms.>", "PRIVATE.forms.>"]; +const MAX_MESSAGES = 2; +const DURABLE_NAME = process.env.DURABLE_NAME || uuidv4(); +const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY || undefined; +const SOURCE_FILTER = process.env.SOURCE || false; +const USERNAME = process.env.USERNAME || "chefsConsumer"; +const PASSWORD = process.env.PASSWORD || "password"; + +const printMsg = (m) => { + // illustrate grabbing the sequence and timestamp from the nats message... + try { + const ts = new Date(m.info.timestampNanos / 1000000).toISOString(); + console.log( + `msg seq: ${m.seq}, subject: ${m.subject}, timestamp: ${ts}, streamSequence: ${m.info.streamSequence}, deliverySequence: ${m.info.deliverySequence}` + ); + // illustrate (one way of) grabbing message content as json + const jsonCodec = JSONCodec(); + const data = jsonCodec.decode(m.data); + let process = true; + if (SOURCE_FILTER) { + process = data["meta"]["source"] === SOURCE_FILTER; + if (!process) { + console.log( + ` not processing message. filter = ${SOURCE_FILTER}, meta.source = ${data["meta"]["source"]}` + ); + } + } + if (process) { + console.log(data); + try { + if (data && data["error"]) { + console.log(`error with payload: ${data["error"]["message"]}`); + } else if ( + data && + data["payload"] && + data["payload"]["data"] && + ENCRYPTION_KEY + ) { + const cryptr = new Cryptr(ENCRYPTION_KEY); + const decryptedData = cryptr.decrypt(data["payload"]["data"]); + const jsonData = JSON.parse(decryptedData); + console.log("decrypted payload data:"); + console.log(jsonData); + } + } catch (err) { + console.error(" Error decrypting payload.data - check ENCRYPTION_KEY"); + } + } + } catch (e) { + console.error(`Error printing message: ${e.message}`); + } +}; + +const init = async () => { + if (nc && nc.info != undefined) { + // already connected. + return; + } else { + // open a connection... + try { + // no credentials provided. + // anonymous connections have read access to the stream + console.log(`connect to nats server(s) ${servers} as '${USERNAME}'...`); + nc = await natsConnect({ + servers: servers, + reconnectTimeWait: 10 * 1000, // 10s + user: USERNAME, + pass: PASSWORD, + }); + + console.log("access jetstream..."); + js = nc.jetstream(); + console.log("get jetstream manager..."); + jsm = await js.jetstreamManager(); + await jsm.consumers.add(STREAM_NAME, { + ack_policy: AckPolicy.Explicit, + durable_name: DURABLE_NAME, + }); + console.log( + `get consumer: stream = ${STREAM_NAME}, durable name = ${DURABLE_NAME}...` + ); + consumer = await js.consumers.get(STREAM_NAME, DURABLE_NAME); + } catch (e) { + console.error(e); + process.exit(0); + } + } +}; + +const pull = async () => { + console.log("fetch..."); + let iter = await consumer.fetch({ + filterSubjects: FILTER_SUBJECTS, + max_messages: MAX_MESSAGES, + }); + for await (const m of iter) { + printMsg(m); + m.ack(); + } +}; + +const main = async () => { + await init(); + await pull(); + setTimeout(main, 5000); // process a batch every 5 seconds +}; + +main(); + +const shutdown = async () => { + console.log("\nshutdown..."); + console.log("drain connection..."); + await nc.drain(); + process.exit(0); +}; + +process.on("SIGTERM", shutdown); +process.on("SIGINT", shutdown); +process.on("SIGUSR1", shutdown); +process.on("SIGUSR2", shutdown); +process.on("exit", () => { + console.log("exit."); +}); diff --git a/event-stream-service/subscriber.js b/event-stream-service/subscriber.js new file mode 100644 index 000000000..c39a3db19 --- /dev/null +++ b/event-stream-service/subscriber.js @@ -0,0 +1,59 @@ +const { connect, consumerOpts } = require("nats"); +// connection info +const servers = ["localhost:4222", "localhost:4223", "localhost:4224"]; + +let nc = undefined; // nats connection +let js = undefined; // jet stream + +// stream info +const SUBJECTS = ["PUBLIC.forms.>", "PRIVATE.forms.>"]; + +const printMsg = (m) => { + try { + const ts = new Date(m.info.timestampNanos / 1000000).toISOString(); + console.log( + `msg seq: ${m.seq}, subject: ${m.subject}, timestamp: ${ts}, streamSequence: ${m.info.streamSequence}, deliverySequence: ${m.info.deliverySequence}` + ); + console.log(JSON.stringify(m.json(), null, 2)); + } catch (e) { + console.error(`Error printing message: ${e.message}`); + } +}; + +const subscribe = async () => { + console.log(`connect to nats server(s) ${servers} as 'anonymous'...`); + nc = await connect({ + servers: servers, + }); + + console.log("access jetstream..."); + js = nc.jetstream(); + + SUBJECTS.forEach(async (key) => { + const opts = consumerOpts(); + opts.maxMessages(100); + opts.ackExplicit(); + opts.deliverNew(); + opts.deliverTo("_INBOX.push_consumer"); + console.log(`subscribe to ${key}`); + const sub = await js.subscribe(key, opts); + await (async () => { + for await (const m of sub) { + printMsg(m); + m.ack(); + } + })(); + }); + + console.log("CTRL-C to close."); +}; + +subscribe(); + +const close = async () => { + console.log("drain nats connection..."); + await nc.drain(); + console.log("done."); +}; + +process.on("SIGINT", close); diff --git a/openshift/README.md b/openshift/README.md index 478e78e8f..f8646e619 100644 --- a/openshift/README.md +++ b/openshift/README.md @@ -157,6 +157,22 @@ oc create -n $NAMESPACE secret generic $APP_NAME-encryption-keys \ --from-literal=proxy=$proxy_key ``` +We need to store a password for Event Stream Service client. Since the server(s) will change along with the password, we will store the server and credentials in a secret per environment (DEV, TEST, PROD) and whether we connect with WebSockets or NATS protocols Pull requests can use the same as DEV. + +```sh + +export ess_servers= +export ess_websockets= +export ess_password= + +oc create -n $NAMESPACE secret generic $APP_NAME-event-stream-service \ + --type=Opaque \ + --from-literal=servers=$ess_servers \ + --from-literal=websockets=$ess_websockets \ + --from-literal=username=chefs \ + --from-literal=password=$ess_password +``` + ## Deployment This application is currently designed as a single application pod deployment. It will host a static frontend containing all of the Vue.js resources and assets, and a Node.js backend which serves the API that the frontend requires. We are currently leveraging Openshift Routes with path based filtering to forward incoming traffic to the right deployment service. diff --git a/openshift/app.dc.yaml b/openshift/app.dc.yaml index 198f0e966..7afebe3a7 100644 --- a/openshift/app.dc.yaml +++ b/openshift/app.dc.yaml @@ -233,6 +233,26 @@ objects: secretKeyRef: key: mailapitoken name: "chefs-${JOB_NAME}-secret" + - name: EVENTSTREAMSERVICE_SERVERS + valueFrom: + secretKeyRef: + key: servers + name: "${APP_NAME}-event-stream-service" + - name: EVENTSTREAMSERVICE_WEBSOCKETS + valueFrom: + secretKeyRef: + key: websockets + name: "${APP_NAME}-event-stream-service" + - name: EVENTSTREAMSERVICE_USERNAME + valueFrom: + secretKeyRef: + key: username + name: "${APP_NAME}-event-stream-service" + - name: EVENTSTREAMSERVICE_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: "${APP_NAME}-event-stream-service" # - name: SERVER_LOGFILE # value: "/var/log/app.log" envFrom: @@ -250,6 +270,8 @@ objects: name: "${APP_NAME}-oidc-config" - configMapRef: name: "${APP_NAME}-custombcaddressformiocomponent-config" + - configMapRef: + name: "${APP_NAME}-${JOB_NAME}-event-stream-service" restartPolicy: Always terminationGracePeriodSeconds: 30 volumes: diff --git a/openshift/ess.cm.yaml b/openshift/ess.cm.yaml new file mode 100644 index 000000000..845619b8d --- /dev/null +++ b/openshift/ess.cm.yaml @@ -0,0 +1,75 @@ +--- +apiVersion: template.openshift.io/v1 +kind: Template +labels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: "${APP_NAME}-${JOB_NAME}" + app.kubernetes.io/managed-by: github + app.kubernetes.io/name: nodejs + app.kubernetes.io/part-of: "${APP_NAME}-${JOB_NAME}" + app: "${APP_NAME}-${JOB_NAME}" + template: "chefs-app-event-stream-service-template" +metadata: + name: "chefs-app-event-stream-service" +objects: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: "${APP_NAME}-${JOB_NAME}-event-stream-service" + data: + EVENTSTREAMSERVICE_STREAMNAME: ${STREAMNAME} + EVENTSTREAMSERVICE_SOURCE: ${SOURCE} + EVENTSTREAMSERVICE_DOMAIN: ${DOMAIN} + EVENTSTREAMSERVICE_MAXAGE: ${MAXAGE} + EVENTSTREAMSERVICE_MAXBYTES: ${MAXBYTES} + EVENTSTREAMSERVICE_MAXMSGS: ${MAXMSGS} + EVENTSTREAMSERVICE_MAXMSGSIZE: ${MAXMSGSIZE} + EVENTSTREAMSERVICE_DUPLICATEWINDOW: ${DUPLICATEWINDOW} + EVENTSTREAMSERVICE_NUMREPLICAS: ${NUMREPLICAS} +parameters: + - name: APP_NAME + description: Application name + displayName: Application name + required: true + - name: JOB_NAME + description: Job identifier (i.e. 'pr-5' OR 'master') + displayName: Job Branch Name + required: true + - name: STREAMNAME + description: Name of the CHEFS Event Stream + displayName: CHEFS Event Stream name + required: true + value: CHEFS + - name: DOMAIN + description: Domain of the CHEFS Event Stream + displayName: CHEFS Event Stream domain + required: true + value: forms + - name: SOURCE + description: Source of CHEFS Event Stream + displayName: CHEFS Event Stream source + required: true + - name: MAXAGE + description: Maximum age of any message in the Stream (milliseconds) + displayName: Age of msgs (ms) + required: true + - name: MAXBYTES + description: Maximum number of bytes stored in the stream. + displayName: Size of stream (bytes) + required: true + - name: MAXMSGS + description: Maximum number of messages stored in the stream. + displayName: Number of msgs in stream + required: true + - name: MAXMSGSIZE + description: The largest message that will be accepted by the Stream (bytes) + displayName: Max Msg Size (bytes) + required: true + - name: DUPLICATEWINDOW + description: Duplicate messages window (milliseconds) + displayName: Duplicate window (ms) + required: true + - name: NUMREPLICAS + description: Number of stream replicas + display: Number of stream replicas + required: true diff --git a/openshift/ess.dev.param b/openshift/ess.dev.param new file mode 100644 index 000000000..db79f8b97 --- /dev/null +++ b/openshift/ess.dev.param @@ -0,0 +1,9 @@ +STREAMNAME=CHEFS +SOURCE=chefs-dev +DOMAIN=forms +MAXAGE="604800000" +MAXBYTES="966367641" +MAXMSGS="1000" +MAXMSGSIZE="966367" +DUPLICATEWINDOW="60000" +NUMREPLICAS="3" \ No newline at end of file diff --git a/openshift/ess.prod.param b/openshift/ess.prod.param new file mode 100644 index 000000000..7f45ab835 --- /dev/null +++ b/openshift/ess.prod.param @@ -0,0 +1,9 @@ +STREAMNAME=CHEFS +SOURCE=chefs +DOMAIN=forms +MAXAGE="604800000" +MAXBYTES="966367641" +MAXMSGS="1000" +MAXMSGSIZE="966367" +DUPLICATEWINDOW="60000" +NUMREPLICAS="3" \ No newline at end of file diff --git a/openshift/ess.test.param b/openshift/ess.test.param new file mode 100644 index 000000000..1164c056d --- /dev/null +++ b/openshift/ess.test.param @@ -0,0 +1,9 @@ +STREAMNAME=CHEFS +SOURCE=chefs-test +DOMAIN=forms +MAXAGE="900000" +MAXBYTES="209715200" +MAXMSGS="500" +MAXMSGSIZE="419430" +DUPLICATEWINDOW="60000" +NUMREPLICAS="3" \ No newline at end of file