diff --git a/.github/workflows/validate-k8s.yml b/.github/workflows/validate-k8s.yml index 4d23e66..8a67a9c 100644 --- a/.github/workflows/validate-k8s.yml +++ b/.github/workflows/validate-k8s.yml @@ -66,13 +66,16 @@ jobs: - name: Install Helmfile run: | - wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz + curl -fsSLO https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz tar -xzf helmfile_0.148.0_linux_amd64.tar.gz sudo mv helmfile /usr/local/bin/ helmfile --version - - name: Install Helm Diff Plugin - run: helm plugin install https://github.com/databus23/helm-diff || true + - name: Debug Helm env + run: | + helm env + echo "HOME=$HOME" + ls -R $HOME/.local/share/helm || true - name: Authenticate with OKE cluster env: diff --git a/README.md b/README.md index bb50d3a..028ca81 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@

- # Maestro Maestro é a API principal da Dadosfera. É responsável pela comunicação do Frontend com nossos microsserviços. diff --git a/deploy/helm-chart/templates/deployment.yaml b/deploy/helm-chart/templates/deployment.yaml index 013aa06..7312b30 100644 --- a/deploy/helm-chart/templates/deployment.yaml +++ b/deploy/helm-chart/templates/deployment.yaml @@ -104,6 +104,10 @@ spec: value: {{ .Values.maestro.redis_host }} - name: REDIS_PORT value: "{{ .Values.maestro.redis_port }}" + - name: REDIS_TLS + value: "{{ .Values.maestro.redis_tls }}" + - name: PLATFORM_API_URL + value: {{ .Values.maestro.platform_api_url }} - name: JWT_PRIVATE_KEY valueFrom: secretKeyRef: @@ -124,3 +128,14 @@ spec: secretKeyRef: name: prd-{{ .Values.app_name }} key: AWS_DEFAULT_REGION + # Elasticsearch + - name: ELASTICSEARCH_URL + valueFrom: + secretKeyRef: + name: prd-{{ .Values.app_name }} + key: ELASTICSEARCH_URL + - name: ELASTICSEARCH_API_KEY + valueFrom: + secretKeyRef: + name: prd-{{ .Values.app_name }} + key: ELASTICSEARCH_API_KEY diff --git a/deploy/helm-chart/templates/secret.yaml b/deploy/helm-chart/templates/secret.yaml index 3bbf5ec..10ea9b7 100644 --- a/deploy/helm-chart/templates/secret.yaml +++ b/deploy/helm-chart/templates/secret.yaml @@ -38,3 +38,15 @@ spec: version: "AWSCURRENT" property: token + - secretKey: ELASTICSEARCH_URL + remoteRef: + key: {{ .Values.maestro.env }}/microservices/elasticsearch + version: "AWSCURRENT" + property: ELASTICSEARCH_URL + + - secretKey: ELASTICSEARCH_API_KEY + remoteRef: + key: {{ .Values.maestro.env }}/microservices/elasticsearch + version: "AWSCURRENT" + property: ELASTICSEARCH_API_KEY + diff --git a/deploy/helm-chart/values-stg.yaml b/deploy/helm-chart/values-stg.yaml index 13e2d05..360aafe 100644 --- a/deploy/helm-chart/values-stg.yaml +++ b/deploy/helm-chart/values-stg.yaml @@ -8,6 +8,7 @@ maestro: open_group_id: e3f98a2f-7748-4981-8505-7695c8ca8218 cookie_secret: "ff7bc13823edb2ae50d248e5780bddc9d4b31c36" redis_database: "1" + platform_api_url: https://xs2hkhq07k.execute-api.us-east-1.amazonaws.com hostname: maestro.stg.dadosfera.ai diff --git a/deploy/helm-chart/values.yaml b/deploy/helm-chart/values.yaml index 0c4db2e..30af31e 100644 --- a/deploy/helm-chart/values.yaml +++ b/deploy/helm-chart/values.yaml @@ -43,11 +43,13 @@ maestro: upload_file_agent_connection: cbc2f881-58c4-4d60-8003-0979b0b5b911 open_customer_id: f239718a-a271-4ef9-ae7e-02a2f0f3aa6e open_group_id: 401573bb-334f-44b2-b30e-88d4cea31ae9 + platform_api_url: https://oz8v2zid1e.execute-api.us-east-1.amazonaws.com dedicated_proxy: "" restricted_ip: "" redis_host: "aaapzppmlyamkocqwstpo7zvopczyyiyuy6xzm2g6c5k4mq3a66be4a-0.redis.sa-saopaulo-1.oci.oraclecloud.com" redis_port: "6379" redis_database: "0" + redis_tls: "true" cookie_secret: "13cc5e136d3074bcc05bec8697092ec1f5f376bf" autoscaling: enabled: false diff --git a/docsfera.json b/docsfera.json index ab3a2c1..68b2806 100644 --- a/docsfera.json +++ b/docsfera.json @@ -1321,9 +1321,6 @@ "Users" ], "security": [ - { - "access-token": [] - }, { "access-token": [] } @@ -3877,6 +3874,36 @@ ], "type": "string" } + }, + { + "name": "owner", + "required": false, + "in": "query", + "description": "ID do usuário owner para filtrar data assets", + "example": "user-id-1,user-id-2", + "schema": { + "type": "string" + } + }, + { + "name": "catalog_date_from", + "required": false, + "in": "query", + "description": "Data inicial para filtro de catálogo (formato: YYYY-MM-DD)", + "example": "2025-01-01", + "schema": { + "type": "string" + } + }, + { + "name": "catalog_date_to", + "required": false, + "in": "query", + "description": "Data final para filtro de catálogo (formato: YYYY-MM-DD)", + "example": "2025-12-31", + "schema": { + "type": "string" + } } ], "responses": { @@ -4006,6 +4033,36 @@ ], "type": "string" } + }, + { + "name": "owner", + "required": false, + "in": "query", + "description": "ID do usuário owner para filtrar data assets", + "example": "user-id-1,user-id-2", + "schema": { + "type": "string" + } + }, + { + "name": "catalog_date_from", + "required": false, + "in": "query", + "description": "Data inicial para filtro de catálogo (formato: YYYY-MM-DD)", + "example": "2025-01-01", + "schema": { + "type": "string" + } + }, + { + "name": "catalog_date_to", + "required": false, + "in": "query", + "description": "Data final para filtro de catálogo (formato: YYYY-MM-DD)", + "example": "2025-12-31", + "schema": { + "type": "string" + } } ], "responses": { @@ -4423,6 +4480,14 @@ "schema": { "type": "string" } + }, + { + "name": "asset_type", + "required": true, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -4471,6 +4536,14 @@ "schema": { "type": "string" } + }, + { + "name": "asset_type", + "required": true, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -6811,6 +6884,836 @@ } } }, + "/platform/pipeline": { + "post": { + "operationId": "PlatformApiController_createPipeline", + "summary": "Create a new pipeline", + "parameters": [], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipelines": { + "get": { + "operationId": "PlatformApiController_getPipelines", + "summary": "List all pipelines for customer", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/{pipelineId}": { + "get": { + "operationId": "PlatformApiController_getPipeline", + "summary": "Get pipeline by ID", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + }, + "patch": { + "operationId": "PlatformApiController_updatePipeline", + "summary": "Update pipeline by ID", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + }, + "delete": { + "operationId": "PlatformApiController_deletePipeline", + "summary": "Delete pipeline by ID", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/execute": { + "post": { + "operationId": "PlatformApiController_executePipeline", + "summary": "Execute a pipeline", + "parameters": [], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/pause": { + "post": { + "operationId": "PlatformApiController_pausePipeline", + "summary": "Pause a pipeline", + "parameters": [], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/unpause": { + "post": { + "operationId": "PlatformApiController_unpausePipeline", + "summary": "Unpause a pipeline", + "parameters": [], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/{pipelineId}/memory": { + "put": { + "operationId": "PlatformApiController_updatePipelineMemory", + "summary": "Update pipeline memory configuration", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/{pipelineId}/metadata": { + "put": { + "operationId": "PlatformApiController_updatePipelineMetadata", + "summary": "Update pipeline metadata", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipelines/metadata": { + "get": { + "operationId": "PlatformApiController_getPipelinesMetadata", + "summary": "Get all pipelines metadata", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/{pipelineId}/pipeline_run": { + "get": { + "operationId": "PlatformApiController_getPipelineRuns", + "summary": "Get pipeline runs for a pipeline", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/{pipelineId}/pipeline_run/{runId}": { + "get": { + "operationId": "PlatformApiController_getPipelineRun", + "summary": "Get specific pipeline run", + "parameters": [ + { + "name": "pipelineId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + }, + { + "name": "runId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/pipeline/pipeline_run/{runId}/logs": { + "get": { + "operationId": "PlatformApiController_getPipelineRunLogs", + "summary": "Get pipeline run logs", + "parameters": [ + { + "name": "runId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/{jobId}/input": { + "put": { + "operationId": "PlatformApiController_updateJobInput", + "summary": "Update job input columns", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + }, + "patch": { + "operationId": "PlatformApiController_patchJobInput", + "summary": "Partial update job input columns", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/{jobId}/memory": { + "put": { + "operationId": "PlatformApiController_updateJobMemory", + "summary": "Update job memory configuration", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/{jobId}/reset-state": { + "post": { + "operationId": "PlatformApiController_resetJobState", + "summary": "Reset job state", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/jdbc/{jobId}": { + "get": { + "operationId": "PlatformApiController_getJdbcJob", + "summary": "Get JDBC job details", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/jdbc/{jobId}/sync-mode": { + "post": { + "operationId": "PlatformApiController_updateJdbcSyncMode", + "summary": "Update JDBC job sync mode", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/jdbc/configs/allowed_datatypes": { + "get": { + "operationId": "PlatformApiController_getJdbcAllowedDatatypes", + "summary": "Get allowed datatypes for JDBC", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/singer/{jobId}": { + "get": { + "operationId": "PlatformApiController_getSingerJob", + "summary": "Get Singer job details", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/singer/{jobId}/sync-mode": { + "post": { + "operationId": "PlatformApiController_updateSingerSyncMode", + "summary": "Update Singer job sync mode", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/jobs/s3/{jobId}": { + "get": { + "operationId": "PlatformApiController_getS3Job", + "summary": "Get S3 job details", + "parameters": [ + { + "name": "jobId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/platform/health": { + "get": { + "operationId": "PlatformApiController_healthCheck", + "summary": "Platform API health check", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Platform API" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, "/health": { "get": { "operationId": "HealthController_check", @@ -7260,6 +8163,15 @@ "hierarchy": { "type": "string" }, + "bio": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "companySite": { + "type": "string" + }, "mfaStatus": { "type": "string" }, @@ -7290,6 +8202,9 @@ "jobTitle", "department", "hierarchy", + "bio", + "companyName", + "companySite", "mfaStatus", "createdAt", "updatedAt", @@ -7423,6 +8338,15 @@ "hierarchy": { "type": "string" }, + "bio": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "companySite": { + "type": "string" + }, "customer": { "$ref": "#/components/schemas/Customer" }, @@ -7450,6 +8374,9 @@ "jobTitle", "department", "hierarchy", + "bio", + "companyName", + "companySite", "mfaStatus", "createdAt", "updatedAt", @@ -7590,6 +8517,15 @@ "hierarchy": { "type": "string" }, + "bio": { + "type": "string" + }, + "companySite": { + "type": "string" + }, + "companyName": { + "type": "string" + }, "roleNames": { "type": "array", "items": { @@ -7630,6 +8566,15 @@ "hierarchy": { "type": "string" }, + "bio": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "companySite": { + "type": "string" + }, "mfaStatus": { "type": "string" }, @@ -7653,6 +8598,9 @@ "jobTitle", "department", "hierarchy", + "bio", + "companyName", + "companySite", "mfaStatus", "createdAt", "updatedAt", diff --git a/environment.d.ts b/environment.d.ts index 3b44250..85d1a52 100644 --- a/environment.d.ts +++ b/environment.d.ts @@ -16,6 +16,7 @@ declare global { OPEN_CUSTOMER_ID: string; DEDICATED_PROXY: string; COOKIE_SECRET: string; + REDIS_TLS?: string; } } } diff --git a/package-lock.json b/package-lock.json index 0a1aa0e..6f67e12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,15 @@ "version": "0.0.0", "license": "UNLICENSED", "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-sdk/client-dynamodb": "^3.414.0", "@aws-sdk/client-secrets-manager": "^3.414.0", + "@aws-sdk/credential-provider-node": "^3.940.0", + "@aws-sdk/lib-dynamodb": "^3.414.0", + "@aws-sdk/signature-v4": "^3.370.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/protospack": "2.5.3", - "@dadosfera/protospack-v2": "3.38.0-beta.14", + "@dadosfera/protospack-v2": "3.38.0-beta.18", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", @@ -214,6 +219,34 @@ "tslib": "^2.1.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", @@ -339,6 +372,228 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.946.0.tgz", + "integrity": "sha512-sMmJRa2Y+jQ+qMzx7V3SBKsTSQzLBvSuVbMNM/YGSZkr849a4a6Frl4U8HfFj4O+o71dvQ6h5PP6vlc/fW672g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/credential-provider-node": "3.946.0", + "@aws-sdk/dynamodb-codec": "3.946.0", + "@aws-sdk/middleware-endpoint-discovery": "3.936.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.946.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.946.0.tgz", + "integrity": "sha512-7QcljCraeaWQNuqmOoAyZs8KpZcuhPiqdeeKoRd397jVGNRehLFsZbIMOvwaluUDFY11oMyXOkQEERe1Zo2fCw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.946.0.tgz", + "integrity": "sha512-a2UwwvzbK5AxHKUBupfg4s7VnkqRAHjYsuezHnKCniczmT4HZfP1NnfwwvLKEH8qaTrwenxjKSfq4UWmWkvG+Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/client-secrets-manager": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.758.0.tgz", @@ -391,7 +646,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.758.0.tgz", "integrity": "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==", @@ -440,29 +695,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/core": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", - "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/core": "^3.1.5", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/signature-v4": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-env": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.758.0.tgz", "integrity": "sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==", @@ -478,7 +711,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-http": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.758.0.tgz", "integrity": "sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==", @@ -499,7 +732,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.758.0.tgz", "integrity": "sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw==", @@ -523,7 +756,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-node": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.758.0.tgz", "integrity": "sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ==", @@ -546,7 +779,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-process": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-process": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.758.0.tgz", "integrity": "sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==", @@ -563,7 +796,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.758.0.tgz", "integrity": "sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ==", @@ -582,7 +815,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.758.0.tgz", "integrity": "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==", @@ -599,6 +832,947 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/nested-clients": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.758.0.tgz", + "integrity": "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/token-providers": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.758.0.tgz", + "integrity": "sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.946.0.tgz", + "integrity": "sha512-kGAs5iIVyUz4p6TX3pzG5q3cNxXnVpC4pwRC6DCSaSv9ozyPjc2d74FsK4fZ+J+ejtvCdJk72uiuQtWJc86Wuw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.946.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.946.0.tgz", + "integrity": "sha512-7QcljCraeaWQNuqmOoAyZs8KpZcuhPiqdeeKoRd397jVGNRehLFsZbIMOvwaluUDFY11oMyXOkQEERe1Zo2fCw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.946.0.tgz", + "integrity": "sha512-a2UwwvzbK5AxHKUBupfg4s7VnkqRAHjYsuezHnKCniczmT4HZfP1NnfwwvLKEH8qaTrwenxjKSfq4UWmWkvG+Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", + "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.946.0.tgz", + "integrity": "sha512-P4l+K6wX1tf8LmWUvZofdQ+BgCNyk6Tb9u1H10npvqpuCD+dCM4pXIBq3PQcv/juUBOvLGGREo+Govuh3lfD0Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.946.0.tgz", + "integrity": "sha512-/zeOJ6E7dGZQ/l2k7KytEoPJX0APIhwt0A79hPf/bUpMF4dDs2P6JmchDrotk0a0Y/MIdNF8sBQ/MEOPnBiYoQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.946.0.tgz", + "integrity": "sha512-Pdgcra3RivWj/TuZmfFaHbqsvvgnSKO0CxlRUMMr0PgBiCnUhyl+zBktdNOeGsOPH2fUzQpYhcUjYUgVSdcSDQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/credential-provider-env": "3.946.0", + "@aws-sdk/credential-provider-http": "3.946.0", + "@aws-sdk/credential-provider-login": "3.946.0", + "@aws-sdk/credential-provider-process": "3.946.0", + "@aws-sdk/credential-provider-sso": "3.946.0", + "@aws-sdk/credential-provider-web-identity": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.946.0.tgz", + "integrity": "sha512-5iqLNc15u2Zx+7jOdQkIbP62N7n2031tw5hkmIG0DLnozhnk64osOh2CliiOE9x3c4P9Pf4frAwgyy9GzNTk2g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.946.0.tgz", + "integrity": "sha512-I7URUqnBPng1a5y81OImxrwERysZqMBREG6svhhGeZgxmqcpAZ8z5ywILeQXdEOCuuES8phUp/ojzxFjPXp/eA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.946.0", + "@aws-sdk/credential-provider-http": "3.946.0", + "@aws-sdk/credential-provider-ini": "3.946.0", + "@aws-sdk/credential-provider-process": "3.946.0", + "@aws-sdk/credential-provider-sso": "3.946.0", + "@aws-sdk/credential-provider-web-identity": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.946.0.tgz", + "integrity": "sha512-GtGHX7OGqIeVQ3DlVm5RRF43Qmf3S1+PLJv9svrdvAhAdy2bUb044FdXXqrtSsIfpzTKlHgQUiRo5MWLd35Ntw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.946.0.tgz", + "integrity": "sha512-LeGSSt2V5iwYey1ENGY75RmoDP3bA2iE/py8QBKW8EDA8hn74XBLkprhrK5iccOvU3UGWY8WrEKFAFGNjJOL9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.946.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/token-providers": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.946.0.tgz", + "integrity": "sha512-ocBCvjWfkbjxElBI1QUxOnHldsNhoU0uOICFvuRDAZAoxvypJHN3m5BJkqb7gqorBbcv3LRgmBdEnWXOAvq+7Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/dynamodb-codec": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.946.0.tgz", + "integrity": "sha512-doDGHfTV8nFvDOnFJDcI4lx0NR4GT6uagTFM20AoIIVngm7UEITQ0toU1uTRRyRa+52ibMktdLkv7c8OcCG6Qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@smithy/core": "^3.18.7", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.946.0" + } + }, + "node_modules/@aws-sdk/dynamodb-codec/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/dynamodb-codec/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.893.0.tgz", + "integrity": "sha512-KSwTfyLZyNLszz5f/yoLC+LC+CRKpeJii/+zVAy7JUOQsKhSykiRUPYUx7o2Sdc4oJfqqUl26A/jSttKYnYtAA==", + "license": "Apache-2.0", + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.370.0.tgz", + "integrity": "sha512-PiaDMum7TNsIE3DGECSsNYwibBIPN2/e13BJbTwi6KgVx8BV2mYA3kQkaUDiy++tEpzN81Nh5OPTFVb7bvgYYg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/lib-dynamodb": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.946.0.tgz", + "integrity": "sha512-1qI5EZUa/E0Ex4GITb9SiY5YvXcGKCYYawGYKiacpnA+YldLzKtllwns23upiTrHLmeBHZxwrXAG1sh1UqM3tw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/util-dynamodb": "3.946.0", + "@smithy/core": "^3.18.7", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.946.0" + } + }, + "node_modules/@aws-sdk/lib-dynamodb/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/lib-dynamodb/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.936.0.tgz", + "integrity": "sha512-wNJZ8PDw0eQK2x4z1q8JqiDvw9l9xd36EoklVT2CIBt8FnqGdrMGjAx93RRbH3G6Fmvwoe+D3VJXbWHBlhD0Bw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/endpoint-cache": "3.893.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.734.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", @@ -662,54 +1836,222 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.758.0.tgz", - "integrity": "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg==", + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.946.0.tgz", + "integrity": "sha512-rjAtEguukeW8mlyEQMQI56vxFoyWlaNwowmz1p1rav948SUjtrzjHAp4TOQWhibb7AR7BUTHBCgIcyCRjBEf4g==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.946.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.946.0.tgz", + "integrity": "sha512-7QcljCraeaWQNuqmOoAyZs8KpZcuhPiqdeeKoRd397jVGNRehLFsZbIMOvwaluUDFY11oMyXOkQEERe1Zo2fCw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.946.0.tgz", + "integrity": "sha512-a2UwwvzbK5AxHKUBupfg4s7VnkqRAHjYsuezHnKCniczmT4HZfP1NnfwwvLKEH8qaTrwenxjKSfq4UWmWkvG+Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.734.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", @@ -727,17 +2069,99 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.758.0.tgz", - "integrity": "sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w==", + "node_modules/@aws-sdk/signature-v4": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.370.0.tgz", + "integrity": "sha512-Mh++NJiXoBxMzz4d8GQPNB37nqjS1gsVwjKoSAWFE67sjgsjb8D5JWRCm9CinqPoXi2iN57+1DcQalTDKQGc0A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/nested-clients": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "@aws-sdk/eventstream-codec": "3.370.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.370.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.946.0.tgz", + "integrity": "sha512-a5c+rM6CUPX2ExmUZ3DlbLlS5rQr4tbdoGcgBsjnAHiYx8MuMNAI+8M7wfjF13i2yvUQj5WEIddvLpayfEZj9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -757,6 +2181,34 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-dynamodb": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.946.0.tgz", + "integrity": "sha512-L1i5w52Ue2R4hmvtrZQ/8hmXiGBLrv6YlWKZCU4nN45E16TDLcn0rNMR7DXbLM/vRU8/pvqrhIwJRxuR1sjy1A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.946.0" + } + }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.743.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.743.0.tgz", @@ -772,6 +2224,18 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", @@ -784,6 +2248,30 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.370.0.tgz", + "integrity": "sha512-Jvs9FZHaQznWGLkRel3PFEP93I1n0Kp6356zxYHk3LIOmjpzoob3R+v96mzyN+dZrnhPdPubYS41qbU2F9lROg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.734.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", @@ -820,6 +2308,81 @@ } } }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/xml-builder/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -1409,9 +2972,9 @@ } }, "node_modules/@dadosfera/protospack-v2": { - "version": "3.38.0-beta.14", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.38.0-beta.14.tgz", - "integrity": "sha512-BiE1fUIHuam3cJjAGIknsRNIlf1Z0JHJOV/VZ2pyAp5mIkGOSoNFBAMXJZb+VMTnd2xsp+vPxNnWVu7OJ/CvMQ==", + "version": "3.38.0-beta.18", + "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.38.0-beta.18.tgz", + "integrity": "sha512-rD2UOKa0P17A6YXIpclXF7F9b86mhRNE7fImTrbFamOcHpGmXBBVU8U53mCfLzzOA+MdDZ4/1uDNurCyQCjsLQ==", "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.9.3", @@ -3069,12 +4632,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", - "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", + "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3082,15 +4645,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", - "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", + "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -3098,18 +4662,20 @@ } }, "node_modules/@smithy/core": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.5.tgz", - "integrity": "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA==", + "version": "3.18.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz", + "integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.0.2", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", - "@smithy/util-utf8": "^4.0.0", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -3117,15 +4683,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", - "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", + "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -3133,15 +4699,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", - "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", + "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, "engines": { @@ -3149,14 +4715,14 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", - "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", + "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/types": "^4.9.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3164,12 +4730,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", - "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", + "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3177,9 +4743,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3189,13 +4755,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", - "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", + "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3203,18 +4769,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.6.tgz", - "integrity": "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg==", + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.14.tgz", + "integrity": "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-middleware": "^4.0.1", + "@smithy/core": "^3.18.7", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -3222,32 +4788,33 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.7.tgz", - "integrity": "sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ==", + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.14.tgz", + "integrity": "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/service-error-classification": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-serde": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", - "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", + "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3255,12 +4822,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", - "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", + "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3268,14 +4835,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", - "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", + "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3283,15 +4850,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.3.tgz", - "integrity": "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", + "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/abort-controller": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3299,12 +4866,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", - "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", + "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3312,12 +4879,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", - "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", + "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3325,13 +4892,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", - "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", + "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-uri-escape": "^4.0.0", + "@smithy/types": "^4.9.0", + "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3339,12 +4906,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", - "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", + "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3352,24 +4919,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", - "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", + "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0" + "@smithy/types": "^4.9.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", - "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", + "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3377,18 +4944,18 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", - "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", + "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3396,17 +4963,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.6.tgz", - "integrity": "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw==", + "version": "4.9.10", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.10.tgz", + "integrity": "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", + "@smithy/core": "^3.18.7", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" }, "engines": { @@ -3414,9 +4981,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", - "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", + "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3426,13 +4993,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", - "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", + "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/querystring-parser": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3440,13 +5007,13 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3454,9 +5021,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3466,9 +5033,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3478,12 +5045,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", + "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3491,9 +5058,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3503,15 +5070,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.7.tgz", - "integrity": "sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q==", + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.13.tgz", + "integrity": "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "bowser": "^2.11.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3519,17 +5085,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.7.tgz", - "integrity": "sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ==", + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.16.tgz", + "integrity": "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.0.1", - "@smithy/credential-provider-imds": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3537,13 +5103,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", - "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", + "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3551,9 +5117,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3563,12 +5129,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", - "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", + "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3576,13 +5142,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", - "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", + "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -3590,18 +5156,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.1.2.tgz", - "integrity": "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", + "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -3609,9 +5175,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3621,12 +5187,38 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz", + "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" }, "engines": { @@ -10048,6 +11640,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "license": "MIT", + "dependencies": { + "obliterator": "^1.6.1" + } + }, "node_modules/module-details-from-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", @@ -10325,6 +11926,12 @@ "node": ">= 0.4" } }, + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "license": "MIT" + }, "node_modules/oidc-token-hash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", diff --git a/package.json b/package.json index e58ffab..0aaef32 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,15 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-sdk/client-dynamodb": "^3.414.0", "@aws-sdk/client-secrets-manager": "^3.414.0", + "@aws-sdk/credential-provider-node": "^3.940.0", + "@aws-sdk/lib-dynamodb": "^3.414.0", + "@aws-sdk/signature-v4": "^3.370.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/protospack": "2.5.3", - "@dadosfera/protospack-v2": "3.38.0-beta.14", + "@dadosfera/protospack-v2": "3.38.0-beta.18", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", diff --git a/src/app.module.ts b/src/app.module.ts index 9de9b19..27d042c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -33,6 +33,7 @@ import { NetworkPolicyModule } from './modules/network-policy/network-policy.mod import { AssignModule } from './modules/assign/assign.module'; import { ShareMetadataModule } from './modules/share-metadata/share-metadata.module'; import { ApiKeyModule } from './modules/api-key/api-key.module'; +import { PlatformApiModule } from './modules/platform-api/platform-api.module'; @Module({ providers: [ @@ -73,6 +74,7 @@ import { ApiKeyModule } from './modules/api-key/api-key.module'; ApiKeyModule, IdentityProviderModule, NetworkPolicyModule, + PlatformApiModule, //Always leave HealthModule last, so it is on the bottom of swagger HealthModule, ], diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 9a4fb01..3d86f14 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -13,6 +13,7 @@ import { Req, Param, Res, + UnauthorizedException, } from '@nestjs/common'; import { ApiHeaders, @@ -102,14 +103,13 @@ export class AuthController { const data = await this.authClient.signIn({ username, password, totp }, metadata); if (data.tokens) { - this.addTokenInCookie(res, { + this.authClient.writeAuthSession(res, { accessToken: data.tokens.accessToken, refreshToken: data.tokens.refreshToken, userId: data.user.id }); } - return res.send(data); } catch (error) { this.logger.error('/auth - SignIn - ERROR', error); @@ -126,27 +126,8 @@ export class AuthController { ) { try { this.logger.info('/auth - SignOut'); - const exp = 1000 * 60 * 3; - - res.cookie('ddf-auth', '', { - domain: 'dadosfera.local', - maxAge: Date.now() - exp, - expires: new Date(), - httpOnly: true, - secure: true, - sameSite: 'none', // Necessário para cookies em requisições cross-site - }); - - res.cookie('ddf-refresh-auth', '', { - domain: 'dadosfera.local', - maxAge: Date.now() - exp, - expires: new Date(), - httpOnly: true, - secure: true, - sameSite: 'none', // Necessário para cookies em requisições cross-site - }); - - this.logger.info('Clean cookie sessions'); + + this.authClient.cleanUpAuthSession(res); return res.send(); } catch (error) { @@ -175,8 +156,9 @@ export class AuthController { const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata); - this.addTokenInCookie(res, { + this.authClient.writeAuthSession(res, { accessToken: data.accessToken, + refreshToken: data.refreshToken, userId }); @@ -492,85 +474,36 @@ export class AuthController { @Get('me') async getMe(@Req() req: Request, @Res() res: Response) { this.logger.info('GET /auth/me ') - // Lê cookies + + // Get token and headers const accessToken = req.cookies['ddf-auth']; - const userId = req.cookies['ddf-user-id']; + const refreshToken = req.cookies['ddf-refresh-auth']; + const userId = req.cookies['ddf-user-id']; + const resourceHost = req.headers["host"] + + const hasUserSession = Boolean(accessToken) && Boolean(userId); + this.logger.info('Has User Session: ' + hasUserSession); + + if (!hasUserSession) { + throw new UnauthorizedException() + } - this.logger.info('Has cookie: ' + Boolean(accessToken)) try { - // Decodifica e valida o JWT de acesso - const userDto = await this.authClient.extractUserFrom(accessToken); + const userDto = await this.authClient.validateUserSession(accessToken, resourceHost); return res.status(200).json(userDto); - } catch (err) { - this.logger.error(err.message); - const refreshToken = req.cookies['ddf-refresh-auth']; + } catch (error) { - this.logger.info('Token is invalid') - this.logger.info('Has Refresh Token: '+ Boolean(refreshToken)) - // Se access token inválido, tenta refresh - if (!refreshToken || !userId) { + if (!refreshToken) { this.logger.error('Invalid refresh token or customer name'); - return res.status(401).json({ error: 'Not authenticated' }); - } - try { - // Chama refreshAccessToken - const metadata = PackTheMetadata({ - }); - this.logger.info('Call Refresh Token') - const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata); - this.logger.info('Finish Refresh Token') - // Retorna novo access token e dados mínimos - this.addTokenInCookie(res, { - accessToken: data.accessToken, - userId - }); - // Decodifica novo token - const userDto = await this.authClient.extractUserFrom(accessToken); - return res.status(200).json(userDto); - } catch (refreshErr) { - this.logger.error(refreshErr) - return res.status(401).json({ error: 'Not authenticated' }); - } - } - } + throw new UnauthorizedException("Invalid refresh token or customer name"); + }; - private addTokenInCookie(res: Response, data: CookiesValues) { - let exp = 1000 * 60 * 5; // 5 minutes - - if (data.accessToken) { - const { exp: expiration } = jwt.decode(data.accessToken) as JwtPayload; - exp = (expiration - 30) * 1000; // exp em segundos, maxAge em ms - - this.logger.info('Set Cookie ddf-auth') - res.cookie('ddf-auth', data.accessToken, { - domain: '.dadosfera.ai', - maxAge: exp, - httpOnly: true, - secure: true, - sameSite: 'none', // Necessário para cookies em requisições cross-site - }); - } - - if (data.refreshToken) { - this.logger.info('Set Cookie ddf-refresh-auth') - res.cookie('ddf-refresh-auth', data.refreshToken, { - domain: '.dadosfera.ai', - maxAge: exp, - httpOnly: true, - secure: true, - sameSite: 'none', // Necessário para cookies em requisições cross-site - }); - } - - if (data.userId) { - this.logger.info('Set Cookie ddf-refresh-auth') - res.cookie('ddf-user-id', data.userId, { - domain: '.dadosfera.ai', - maxAge: exp, - httpOnly: true, - secure: true, - sameSite: 'none', // Necessário para cookies em requisições cross-site - }); + const { + authSession, + user + } = await this.authClient.refreshUserSession(refreshToken, userId, resourceHost); + this.authClient.writeAuthSession(res, authSession); + return res.status(200).json(user); } } } diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 3a17327..c1b85f6 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,10 +1,20 @@ -import { OnModuleInit, Inject, Injectable, ForbiddenException } from '@nestjs/common'; +import { + OnModuleInit, + Inject, + Injectable, + ForbiddenException, + HttpException, + HttpStatus, +} from '@nestjs/common'; import { ClientGrpc } from '@nestjs/microservices'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { lastValueFrom } from 'rxjs'; import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc'; -import { AuthProtoService as AuthServiceInterface, IdentityProviderProtoService, UsersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; +import { + AuthProtoService as AuthServiceInterface, + UsersProtoService, +} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; import { AuthSnowflakeSignInRequest, AuthSignInRequest, @@ -22,12 +32,18 @@ import { import { DucClient } from '../duc/client.config'; import { Metadata } from '@grpc/grpc-js'; import { BulkEditResponse, UserDTO } from './dtos/login'; -import jwt from 'jsonwebtoken'; +import jwt, { JwtPayload } from 'jsonwebtoken'; import { PackTheMetadata } from 'src/utils/PackTheMetadata'; +import { Request, Response } from 'express'; + +type AuthSession = { + accessToken?: string; + refreshToken?: string; + userId?: string; +}; @Injectable() export class AuthClientService implements OnModuleInit { - logger: DadosferaLogger; private authService: AuthServiceInterface; @@ -63,11 +79,11 @@ export class AuthClientService implements OnModuleInit { return lastValueFrom(this.authService.AuthSnowflakeSignIn(input)); } - checkDedicatedProxy({ - customer - }: AuthSignInResponse) { + checkDedicatedProxy({ customer }: AuthSignInResponse) { const DEDICATED_PROXY = process.env.DEDICATED_PROXY || ''; - this.logger.info('SignIn - Setting customer ID for dedicated proxy: ' + DEDICATED_PROXY); + this.logger.info( + 'SignIn - Setting customer ID for dedicated proxy: ' + DEDICATED_PROXY, + ); this.logger.info('Customer ID: ' + customer.id); if (DEDICATED_PROXY !== '' && DEDICATED_PROXY !== customer.id) { @@ -75,7 +91,9 @@ export class AuthClientService implements OnModuleInit { } // Bloquear o customer de acesso o maestro publico - this.logger.info('Check if customer have network policy: ' + customer.modules); + this.logger.info( + 'Check if customer have network policy: ' + customer.modules, + ); const hasNetworkPolicyModule = customer.modules.includes('network-policy'); if (hasNetworkPolicyModule && DEDICATED_PROXY === '') { throw new ForbiddenException(); @@ -94,7 +112,6 @@ export class AuthClientService implements OnModuleInit { result = await lastValueFrom( this.authService.AuthSignIn({ username, password, totp }, metadata), ); - } catch (error) { this.logger.error('SignIn - Error during sign-in'); this.logger.error(error); @@ -105,7 +122,7 @@ export class AuthClientService implements OnModuleInit { this.checkDedicatedProxy(result); } - return result + return result; } async refreshAccessToken( @@ -115,7 +132,10 @@ export class AuthClientService implements OnModuleInit { this.logger.info('RefreshAccessToken'); return lastValueFrom( - this.authService.AuthRefreshAccessToken({ refreshToken, userId }, metadata), + this.authService.AuthRefreshAccessToken( + { refreshToken, userId }, + metadata, + ), ); } @@ -284,50 +304,189 @@ export class AuthClientService implements OnModuleInit { } } - private async getUser(id: string, metadata: Metadata) { - this.logger.info('getUser'); - return await lastValueFrom( - this.userService.UserFindOneById({ id }, metadata), - ); + public async validateUserSession(accessToken: any, resourceHost: string) { + const payload = await this.validateJwtToken(accessToken); + + const userDto = await this.getUserfromPayload(payload); + + this.validateResourceAccess(resourceHost, userDto); + return userDto; } - public async extractUserFrom(token: string) { - const decoded: any = token && jwt.decode(token, { complete: true }); - if (!decoded) throw new Error('Invalid token'); + public async refreshUserSession( + refreshToken: string, + userId: string, + originHeader: string, + ): Promise<{ + user: UserDTO; + authSession: AuthSession; + }> { + const metadata = PackTheMetadata({}); - const { kid } = decoded.header; - // Busca a chave pública - const { keys } = await this.getPublicKeys(); - const pemValue = keys.find((k) => k.kid === kid)?.pem; - if (!pemValue) - throw new Error('Public key not found'); - jwt.verify(token, pemValue); + this.logger.info('Call Refresh Token'); + const refreshCredentials = await this.refreshAccessToken( + { refreshToken, userId }, + metadata, + ); + this.logger.info('Finish Refresh Token'); - const payload = decoded.payload; - const metadata = PackTheMetadata({ - customer_id: payload.customer_id - }) + const userDto = await this.validateUserSession( + refreshCredentials.accessToken, + originHeader, + ); + return { + user: userDto, + authSession: { + accessToken: refreshCredentials.accessToken, + refreshToken: refreshCredentials.refreshToken, + userId, + }, + }; + } - const { - user - } = await this.getUser( - payload.user_id, - metadata - ); + public writeAuthSession(res: Response, data: AuthSession) { + let exp = 1000 * 60 * 5; // 5 minutes - const userDto: UserDTO = { - id: user.id, - name: user.username, - jobTitle: user?.jobTitle || null, - department: user?.department || null, - hierarchy: user?.hierarchy || null, - customer: { - id: payload.customer_id, - name: payload.customer_name, - tier: payload.customer_tier, - } - }; + if (data.accessToken) { + const { exp: expiration } = jwt.decode(data.accessToken) as JwtPayload; + exp = (expiration - 30) * 1000; // exp em segundos, maxAge em ms - return userDto; + this.logger.info('Set Cookie ddf-auth'); + res.cookie('ddf-auth', data.accessToken, { + domain: '.dadosfera.ai', + maxAge: exp, + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); } + + if (data.refreshToken) { + this.logger.info('Set Cookie ddf-refresh-auth'); + res.cookie('ddf-refresh-auth', data.refreshToken, { + domain: '.dadosfera.ai', + maxAge: exp, + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + } + + if (data.userId) { + this.logger.info('Set Cookie ddf-refresh-auth'); + res.cookie('ddf-user-id', data.userId, { + domain: '.dadosfera.ai', + maxAge: exp, + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + } + } + + public cleanUpAuthSession(res: Response) { + const exp = 1000 * 60 * 3; + + res.cookie('ddf-auth', '', { + domain: 'dadosfera.ai', + maxAge: Date.now() - exp, + expires: new Date(), + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + + res.cookie('ddf-refresh-auth', '', { + domain: 'dadosfera.ai', + maxAge: Date.now() - exp, + expires: new Date(), + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + + this.logger.info('Clean cookie sessions'); + } + + private async validateJwtToken(token: string) { + const decoded: any = token && jwt.decode(token, { complete: true }); + if (!decoded) throw new Error('Invalid token'); + + const { kid } = decoded.header; + // Busca a chave pública + const { keys } = await this.getPublicKeys(); + const pemValue = keys.find((k) => k.kid === kid)?.pem; + if (!pemValue) throw new Error('Public key not found'); + jwt.verify(token, pemValue); + + return decoded.payload; + } + + private async getUserfromPayload(payload: JwtPayload): Promise { + this.logger.info('getUser'); + + const metadata = PackTheMetadata({ + customer_id: payload.customer_id, + }); + + const { user } = await lastValueFrom( + this.userService.UserFindOneById({ id: payload.user_id }, metadata), + ); + + const userDto: UserDTO = { + id: user.id, + name: user.username, + jobTitle: user?.jobTitle || null, + department: user?.department || null, + hierarchy: user?.hierarchy || null, + customer: { + id: payload.customer_id, + name: payload.customer_name, + tier: payload.customer_tier, + }, + }; + + return userDto; + } + + private validateResourceAccess(host: string, user: UserDTO) { + this.logger.info( + "Validate whether the source URL is a resource belonging to the user's client", + ); + this.logger.info('Host: ' + host); + this.logger.info('Customer: ' + user.customer.name); + + const hostParts = host.split('.'); + const domain = hostParts[0]; + const isResouceStg = hostParts[1] === 'stg'; + + const notFoundCustomerInDomain = !domain.includes('-') + + if (notFoundCustomerInDomain) { + this.logger.info(`Not found Customer Name in domain`); + return; + } + + const domainParts = domain.split('-'); + + const customerInDomain = domainParts[domainParts.length - 1]; + + if (isResouceStg && process.env.ENV !== 'stg') { + this.logger.error(`Customer ${user.customer.name} cannot access ${host}`); + throw new HttpException( + `Customer ${user.customer.name} cannot access ${host}`, + HttpStatus.FORBIDDEN + ); + } + + if (customerInDomain != user.customer.name) { + this.logger.error(`Customer ${user.customer.name} cannot access ${host}`); + throw new HttpException( + `Customer ${user.customer.name} cannot access ${host}`, + HttpStatus.FORBIDDEN + ); + } + + return; + } } diff --git a/src/modules/catalog/catalog.controller.ts b/src/modules/catalog/catalog.controller.ts index b87829b..83c1386 100644 --- a/src/modules/catalog/catalog.controller.ts +++ b/src/modules/catalog/catalog.controller.ts @@ -423,6 +423,7 @@ export class CatalogController { @User() user: RequestUser, @Language() language: LanguageEnum, @Param('id') id: string, + @Query('asset_type') asset_type: string, ): Promise { const { customer_name, customer_id, user_id, username } = user; @@ -439,7 +440,7 @@ export class CatalogController { language, }); - const docs = await this.catalogService.getDataDocs(id, metadata); + const docs = await this.catalogService.getDataDocs(id, asset_type, metadata); return { docs }; } @@ -487,21 +488,32 @@ export class CatalogController { @Headers() headers, @Param('id') table_id: string, @Body('docs') docs: string, + @Query('asset_type') asset_type: string, ) { - const { user_id, customer_name } = user; + const { user_id, customer_name, customer_id, username } = user; + + const metadata = PackTheMetadata({ + customer_id, + customer_name, + user_id, + username, + }); - this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, { + this.logger.info(`/catalog - ON POST DATA DOCS ROUTE`, { user_id, customer_name, }); - const res = await this.catalogService.createDataDocs({ + const body = { table_id, docs, + asset_type, info: { customer: customer_name, }, - }); + } + + const res = await this.catalogService.createDataDocs(body, metadata); return res; } diff --git a/src/modules/catalog/catalog.service.ts b/src/modules/catalog/catalog.service.ts index 1848dcd..89dcf84 100644 --- a/src/modules/catalog/catalog.service.ts +++ b/src/modules/catalog/catalog.service.ts @@ -24,9 +24,11 @@ import { CatalogClientConfiguration } from './catalog-client'; import { UsersService } from '../users/users.service'; import { RolesService } from '../roles/roles.service'; import { Metadata } from '@grpc/grpc-js'; +import { PackTheMetadata } from 'src/utils/PackTheMetadata'; import { AssetReporter, BatchRemoveRlsRulesRequest, + CreateDataDocsDTO, IUpdateDataRequest, TriggerCatalogReq, } from './dtos'; @@ -85,25 +87,23 @@ class CatalogService implements OnModuleInit { } async getPiiReporter(metadata: Metadata, type: TypeParser) { - this.logger.info('getPiiReporter: ' + type) + this.logger.info('getPiiReporter: ' + type); try { - const { - data - } = await lastValueFrom( - this.catalogWriteService.GetPiiReporter({}, metadata) - ) - this.logger.info("Finish grpc call") + const { data } = await lastValueFrom( + this.catalogWriteService.GetPiiReporter({}, metadata), + ); + this.logger.info('Finish grpc call'); const parser = ParserBuilder.build(type); - this.logger.info('parser file to: ' + type) - const file = await parser.parse(data) - this.logger.info('finish parser') + this.logger.info('parser file to: ' + type); + const file = await parser.parse(data); + this.logger.info('finish parser'); const mimeTypes: Record = { - 'csv': 'text/csv', - 'html': 'text/html', - 'pdf': 'application/pdf' - } + csv: 'text/csv', + html: 'text/html', + pdf: 'application/pdf', + }; const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `relatorio-pii-${timestamp}.${type}`; @@ -111,13 +111,12 @@ class CatalogService implements OnModuleInit { return { file, filename: filename, - type: mimeTypes[type] - } + type: mimeTypes[type], + }; } catch (error) { this.logger.error(error.message); throw error; } - } async createDataAsset(data: Messages.CreateDataAssetRequest, metadata) { @@ -209,10 +208,99 @@ class CatalogService implements OnModuleInit { metadata: Metadata, customer_id: string, ) { - this.logger.info('CatalogService - searchDataAssets'); + this.logger.info('CatalogService - searchDataAssets', { query }); const { search, page, size, sort_by, order, ...filters } = query; + this.logger.debug('Extracted filters:', { filters }); + + console.log('MAESTRO VAI CHAMAR PI-FACTORY COM (ANTES AJUSTE):', { + search, + page, + size, + sort_by, + order, + filters, + }); + + if ( + filters.manually !== undefined && + filters.manually !== null && + filters.manually !== '' + ) { + filters.manually = Number(filters.manually); // 1 ou 0 + } else { + delete filters.manually; + } + + console.log('MAESTRO VAI CHAMAR PI-FACTORY COM (DEPOIS AJUSTE):', { + search, + page, + size, + sort_by, + order, + filters, + }); + + if (filters.owner) { + const { users: customer_users } = + await this.userService.findAllUsersByCustomerId(customer_id); + + this.logger.info('Available users in database count:', { + count: customer_users.length, + }); + this.logger.info('First 5 users:', { + users: customer_users + .slice(0, 5) + .map((u) => ({ id: u.id, email: u.email, name: u.name })), + }); + + const ownerValues = Array.isArray(filters.owner) + ? filters.owner + : typeof filters.owner === 'string' && filters.owner.includes(',') + ? filters.owner.split(',').map((o: string) => o.trim()) + : [filters.owner]; + + this.logger.info('Owner values to convert:', { + ownerValues, + ownerFiltersOriginal: filters.owner, + }); + + const ownerIds = ownerValues + .map((ownerValue: string) => { + const normalizedOwner = ownerValue.replace(/\s/g, '+'); + const user = customer_users.find((u) => { + const isIdMatch = u.id === ownerValue; + const isEmailMatch = + u.email === ownerValue || u.email === normalizedOwner; + const isNameMatch = + u.name === ownerValue || u.name === normalizedOwner; + this.logger.info('Comparing:', { + userId: u.id, + userEmail: u.email, + userName: u.name, + filterValue: ownerValue, + normalizedFilter: normalizedOwner, + idMatch: isIdMatch, + emailMatch: isEmailMatch, + nameMatch: isNameMatch, + }); + return isIdMatch || isEmailMatch || isNameMatch; + }); + this.logger.info('Looking for owner result:', { + ownerValue, + found: !!user, + userId: user?.id, + }); + return user?.id || ownerValue; + }) + .filter((id: string) => id); + + if (ownerIds.length > 0) { + filters.owner = ownerIds; + } + } + const { data_assets, total } = await lastValueFrom( this.catalogReadService.GetAllDataAssets( { @@ -227,6 +315,8 @@ class CatalogService implements OnModuleInit { ), ); + console.log('MAESTRO RECEBEU RESPOSTA DO PI-FACTORY'); + const result = JSON.parse(data_assets); const response = await this.getAssetsUsersAndRoles( @@ -244,13 +334,13 @@ class CatalogService implements OnModuleInit { ) { const data = await this.searchDataAssets(query, metadata, customer_id); - const formatData = data.data_assets.map(asset => ({ + const formatData = data.data_assets.map((asset) => ({ id: asset.id, display_name: asset.display_name, data_asset_type: asset.data_asset_type, created_at: asset.created_at, - tags: '[' + asset.tags.join(', ') + ']' - })) + tags: '[' + asset.tags.join(', ') + ']', + })); const parser = ParserBuilder.build('csv'); @@ -261,8 +351,8 @@ class CatalogService implements OnModuleInit { return { file, - filename - } + filename, + }; } async getOneDataAsset(data: { @@ -337,11 +427,11 @@ class CatalogService implements OnModuleInit { return { data_asset: asset[0] }; } - async getDataDocs(id: string, metadata: Metadata) { + async getDataDocs(id: string, assetType: string, metadata: Metadata) { const { documentation } = await lastValueFrom( - this.catalogReadService.GetDatasetDoc({ id, type: undefined }, metadata), + this.catalogReadService.GetDatasetDoc({ id }, metadata), ); - + const docs = JSON.parse(documentation); return docs; } @@ -368,7 +458,16 @@ class CatalogService implements OnModuleInit { return result; } - async createDataDocs(body) { + async createDataDocs(body: CreateDataDocsDTO, metadata: Metadata) { + if (body.asset_type === 'table' || body.asset_type === 'view') { + return this.createDataDocsViaNimbus(body); + } + + return this.createDataDocsViaGrpc(body, metadata); + } + + private async createDataDocsViaNimbus(body: CreateDataDocsDTO) { + this.logger.info('Creating data docs via Nimbus for table/view'); const nimbusUrl = this._getNimbusUrl(body); const { data } = await axios.post( `${nimbusUrl}/api/catalog/data-docs/`, @@ -377,6 +476,29 @@ class CatalogService implements OnModuleInit { return data; } + private async createDataDocsViaGrpc(body: CreateDataDocsDTO, metadata: Metadata) { + this.logger.info('Creating data docs via gRPC for other asset types'); + try { + const response: any = await lastValueFrom( + this.catalogWriteService.UpdateDataAssetDoc( + { + id: body.table_id, + docs: body.docs, + }, + metadata, + ), + ); + + return response; + } catch (error) { + this.logger.error('Error creating data asset docs:', error); + throw new HttpException( + 'Failed to create data asset documentation', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async findAllTags(data, metadata) { this.logger.info('CatalogService - findAllCustomerTags'); @@ -394,6 +516,7 @@ class CatalogService implements OnModuleInit { return response; } + async getAssetsUsersAndRoles(data_assets: Array, customer_id: string) { const { users: customer_users } = await this.userService.findAllUsersByCustomerId(customer_id); @@ -404,20 +527,20 @@ class CatalogService implements OnModuleInit { return data_assets.map((data_asset) => { const owner = customer_users.find( (u) => u.id === data_asset.owner, - )?.username; + )?.email; const roles = []; const users = []; - const data_asset_roles = data_asset?.roles || [] + const data_asset_roles = data_asset?.roles || []; for (const role_id of data_asset_roles) { const role = customer_roles.find((r) => r.id === role_id); if (role) roles.push({ id: role.id, name: role.name }); } - const data_asset_users = data_asset?.users || [] + const data_asset_users = data_asset?.users || []; for (const user_id of data_asset_users) { const user = customer_users.find((r) => r.id === user_id); - if (user) users.push({ id: user.id, username: user.username }); + if (user) users.push({ id: user.id, email: user.email }); } return { ...data_asset, @@ -507,27 +630,36 @@ class CatalogService implements OnModuleInit { async createTableMetadata(body: any): Promise { const nimbusUrl = this._getNimbusUrl(body); - this.logger.info(`Nimbus URL: ${nimbusUrl}`, {...body.logMetadata}); + this.logger.info(`Nimbus URL: ${nimbusUrl}`, { ...body.logMetadata }); const endpoint = `${nimbusUrl}/api/catalog/table-metadata/`; - this.logger.info(`Creating table metadata for table ${body.table_metadata.table_name}`, {...body.logMetadata}); - this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata}); - this.logger.debug(`Payload: ${JSON.stringify(body.table_metadata)}`, {...body.logMetadata}); + this.logger.info( + `Creating table metadata for table ${body.table_metadata.table_name}`, + { ...body.logMetadata }, + ); + this.logger.info(`Using endpoint: ${endpoint}`, { ...body.logMetadata }); + this.logger.debug(`Payload: ${JSON.stringify(body.table_metadata)}`, { + ...body.logMetadata, + }); try { - const { data, status } = await axios.post(endpoint, {...body.table_metadata}); + const { data, status } = await axios.post(endpoint, { + ...body.table_metadata, + }); this.logger.info( `Table metadata created successfully with status ${status} for table ${body.table_metadata.table_name}`, - {...body.logMetadata}, + { ...body.logMetadata }, ); return data.id; } catch (error) { this.logger.error( `Failed to create table metadata for table ${body.table_metadata.table_name} failed with status ${ error.response?.status - } because of ${JSON.stringify(error.response?.data) || error.message}`, {...body.logMetadata}); + } because of ${JSON.stringify(error.response?.data) || error.message}`, + { ...body.logMetadata }, + ); throw new Error(error.response?.data?.message || error.message); } } @@ -537,43 +669,58 @@ class CatalogService implements OnModuleInit { this.logger.info(`Nimbus URL: ${nimbusUrl}`, body.logMetadata); const endpoint = `${nimbusUrl}/api/catalog/column-metadata/`; - - try { - this.logger.info(`Creating column metadata for table ${body.column_metadata.table_name}`, {...body.logMetadata}); - this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata}); - this.logger.debug(`Payload: ${JSON.stringify(body.column_metadata)}`, {...body.logMetadata}); - const { data, status } = await axios.post(endpoint, body.column_metadata); + this.logger.info( + `Creating column metadata for table ${body.column_metadata.table_name}`, + { ...body.logMetadata }, + ); + this.logger.info(`Using endpoint: ${endpoint}`, { ...body.logMetadata }); + this.logger.debug( + `Payload: ${JSON.stringify(body.column_metadata)}`, + { ...body.logMetadata }, + ); + const { data, status } = await axios.post( + endpoint, + body.column_metadata, + ); this.logger.info( `Column metadata created successfully with status ${status} for table ${body.column_metadata.table_name}`, - {...body.logMetadata}, + { ...body.logMetadata }, ); return data.map((column) => column.id); } catch (error) { this.logger.error( `Failed to create column metadata failed with status for table ${body.column_metadata.table_name} ${ error.response?.status - } because of ${error.response?.data || error.message}`, {...body.logMetadata}); + } because of ${error.response?.data || error.message}`, + { ...body.logMetadata }, + ); throw new Error(error.response?.data?.message || error.message); } } async createDataPreview(body: any): Promise { const nimbusUrl = this._getNimbusUrl(body); - this.logger.info(`Nimbus URL: ${nimbusUrl}`, {...body.logMetadata}); + this.logger.info(`Nimbus URL: ${nimbusUrl}`, { ...body.logMetadata }); const endpoint = `${nimbusUrl}/api/catalog/data-preview/`; - this.logger.info(`Creating data preview for table ${body.data_preview.table_name}`, {...body.logMetadata}); - this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata}); - this.logger.debug(`Payload: ${JSON.stringify(body.data_preview)}`, {...body.logMetadata}); + this.logger.info( + `Creating data preview for table ${body.data_preview.table_name}`, + { ...body.logMetadata }, + ); + this.logger.info(`Using endpoint: ${endpoint}`, { ...body.logMetadata }); + this.logger.debug( + `Payload: ${JSON.stringify(body.data_preview)}`, + { ...body.logMetadata }, + ); try { const { data, status } = await axios.post(endpoint, body.data_preview); this.logger.info( `Data preview created successfully with status ${status} for table ${body.data_preview.table_name}`, - {...body.logMetadata}, + { ...body.logMetadata }, ); return data.id; } catch (error) { @@ -581,7 +728,7 @@ class CatalogService implements OnModuleInit { `Failed to create data preview for table ${body.data_preview.table_name} failed with status ${ error.response?.status } because of ${error.response?.data || error.message}`, - {...body.logMetadata}, + { ...body.logMetadata }, ); throw new Error(error.response?.data?.message || error.message); } @@ -612,4 +759,4 @@ class CatalogService implements OnModuleInit { } } -export { CatalogService }; \ No newline at end of file +export { CatalogService }; diff --git a/src/modules/catalog/dtos/index.ts b/src/modules/catalog/dtos/index.ts index ef12e78..d8e3c2d 100644 --- a/src/modules/catalog/dtos/index.ts +++ b/src/modules/catalog/dtos/index.ts @@ -147,6 +147,24 @@ export class ICatalogAllRequest { description: 'Tipo de ordenação - `asc`: crescente; `desc`: decrescente ', }) order?: OrderEnum; + + @ApiPropertyOptional({ + description: 'ID do usuário owner para filtrar data assets', + example: 'user-id-1,user-id-2', + }) + owner?: string; + + @ApiPropertyOptional({ + description: 'Data inicial para filtro de catálogo (formato: YYYY-MM-DD)', + example: '2025-01-01', + }) + catalog_date_from?: string; + + @ApiPropertyOptional({ + description: 'Data final para filtro de catálogo (formato: YYYY-MM-DD)', + example: '2025-12-31', + }) + catalog_date_to?: string; } export class ICatalogAllResponse { @@ -328,3 +346,10 @@ export type AssetReporter = { created_at: string; tags: string; } + +export type CreateDataDocsDTO = { + table_id: string; + docs: string; + asset_type: string; + +} \ No newline at end of file diff --git a/src/modules/catalog/share/share.service.ts b/src/modules/catalog/share/share.service.ts index 3e088a6..7c45128 100644 --- a/src/modules/catalog/share/share.service.ts +++ b/src/modules/catalog/share/share.service.ts @@ -160,7 +160,7 @@ export class ShareService implements OnModuleInit { }); const { documentation } = await lastValueFrom( - this.catalogReadService.GetDatasetDoc({ id, type: undefined }, metadata), + this.catalogReadService.GetDatasetDoc({ id }, metadata), ); console.log(documentation); const docs = JSON.parse(documentation); @@ -180,7 +180,7 @@ export class ShareService implements OnModuleInit { return data_assets.map((data_asset) => { const owner = customer_users.find( (u) => u.id === data_asset.owner, - )?.username; + )?.email; const roles = []; const users = []; @@ -190,7 +190,7 @@ export class ShareService implements OnModuleInit { } for (const user_id of data_asset.users) { const user = customer_users.find((r) => r.id === user_id); - if (user) users.push({ id: user.id, username: user.username }); + if (user) users.push({ id: user.id, email: user.email }); } return { ...data_asset, diff --git a/src/modules/platform-api/platform-api.config.ts b/src/modules/platform-api/platform-api.config.ts new file mode 100644 index 0000000..5875059 --- /dev/null +++ b/src/modules/platform-api/platform-api.config.ts @@ -0,0 +1,11 @@ +export const PLATFORM_API_CONFIG = { + getUrl: (): string => { + const url = process.env.PLATFORM_API_URL; + if (!url) { + throw new Error('PLATFORM_API_URL environment variable is not set'); + } + return url; + }, + region: process.env.AWS_REGION || 'us-east-1', + timeout: parseInt(process.env.PLATFORM_API_TIMEOUT || '30000', 10), +}; diff --git a/src/modules/platform-api/platform-api.controller.ts b/src/modules/platform-api/platform-api.controller.ts new file mode 100644 index 0000000..e0d59f2 --- /dev/null +++ b/src/modules/platform-api/platform-api.controller.ts @@ -0,0 +1,977 @@ +import { + Controller, + Get, + Post, + Put, + Patch, + Delete, + Param, + Body, + Query, + Inject, + BadRequestException, +} from '@nestjs/common'; +import { ApiTags, ApiOperation } from '@nestjs/swagger'; +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; + +import { + Authenticated, + RequireAllPermissions, +} from '../../decorators/authentication.decorator'; +import { User, RequestUser } from '../../decorators/user.decorator'; +import { PlatformApiService } from './platform-api.service'; +import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum'; +import { ElasticsearchService } from '../../services/elasticsearch'; +import { DynamoDBService, ReferenceColumn } from '../../services/dynamodb'; +import { CustomersService } from '../customers/customers.service'; +import { validateCronAgainstScheduleLimit } from '../../utils/cron-validation'; + +@ApiTags('Platform API') +@Controller('platform') +export class PlatformApiController { + private logger: any; + + constructor( + private readonly platformApiService: PlatformApiService, + private readonly elasticsearchService: ElasticsearchService, + private readonly dynamoDBService: DynamoDBService, + private readonly customersService: CustomersService, + @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, + ) { + this.logger = dadosferaLogger.logger; + } + + /** + * Normalize pipeline ID to match Platform-API format. + * Platform-API replaces '-' with '_' in pipeline IDs. + */ + private normalizePipelineId(id: string): string { + return id?.replace(/-/g, '_') || ''; + } + + /** + * Denormalize ID back to UUID format (replace _ with -). + * Used when we receive a normalized ID but need the original UUID. + */ + private denormalizeId(id: string): string { + return id?.replace(/_/g, '-') || ''; + } + + /** + * Normalize job ID to match Platform-API format. + * Platform-API replaces '-' with '_' in job IDs. + * + * Example: "2ccf5481-59f5-4036-8a94-7d5f28f4f899-0" -> "2ccf5481_59f5_4036_8a94_7d5f28f4f899_0" + */ + private normalizeJobId(jobId: string): string { + return jobId?.replace(/-/g, '_') || ''; + } + + /** + * Extract the pipeline ID (base UUID) from a job ID. + * Job IDs have format "uuid-suffix" where suffix is the job index (e.g., "0", "1"). + * Handles both hyphenated and underscored formats, always returns hyphenated UUID for ES. + * + * Examples: + * - "2ccf5481-59f5-4036-8a94-7d5f28f4f899-0" -> "2ccf5481-59f5-4036-8a94-7d5f28f4f899" + * - "2ccf5481_59f5_4036_8a94_7d5f28f4f899_0" -> "2ccf5481-59f5-4036-8a94-7d5f28f4f899" + */ + private extractPipelineIdFromJobId(jobId: string): string { + if (!jobId) return ''; + + // Determine the separator used in the jobId + const hasUnderscores = jobId.includes('_'); + const separator = hasUnderscores ? '_' : '-'; + + const parts = jobId.split(separator); + // UUID has 5 parts (8-4-4-4-12), job suffix is the 6th part + if (parts.length >= 6) { + // Always return hyphenated format for Elasticsearch lookup + return parts.slice(0, 5).join('-'); + } + // If no suffix found, return the ID in hyphenated format + return hasUnderscores ? jobId.replace(/_/g, '-') : jobId; + } + + private readonly VALID_CONNECTORS = ['jdbc', 'singer', 's3']; + private readonly MAX_MEMORY_MB = 12000; // 12GB maximum memory per pipeline/job + + /** + * Validate that connector is provided and is a valid type. + */ + private validateConnector(connector: string | undefined): void { + if (!connector || !this.VALID_CONNECTORS.includes(connector)) { + throw new BadRequestException( + `connector is required in job input and must be one of: ${this.VALID_CONNECTORS.join(', ')}`, + ); + } + } + + /** + * Validate memory allocation against maximum limit. + */ + private validateMemory(memoryMb: number): void { + if (memoryMb > this.MAX_MEMORY_MB) { + throw new BadRequestException( + `Memory limit exceeded. Maximum allowed: ${this.MAX_MEMORY_MB}MB (12GB)`, + ); + } + } + + /** + * Validate cron expression against customer's schedule limit. + * Fetches current scheduleLimit from DUC to ensure up-to-date configuration. + */ + private async validateScheduleLimit(cron: string, customerId: string): Promise { + if (!cron) return; + + const { customer } = await this.customersService.getCustomer(customerId); + const scheduleLimit = customer?.scheduleLimit || 'day'; + + const result = validateCronAgainstScheduleLimit(cron, scheduleLimit); + if (!result.valid) { + throw new BadRequestException(result.message); + } + } + + /** + * Map connector type to DynamoDB type. + * jdbc -> 'database', singer -> 'application', s3 -> 'file' + */ + private mapConnectorToDynamoType(connector: string): string { + switch (connector) { + case 'jdbc': + return 'database'; + case 'singer': + return 'application'; + case 's3': + return 'file'; + default: + return connector; + } + } + + /** + * Extract and transform tables from jobs for DynamoDB input. + * Maps connector-specific fields to a common table format. + * + * - JDBC: load_type, table_name, column_include_list (columns), incremental_column_name/type (reference_column object) + * - Singer: type maps replication_method (FULL_TABLE -> full_load, INCREMENTAL -> incremental), no columns + * - S3: same mapping as Singer, no columns + */ + private extractTablesFromJobs(jobs: any[], connector: string): Array<{ + name: string; + type: string; + columns?: string[]; + reference_column?: ReferenceColumn; + }> { + if (!jobs || jobs.length === 0) return []; + + const tables: Array<{ + name: string; + type: string; + columns?: string[]; + reference_column?: ReferenceColumn; + }> = []; + + for (const job of jobs) { + const input = job.input; + if (!input) continue; + + if (connector === 'jdbc') { + // JDBC: table_name, load_type, column_include_list, incremental_column_name/type + const table: { + name: string; + type: string; + columns?: string[]; + reference_column?: ReferenceColumn; + } = { + name: input.table_name || '', + type: input.load_type || 'full_load', + }; + if (input.column_include_list && input.column_include_list.length > 0) { + table.columns = input.column_include_list; + } + if (input.incremental_column_name) { + // reference_column is stored as an object with name and type + table.reference_column = { + name: input.incremental_column_name, + type: input.incremental_column_type || 'unknown', + }; + } + tables.push(table); + } else if (connector === 'singer' || connector === 's3') { + // Singer/S3: replication_method -> type mapping, no columns + let type = 'full_load'; + if (input.replication_method === 'INCREMENTAL') { + type = 'incremental'; + } else if (input.replication_method === 'FULL_TABLE') { + type = 'full_load'; + } + + tables.push({ + name: input.table_name || '', + type, + }); + } + } + + return tables; + } + + /** + * Build properties object for Elasticsearch based on connector type. + * Different connectors have different property structures. + * + * Note: In pi-factory flow, properties come pre-built from frontend. + * In Maestro proxy flow, we reconstruct from job input fields. + */ + private buildPipelineProperties(jobInput: any): Record { + if (!jobInput) return {}; + + const connector = jobInput.connector; + const properties: Record = {}; + + // Determine credentials_type + if (jobInput.auth_parameters?.credentials_type) { + properties.credentials_type = jobInput.auth_parameters.credentials_type; + } else { + // Default based on connector type + // S3 connector typically uses iam_user, others use basic_auth + properties.credentials_type = connector === 's3' ? 'iam_user' : 'basic_auth'; + } + + if (connector === 'jdbc') { + // JDBC connectors: schema comes from table_schema + if (jobInput.table_schema) { + properties.schema = jobInput.table_schema; + } + } else if (connector === 'singer') { + // Singer connectors: merge config fields (dates, selected_competitions, etc.) + if (jobInput.config) { + Object.assign(properties, jobInput.config); + } + } else if (connector === 's3') { + // S3 connector + if (jobInput.engine) properties.engine = jobInput.engine; + if (jobInput.source_bucket) properties.source_bucket = jobInput.source_bucket; + if (jobInput.source_prefix) properties.source_prefix = jobInput.source_prefix; + if (jobInput.file_format_params) properties.file_format_params = jobInput.file_format_params; + } + + return properties; + } + + /** + * Sync job input changes to DynamoDB for a specific connector type. + * Extracts pipeline ID from job ID, fetches ES document to find input ID, + * then updates the table entry in DynamoDB. + * + * Job ID transformations: + * - Raw format (from endpoint): "2ccf5481-59f5-4036-8a94-7d5f28f4f899-0" + * - Platform API format: "2ccf5481_59f5_4036_8a94_7d5f28f4f899_0" (underscores) + * - Elasticsearch pipeline ID: "2ccf5481-59f5-4036-8a94-7d5f28f4f899" (UUID only, hyphens) + * + * @param connectorType - The connector type ('jdbc', 'singer', 's3') for the Platform API endpoint + */ + private async syncJobInputToDynamoDB( + jobId: string, + body: any, + user: RequestUser, + connectorType: 'jdbc' | 'singer' | 's3', + ): Promise { + try { + // Normalize job ID for Platform API GET (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + // Get job details using connector-specific endpoint to find table_name + const jobResult = await this.platformApiService.proxy( + 'GET', + `/jobs/${connectorType}/${normalizedJobId}`, + user, + ); + + // Extract the pipeline ID (base UUID) from the raw job ID for ES lookup + const esPipelineId = this.extractPipelineIdFromJobId(jobId); + const tableName = body.table_name || jobResult.source_config?.table_name; + + if (!esPipelineId || !tableName) { + this.logger.warn('Cannot sync job input: missing pipeline_id or table_name', { + jobId, + esPipelineId, + tableName, + }); + return; + } + + // Get pipeline from ES to find input ID (stored in config.tables) + const pipeline = await this.elasticsearchService.getPipeline( + user.customer_name, + esPipelineId, + ); + + const inputId = pipeline?.config?.tables; + if (!inputId) { + this.logger.warn('Cannot sync job input: no input ID in ES', { + jobId, + esPipelineId, + }); + return; + } + + // Build changes for DynamoDB table entry + // reference_column is stored as an object with name and type + const changes: { + type?: string; + columns?: string[]; + reference_column?: ReferenceColumn | null; + } = {}; + + + if ('target_load_type' in body) { + changes.type = body.target_load_type; + } + if ('column_include_list' in body) { + changes.columns = body.column_include_list; + } + if ('incremental_column_name' in body) { + // reference_column is stored as an object with name and type + if (body.incremental_column_name) { + changes.reference_column = { + name: body.incremental_column_name, + type: body.incremental_column_type || 'unknown', + }; + } else { + changes.reference_column = null; + } + } + + // Update DynamoDB if there are changes + if (Object.keys(changes).length > 0) { + await this.dynamoDBService.updateInputTable( + user.customer_id, + inputId, + tableName, + changes, + ); + } + } catch (error) { + this.logger.error('Failed to sync job input to DynamoDB', { + jobId, + connectorType, + error: error.message, + }); + // Don't throw - Platform API update succeeded, just log the sync error + } + } + + /** + * Sync sync-mode changes to DynamoDB for JDBC connectors. + * Always passes both target_load_type and incremental_column_name to ensure proper sync. + */ + private async syncJdbcSyncModeToDynamoDB( + jobId: string, + body: any, + user: RequestUser, + ): Promise { + // JDBC sync mode uses target_load_type field + const changes: any = {}; + + if ('target_load_type' in body) { + changes.target_load_type = body.target_load_type; + } + + // Handle incremental_column_name: + // - If provided in body, use that value + // - If changing to full_load, explicitly clear it + if ('incremental_column_name' in body) { + changes.incremental_column_name = body.incremental_column_name; + changes.incremental_column_type = body.incremental_column_type; + } else if (body.target_load_type === 'full_load') { + // Changing to full_load without specifying incremental_column - clear it + changes.incremental_column_name = null; + } + + await this.syncJobInputToDynamoDB(jobId, changes, user, 'jdbc'); + } + + /** + * Sync sync-mode changes to DynamoDB for Singer connectors. + */ + private async syncSingerSyncModeToDynamoDB( + jobId: string, + body: any, + user: RequestUser, + ): Promise { + // Singer sync mode uses replication_method field + // Map to DynamoDB type: FULL_TABLE -> full_load, INCREMENTAL -> incremental + if ('replication_method' in body) { + const type = body.replication_method === 'INCREMENTAL' ? 'incremental' : 'full_load'; + await this.syncJobInputToDynamoDB(jobId, { load_type: type }, user, 'singer'); + } + } + + // ==================== PIPELINE ROUTES ==================== + + @Post('pipeline') + @ApiOperation({ summary: 'Create a new pipeline' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE) + async createPipeline(@Body() body: any, @User() user: RequestUser) { + // Validate that pipeline has jobs + if (!body.jobs || body.jobs.length === 0) { + throw new BadRequestException('Pipeline must have at least one job'); + } + + // Validate connector before proceeding + const firstJob = body.jobs[0]?.input; + this.validateConnector(firstJob?.connector); + + // Validate cron against customer's schedule limit + await this.validateScheduleLimit(body.cron, user.customer_id); + + // Inject customer_id (actually customer_name) and normalized ID into body for Platform-API + // Note: Platform-API was created before customer_id existed, so it expects customer_name in the customer_id field + const enrichedBody = { + ...body, + id: this.normalizePipelineId(body.id), + customer_id: user.customer_name, + }; + + const result = await this.platformApiService.proxy('POST', '/pipeline', user, enrichedBody); + + // Sync to Elasticsearch and DynamoDB + try { + const plugin = firstJob?.plugin; + const connectorType = firstJob?.connector; + const connectionId = firstJob?.auth_parameters?.config_id; + + // Build properties based on connector type + const properties = this.buildPipelineProperties(firstJob); + + const connector = plugin + ? await this.elasticsearchService.getConnectorByPlugin(plugin) + : null; + + // Extract tables from jobs and create DynamoDB input + const tables = this.extractTablesFromJobs(body.jobs, connectorType); + let inputId: string | undefined; + + if (tables.length > 0) { + const inputDoc = await this.dynamoDBService.createInput( + user.customer_id, + user.user_id, + { + name: body.name, + description: body.description, + plugin: plugin || '', + type: this.mapConnectorToDynamoType(connectorType), + tables, + }, + ); + inputId = inputDoc.id; + this.logger.info('Created DynamoDB input for tables config', { + inputId, + pipelineId: body.id, + tablesCount: tables.length, + }); + } + + const pipelineType = this.mapConnectorToDynamoType(connectorType); + this.logger.info('Syncing pipeline to Elasticsearch', { + customerName: user.customer_name, + pipelineId: body.id, + plugin, + connector: connectorType, + type: pipelineType, + properties, + inputId, + }); + + // Keep original UUID format for Elasticsearch (not normalized) + await this.elasticsearchService.createPipeline( + user.customer_name, + body.id, + { + name: body.name, + description: body.description, + user_id: user.user_id, + username: user.username, + customer_id: user.customer_id, + plugin, + connection_id: connectionId, + cron: body.cron, + tables: inputId, + properties, + type: pipelineType, + }, + connector, + ); + } catch (error) { + this.logger.error('Failed to sync pipeline creation to Elasticsearch/DynamoDB', { + pipelineId: body.id, + customerName: user.customer_name, + error: error.message, + errorName: error.name, + }); + } + + return result; + } + + @Get('pipelines') + @ApiOperation({ summary: 'List all pipelines for customer' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getPipelines( + @User() user: RequestUser, + @Query() query: Record, + ) { + return this.platformApiService.proxy( + 'GET', + '/pipelines', + user, + undefined, + query, + ); + } + + @Get('pipeline/:pipelineId') + @ApiOperation({ summary: 'Get pipeline by ID' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getPipeline( + @Param('pipelineId') pipelineId: string, + @User() user: RequestUser, + ) { + const normalizedId = this.normalizePipelineId(pipelineId); + return this.platformApiService.proxy('GET', `/pipeline/${normalizedId}`, user); + } + + @Patch('pipeline/:pipelineId') + @ApiOperation({ summary: 'Update pipeline by ID' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updatePipeline( + @Param('pipelineId') pipelineId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Validate cron against customer's schedule limit if cron is being updated + if (body.cron) { + await this.validateScheduleLimit(body.cron, user.customer_id); + } + + const normalizedId = this.normalizePipelineId(pipelineId); + const result = await this.platformApiService.proxy( + 'PATCH', + `/pipeline/${normalizedId}`, + user, + body, + ); + + // Sync to Elasticsearch (use original UUID, not normalized) + // Only pass fields that are explicitly provided in the request body + try { + const esChanges: { + name?: string; + description?: string; + cron?: string; + status?: string; + } = {}; + + if ('name' in body) esChanges.name = body.name; + if ('description' in body) esChanges.description = body.description; + if ('cron' in body) esChanges.cron = body.cron; + if ('status' in body) esChanges.status = body.status; + + await this.elasticsearchService.updatePipeline( + user.customer_name, + pipelineId, + esChanges, + ); + } catch (error) { + this.logger.error('Failed to sync pipeline update to Elasticsearch', { + pipelineId, + error: error.message, + }); + } + + return result; + } + + @Delete('pipeline/:pipelineId') + @ApiOperation({ summary: 'Delete pipeline by ID' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE) + async deletePipeline( + @Param('pipelineId') pipelineId: string, + @User() user: RequestUser, + ) { + const normalizedId = this.normalizePipelineId(pipelineId); + const result = await this.platformApiService.proxy( + 'DELETE', + `/pipeline/${normalizedId}`, + user, + ); + + // Sync to Elasticsearch (use original UUID, not normalized) + try { + await this.elasticsearchService.deletePipeline( + user.customer_name, + pipelineId, + ); + } catch (error) { + this.logger.error('Failed to sync pipeline deletion to Elasticsearch', { + pipelineId, + error: error.message, + }); + } + + return result; + } + + @Post('pipeline/execute') + @ApiOperation({ summary: 'Execute a pipeline' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async executePipeline(@Body() body: any, @User() user: RequestUser) { + // Inject customer_id (actually customer_name) into body for Platform-API + // Note: Platform-API was created before customer_id existed, so it expects customer_name in the customer_id field + const enrichedBody = { + ...body, + customer_id: user.customer_name, + }; + return this.platformApiService.proxy('POST', '/pipeline/execute', user, enrichedBody); + } + + @Post('pipeline/pause') + @ApiOperation({ summary: 'Pause a pipeline' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async pausePipeline(@Body() body: any, @User() user: RequestUser) { + // Inject customer_id (actually customer_name) into body for Platform-API + // Note: Platform-API was created before customer_id existed, so it expects customer_name in the customer_id field + const enrichedBody = { + ...body, + customer_id: user.customer_name, + }; + return this.platformApiService.proxy('POST', '/pipeline/pause', user, enrichedBody); + } + + @Post('pipeline/unpause') + @ApiOperation({ summary: 'Unpause a pipeline' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async unpausePipeline(@Body() body: any, @User() user: RequestUser) { + // Inject customer_id (actually customer_name) into body for Platform-API + // Note: Platform-API was created before customer_id existed, so it expects customer_name in the customer_id field + const enrichedBody = { + ...body, + customer_id: user.customer_name, + }; + return this.platformApiService.proxy('POST', '/pipeline/unpause', user, enrichedBody); + } + + @Put('pipeline/:pipelineId/memory') + @ApiOperation({ summary: 'Update pipeline memory configuration' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updatePipelineMemory( + @Param('pipelineId') pipelineId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Validate memory limit + if (body.amount) { + this.validateMemory(body.amount); + } + + return this.platformApiService.proxy( + 'PUT', + `/pipeline/${pipelineId}/memory`, + user, + body, + ); + } + + // ==================== PIPELINE METADATA ROUTES ==================== + + @Put('pipeline/:pipelineId/metadata') + @ApiOperation({ summary: 'Update pipeline metadata' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updatePipelineMetadata( + @Param('pipelineId') pipelineId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + return this.platformApiService.proxy( + 'PUT', + `/pipeline/${pipelineId}/metadata`, + user, + body, + ); + } + + @Get('pipelines/metadata') + @ApiOperation({ summary: 'Get all pipelines metadata' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getPipelinesMetadata( + @User() user: RequestUser, + @Query() query: Record, + ) { + return this.platformApiService.proxy( + 'GET', + '/pipelines/metadata', + user, + undefined, + query, + ); + } + + // ==================== PIPELINE RUN ROUTES ==================== + + @Get('pipeline/:pipelineId/pipeline_run') + @ApiOperation({ summary: 'Get pipeline runs for a pipeline' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getPipelineRuns( + @Param('pipelineId') pipelineId: string, + @User() user: RequestUser, + @Query() query: Record, + ) { + const normalizedId = this.normalizePipelineId(pipelineId); + return this.platformApiService.proxy( + 'GET', + `/pipeline/${normalizedId}/pipeline_run`, + user, + undefined, + query, + ); + } + + @Get('pipeline/:pipelineId/pipeline_run/:runId') + @ApiOperation({ summary: 'Get specific pipeline run' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getPipelineRun( + @Param('pipelineId') pipelineId: string, + @Param('runId') runId: string, + @User() user: RequestUser, + ) { + const normalizedPipelineId = this.normalizePipelineId(pipelineId); + const normalizedRunId = this.normalizePipelineId(runId); + return this.platformApiService.proxy( + 'GET', + `/pipeline/${normalizedPipelineId}/pipeline_run/${normalizedRunId}`, + user, + ); + } + + @Get('pipeline/pipeline_run/:runId/logs') + @ApiOperation({ summary: 'Get pipeline run logs' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getPipelineRunLogs( + @Param('runId') runId: string, + @User() user: RequestUser, + @Query() query: Record, + ) { + const normalizedRunId = this.normalizePipelineId(runId); + return this.platformApiService.proxy( + 'GET', + `/pipeline/pipeline_run/${normalizedRunId}/logs`, + user, + undefined, + query, + ); + } + + // ==================== JOBS - COLUMN EDITING ROUTES ==================== + + @Put('jobs/:jobId/input') + @ApiOperation({ summary: 'Update job input columns' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updateJobInput( + @Param('jobId') jobId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + const result = await this.platformApiService.proxy( + 'PUT', + `/jobs/${normalizedJobId}/input`, + user, + body, + ); + + // Sync to DynamoDB if connector type is provided + const connectorType = body.connector as 'jdbc' | 'singer' | 's3' | undefined; + if (connectorType && this.VALID_CONNECTORS.includes(connectorType)) { + await this.syncJobInputToDynamoDB(jobId, body, user, connectorType); + } + + return result; + } + + @Patch('jobs/:jobId/input') + @ApiOperation({ summary: 'Partial update job input columns' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async patchJobInput( + @Param('jobId') jobId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + const result = await this.platformApiService.proxy( + 'PATCH', + `/jobs/${normalizedJobId}/input`, + user, + body, + ); + + // Sync to DynamoDB if connector type is provided + const connectorType = body.connector as 'jdbc' | 'singer' | 's3' | undefined; + if (connectorType && this.VALID_CONNECTORS.includes(connectorType)) { + await this.syncJobInputToDynamoDB(jobId, body, user, connectorType); + } + + return result; + } + + @Put('jobs/:jobId/memory') + @ApiOperation({ summary: 'Update job memory configuration' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updateJobMemory( + @Param('jobId') jobId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Validate memory limit + if (body.amount) { + this.validateMemory(body.amount); + } + + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + return this.platformApiService.proxy( + 'PUT', + `/jobs/${normalizedJobId}/memory`, + user, + body, + ); + } + + @Post('jobs/:jobId/reset-state') + @ApiOperation({ summary: 'Reset job state' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async resetJobState( + @Param('jobId') jobId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + return this.platformApiService.proxy( + 'POST', + `/jobs/${normalizedJobId}/reset-state`, + user, + body, + ); + } + + // ==================== JOBS - JDBC SYNC MODE ROUTES ==================== + + @Get('jobs/jdbc/:jobId') + @ApiOperation({ summary: 'Get JDBC job details' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getJdbcJob(@Param('jobId') jobId: string, @User() user: RequestUser) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + return this.platformApiService.proxy('GET', `/jobs/jdbc/${normalizedJobId}`, user); + } + + @Post('jobs/jdbc/:jobId/sync-mode') + @ApiOperation({ summary: 'Update JDBC job sync mode' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updateJdbcSyncMode( + @Param('jobId') jobId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + const result = await this.platformApiService.proxy( + 'POST', + `/jobs/jdbc/${normalizedJobId}/sync-mode`, + user, + body, + ); + + // Sync to DynamoDB (pass raw jobId for pipeline extraction) + await this.syncJdbcSyncModeToDynamoDB(jobId, body, user); + + return result; + } + + @Get('jobs/jdbc/configs/allowed_datatypes') + @ApiOperation({ summary: 'Get allowed datatypes for JDBC' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getJdbcAllowedDatatypes(@User() user: RequestUser) { + return this.platformApiService.proxy( + 'GET', + '/jobs/jdbc/configs/allowed_datatypes', + user, + ); + } + + // ==================== JOBS - SINGER REPLICATION ROUTES ==================== + + @Get('jobs/singer/:jobId') + @ApiOperation({ summary: 'Get Singer job details' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getSingerJob(@Param('jobId') jobId: string, @User() user: RequestUser) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + return this.platformApiService.proxy('GET', `/jobs/singer/${normalizedJobId}`, user); + } + + @Post('jobs/singer/:jobId/sync-mode') + @ApiOperation({ summary: 'Update Singer job sync mode' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE) + async updateSingerSyncMode( + @Param('jobId') jobId: string, + @Body() body: any, + @User() user: RequestUser, + ) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + + const result = await this.platformApiService.proxy( + 'POST', + `/jobs/singer/${normalizedJobId}/sync-mode`, + user, + body, + ); + + // Sync to DynamoDB (pass raw jobId for pipeline extraction) + await this.syncSingerSyncModeToDynamoDB(jobId, body, user); + + return result; + } + + // ==================== JOBS - S3 ROUTES ==================== + + @Get('jobs/s3/:jobId') + @ApiOperation({ summary: 'Get S3 job details' }) + @RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.GET) + async getS3Job(@Param('jobId') jobId: string, @User() user: RequestUser) { + // Normalize job ID for Platform API (replace - with _) + const normalizedJobId = this.normalizeJobId(jobId); + return this.platformApiService.proxy('GET', `/jobs/s3/${normalizedJobId}`, user); + } + + // ==================== HEALTH ROUTE ==================== + + @Get('health') + @ApiOperation({ summary: 'Platform API health check' }) + @Authenticated() + async healthCheck(@User() user: RequestUser) { + return this.platformApiService.proxy('GET', '/health', user); + } +} diff --git a/src/modules/platform-api/platform-api.module.ts b/src/modules/platform-api/platform-api.module.ts new file mode 100644 index 0000000..c2b31e4 --- /dev/null +++ b/src/modules/platform-api/platform-api.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; + +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; + +import { PlatformApiController } from './platform-api.controller'; +import { PlatformApiService } from './platform-api.service'; +import { ElasticsearchModule } from '../../services/elasticsearch'; +import { DynamoDBModule } from '../../services/dynamodb'; +import { CustomersModule } from '../customers/customers.module'; + +@Module({ + imports: [ElasticsearchModule, DynamoDBModule, CustomersModule], + controllers: [PlatformApiController], + providers: [PlatformApiService, DadosferaLogger], + exports: [PlatformApiService], +}) +export class PlatformApiModule {} diff --git a/src/modules/platform-api/platform-api.service.ts b/src/modules/platform-api/platform-api.service.ts new file mode 100644 index 0000000..97797a3 --- /dev/null +++ b/src/modules/platform-api/platform-api.service.ts @@ -0,0 +1,123 @@ +import { Injectable, Inject, HttpException } from '@nestjs/common'; +import { SignatureV4 } from '@aws-sdk/signature-v4'; +import { Sha256 } from '@aws-crypto/sha256-js'; +import { defaultProvider } from '@aws-sdk/credential-provider-node'; +import axios, { AxiosResponse, Method } from 'axios'; +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; + +import { RequestUser } from '../../decorators/user.decorator'; +import { PLATFORM_API_CONFIG } from './platform-api.config'; + +@Injectable() +export class PlatformApiService { + private signer: SignatureV4; + private logger: any; + + constructor( + @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, + ) { + this.logger = dadosferaLogger.logger; + this.signer = new SignatureV4({ + service: 'execute-api', + region: PLATFORM_API_CONFIG.region, + credentials: defaultProvider(), + sha256: Sha256, + }); + } + + async proxy( + method: string, + path: string, + user: RequestUser, + body?: any, + query?: Record, + ): Promise { + const baseUrl = PLATFORM_API_CONFIG.getUrl(); + const url = new URL(`${baseUrl}${path}`); + + // Add query params + if (query) { + Object.entries(query).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + url.searchParams.set(key, String(value)); + } + }); + } + + const headers: Record = { + host: url.hostname, + 'content-type': 'application/json', + // Forward user context headers + // Note: platform-api expects customer_name in the 'customer_id' header (contract inconsistency) + 'customer_id': user.customer_name || '', + 'customer_name': user.customer_name || '', + 'x-user-id': user.user_id || '', + 'x-username': user.username || '', + 'x-customer-tier': user.customer_tier || '', + 'x-customer-id': user.customer_id || '', + }; + + const requestToSign = { + method: method.toUpperCase(), + protocol: url.protocol, + hostname: url.hostname, + port: url.port ? parseInt(url.port, 10) : undefined, + path: url.pathname + url.search, + headers, + body: body ? JSON.stringify(body) : undefined, + }; + + this.logger.info('Proxying request to platform-api', { + method: method.toUpperCase(), + path, + customer_id: user.customer_id, + user_id: user.user_id, + }); + + try { + // Sign with IAM v4 + const signedRequest = await this.signer.sign(requestToSign); + + const response: AxiosResponse = await axios({ + method: method as Method, + url: url.href, + headers: signedRequest.headers as Record, + data: body, + timeout: PLATFORM_API_CONFIG.timeout, + validateStatus: () => true, // Don't throw on non-2xx + }); + + // Propagate non-2xx responses as HttpExceptions + if (response.status >= 400) { + throw new HttpException(response.data, response.status); + } + + return response.data; + } catch (error) { + this.logger.error('Platform API proxy error', { + error: error.message, + status: error.response?.status, + path, + method: method.toUpperCase(), + }); + + if (error instanceof HttpException) { + throw error; + } + + if (error.response) { + throw new HttpException(error.response.data, error.response.status); + } + + if (error.code === 'ECONNREFUSED') { + throw new HttpException('Platform API service unavailable', 503); + } + + if (error.code === 'ETIMEDOUT' || error.code === 'ECONNABORTED') { + throw new HttpException('Platform API request timeout', 504); + } + + throw new HttpException('Internal server error', 500); + } + } +} diff --git a/src/modules/roles/roles.service.ts b/src/modules/roles/roles.service.ts index bd41ca5..d0045e5 100644 --- a/src/modules/roles/roles.service.ts +++ b/src/modules/roles/roles.service.ts @@ -229,27 +229,33 @@ export class RolesService { const [roleTreated] = this.getRolesPermissionsName([role.role]); return { role: roleTreated }; } + getRolesPermissionsName(roles: GetRolesPermissionsName[]): RoleDto[] { const newRoles: RoleDto[] = []; for (const role of roles) { - const allPermissions = this.permissionsService.getAllPermissions( - this.language, - ); - const newPermissions = role.permissions.map((p) => { - const permission = allPermissions.find((per) => per.seqid === p.seqid); - return { - ...p, - name: permission.name, - id: p.seqid, - }; - }); - const newRole: RoleDto = { - ...role, - permissions: newPermissions, - isPublic: role.isPublic, - }; + const newRole: RoleDto = this.formatRole(role); newRoles.push(newRole); } return newRoles; } + + private formatRole(role: GetRolesPermissionsName): RoleDto { + const allPermissions = this.permissionsService.getAllPermissions( + this.language + ); + const newPermissions = role.permissions.map((p) => { + const permission = allPermissions.find((per) => per.seqid === p.seqid); + return { + ...p, + name: permission.name, + id: p.seqid, + }; + }); + + return { + ...role, + permissions: newPermissions, + isPublic: role.isPublic, + };; + } } diff --git a/src/modules/users/dtos/entities.ts b/src/modules/users/dtos/entities.ts index 8e8b852..0f0e5a3 100644 --- a/src/modules/users/dtos/entities.ts +++ b/src/modules/users/dtos/entities.ts @@ -38,6 +38,12 @@ export class User { department?: string; @ApiProperty() hierarchy?: string; + @ApiProperty() + bio?: string; + @ApiProperty() + companyName?: string; + @ApiProperty() + companySite?: string; @ApiPropertyOptional() customer?: Customer; @ApiProperty() @@ -109,6 +115,12 @@ export class UpdateUserReq { @ApiPropertyOptional() hierarchy?: string; @ApiPropertyOptional() + bio?: string; + @ApiPropertyOptional() + companySite?: string; + @ApiPropertyOptional() + companyName?: string; + @ApiPropertyOptional() roleNames?: string[]; } diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index e565f18..b5cb32b 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -266,7 +266,6 @@ export class UsersController { } @Patch(':id') - @RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN) @ApiOkResponse({ type: UpdateUserRes }) async updateUser( @User() user: RequestUser, @@ -274,6 +273,10 @@ export class UsersController { @Param('id') id: string, @Language() language: LanguageEnum, ) { + + if (user.user_id !== id || !user.permissions.includes(PERMISSIONS_GROUPS.USERS.permissions.ADMIN.seqid)) { + throw new ErrorBuilder(ErrorCodes.AUTH.FORBIDDEN); + } this.logger.info('updateUser', { user }); this.userService.setLanguage(language); return await this.userService.updateUser(body, id, user.customer_id); diff --git a/src/modules/users/users.service.ts b/src/modules/users/users.service.ts index 3057010..278188c 100644 --- a/src/modules/users/users.service.ts +++ b/src/modules/users/users.service.ts @@ -148,20 +148,23 @@ export class UsersService implements OnModuleInit { } async updateUser(req: UpdateUserReq, id: string, customerId: string) { - const { department, hierarchy, jobTitle, name, roleNames, email } = req; - if (roleNames) { + const { roleNames, ...updateUserDTO } = req; + if (roleNames && roleNames.length > 0) { await this.setRoles({ roleNames, userId: id }, customerId); } const { user } = await lastValueFrom( this.usersClientService.UserUpdate({ - name, + department: updateUserDTO.department, + email: updateUserDTO.email, + hierarchy: updateUserDTO.hierarchy, + jobTitle: updateUserDTO.jobTitle, + name: updateUserDTO.name, + bio: updateUserDTO.bio, + companyName: updateUserDTO.companyName, + companySite: updateUserDTO.companySite, customerId, id, - department, - hierarchy, - jobTitle, - email, metabaseUserId: undefined, }), ); diff --git a/src/services/dynamodb/dynamodb.config.ts b/src/services/dynamodb/dynamodb.config.ts new file mode 100644 index 0000000..011ea25 --- /dev/null +++ b/src/services/dynamodb/dynamodb.config.ts @@ -0,0 +1,4 @@ +export const DYNAMODB_CONFIG = { + region: () => process.env.AWS_REGION || 'us-east-1', + inputsTable: () => process.env.INPUTS_DB || 'dadosfera-inputs-prd', +}; diff --git a/src/services/dynamodb/dynamodb.module.ts b/src/services/dynamodb/dynamodb.module.ts new file mode 100644 index 0000000..d50d635 --- /dev/null +++ b/src/services/dynamodb/dynamodb.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; +import { DynamoDBService } from './dynamodb.service'; + +@Module({ + providers: [DynamoDBService, DadosferaLogger], + exports: [DynamoDBService], +}) +export class DynamoDBModule {} diff --git a/src/services/dynamodb/dynamodb.service.ts b/src/services/dynamodb/dynamodb.service.ts new file mode 100644 index 0000000..15dde41 --- /dev/null +++ b/src/services/dynamodb/dynamodb.service.ts @@ -0,0 +1,234 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { + DynamoDBDocumentClient, + GetCommand, + PutCommand, + DeleteCommand, + TranslateConfig, +} from '@aws-sdk/lib-dynamodb'; +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; +import { v4 as uuid } from 'uuid'; +import { DYNAMODB_CONFIG } from './dynamodb.config'; + +export interface ReferenceColumn { + name: string; + type: string; +} + +export interface InputDocument { + id: string; + client_id: string; + user_id: string; + created_at: string; + name: string; + description?: string; + plugin: string; + type: string; + tables?: Array<{ + name: string; + type: string; + columns?: string[]; + reference_column?: ReferenceColumn; + }>; + credentials?: Record; +} + +@Injectable() +export class DynamoDBService { + private documentClient: DynamoDBDocumentClient; + private logger: any; + + constructor( + @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, + ) { + this.logger = dadosferaLogger.logger; + + const dynamoConfig = { region: DYNAMODB_CONFIG.region() }; + const marshallOptions: TranslateConfig = { + marshallOptions: { + removeUndefinedValues: true, + }, + }; + + const dynamoDb = new DynamoDBClient(dynamoConfig); + this.documentClient = DynamoDBDocumentClient.from(dynamoDb, marshallOptions); + } + + async createInput( + clientId: string, + userId: string, + data: { + name: string; + description?: string; + plugin: string; + type: string; + tables?: Array<{ + name: string; + type: string; + columns?: string[]; + reference_column?: ReferenceColumn; + }>; + }, + ): Promise { + const tableName = DYNAMODB_CONFIG.inputsTable(); + const id = uuid(); + const created_at = new Date().toISOString(); + + const item: InputDocument = { + id, + client_id: clientId, + user_id: userId, + created_at, + name: data.name, + description: data.description, + plugin: data.plugin, + type: data.type, + tables: data.tables, + }; + + this.logger.info('DynamoDB: Creating input', { + tableName, + inputId: id, + plugin: data.plugin, + }); + + const putCommand = new PutCommand({ + TableName: tableName, + Item: item, + }); + + try { + await this.documentClient.send(putCommand); + this.logger.info('DynamoDB: Input created successfully', { inputId: id }); + return item; + } catch (error) { + this.logger.error('DynamoDB: Failed to create input', { + tableName, + inputId: id, + region: DYNAMODB_CONFIG.region(), + error: error.message, + errorName: error.name, + }); + throw error; + } + } + + async findInput(clientId: string, inputId: string): Promise { + const tableName = DYNAMODB_CONFIG.inputsTable(); + + const getCommand = new GetCommand({ + TableName: tableName, + Key: { + id: inputId, + client_id: clientId, + }, + }); + + const { Item } = await this.documentClient.send(getCommand); + + return Item as InputDocument | null; + } + + async deleteInput(clientId: string, inputId: string): Promise { + const tableName = DYNAMODB_CONFIG.inputsTable(); + + this.logger.info('DynamoDB: Deleting input', { + tableName, + inputId, + }); + + const deleteCommand = new DeleteCommand({ + TableName: tableName, + Key: { + id: inputId, + client_id: clientId, + }, + }); + + await this.documentClient.send(deleteCommand); + + this.logger.info('DynamoDB: Input deleted successfully', { inputId }); + } + + /** + * Update a specific table entry in the input document. + * Fetches the current document, updates the matching table, and saves. + */ + async updateInputTable( + clientId: string, + inputId: string, + tableName: string, + changes: { + type?: string; + columns?: string[]; + reference_column?: ReferenceColumn | null; + }, + ): Promise { + const dynamoTableName = DYNAMODB_CONFIG.inputsTable(); + + this.logger.info('DynamoDB: Updating input table', { + inputId, + tableName, + changes: Object.keys(changes), + }); + + // Get current document + const current = await this.findInput(clientId, inputId); + if (!current) { + this.logger.warn('DynamoDB: Input not found for update', { inputId }); + return; + } + + // Find and update the matching table + const tables = current.tables || []; + const tableIndex = tables.findIndex((t) => t.name === tableName); + + if (tableIndex === -1) { + this.logger.warn('DynamoDB: Table not found in input', { + inputId, + tableName, + }); + return; + } + + // Merge changes into the table entry + const updatedTable = { ...tables[tableIndex] }; + if ('type' in changes) updatedTable.type = changes.type; + if ('columns' in changes) updatedTable.columns = changes.columns; + if ('reference_column' in changes) { + if (changes.reference_column === null) { + delete updatedTable.reference_column; + } else { + updatedTable.reference_column = changes.reference_column; + } + } + + tables[tableIndex] = updatedTable; + + // Save updated document + const putCommand = new PutCommand({ + TableName: dynamoTableName, + Item: { + ...current, + tables, + updated_at: new Date().toISOString(), + }, + }); + + try { + await this.documentClient.send(putCommand); + this.logger.info('DynamoDB: Input table updated successfully', { + inputId, + tableName, + }); + } catch (error) { + this.logger.error('DynamoDB: Failed to update input table', { + inputId, + tableName, + error: error.message, + }); + throw error; + } + } +} diff --git a/src/services/dynamodb/index.ts b/src/services/dynamodb/index.ts new file mode 100644 index 0000000..c47362e --- /dev/null +++ b/src/services/dynamodb/index.ts @@ -0,0 +1,3 @@ +export * from './dynamodb.service'; +export * from './dynamodb.module'; +export * from './dynamodb.config'; diff --git a/src/services/elasticsearch/elasticsearch.config.ts b/src/services/elasticsearch/elasticsearch.config.ts new file mode 100644 index 0000000..b20d7fe --- /dev/null +++ b/src/services/elasticsearch/elasticsearch.config.ts @@ -0,0 +1,5 @@ +export const ELASTICSEARCH_CONFIG = { + getUrl: () => process.env.ELASTICSEARCH_URL || 'http://localhost:9200', + getApiKey: () => process.env.ELASTICSEARCH_API_KEY || '', + timeout: 10000, // 10 seconds +}; diff --git a/src/services/elasticsearch/elasticsearch.module.ts b/src/services/elasticsearch/elasticsearch.module.ts new file mode 100644 index 0000000..4d9d816 --- /dev/null +++ b/src/services/elasticsearch/elasticsearch.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; +import { ElasticsearchService } from './elasticsearch.service'; + +@Module({ + providers: [ElasticsearchService, DadosferaLogger], + exports: [ElasticsearchService], +}) +export class ElasticsearchModule {} diff --git a/src/services/elasticsearch/elasticsearch.service.ts b/src/services/elasticsearch/elasticsearch.service.ts new file mode 100644 index 0000000..e2c1857 --- /dev/null +++ b/src/services/elasticsearch/elasticsearch.service.ts @@ -0,0 +1,382 @@ +import { Injectable, Inject } from '@nestjs/common'; +import axios, { AxiosInstance, AxiosError } from 'axios'; +import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; +import { ELASTICSEARCH_CONFIG } from './elasticsearch.config'; + +interface MultiLang { + 'en-us': string; + 'pt-br': string; + 'es-es': string; +} + +interface MultiLangArray { + 'en-us': string[]; + 'pt-br': string[]; + 'es-es': string[]; +} + +interface ConnectorInfo { + plugin: string; + name: MultiLang; + image: string; + version: string; + tags: string[]; +} + +interface PipelineDocument { + id: string; + name: MultiLang; + description: MultiLang; + customer_id: string; + user_id: string; + username: string; + status: string; + created_at: string; + updated_at: string; + last_status_updated: string; + tags: string[]; + // Connector metadata + connection_id: string; + connector_name: string; + connector_plugin: string; + connector_version: string; + image_url: string; + // Config + config: { + cron: string; + tables?: string; + }; + properties: string; + type: string; + in_use: number; + keywords: MultiLangArray; +} + +@Injectable() +export class ElasticsearchService { + private client: AxiosInstance; + private logger: any; + + constructor( + @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, + ) { + this.logger = dadosferaLogger.logger; + this.client = axios.create({ + baseURL: ELASTICSEARCH_CONFIG.getUrl(), + headers: { + Authorization: `ApiKey ${ELASTICSEARCH_CONFIG.getApiKey()}`, + 'Content-Type': 'application/json', + }, + timeout: ELASTICSEARCH_CONFIG.timeout, + }); + } + + private getIndex(customerName: string): string { + return `${customerName}_pipelines`; + } + + private formatMultiLang(value: string): MultiLang { + return { + 'en-us': value, + 'pt-br': value, + 'es-es': value, + }; + } + + private formatMultiLangArray(value: string[] = []): MultiLangArray { + return { + 'en-us': value, + 'pt-br': value, + 'es-es': value, + }; + } + + buildPipelineDocument( + pipelineId: string, + data: { + name: string; + description?: string; + user_id: string; + username: string; + customer_id: string; + plugin: string; + connection_id: string; + cron?: string; + tables?: string; + properties?: Record; + type?: string; + status?: string; + created_at?: string; + keywords?: string[]; + }, + connector: ConnectorInfo | null, + ): PipelineDocument { + const now = new Date().toISOString(); + + return { + id: pipelineId, + connection_id: data.connection_id, + connector_name: connector?.name?.['en-us'] || '', + connector_plugin: connector?.plugin || data.plugin, + connector_version: connector?.version || '1.0.0', + created_at: data.created_at || now, + updated_at: now, + customer_id: data.customer_id, + description: this.formatMultiLang(data.description || ''), + image_url: connector?.image || '', + keywords: this.formatMultiLangArray(data.keywords), + name: this.formatMultiLang(data.name || ''), + status: data.status || 'CREATED', + user_id: data.user_id, + username: data.username, + config: { + cron: data.cron, + tables: data.tables, + }, + properties: data.properties ? JSON.stringify(data.properties) : '{}', + type: data.type, + in_use: 1, + last_status_updated: now, + tags: connector?.tags || [], + }; + } + + async getConnectorByPlugin(plugin: string): Promise { + this.logger.info('Elasticsearch: Looking up connector', { plugin }); + + try { + const response = await this.client.post('/connectors/_search', { + query: { + term: { plugin: plugin }, + }, + size: 1, + }); + + const hits = response.data.hits?.hits || []; + if (hits.length === 0) { + this.logger.warn('Elasticsearch: Connector not found', { plugin }); + return null; + } + + const source = hits[0]._source; + return { + plugin: source.plugin, + name: source.name, + image: source.image, + version: source.version, + tags: source.tags || [], + }; + } catch (error) { + this.handleError('getConnectorByPlugin', error, { plugin }); + return null; + } + } + + async createPipeline( + customerName: string, + pipelineId: string, + data: { + name: string; + description?: string; + user_id: string; + username: string; + customer_id: string; + status?: string; + created_at?: string; + plugin: string; + connection_id: string; + cron?: string; + tables?: string; + properties?: Record; + type?: string; + }, + connector: ConnectorInfo | null, + ): Promise { + const index = this.getIndex(customerName); + const document = this.buildPipelineDocument(pipelineId, data, connector); + + this.logger.info('Elasticsearch: Creating pipeline', { + index, + pipelineId, + plugin: document.connector_plugin, + }); + + try { + const response = await this.client.post( + `${index}/_doc/${pipelineId}`, + document, + { params: { refresh: 'wait_for' } }, + ); + + this.logger.info('Elasticsearch: Pipeline created successfully', { + pipelineId, + result: response.data.result, + }); + + return response.data; + } catch (error) { + this.handleError('createPipeline', error, { pipelineId, index }); + throw error; + } + } + + async updatePipeline( + customerName: string, + pipelineId: string, + changes: { + name?: string; + description?: string; + cron?: string; + status?: string; + tags?: string[]; + }, + ): Promise { + const index = this.getIndex(customerName); + const now = new Date().toISOString(); + + this.logger.info('Elasticsearch: Updating pipeline', { + index, + pipelineId, + fields: Object.keys(changes), + }); + + try { + // Fetch current document + const currentDoc = await this.client.get(`${index}/_doc/${pipelineId}`); + const current = currentDoc.data._source; + + // Build updated document, preserving existing values + const updated: Record = { + ...current, + updated_at: now, + }; + + if ('name' in changes) { + updated.name = this.formatMultiLang(changes.name); + } + + if ('description' in changes) { + updated.description = this.formatMultiLang(changes.description); + } + + if ('cron' in changes) { + updated.config = { + ...current.config, + cron: changes.cron, + }; + } + + if ('status' in changes) { + updated.status = changes.status; + updated.last_status_updated = now; + } + + if ('tags' in changes) { + updated.tags = changes.tags; + } + + const response = await this.client.post( + `${index}/_doc/${pipelineId}`, + updated, + { params: { refresh: 'wait_for' } }, + ); + + this.logger.info('Elasticsearch: Pipeline updated successfully', { + pipelineId, + result: response.data.result, + }); + + return response.data; + } catch (error) { + this.handleError('updatePipeline', error, { pipelineId, index }); + throw error; + } + } + + async getPipeline( + customerName: string, + pipelineId: string, + ): Promise { + const index = this.getIndex(customerName); + + this.logger.info('Elasticsearch: Getting pipeline', { + index, + pipelineId, + }); + + try { + const response = await this.client.get(`${index}/_doc/${pipelineId}`); + return response.data._source as PipelineDocument; + } catch (error) { + if (error instanceof AxiosError && error.response?.status === 404) { + this.logger.warn('Elasticsearch: Pipeline not found', { + pipelineId, + index, + }); + return null; + } + this.handleError('getPipeline', error, { pipelineId, index }); + throw error; + } + } + + async deletePipeline( + customerName: string, + pipelineId: string, + ): Promise { + const index = this.getIndex(customerName); + + this.logger.info('Elasticsearch: Deleting pipeline', { + index, + pipelineId, + }); + + try { + const response = await this.client.delete( + `${index}/_doc/${pipelineId}`, + { params: { refresh: 'wait_for' } }, + ); + + this.logger.info('Elasticsearch: Pipeline deleted successfully', { + pipelineId, + result: response.data.result, + }); + + return response.data; + } catch (error) { + // If document not found, log warning but don't throw + if (error instanceof AxiosError && error.response?.status === 404) { + this.logger.warn('Elasticsearch: Pipeline not found for deletion', { + pipelineId, + index, + }); + return { result: 'not_found' }; + } + + this.handleError('deletePipeline', error, { pipelineId, index }); + throw error; + } + } + + private handleError( + operation: string, + error: any, + context: Record, + ): void { + if (error instanceof AxiosError) { + this.logger.error(`Elasticsearch: ${operation} failed`, { + ...context, + status: error.response?.status, + statusText: error.response?.statusText, + errorData: error.response?.data, + message: error.message, + }); + } else { + this.logger.error(`Elasticsearch: ${operation} failed`, { + ...context, + message: error.message, + stack: error.stack, + }); + } + } +} diff --git a/src/services/elasticsearch/index.ts b/src/services/elasticsearch/index.ts new file mode 100644 index 0000000..852bfc9 --- /dev/null +++ b/src/services/elasticsearch/index.ts @@ -0,0 +1,3 @@ +export * from './elasticsearch.module'; +export * from './elasticsearch.service'; +export * from './elasticsearch.config'; diff --git a/src/services/service.module.ts b/src/services/service.module.ts index 0286b01..8bf406d 100644 --- a/src/services/service.module.ts +++ b/src/services/service.module.ts @@ -14,7 +14,7 @@ import { redisStore } from 'cache-manager-ioredis-yet'; keyPrefix: 'maestro:sso', } - if (process.env.ENV !== 'local') { + if (process.env.REDIS_TLS === 'true') { baseRedisConfig['tls'] = { servername: process.env.REDIS_HOST, } diff --git a/src/utils/cron-validation.ts b/src/utils/cron-validation.ts new file mode 100644 index 0000000..b46d0f6 --- /dev/null +++ b/src/utils/cron-validation.ts @@ -0,0 +1,90 @@ +import CronParser from 'cron-parser'; + +export enum ScheduleLimits { + MINUTE = 'minute', + HOUR = 'hour', + DAY = 'day', + UNLIMITED = 'unlimited', +} + +const SECONDS_IN_MINUTE = 60; +const SECONDS_IN_HOUR = 3600; +const SECONDS_IN_DAY = 86400; + +/** + * Airflow preset schedules mapped to cron expressions. + * @once is special - it means run only once (no recurring schedule). + */ +const AIRFLOW_PRESETS: Record = { + '@once': null, // No recurring schedule - always valid + '@hourly': '0 * * * *', // Every hour + '@daily': '0 0 * * *', // Every day at midnight + '@weekly': '0 0 * * 0', // Every week on Sunday + '@monthly': '0 0 1 * *', // First day of every month + '@yearly': '0 0 1 1 *', // First day of every year + '@annually': '0 0 1 1 *', // Same as @yearly +}; + +/** + * Convert Airflow preset to cron expression. + * Returns null for @once (no recurring schedule). + * Returns original string if not an Airflow preset. + */ +export function convertAirflowPresetToCron(schedule: string): string | null { + const preset = AIRFLOW_PRESETS[schedule.toLowerCase()]; + if (preset !== undefined) { + return preset; + } + return schedule; +} + +export function getMinimumIntervalSeconds(scheduleLimit: string): number { + switch (scheduleLimit) { + case ScheduleLimits.MINUTE: + return SECONDS_IN_MINUTE; + case ScheduleLimits.HOUR: + return SECONDS_IN_HOUR; + case ScheduleLimits.DAY: + return SECONDS_IN_DAY; + case ScheduleLimits.UNLIMITED: + default: + return 0; + } +} + +export function getCronIntervalSeconds(cron: string): number { + const interval = CronParser.parseExpression(cron); + const nextDate = interval.next().toDate(); + const afterNextDate = interval.next().toDate(); + return Math.floor((afterNextDate.getTime() - nextDate.getTime()) / 1000); +} + +export function validateCronAgainstScheduleLimit( + cron: string, + scheduleLimit: string, +): { valid: boolean; message?: string } { + if (!cron) return { valid: true }; + + // Convert Airflow presets to cron expressions + const cronExpression = convertAirflowPresetToCron(cron); + + // @once returns null - no recurring schedule, always valid + if (cronExpression === null) { + return { valid: true }; + } + + try { + const cronInterval = getCronIntervalSeconds(cronExpression); + const minInterval = getMinimumIntervalSeconds(scheduleLimit); + + if (cronInterval < minInterval) { + return { + valid: false, + message: `Schedule interval (${cronInterval}s) is below customer limit (${scheduleLimit}: ${minInterval}s minimum)`, + }; + } + return { valid: true }; + } catch (error) { + return { valid: false, message: `Invalid cron expression: ${error.message}` }; + } +}