From 46631db5b2833bfc26c1cad746d54d80326ec5b5 Mon Sep 17 00:00:00 2001 From: Marcel Arndt Date: Thu, 10 Apr 2025 17:32:06 +0200 Subject: [PATCH] init data-connector --- service/data-hub/.gitkeep | 0 service/data-hub/data-connector/Dockerfile | 19 +- .../data-connector/docker-compose.yaml | 1 + service/data-hub/data-connector/nest-cli.json | 5 +- .../data-hub/data-connector/package-lock.json | 3301 ++++++++++++++++- service/data-hub/data-connector/package.json | 16 +- .../prisma/seed/tariffs/ktw-aok.ts | 4 +- .../prisma/seed/tariffs/ktw-vdek.ts | 93 +- .../prisma/seed/tariffs/tsw-dak-vdek.ts | 42 +- .../data-connector/src/app.controller.ts | 135 +- .../data-hub/data-connector/src/app.module.ts | 70 +- .../data-connector/src/app.service.ts | 2 +- .../src/core/base/interval.object-type.ts | 9 + .../src/core/base/pagination.input.ts | 57 + .../src/core/base/pagination.util.ts | 11 + .../src/core/config/config.service.ts | 15 + .../database/database.module-definition.ts | 15 + .../src/core/database/database.module.ts | 106 +- .../core/database/prisma-module.options.ts | 4 +- .../src/core/database/schema.gql | 293 ++ .../data-hub/data-connector/src/instrument.js | 38 + service/data-hub/data-connector/src/main.ts | 2 + .../app-accounting/accounting-arg.types.ts | 37 + .../app-accounting/accounting-filter.ts | 66 + .../app-accounting/accounting-state.ts | 5 + .../accounting-validation.repository.ts | 211 ++ .../accounting-validation.resolver.ts | 35 + .../accounting-validation.service.ts | 52 + .../app-accounting/accounting.object-type.ts | 11 + .../app-accounting/app-accounting.module.ts | 16 + .../app-accounting/tour.object-type.ts | 110 + .../app-control-center/anomaly.object-type.ts | 65 + .../app-control-center.module.ts | 10 + .../control-center.resolver.ts | 108 + .../solution-targets.resolver.ts | 21 + .../modules/app-manager/app-manager.module.ts | 22 + .../car-entity/car-entity.repository.ts | 130 + .../car-entity/car-entity.service.ts | 106 + .../app-manager/car-entity/car.entity.ts | 160 + .../app-manager/manager.object-type.ts | 86 + .../modules/app-manager/manager.resolver.ts | 46 + .../app-manager/time-per-tour-kpi.service.ts | 145 + .../time-per-tour-metrics.service.ts | 214 ++ .../tour-amount-forecast.service.ts | 243 ++ .../app-ticket-system.module.ts | 18 + .../app-ticket-system/ticket-arg.types.ts | 42 + .../app-ticket-system/ticket-filter.ts | 71 + .../ticket-lifecycle.service.ts | 240 ++ .../app-ticket-system/ticket-system.events.ts | 7 + .../ticket-validation.functions.ts | 153 + .../app-ticket-system/ticket.object-type.ts | 87 + .../app-ticket-system/ticket.repository.ts | 619 ++++ .../app-ticket-system/tickets.resolver.ts | 82 + .../app-ticket-system/tickets.service.ts | 112 + .../feat-anomalies/anomalies.service.ts | 25 +- .../anomaly-detector.service.ts | 3 + .../employees-logged-in.detector.ts | 13 - .../one-tour-at-a-time.detector.ts | 364 +- .../feat-calculation/calculation.service.ts | 27 +- ...n.module.ts => feat-calculation.module.ts} | 2 +- .../feat-calculation/infection-types.ts | 17 + .../src/modules/feat-etl/feat-etl.module.ts | 8 + .../data-access-extractors.module.ts | 0 .../dispo-live-extractors.service.ts | 6 +- .../dyflexis-extractors.service.ts | 0 .../feat-extractors/extractors.service.ts | 0 .../feat-extractors/source.interface.ts | 0 .../feat-extractors/source.repository.ts | 0 .../feat-importers/events/event-types.enum.ts | 2 +- .../events/extraction-done.event.ts | 0 .../events/loading-done.event.ts | 0 .../events/transformation-done.event.ts | 0 .../feat-importers/feature-importer.module.ts | 0 .../feat-importers/importer.service.ts | 8 +- .../data-access-loaders.module.ts | 0 .../feat-loaders/final.repository.ts | 45 +- .../feat-loaders/loaders.service.ts | 0 .../loaders/attendance-registration.loader.ts | 0 .../loaders/employee-dispolive.ts | 0 .../feat-loaders/loaders/employee-dyflexis.ts | 0 .../feat-loaders/loaders/patient.loader.ts | 0 .../loaders/planned-time.loader.ts | 0 .../feat-loaders/loaders/tour.loader.ts | 0 .../feat-loaders/loaders/work-time.loader.ts | 0 .../adapter/tours-calculation.adapter.ts | 0 .../data-access-transformers.module.ts | 4 +- .../feat-transformers/staging.interface.ts | 0 .../feat-transformers/staging.repository.ts | 0 .../transformer.functions.ts | 2 +- .../feat-transformers/transformers.service.ts | 0 .../attendance-registration.transformer.ts | 0 .../employee-dispolive.transformer.ts | 0 .../employee-dyflexis.transformer.ts | 0 .../transformers/patient.transformer.ts | 4 + .../transformers/planned-time.transformer.ts | 3 + .../transformers/tour.transformer.ts | 11 + .../transformers/work-time.transformer.ts | 3 + .../feat-tickets/ticket-lifecycle.service.ts | 39 +- .../modules/feat-tickets/ticket.repository.ts | 57 +- .../data-connector/src/utils/group-by.spec.ts | 14 + .../data-connector/src/utils/group-by.ts | 12 + .../data-connector/src/utils/helper.spec.ts | 63 + .../data-connector/src/utils/index.ts | 1 + service/data-hub/workflows/avic-etl.yml | 63 + service/data-hub/workflows/avic-tickets.yml | 55 + 105 files changed, 8081 insertions(+), 338 deletions(-) delete mode 100644 service/data-hub/.gitkeep create mode 100644 service/data-hub/data-connector/src/core/base/interval.object-type.ts create mode 100644 service/data-hub/data-connector/src/core/base/pagination.input.ts create mode 100644 service/data-hub/data-connector/src/core/base/pagination.util.ts create mode 100644 service/data-hub/data-connector/src/core/database/database.module-definition.ts create mode 100644 service/data-hub/data-connector/src/core/database/schema.gql create mode 100644 service/data-hub/data-connector/src/instrument.js create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting-arg.types.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting-filter.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting-state.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.repository.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.resolver.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.service.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/accounting.object-type.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/app-accounting.module.ts create mode 100644 service/data-hub/data-connector/src/modules/app-accounting/tour.object-type.ts create mode 100644 service/data-hub/data-connector/src/modules/app-control-center/anomaly.object-type.ts create mode 100644 service/data-hub/data-connector/src/modules/app-control-center/app-control-center.module.ts create mode 100644 service/data-hub/data-connector/src/modules/app-control-center/control-center.resolver.ts create mode 100644 service/data-hub/data-connector/src/modules/app-control-center/solution-targets.resolver.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/app-manager.module.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.repository.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.service.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/car-entity/car.entity.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/manager.object-type.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/manager.resolver.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/time-per-tour-kpi.service.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/time-per-tour-metrics.service.ts create mode 100644 service/data-hub/data-connector/src/modules/app-manager/tour-amount-forecast.service.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/app-ticket-system.module.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket-arg.types.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket-filter.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket-lifecycle.service.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket-system.events.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket-validation.functions.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket.object-type.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/ticket.repository.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/tickets.resolver.ts create mode 100644 service/data-hub/data-connector/src/modules/app-ticket-system/tickets.service.ts rename service/data-hub/data-connector/src/modules/feat-calculation/{util-calculation.module.ts => feat-calculation.module.ts} (90%) create mode 100644 service/data-hub/data-connector/src/modules/feat-etl/feat-etl.module.ts rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-extractors/data-access-extractors.module.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-extractors/dispo-live-extractors.service.ts (98%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-extractors/dyflexis-extractors.service.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-extractors/extractors.service.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-extractors/source.interface.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-extractors/source.repository.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-importers/events/event-types.enum.ts (80%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-importers/events/extraction-done.event.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-importers/events/loading-done.event.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-importers/events/transformation-done.event.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-importers/feature-importer.module.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-importers/importer.service.ts (95%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/data-access-loaders.module.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/final.repository.ts (92%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders.service.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/attendance-registration.loader.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/employee-dispolive.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/employee-dyflexis.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/patient.loader.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/planned-time.loader.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/tour.loader.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-loaders/loaders/work-time.loader.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/adapter/tours-calculation.adapter.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/data-access-transformers.module.ts (76%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/staging.interface.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/staging.repository.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformer.functions.ts (96%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers.service.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/attendance-registration.transformer.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/employee-dispolive.transformer.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/employee-dyflexis.transformer.ts (100%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/patient.transformer.ts (86%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/planned-time.transformer.ts (90%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/tour.transformer.ts (86%) rename service/data-hub/data-connector/src/modules/{ => feat-etl}/feat-transformers/transformers/work-time.transformer.ts (88%) create mode 100644 service/data-hub/data-connector/src/utils/group-by.spec.ts create mode 100644 service/data-hub/data-connector/src/utils/group-by.ts create mode 100644 service/data-hub/data-connector/src/utils/helper.spec.ts create mode 100644 service/data-hub/workflows/avic-etl.yml create mode 100644 service/data-hub/workflows/avic-tickets.yml diff --git a/service/data-hub/.gitkeep b/service/data-hub/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/service/data-hub/data-connector/Dockerfile b/service/data-hub/data-connector/Dockerfile index ed1efde..2a91b2f 100644 --- a/service/data-hub/data-connector/Dockerfile +++ b/service/data-hub/data-connector/Dockerfile @@ -1,14 +1,27 @@ -FROM node:20.15.1-alpine as builder +FROM node:20.15.1-alpine AS builder WORKDIR /app/builder COPY . . -RUN npm install \ - && npm run build +RUN npm install +RUN npm run build FROM node:20.15.1-alpine +ENV OTEL_TRACES_EXPORTER="otlp" +ENV OTEL_METRICS_EXPORTER="otlp" +ENV OTEL_LOGS_EXPORTER="otlp" +ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" + +# To setup your own Service Instance ID +ENV OTEL_RESOURCE_ATTRIBUTES="service.instance.id=avicenna.data-connector" +# ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os" + +# To have a Service Instance ID generated using a random UUID +# Requires auto-instrumentations-node version 0.46.0+ +ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os,serviceinstance,container" + WORKDIR /app COPY --from=builder /app/builder/node_modules ./node_modules diff --git a/service/data-hub/data-connector/docker-compose.yaml b/service/data-hub/data-connector/docker-compose.yaml index 4651052..296c4fc 100644 --- a/service/data-hub/data-connector/docker-compose.yaml +++ b/service/data-hub/data-connector/docker-compose.yaml @@ -34,6 +34,7 @@ services: POSTGRES_PASSWORD: ${DATABASE_PASSWORD} POSTGRES_USER: ${DATABASE_USERNAME} POSTGRES_DB: ${DATABASE_DATABASE} + POSTGRES_HOST_AUTH_METHOD: password PG_DATA: /var/lib/postgresql/data volumes: - ./.local/pgdata:/var/lib/postgresql/data diff --git a/service/data-hub/data-connector/nest-cli.json b/service/data-hub/data-connector/nest-cli.json index f9aa683..fdf27b8 100644 --- a/service/data-hub/data-connector/nest-cli.json +++ b/service/data-hub/data-connector/nest-cli.json @@ -3,6 +3,9 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { - "deleteOutDir": true + "deleteOutDir": true, + "assets": [ + "**/instrument.js" + ] } } diff --git a/service/data-hub/data-connector/package-lock.json b/service/data-hub/data-connector/package-lock.json index 67151a5..9febc80 100644 --- a/service/data-hub/data-connector/package-lock.json +++ b/service/data-hub/data-connector/package-lock.json @@ -9,16 +9,30 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@apollo/server": "^4.10.5", + "@faker-js/faker": "^9.0.1", + "@nestjs/apollo": "^12.2.0", "@nestjs/axios": "^3.0.2", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", "@nestjs/event-emitter": "^2.0.4", + "@nestjs/graphql": "^12.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/schedule": "^4.1.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.56.1", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.200.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.200.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.200.0", + "@opentelemetry/semantic-conventions": "^1.30.0", "@prisma/client": "^5.16.2", "date-fns": "^3.6.0", "date-fns-tz": "^3.1.3", + "graphql": "^16.9.0", + "graphql-subscriptions": "^2.0.0", + "graphql-ws": "^5.16.0", "kysely": "^0.27.4", "pg": "^8.12.0", "prisma-kysely": "^1.8.0", @@ -47,7 +61,7 @@ "ts-loader": "^9.4.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3" + "typescript": "^5.7.2" } }, "node_modules/@ampproject/remapping": { @@ -209,6 +223,281 @@ "nun": "bin/nun.mjs" } }, + "node_modules/@apollo/cache-control-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz", + "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==", + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/protobufjs": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz", + "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "long": "^4.0.0" + }, + "bin": { + "apollo-pbjs": "bin/pbjs", + "apollo-pbts": "bin/pbts" + } + }, + "node_modules/@apollo/server": { + "version": "4.10.5", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.10.5.tgz", + "integrity": "sha512-I+Oi1CnphXExSAAsZbclgRDN4k4CEMxyKRzYg3bir5N8OmydEhzXDlIGAOETP/TKblxR7HPXGvwn2cJKzbl46w==", + "dependencies": { + "@apollo/cache-control-types": "^1.0.3", + "@apollo/server-gateway-interface": "^1.1.1", + "@apollo/usage-reporting-protobuf": "^4.1.1", + "@apollo/utils.createhash": "^2.0.0", + "@apollo/utils.fetcher": "^2.0.0", + "@apollo/utils.isnodelike": "^2.0.0", + "@apollo/utils.keyvaluecache": "^2.1.0", + "@apollo/utils.logger": "^2.0.0", + "@apollo/utils.usagereporting": "^2.1.0", + "@apollo/utils.withrequired": "^2.0.0", + "@graphql-tools/schema": "^9.0.0", + "@types/express": "^4.17.13", + "@types/express-serve-static-core": "^4.17.30", + "@types/node-fetch": "^2.6.1", + "async-retry": "^1.2.1", + "cors": "^2.8.5", + "express": "^4.17.1", + "loglevel": "^1.6.8", + "lru-cache": "^7.10.1", + "negotiator": "^0.6.3", + "node-abort-controller": "^3.1.1", + "node-fetch": "^2.6.7", + "uuid": "^9.0.0", + "whatwg-mimetype": "^3.0.0" + }, + "engines": { + "node": ">=14.16.0" + }, + "peerDependencies": { + "graphql": "^16.6.0" + } + }, + "node_modules/@apollo/server-gateway-interface": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-1.1.1.tgz", + "integrity": "sha512-pGwCl/po6+rxRmDMFgozKQo2pbsSwE91TpsDBAOgf74CRDPXHHtM88wbwjab0wMMZh95QfR45GGyDIdhY24bkQ==", + "dependencies": { + "@apollo/usage-reporting-protobuf": "^4.1.1", + "@apollo/utils.fetcher": "^2.0.0", + "@apollo/utils.keyvaluecache": "^2.1.0", + "@apollo/utils.logger": "^2.0.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/server-plugin-landing-page-graphql-playground": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@apollo/server-plugin-landing-page-graphql-playground/-/server-plugin-landing-page-graphql-playground-4.0.0.tgz", + "integrity": "sha512-PBDtKI/chJ+hHeoJUUH9Kuqu58txQl00vUGuxqiC9XcReulIg7RjsyD0G1u3drX4V709bxkL5S0nTeXfRHD0qA==", + "deprecated": "The use of GraphQL Playground in Apollo Server was supported in previous versions, but this is no longer the case as of December 31, 2022. This package exists for v4 migration purposes only. We do not intend to resolve security issues or other bugs with this package if they arise, so please migrate away from this to [Apollo Server's default Explorer](https://www.apollographql.com/docs/apollo-server/api/plugin/landing-pages) as soon as possible.", + "dependencies": { + "@apollographql/graphql-playground-html": "1.6.29" + }, + "engines": { + "node": ">=14.0" + }, + "peerDependencies": { + "@apollo/server": "^4.0.0" + } + }, + "node_modules/@apollo/server/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/@apollo/server/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@apollo/usage-reporting-protobuf": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz", + "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==", + "dependencies": { + "@apollo/protobufjs": "1.2.7" + } + }, + "node_modules/@apollo/utils.createhash": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-2.0.1.tgz", + "integrity": "sha512-fQO4/ZOP8LcXWvMNhKiee+2KuKyqIcfHrICA+M4lj/h/Lh1H10ICcUtk6N/chnEo5HXu0yejg64wshdaiFitJg==", + "dependencies": { + "@apollo/utils.isnodelike": "^2.0.1", + "sha.js": "^2.4.11" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@apollo/utils.dropunuseddefinitions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz", + "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.fetcher": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-2.0.1.tgz", + "integrity": "sha512-jvvon885hEyWXd4H6zpWeN3tl88QcWnHp5gWF5OPF34uhvoR+DFqcNxs9vrRaBBSY3qda3Qe0bdud7tz2zGx1A==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@apollo/utils.isnodelike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-2.0.1.tgz", + "integrity": "sha512-w41XyepR+jBEuVpoRM715N2ZD0xMD413UiJx8w5xnAZD2ZkSJnMJBoIzauK83kJpSgNuR6ywbV29jG9NmxjK0Q==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@apollo/utils.keyvaluecache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-2.1.1.tgz", + "integrity": "sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==", + "dependencies": { + "@apollo/utils.logger": "^2.0.1", + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@apollo/utils.keyvaluecache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/@apollo/utils.logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-2.0.1.tgz", + "integrity": "sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@apollo/utils.printwithreducedwhitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz", + "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.removealiases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz", + "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.sortast": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz", + "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==", + "dependencies": { + "lodash.sortby": "^4.7.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.stripsensitiveliterals": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz", + "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.usagereporting": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz", + "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==", + "dependencies": { + "@apollo/usage-reporting-protobuf": "^4.1.0", + "@apollo/utils.dropunuseddefinitions": "^2.0.1", + "@apollo/utils.printwithreducedwhitespace": "^2.0.1", + "@apollo/utils.removealiases": "2.0.1", + "@apollo/utils.sortast": "^2.0.1", + "@apollo/utils.stripsensitiveliterals": "^2.0.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.withrequired": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-2.0.1.tgz", + "integrity": "sha512-YBDiuAX9i1lLc6GeTy1m7DGLFn/gMnvXqlalOIMjM7DeOgIacEjjfwPqb0M1CQ2v11HhR15d1NmxJoRCfrNqcA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@apollographql/graphql-playground-html": { + "version": "1.6.29", + "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz", + "integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==", + "dependencies": { + "xss": "^1.0.8" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -943,6 +1232,101 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.1.tgz", + "integrity": "sha512-4mDeYIgM3By7X6t5E6eYwLAa+2h4DeZDF7thhzIg6XB76jeEvMwadYAMCFJL/R4AnEBcAUO9+gL0vhy3s+qvZA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, + "node_modules/@graphql-tools/merge": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz", + "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==", + "dependencies": { + "@graphql-tools/utils": "^9.2.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/schema": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", + "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", + "dependencies": { + "@graphql-tools/merge": "^8.4.1", + "@graphql-tools/utils": "^9.2.1", + "tslib": "^2.4.0", + "value-or-promise": "^1.0.12" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/utils": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", + "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.2.tgz", + "integrity": "sha512-nnR5nmL6lxF8YBqb6gWvEgLdLh/Fn+kvAdX5hUOnt48sNSb0riz/93ASd2E5gvanPA41X6Yp25bIfGRp1SMb2g==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1583,6 +1967,15 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@ljharb/through": { "version": "2.3.13", "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", @@ -1615,6 +2008,38 @@ "node": ">=16" } }, + "node_modules/@nestjs/apollo": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/apollo/-/apollo-12.2.0.tgz", + "integrity": "sha512-z1zpbgrxaEaIdP6luiDdQ6f4OH3/xhszakxekXFvLq77wqO3nezKvZvz/etTaSlVW5y06jaCYKhypfXVv4sgzQ==", + "dependencies": { + "@apollo/server-plugin-landing-page-graphql-playground": "4.0.0", + "iterall": "1.3.0", + "lodash.omit": "4.5.0", + "tslib": "2.6.3" + }, + "peerDependencies": { + "@apollo/gateway": "^2.0.0", + "@apollo/server": "^4.3.2", + "@apollo/subgraph": "^2.0.0", + "@as-integrations/fastify": "^1.3.0 || ^2.0.0", + "@nestjs/common": "^9.3.8 || ^10.0.0", + "@nestjs/core": "^9.3.8 || ^10.0.0", + "@nestjs/graphql": "^12.0.0", + "graphql": "^16.6.0" + }, + "peerDependenciesMeta": { + "@apollo/gateway": { + "optional": true + }, + "@apollo/subgraph": { + "optional": true + }, + "@as-integrations/fastify": { + "optional": true + } + } + }, "node_modules/@nestjs/axios": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.2.tgz", @@ -1774,6 +2199,131 @@ "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/@nestjs/graphql": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/graphql/-/graphql-12.2.0.tgz", + "integrity": "sha512-du/aI+EXADxtJrHF1mAXR6RYRHuEWPNnJyHTmIOPW2Wx5qN32P7lQoHGD7TySATMl5aa47w05lPzxcasdUmpMQ==", + "dependencies": { + "@graphql-tools/merge": "9.0.4", + "@graphql-tools/schema": "10.0.4", + "@graphql-tools/utils": "10.2.3", + "@nestjs/mapped-types": "2.0.5", + "chokidar": "3.6.0", + "fast-glob": "3.3.2", + "graphql-tag": "2.12.6", + "graphql-ws": "5.16.0", + "lodash": "4.17.21", + "normalize-path": "3.0.0", + "subscriptions-transport-ws": "0.11.0", + "tslib": "2.6.3", + "uuid": "9.0.1", + "ws": "8.17.1" + }, + "peerDependencies": { + "@apollo/subgraph": "^2.0.0", + "@nestjs/common": "^9.3.8 || ^10.0.0", + "@nestjs/core": "^9.3.8 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "graphql": "^16.6.0", + "reflect-metadata": "^0.1.13 || ^0.2.0", + "ts-morph": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0" + }, + "peerDependenciesMeta": { + "@apollo/subgraph": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + }, + "ts-morph": { + "optional": true + } + } + }, + "node_modules/@nestjs/graphql/node_modules/@graphql-tools/merge": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.4.tgz", + "integrity": "sha512-MivbDLUQ+4Q8G/Hp/9V72hbn810IJDEZQ57F01sHnlrrijyadibfVhaQfW/pNH+9T/l8ySZpaR/DpL5i+ruZ+g==", + "dependencies": { + "@graphql-tools/utils": "^10.0.13", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@nestjs/graphql/node_modules/@graphql-tools/schema": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.4.tgz", + "integrity": "sha512-HuIwqbKxPaJujox25Ra4qwz0uQzlpsaBOzO6CVfzB/MemZdd+Gib8AIvfhQArK0YIN40aDran/yi+E5Xf0mQww==", + "dependencies": { + "@graphql-tools/merge": "^9.0.3", + "@graphql-tools/utils": "^10.2.1", + "tslib": "^2.4.0", + "value-or-promise": "^1.0.12" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@nestjs/graphql/node_modules/@graphql-tools/utils": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.2.3.tgz", + "integrity": "sha512-j7x0sO0VtWVhD3FubyY42abx+g61/at5W5Y3DSOckPkBo7yVjkcnAsXoB4jiUnznhGme/o+uX5VgA8HrjyR5ZQ==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "cross-inspect": "1.0.0", + "dset": "^3.1.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@nestjs/graphql/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/platform-express": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.10.tgz", @@ -1906,13 +2456,2192 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", - "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "engines": { "node": ">=8.0.0" } }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.56.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.56.1.tgz", + "integrity": "sha512-4cK0+unfkXRRbQQg2r9K3ki8JlE0j9Iw8+4DZEkChShAnmviiE+/JMgHGvK+VVcLrSlgV6BBHv4+ZTLukQwhkA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/instrumentation-amqplib": "^0.46.1", + "@opentelemetry/instrumentation-aws-lambda": "^0.50.3", + "@opentelemetry/instrumentation-aws-sdk": "^0.49.1", + "@opentelemetry/instrumentation-bunyan": "^0.45.1", + "@opentelemetry/instrumentation-cassandra-driver": "^0.45.1", + "@opentelemetry/instrumentation-connect": "^0.43.1", + "@opentelemetry/instrumentation-cucumber": "^0.14.1", + "@opentelemetry/instrumentation-dataloader": "^0.16.1", + "@opentelemetry/instrumentation-dns": "^0.43.1", + "@opentelemetry/instrumentation-express": "^0.47.1", + "@opentelemetry/instrumentation-fastify": "^0.44.2", + "@opentelemetry/instrumentation-fs": "^0.19.1", + "@opentelemetry/instrumentation-generic-pool": "^0.43.1", + "@opentelemetry/instrumentation-graphql": "^0.47.1", + "@opentelemetry/instrumentation-grpc": "^0.57.1", + "@opentelemetry/instrumentation-hapi": "^0.45.2", + "@opentelemetry/instrumentation-http": "^0.57.1", + "@opentelemetry/instrumentation-ioredis": "^0.47.1", + "@opentelemetry/instrumentation-kafkajs": "^0.7.1", + "@opentelemetry/instrumentation-knex": "^0.44.1", + "@opentelemetry/instrumentation-koa": "^0.47.1", + "@opentelemetry/instrumentation-lru-memoizer": "^0.44.1", + "@opentelemetry/instrumentation-memcached": "^0.43.1", + "@opentelemetry/instrumentation-mongodb": "^0.52.0", + "@opentelemetry/instrumentation-mongoose": "^0.46.1", + "@opentelemetry/instrumentation-mysql": "^0.45.1", + "@opentelemetry/instrumentation-mysql2": "^0.45.2", + "@opentelemetry/instrumentation-nestjs-core": "^0.44.1", + "@opentelemetry/instrumentation-net": "^0.43.1", + "@opentelemetry/instrumentation-pg": "^0.51.1", + "@opentelemetry/instrumentation-pino": "^0.46.1", + "@opentelemetry/instrumentation-redis": "^0.46.1", + "@opentelemetry/instrumentation-redis-4": "^0.46.1", + "@opentelemetry/instrumentation-restify": "^0.45.1", + "@opentelemetry/instrumentation-router": "^0.44.1", + "@opentelemetry/instrumentation-socket.io": "^0.46.1", + "@opentelemetry/instrumentation-tedious": "^0.18.1", + "@opentelemetry/instrumentation-undici": "^0.10.1", + "@opentelemetry/instrumentation-winston": "^0.44.1", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.30.1", + "@opentelemetry/resource-detector-aws": "^1.12.0", + "@opentelemetry/resource-detector-azure": "^0.6.1", + "@opentelemetry/resource-detector-container": "^0.6.1", + "@opentelemetry/resource-detector-gcp": "^0.33.1", + "@opentelemetry/resources": "^1.24.0", + "@opentelemetry/sdk-node": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.57.2.tgz", + "integrity": "sha512-eovEy10n3umjKJl2Ey6TLzikPE+W4cUQ4gCwgGP1RqzTGtgDra0WjIqdy29ohiUKfvmbiL3MndZww58xfIvyFw==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/sdk-logs": "0.57.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.57.2.tgz", + "integrity": "sha512-0rygmvLcehBRp56NQVLSleJ5ITTduq/QfU7obOkyWgPpFHulwpw2LYTqNIz5TczKZuy5YY+5D3SDnXZL1tXImg==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/sdk-logs": "0.57.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.57.2.tgz", + "integrity": "sha512-ta0ithCin0F8lu9eOf4lEz9YAScecezCHkMMyDkvd9S7AnZNX5ikUmC5EQOQADU+oCcgo/qkQIaKcZvQ0TYKDw==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.2", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.57.2.tgz", + "integrity": "sha512-r70B8yKR41F0EC443b5CGB4rUaOMm99I5N75QQt6sHKxYDzSEc6gm48Diz1CI1biwa5tDPznpylTrywO/pT7qw==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.57.2.tgz", + "integrity": "sha512-ttb9+4iKw04IMubjm3t0EZsYRNWr3kg44uUuzfo9CaccYlOh8cDooe4QObDUkvx9d5qQUrbEckhrWKfJnKhemA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.57.2.tgz", + "integrity": "sha512-HX068Q2eNs38uf7RIkNN9Hl4Ynl+3lP0++KELkXMCpsCbFO03+0XNNZ1SkwxPlP9jrhQahsMPMkzNXpq3fKsnw==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.57.2.tgz", + "integrity": "sha512-VqIqXnuxWMWE/1NatAGtB1PvsQipwxDcdG4RwA/umdBcW3/iOHp0uejvFHTRN2O78ZPged87ErJajyUBPUhlDQ==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.57.2.tgz", + "integrity": "sha512-gHU1vA3JnHbNxEXg5iysqCWxN9j83d7/epTYBZflqQnTyCC4N7yZXn/dMM+bEmyhQPGjhCkNZLx4vZuChH1PYw==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.57.2.tgz", + "integrity": "sha512-sB/gkSYFu+0w2dVQ0PWY9fAMl172PKMZ/JrHkkW8dmjCL0CYkmXeE+ssqIL/yBUTPOvpLIpenX5T9RwXRBW/3g==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.57.2.tgz", + "integrity": "sha512-awDdNRMIwDvUtoRYxRhja5QYH6+McBLtoz1q9BeEsskhZcrGmH/V1fWpGx8n+Rc+542e8pJA6y+aullbIzQmlw==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.30.1.tgz", + "integrity": "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.57.2.tgz", + "integrity": "sha512-XdxEzL23Urhidyebg5E6jZoaiW5ygP/mRjxLHixogbqwDy2Faduzb5N0o/Oi+XTIJu+iyxXdVORjXax+Qgfxag==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-transformer": "0.57.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.57.2.tgz", + "integrity": "sha512-USn173KTWy0saqqRB5yU9xUZ2xdgb1Rdu5IosJnm9aV4hMTuFFRTUsQxbgc24QxpCHeoKzzCSnS/JzdV0oM2iQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.57.2.tgz", + "integrity": "sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.2", + "@opentelemetry/sdk-metrics": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-b3": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", + "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", + "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.57.2.tgz", + "integrity": "sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-node": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.57.2.tgz", + "integrity": "sha512-8BaeqZyN5sTuPBtAoY+UtKwXBdqyuRKmekN5bFzAO40CgbGzAxfTpiL3PBerT7rhZ7p2nBdq7FaMv/tBQgHE4A==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.57.2", + "@opentelemetry/exporter-logs-otlp-http": "0.57.2", + "@opentelemetry/exporter-logs-otlp-proto": "0.57.2", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.57.2", + "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", + "@opentelemetry/exporter-metrics-otlp-proto": "0.57.2", + "@opentelemetry/exporter-prometheus": "0.57.2", + "@opentelemetry/exporter-trace-otlp-grpc": "0.57.2", + "@opentelemetry/exporter-trace-otlp-http": "0.57.2", + "@opentelemetry/exporter-trace-otlp-proto": "0.57.2", + "@opentelemetry/exporter-zipkin": "1.30.1", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.2", + "@opentelemetry/sdk-metrics": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "@opentelemetry/sdk-trace-node": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", + "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.30.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/propagator-b3": "1.30.1", + "@opentelemetry/propagator-jaeger": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.0.tgz", + "integrity": "sha512-IEkJGzK1A9v3/EHjXh3s2IiFc6L4jfK+lNgKVgUjeUJQRRhnVFMIO3TAvKwonm9O1HebCuoOt98v8bZW7oVQHA==", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.200.0.tgz", + "integrity": "sha512-+3MDfa5YQPGM3WXxW9kqGD85Q7s9wlEMVNhXXG7tYFLnIeaseUt9YtCeFhEDFzfEktacdFpOtXmJuNW8cHbU5A==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/sdk-logs": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.200.0.tgz", + "integrity": "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/sdk-logs": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.200.0.tgz", + "integrity": "sha512-GmahpUU/55hxfH4TP77ChOfftADsCq/nuri73I/AVLe2s4NIglvTsaACkFVZAVmnXXyPS00Fk3x27WS3yO07zA==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-trace-base": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.200.0.tgz", + "integrity": "sha512-uHawPRvKIrhqH09GloTuYeq2BjyieYHIpiklOvxm9zhrCL2eRsnI/6g9v2BZTVtGp8tEgIa7rCQ6Ltxw6NBgew==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.200.0.tgz", + "integrity": "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.200.0.tgz", + "integrity": "sha512-E+uPj0yyvz81U9pvLZp3oHtFrEzNSqKGVkIViTQY1rH3TOobeJPSpLnTVXACnCwkPR5XeTvPnK3pZ2Kni8AFMg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.200.0.tgz", + "integrity": "sha512-ZYdlU9r0USuuYppiDyU2VFRA0kFl855ylnb3N/2aOlXrbA4PMCznen7gmPbetGQu7pz8Jbaf4fwvrDnVdQQXSw==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.200.0.tgz", + "integrity": "sha512-hmeZrUkFl1YMsgukSuHCFPYeF9df0hHoKeHUthRKFCxiURs+GwF1VuabuHmBMZnjTbsuvNjOB+JSs37Csem/5Q==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.200.0.tgz", + "integrity": "sha512-Goi//m/7ZHeUedxTGVmEzH19NgqJY+Bzr6zXo1Rni1+hwqaksEyJ44gdlEMREu6dzX1DlAaH/qSykSVzdrdafA==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.200.0.tgz", + "integrity": "sha512-V9TDSD3PjK1OREw2iT9TUTzNYEVWJk4Nhodzhp9eiz4onDMYmPy3LaGbPv81yIR6dUb/hNp/SIhpiCHwFUq2Vg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.0.tgz", + "integrity": "sha512-icxaKZ+jZL/NHXX8Aru4HGsrdhK0MLcuRXkX5G5IRmCgoRLw+Br6I/nMVozX2xjGGwV7hw2g+4Slj8K7s4HbVg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", + "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz", + "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.50.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.50.3.tgz", + "integrity": "sha512-kotm/mRvSWUauudxcylc5YCDei+G/r+jnOH6q5S99aPLQ/Ms8D2yonMIxEJUILIPlthEmwLYxkw3ualWzMjm/A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "8.10.147" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.49.1.tgz", + "integrity": "sha512-Vbj4BYeV/1K4Pbbfk+gQ8gwYL0w+tBeUwG88cOxnF7CLPO1XnskGV8Q3Gzut2Ah/6Dg17dBtlzEqL3UiFP2Z6A==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/propagation-utils": "^0.30.16", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.45.1.tgz", + "integrity": "sha512-T9POV9ccS41UjpsjLrJ4i0m8LfplBiN3dMeH9XZ2btiDrjoaWtDrst6tNb1avetBjkeshOuBp1EWKP22EVSr0g==", + "dependencies": { + "@opentelemetry/api-logs": "^0.57.1", + "@opentelemetry/instrumentation": "^0.57.1", + "@types/bunyan": "1.8.11" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.45.1.tgz", + "integrity": "sha512-RqnP0rK2hcKK1AKcmYvedLiL6G5TvFGiSUt2vI9wN0cCBdTt9Y9+wxxY19KoGxq7e9T/aHow6P5SUhCVI1sHvQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.1.tgz", + "integrity": "sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.14.1.tgz", + "integrity": "sha512-ybO+tmH85pDO0ywTskmrMtZcccKyQr7Eb7wHy1keR2HFfx46SzZbjHo1AuGAX//Hook3gjM7+w211gJ2bwKe1Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.1.tgz", + "integrity": "sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.43.1.tgz", + "integrity": "sha512-e/tMZYU1nc+k+J3259CQtqVZIPsPRSLNoAQbGEmSKrjLEY/KJSbpBZ17lu4dFVBzqoF1cZYIZxn9WPQxy4V9ng==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz", + "integrity": "sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.44.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.44.2.tgz", + "integrity": "sha512-arSp97Y4D2NWogoXRb8CzFK3W2ooVdvqRRtQDljFt9uC3zI6OuShgey6CVFC0JxT1iGjkAr1r4PDz23mWrFULQ==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.1.tgz", + "integrity": "sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.1.tgz", + "integrity": "sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.1.tgz", + "integrity": "sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.57.2.tgz", + "integrity": "sha512-TR6YQA67cLSZzdxbf2SrbADJy2Y8eBW1+9mF15P0VK2MYcpdoUSmQTF1oMkBwa3B9NwqDFA2fq7wYTTutFQqaQ==", + "dependencies": { + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.2.tgz", + "integrity": "sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz", + "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/semantic-conventions": "1.28.0", + "forwarded-parse": "2.1.2", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.1.tgz", + "integrity": "sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.1.tgz", + "integrity": "sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.1.tgz", + "integrity": "sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz", + "integrity": "sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.1.tgz", + "integrity": "sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.43.1.tgz", + "integrity": "sha512-rK5YWC22gmsLp2aEbaPk5F+9r6BFFZuc9GTnW/ErrWpz2XNHUgeFInoPDg4t+Trs8OttIfn8XwkfFkSKqhxanw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.52.0.tgz", + "integrity": "sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.1.tgz", + "integrity": "sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.1.tgz", + "integrity": "sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/mysql": "2.15.26" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.2.tgz", + "integrity": "sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.44.1.tgz", + "integrity": "sha512-4TXaqJK27QXoMqrt4+hcQ6rKFd8B6V4JfrTJKnqBmWR1cbaqd/uwyl9yxhNH1JEkyo8GaBfdpBC4ZE4FuUhPmg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.43.1.tgz", + "integrity": "sha512-TaMqP6tVx9/SxlY81dHlSyP5bWJIKq+K7vKfk4naB/LX4LBePPY3++1s0edpzH+RfwN+tEGVW9zTb9ci0up/lQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.1.tgz", + "integrity": "sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.46.1.tgz", + "integrity": "sha512-HB8gD/9CNAKlTV+mdZehnFC4tLUtQ7e+729oGq88e4WipxzZxmMYuRwZ2vzOA9/APtq+MRkERJ9PcoDqSIjZ+g==", + "dependencies": { + "@opentelemetry/api-logs": "^0.57.1", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.46.1.tgz", + "integrity": "sha512-AN7OvlGlXmlvsgbLHs6dS1bggp6Fcki+GxgYZdSrb/DB692TyfjR7sVILaCe0crnP66aJuXsg9cge3hptHs9UA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.1.tgz", + "integrity": "sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.45.1.tgz", + "integrity": "sha512-Zd6Go9iEa+0zcoA2vDka9r/plYKaT3BhD3ESIy4JNIzFWXeQBGbH3zZxQIsz0jbNTMEtonlymU7eTLeaGWiApA==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.44.1.tgz", + "integrity": "sha512-l4T/S7ByjpY5TCUPeDe1GPns02/5BpR0jroSMexyH3ZnXJt9PtYqx1IKAlOjaFEGEOQF2tGDsMi4PY5l+fSniQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.46.1.tgz", + "integrity": "sha512-9AsCVUAHOqvfe2RM/2I0DsDnx2ihw1d5jIN4+Bly1YPFTJIbk4+bXjAkr9+X6PUfhiV5urQHZkiYYPU1Q4yzPA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.1.tgz", + "integrity": "sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.1.tgz", + "integrity": "sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.44.1.tgz", + "integrity": "sha512-iexblTsT3fP0hHUz/M1mWr+Ylg3bsYN2En/jvKXZtboW3Qkvt17HrQJYTF9leVIkXAfN97QxAcTE99YGbQW7vQ==", + "dependencies": { + "@opentelemetry/api-logs": "^0.57.1", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.200.0.tgz", + "integrity": "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-transformer": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.200.0.tgz", + "integrity": "sha512-CK2S+bFgOZ66Bsu5hlDeOX6cvW5FVtVjFFbWuaJP0ELxJKBB6HlbLZQ2phqz/uLj1cWap5xJr/PsR3iGoB7Vqw==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.200.0.tgz", + "integrity": "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-metrics": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagation-utils": { + "version": "0.30.16", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.16.tgz", + "integrity": "sha512-ZVQ3Z/PQ+2GQlrBfbMMMT0U7MzvYZLCPP800+ooyaBqm4hMvuQHfP028gB9/db0mwkmyEAMad9houukUVxhwcw==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.0.tgz", + "integrity": "sha512-blx9S2EI49Ycuw6VZq+bkpaIoiJFhsDuvFGhBIoH3vJ5oYjJ2U0s3fAM5jYft99xVIAv6HqoPtlP9gpVA2IZtA==", + "dependencies": { + "@opentelemetry/core": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.0.tgz", + "integrity": "sha512-Mbm/LSFyAtQKP0AQah4AfGgsD+vsZcyreZoQ5okFBk33hU7AquU4TltgyL9dvaO8/Zkoud8/0gEvwfOZ5d7EPA==", + "dependencies": { + "@opentelemetry/core": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", + "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.30.1.tgz", + "integrity": "sha512-9l0FVP3F4+Z6ax27vMzkmhZdNtxAbDqEfy7rduzya3xFLaRiJSvOpw6cru6Edl5LwO+WvgNui+VzHa9ViE8wCg==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.12.0.tgz", + "integrity": "sha512-Cvi7ckOqiiuWlHBdA1IjS0ufr3sltex2Uws2RK6loVp4gzIJyOijsddAI6IZ5kiO8h/LgCWe8gxPmwkTKImd+Q==", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.6.1.tgz", + "integrity": "sha512-Djr31QCExVfWViaf9cGJnH+bUInD72p0GEfgDGgjCAztyvyji6WJvKjs6qmkpPN+Ig6KLk0ho2VgzT5Kdl4L2Q==", + "dependencies": { + "@opentelemetry/core": "^1.25.1", + "@opentelemetry/resources": "^1.10.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.6.1.tgz", + "integrity": "sha512-o4sLzx149DQXDmVa8pgjBDEEKOj9SuQnkSLbjUVOpQNnn10v0WNR6wLwh30mFsK26xOJ6SpqZBGKZiT7i5MjlA==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.33.1.tgz", + "integrity": "sha512-/aZJXI1rU6Eus04ih2vU0hxXAibXXMzH1WlDZ8bXcTJmhwmTY8cP392+6l7cWeMnTQOibBUz8UKV3nhcCBAefw==", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.200.0.tgz", + "integrity": "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.0.tgz", + "integrity": "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.200.0.tgz", + "integrity": "sha512-S/YSy9GIswnhYoDor1RusNkmRughipvTCOQrlF1dzI70yQaf68qgf5WMnzUxdlCl3/et/pvaO75xfPfuEmCK5A==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", + "@opentelemetry/exporter-logs-otlp-http": "0.200.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", + "@opentelemetry/exporter-prometheus": "0.200.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", + "@opentelemetry/exporter-trace-otlp-http": "0.200.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", + "@opentelemetry/exporter-zipkin": "2.0.0", + "@opentelemetry/instrumentation": "0.200.0", + "@opentelemetry/propagator-b3": "2.0.0", + "@opentelemetry/propagator-jaeger": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-metrics": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "@opentelemetry/sdk-trace-node": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.200.0.tgz", + "integrity": "sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg==", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "shimmer": "^1.2.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.0.tgz", + "integrity": "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw==", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.0.tgz", + "integrity": "sha512-omdilCZozUjQwY3uZRBwbaRMJ3p09l4t187Lsdf0dGMye9WKD4NGcpgZRvqhI1dwcH6og+YXQEtoO9Wx3ykilg==", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.0.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz", + "integrity": "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2091,6 +4820,14 @@ "ts-pattern": "4.3.0" } }, + "node_modules/@prisma/internals/node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@prisma/internals/node_modules/@prisma/debug": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.3.1.tgz", @@ -2230,6 +4967,60 @@ "resolved": "https://registry.npmjs.org/@prisma/prisma-schema-wasm/-/prisma-schema-wasm-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", "integrity": "sha512-+zUI7NQDXfcNnU8HgrAj4jRMv8yRfITLzcfv0Urf0adKimM+hkkVG4rX38i9zWMlxekkEBw7NLFx3Gxxy8d3iQ==" }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2278,6 +5069,11 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@types/aws-lambda": { + "version": "8.10.147", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.147.tgz", + "integrity": "sha512-nD0Z9fNIZcxYX5Mai2CTmFD7wX7UldCkW2ezCF8D1T5hdiLsnTWDGRpfRYntU6VjTdLQjOvyszru7I1c1oCQew==" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2323,17 +5119,23 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" } }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -2390,7 +5192,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -2402,7 +5203,6 @@ "version": "4.19.5", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", - "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -2422,8 +5222,7 @@ "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -2465,11 +5264,24 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/luxon": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -2479,14 +5291,21 @@ "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, + "node_modules/@types/mysql": { + "version": "2.15.26", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz", + "integrity": "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.14.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", @@ -2495,22 +5314,47 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", + "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", + "dependencies": { + "@types/pg": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/retry": { "version": "0.12.0", @@ -2521,7 +5365,6 @@ "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -2531,13 +5374,17 @@ "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2565,6 +5412,14 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -2942,10 +5797,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "bin": { "acorn": "bin/acorn" }, @@ -2957,7 +5811,6 @@ "version": "1.9.5", "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, "peerDependencies": { "acorn": "^8" } @@ -3099,7 +5952,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3112,7 +5964,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3265,6 +6116,14 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dependencies": { + "retry": "0.13.1" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3397,6 +6256,11 @@ "@babel/core": "^7.0.0" } }, + "node_modules/backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3421,11 +6285,18 @@ } ] }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "engines": { "node": ">=8" }, @@ -3781,7 +6652,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3828,8 +6698,7 @@ "node_modules/cjs-module-lexer": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==" }, "node_modules/clean-stack": { "version": "2.2.0", @@ -3906,7 +6775,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3920,7 +6788,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4234,6 +7101,17 @@ "luxon": "~3.4.0" } }, + "node_modules/cross-inspect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.0.tgz", + "integrity": "sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4255,6 +7133,11 @@ "node": ">=8" } }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" + }, "node_modules/date-fns": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", @@ -4470,6 +7353,14 @@ "node": ">=12" } }, + "node_modules/dset": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz", + "integrity": "sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4578,7 +7469,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, "engines": { "node": ">=6" } @@ -4865,6 +7755,11 @@ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" }, + "node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4985,6 +7880,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -5331,6 +8231,11 @@ "node": ">= 0.6" } }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==" + }, "node_modules/fp-ts": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz", @@ -5397,7 +8302,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -5415,6 +8319,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5428,7 +8372,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5559,6 +8502,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -5581,6 +8532,53 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-subscriptions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz", + "integrity": "sha512-s6k2b8mmt9gF9pEfkxsaO1lTxaySfKoEJzEfmwguBbQ//Oq23hIXCfR1hm4kdh5hnR20RdwB+s3BCb+0duHSZA==", + "dependencies": { + "iterall": "^1.3.0" + }, + "peerDependencies": { + "graphql": "^15.7.2 || ^16.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/graphql-ws": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", + "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", + "workspaces": [ + "website" + ], + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": ">=0.11 <=16" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5808,6 +8806,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-in-the-middle": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.13.1.tgz", + "integrity": "sha512-k2V9wNm9B+ysuelDTHjI9d5KPc4l8zAZTGqj+pcynvWkypZd857ryzN8jNC7Pg2YZXNMJcHRPpaDyCBbNyVRpA==", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -5910,7 +8919,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6142,6 +9150,11 @@ "node": ">=8" } }, + "node_modules/iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + }, "node_modules/iterare": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", @@ -6869,6 +9882,14 @@ "node": ">=4" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7020,6 +10041,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -7052,6 +10078,16 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, "node_modules/lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", @@ -7073,6 +10109,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7289,6 +10342,11 @@ "mkdirp": "bin/cmd.js" } }, + "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", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7348,8 +10406,7 @@ "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" }, "node_modules/node-emoji": { "version": "1.11.0", @@ -8130,6 +11187,34 @@ "node": ">= 6" } }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -8379,7 +11464,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -8391,7 +11475,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -8433,7 +11516,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8447,6 +11529,19 @@ "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -8801,6 +11896,18 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8820,6 +11927,11 @@ "node": ">=8" } }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -9100,6 +12212,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/subscriptions-transport-ws": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", + "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", + "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md", + "dependencies": { + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependencies": { + "graphql": "^15.7.2 || ^16.0.0" + } + }, + "node_modules/subscriptions-transport-ws/node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/subscriptions-transport-ws/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -9729,9 +12885,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9876,6 +13032,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/value-or-promise": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", + "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", + "engines": { + "node": ">=12" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10007,6 +13171,14 @@ "node": ">=4.0" } }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "engines": { + "node": ">=12" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -10095,6 +13267,46 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -10107,7 +13319,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -10122,7 +13333,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -10140,7 +13350,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } diff --git a/service/data-hub/data-connector/package.json b/service/data-hub/data-connector/package.json index 8289a40..b23bf0f 100644 --- a/service/data-hub/data-connector/package.json +++ b/service/data-hub/data-connector/package.json @@ -23,16 +23,30 @@ "seed": "ts-node prisma/seed/seed.ts" }, "dependencies": { + "@apollo/server": "^4.10.5", + "@faker-js/faker": "^9.0.1", + "@nestjs/apollo": "^12.2.0", "@nestjs/axios": "^3.0.2", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", "@nestjs/event-emitter": "^2.0.4", + "@nestjs/graphql": "^12.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/schedule": "^4.1.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.56.1", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.200.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.200.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.200.0", + "@opentelemetry/semantic-conventions": "^1.30.0", "@prisma/client": "^5.16.2", "date-fns": "^3.6.0", "date-fns-tz": "^3.1.3", + "graphql": "^16.9.0", + "graphql-subscriptions": "^2.0.0", + "graphql-ws": "^5.16.0", "kysely": "^0.27.4", "pg": "^8.12.0", "prisma-kysely": "^1.8.0", @@ -61,7 +75,7 @@ "ts-loader": "^9.4.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3" + "typescript": "^5.7.2" }, "jest": { "moduleFileExtensions": [ diff --git a/service/data-hub/data-connector/prisma/seed/tariffs/ktw-aok.ts b/service/data-hub/data-connector/prisma/seed/tariffs/ktw-aok.ts index 3fa303b..eec4877 100644 --- a/service/data-hub/data-connector/prisma/seed/tariffs/ktw-aok.ts +++ b/service/data-hub/data-connector/prisma/seed/tariffs/ktw-aok.ts @@ -155,7 +155,7 @@ export const KTW_AOK_TARIFFS = [ '1/KTW-04', '1/KTW-05', '1/KTW-06', - ] + ], }, { description: 'Leitstellenmanagement', @@ -241,7 +241,7 @@ export const KTW_AOK_TARIFFS = [ '1/KTW-04', '1/KTW-05-E', '1/KTW-06', - ] + ], }, { description: 'Leitstellenmanagement', diff --git a/service/data-hub/data-connector/prisma/seed/tariffs/ktw-vdek.ts b/service/data-hub/data-connector/prisma/seed/tariffs/ktw-vdek.ts index 9409ff1..c8eaaab 100644 --- a/service/data-hub/data-connector/prisma/seed/tariffs/ktw-vdek.ts +++ b/service/data-hub/data-connector/prisma/seed/tariffs/ktw-vdek.ts @@ -190,7 +190,7 @@ export const KTW_VDEK_TARIFFS = [ { description: 'VDEK KTW', validFrom: startOfDay(new Date('2023-05-01')), - validTo: null, + validTo: endOfDay(new Date('2024-04-31')), costTypes: [ { description: 'Grundpauschale je Fahrt', @@ -278,4 +278,95 @@ export const KTW_VDEK_TARIFFS = [ }, ], }, + { + description: 'VDEK KTW', + validFrom: startOfDay(new Date('2024-05-01')), + validTo: null, + costTypes: [ + { + description: 'Grundpauschale je Fahrt', + variant: CostTypeVariant.GRUNDPREIS, + costAmount: 7634, + }, + { + description: 'QMS Zuschlag', + variant: CostTypeVariant.GRUNDPREIS, + costAmount: 3008, + }, + { + description: 'Modernes Einsatzfahrzeug', + variant: CostTypeVariant.GRUNDPREIS, + costAmount: 1885, + }, + { + description: 'Leitstellenmanagement', + variant: CostTypeVariant.GRUNDPREIS, + costAmount: 699, + }, + { + description: 'ab dem 17. km', + variant: CostTypeVariant.KILOMETERZUSCHLAG, + costAmount: 385, + kmInclusive: 16, + }, + { + description: 'Nachtzuschlag', + variant: CostTypeVariant.NACHTZUSCHLAG, + costAmount: 5970, + timeRange: '19:00-19:59', + }, + { + description: 'Nachtzuschlag', + variant: CostTypeVariant.NACHTZUSCHLAG, + costAmount: 6924, + timeRange: '20:00-21:59', + }, + { + description: 'Nachtzuschlag', + variant: CostTypeVariant.NACHTZUSCHLAG, + costAmount: 7502, + timeRange: '22:00-04:59', + }, + { + description: 'Nachtzuschlag', + variant: CostTypeVariant.NACHTZUSCHLAG, + costAmount: 5970, + timeRange: '05:00-06:00', + }, + { + description: 'Wochenendzuschlag', + variant: CostTypeVariant.WOCHENENDZUSCHLAG, + costAmount: 6697, + }, + { + description: 'Feiertagszuschlag', + variant: CostTypeVariant.FEIERTAGSZUSCHLAG, + costAmount: 6697, + }, + { + description: 'Desinfektionszuschlag 2.A', + variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG, + costAmount: 4457, + infectionType: '2.A', + }, + { + description: 'Desinfektionszuschlag 2.B', + variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG, + costAmount: 18265, + infectionType: '2.B', + }, + { + description: 'Desinfektionszuschlag 3.1', + variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG, + costAmount: 21121, + infectionType: '3.1', + }, + { + description: 'Desinfektionszuschlag 3.2', + variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG, + costAmount: 21121, + infectionType: '3.2', + }, + ], + }, ]; diff --git a/service/data-hub/data-connector/prisma/seed/tariffs/tsw-dak-vdek.ts b/service/data-hub/data-connector/prisma/seed/tariffs/tsw-dak-vdek.ts index 6166f74..a567335 100644 --- a/service/data-hub/data-connector/prisma/seed/tariffs/tsw-dak-vdek.ts +++ b/service/data-hub/data-connector/prisma/seed/tariffs/tsw-dak-vdek.ts @@ -23,7 +23,7 @@ export const TSW_DAK_VDEK_TARIFFS = [ { description: 'DAK + VDEK TSW', validFrom: startOfDay(new Date('2023-06-01')), - validTo: null, + validTo: endOfDay(new Date('2024-08-31')), costTypes: [ { description: 'Grundpauschale je Fahrt (TSW 2)', @@ -55,4 +55,44 @@ export const TSW_DAK_VDEK_TARIFFS = [ }, ], }, + { + description: 'DAK + VDEK TSW', + validFrom: startOfDay(new Date('2024-09-01')), + validTo: null, + costTypes: [ + { + description: 'Grundpauschale je Fahrt (TS 2)', + variant: CostTypeVariant.TSWDIFFERENZIERUNG, + costAmount: 6434, + }, + { + description: 'Grundpauschale je Fahrt (TS 1)', + variant: CostTypeVariant.TSWDIFFERENZIERUNG, + costAmount: 4793, + }, + { + description: 'Grundpauschale je Fahrt (TS+R)', + variant: CostTypeVariant.TSWDIFFERENZIERUNG, + costAmount: 5793, + }, + { + description: 'Nachtzuschlag', + variant: CostTypeVariant.NACHTZUSCHLAG, + costAmount: 407, + timeRange: '05:00-07:00', + }, + { + description: 'Fallmanagementzuschlag', + variant: CostTypeVariant.NACHTZUSCHLAG, + costAmount: 750, + timeRange: '14:30-23:00', + }, + { + description: 'ab dem 22. km', + variant: CostTypeVariant.KILOMETERZUSCHLAG, + costAmount: 180, + kmInclusive: 21, + }, + ], + }, ]; diff --git a/service/data-hub/data-connector/src/app.controller.ts b/service/data-hub/data-connector/src/app.controller.ts index ae544b2..2d75e96 100644 --- a/service/data-hub/data-connector/src/app.controller.ts +++ b/service/data-hub/data-connector/src/app.controller.ts @@ -1,9 +1,19 @@ import { Controller, Get, Query } from '@nestjs/common'; -import { ImporterService } from './modules/feat-importers/importer.service'; +import { PrismaService } from './core/database/prisma.service'; +import { AnomaliesService } from './modules/feat-anomalies/anomalies.service'; +import { CalculationService } from './modules/feat-calculation/calculation.service'; +import { ImporterService } from './modules/feat-etl/feat-importers/importer.service'; +import { TicketService } from './modules/feat-tickets/tickets.service'; @Controller() export class AppController { - constructor(private readonly importer: ImporterService) {} + constructor( + private readonly importer: ImporterService, + private readonly anomaliesService: AnomaliesService, + private readonly ticketService: TicketService, + private readonly calculationService: CalculationService, + private readonly prisma: PrismaService, + ) {} @Get('etl') etl(@Query('from') from: string, @Query('to') to: string) { @@ -19,9 +29,124 @@ export class AppController { return this.importer.runImport(interval); } - // Call Extractors + @Get('tickets') + tickets(@Query('from') from: string, @Query('to') to: string) { + if (!from || !to) { + return { + error: 'from and to must be defined', + }; + } + const interval = { + start: new Date(from), + end: new Date(to), + }; + return this.ticketService.createTickets(interval); + } - // Call Transformers + @Get('anomalies') + anomalies(@Query('from') from: string, @Query('to') to: string) { + if (!from || !to) { + return { + error: 'from and to must be defined', + }; + } + const interval = { + start: new Date(from), + end: new Date(to), + }; + return this.anomaliesService + .detectAnomalies(interval) + .then(() => this.anomaliesService.validateAnomalies()); + } - // Call Loaders + @Get('tour-calculation') + async tourCalculation(@Query('from') from: string, @Query('to') to: string) { + const interval = { + start: new Date(from), + end: new Date(to), + }; + const tours = await this.prisma.tour.findMany({ + where: { + AND: [ + { + startDate: { + gte: new Date(interval.start), + }, + }, + { + startDate: { + lt: new Date(interval.end), + }, + }, + { + healthInsurance: { + not: 'KOSTENTRÄGER PATIENT', + }, + }, + { + OR: [ + { + carName: null, + }, + { + AND: [ + { + carName: { + not: 'Storno', + }, + }, + { + carName: { + not: 'Löschen', + }, + }, + { + carName: { + not: 'Abgabe', + }, + }, + ], + }, + ], + }, + { + check: { + not: 3, + }, + }, + { + deletedAt: null, + }, + { + OR: [ + { + patientId: null, + }, + { + patientId: { + // Pause + not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979', + }, + }, + { + patientId: { + // Feierabend + not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a', + }, + }, + ], + }, + ], + }, + }); + const toursWithRevenue = []; + for (const tour of tours) { + toursWithRevenue.push({ + ...tour, + revenue: await this.calculationService.calculateTourRevenue(tour), + }); + } + console.log(this.calculationService.getHealthInsurancesWithNoTenant()); + return toursWithRevenue; + } } diff --git a/service/data-hub/data-connector/src/app.module.ts b/service/data-hub/data-connector/src/app.module.ts index 852167f..04e3e00 100644 --- a/service/data-hub/data-connector/src/app.module.ts +++ b/service/data-hub/data-connector/src/app.module.ts @@ -1,24 +1,54 @@ +import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { EventEmitterModule } from '@nestjs/event-emitter'; +import { GraphQLModule } from '@nestjs/graphql'; import { ScheduleModule } from '@nestjs/schedule'; +import { PostgresDialect } from 'kysely'; +import { join } from 'path'; +import { Pool } from 'pg'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { PrismaModule } from './core/database/prisma.module'; -import { FeatureImporterModule } from './modules/feat-importers/feature-importer.module'; import { ConfigModule } from './core/config/config.module'; -import { TicketSystemModule } from './modules/feat-tickets/ticket-system.module'; import { DatabaseModule } from './core/database/database.module'; -import { PostgresDialect } from 'kysely'; -import { Pool } from 'pg'; +import { PrismaModule } from './core/database/prisma.module'; +import { ApplicationAccountingModule } from './modules/app-accounting/app-accounting.module'; +import { ApplicationControlCenterModule } from './modules/app-control-center/app-control-center.module'; +import { ApplicationManagerModule } from './modules/app-manager/app-manager.module'; +import { ApplicationTicketSystemModule } from './modules/app-ticket-system/app-ticket-system.module'; +import { AnomalyDetectionModule } from './modules/feat-anomalies/anomaly-detection.module'; +import { FeatureEtlModule } from './modules/feat-etl/feat-etl.module'; import { FeatReportsModule } from './modules/feat-reports/feat-reports.module'; +import { TicketSystemModule } from './modules/feat-tickets/ticket-system.module'; +import { FeatureCalculationModule } from './modules/feat-calculation/feat-calculation.module'; @Module({ imports: [ ConfigModule, + + // DatabaseModule.forRootAsync({ + // isGlobal: true, + // inject: [ConfigService], + // useFactory(config: ConfigService) { + // return { + // config: { + // dialect: new PostgresDialect({ + // pool: new Pool({ + // host: config.databaseHost, + // port: config.databasePort, + // database: config.databaseName, + // user: config.databaseUser, + // password: config.databasePassword, + // }), + // }), + // }, + // }; + // }, + // }), DatabaseModule.forRoot({ + // isGlobal: true, dialect: new PostgresDialect({ pool: new Pool({ - host: 'localhost', + host: 'db', port: 5432, database: 'avicenna', user: 'johndoe', @@ -26,12 +56,38 @@ import { FeatReportsModule } from './modules/feat-reports/feat-reports.module'; }), }), }), + // DatabaseModule.forRoot({ + // dialect: new PostgresDialect({ + // pool: new Pool({ + // host: 'localhost', + // port: 54322, + // database: 'postgres', + // user: 'postgres', + // password: 'postgres', + // }), + // }), + // }), PrismaModule.forRoot(), ScheduleModule.forRoot(), EventEmitterModule.forRoot(), - FeatureImporterModule, + GraphQLModule.forRoot({ + driver: ApolloDriver, + playground: true, + autoSchemaFile: join(process.cwd(), 'src/core/database/schema.gql'), + sortSchema: true, + subscriptions: { + 'graphql-ws': true, + }, + }), + FeatureEtlModule, FeatReportsModule, + FeatureCalculationModule, TicketSystemModule, + AnomalyDetectionModule, + ApplicationManagerModule, + ApplicationAccountingModule, + ApplicationControlCenterModule, + ApplicationTicketSystemModule, ], controllers: [AppController], providers: [AppService], diff --git a/service/data-hub/data-connector/src/app.service.ts b/service/data-hub/data-connector/src/app.service.ts index 30ca90b..c22edff 100644 --- a/service/data-hub/data-connector/src/app.service.ts +++ b/service/data-hub/data-connector/src/app.service.ts @@ -10,7 +10,7 @@ import { startOfDay, } from 'date-fns'; import { concatMap, from, lastValueFrom, switchMap, take, tap } from 'rxjs'; -import { ImporterService } from './modules/feat-importers/importer.service'; +import { ImporterService } from './modules/feat-etl/feat-importers/importer.service'; import { TicketService } from './modules/feat-tickets/tickets.service'; import { chunk } from './utils'; diff --git a/service/data-hub/data-connector/src/core/base/interval.object-type.ts b/service/data-hub/data-connector/src/core/base/interval.object-type.ts new file mode 100644 index 0000000..5554485 --- /dev/null +++ b/service/data-hub/data-connector/src/core/base/interval.object-type.ts @@ -0,0 +1,9 @@ +import { ObjectType, Field } from '@nestjs/graphql'; + +@ObjectType('Interval') +export class IntervalObjectType { + @Field() + start: Date; + @Field() + end: Date; +} diff --git a/service/data-hub/data-connector/src/core/base/pagination.input.ts b/service/data-hub/data-connector/src/core/base/pagination.input.ts new file mode 100644 index 0000000..3f996b4 --- /dev/null +++ b/service/data-hub/data-connector/src/core/base/pagination.input.ts @@ -0,0 +1,57 @@ +import { Type } from '@nestjs/common'; +import { ArgsType, Field, Int, ObjectType } from '@nestjs/graphql'; + +@ArgsType() +export class PaginationArgs { + @Field(() => Int) + take = 10; + @Field(() => String, { nullable: true }) + cursor?: string; +} + +// interface IEdgeType { +// cursor: string; +// node: T; +// } + +export interface IPaginatedType { + // edges: IEdgeType[]; + nodes: T[]; + prevCursor: string; + endCursor: string; + totalCount: number; + hasNextPage: boolean; +} + +export function Paginated(classRef: Type): Type> { + // @ObjectType(`${classRef.name}Edge`) + // abstract class EdgeType { + // @Field(() => String) + // cursor: string; + // @Field(() => classRef) + // node: T; + // } + + @ObjectType({ isAbstract: true }) + abstract class PaginatedType implements IPaginatedType { + // @Field(() => [EdgeType], { nullable: true }) + // edges: EdgeType[]; + + @Field(() => [classRef]) + nodes: T[]; + + @Field(() => String) + prevCursor: string; + + @Field(() => String) + endCursor: string; + + @Field(() => Int) + totalCount: number; + + @Field() + hasNextPage: boolean; + } + + return PaginatedType as Type>; +} diff --git a/service/data-hub/data-connector/src/core/base/pagination.util.ts b/service/data-hub/data-connector/src/core/base/pagination.util.ts new file mode 100644 index 0000000..1d8f41a --- /dev/null +++ b/service/data-hub/data-connector/src/core/base/pagination.util.ts @@ -0,0 +1,11 @@ +export function encodeCursor(cursor: string | number): string { + return Buffer.from(`${cursor}`).toString('base64'); +} + +export function decodeCursor(cursor: string): string { + return Buffer.from(cursor, 'base64').toString('ascii'); +} + +export function decodeOffsetCursor(cursor: string): number { + return Number.parseInt(decodeCursor(cursor)); +} diff --git a/service/data-hub/data-connector/src/core/config/config.service.ts b/service/data-hub/data-connector/src/core/config/config.service.ts index 0b96c37..cf0c87a 100644 --- a/service/data-hub/data-connector/src/core/config/config.service.ts +++ b/service/data-hub/data-connector/src/core/config/config.service.ts @@ -24,4 +24,19 @@ export class ConfigService extends NestConfigService { dyflexisV2ApiKey(): string { return this.get('DYFLEXIS_V2_API_KEY'); } + databasePort(): string { + return this.get('DATABASE_PORT'); + } + databaseHost(): string { + return this.get('DATABASE_HOST'); + } + databaseUser(): string { + return this.get('DATABASE_USERNAME'); + } + databasePassword(): string { + return this.get('DATABASE_PASSWORD'); + } + databaseName(): string { + return this.get('DATABASE_DATABASE'); + } } diff --git a/service/data-hub/data-connector/src/core/database/database.module-definition.ts b/service/data-hub/data-connector/src/core/database/database.module-definition.ts new file mode 100644 index 0000000..b3ab085 --- /dev/null +++ b/service/data-hub/data-connector/src/core/database/database.module-definition.ts @@ -0,0 +1,15 @@ +import { ConfigurableModuleBuilder } from '@nestjs/common'; +import { KyselyModuleAsyncOptions } from './database.module'; + +export const { + ConfigurableModuleClass, + MODULE_OPTIONS_TOKEN, + ASYNC_OPTIONS_TYPE, + OPTIONS_TYPE, +} = new ConfigurableModuleBuilder() + .setClassMethodName('forRoot') + .setExtras<{ isGlobal?: boolean }>( + { isGlobal: false }, + (definition, extras) => ({ ...definition, global: extras.isGlobal }), + ) + .build(); diff --git a/service/data-hub/data-connector/src/core/database/database.module.ts b/service/data-hub/data-connector/src/core/database/database.module.ts index d691d44..799d116 100644 --- a/service/data-hub/data-connector/src/core/database/database.module.ts +++ b/service/data-hub/data-connector/src/core/database/database.module.ts @@ -1,8 +1,33 @@ -import { DynamicModule, Global, Module, Provider } from '@nestjs/common'; +import { + DynamicModule, + Global, + Module, + ModuleMetadata, + Provider, + Type, +} from '@nestjs/common'; import { KyselyConfig } from 'kysely'; import { createKyselyProvider } from './kysely-client.factory'; export const KYSELY_CLIENT_PROVIDER_TOKEN = 'KyselyClientProviderToken'; +export const KYSELY_CONFIG_TOKEN = 'KYSELY_CONFIG_TOKEN'; + +export interface KyselyConfigFactory { + createKyselyConfig(): Promise | KyselyConfig; +} + +export interface KyselyModuleAsyncOptions + extends Pick { + isGlobal?: boolean; + useExisting?: Type; + useClass?: Type; + useFactory?: ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...args: any[] + ) => KyselyConfig; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + inject?: any[]; +} @Global() @Module({}) @@ -16,4 +41,83 @@ export class DatabaseModule { providers: [provider], }; } + + public static forRootAsync(options: KyselyModuleAsyncOptions): DynamicModule { + return { + global: options.isGlobal, + module: DatabaseModule, + imports: options.imports || [], + providers: this.createAsyncProviders(options), + }; + } + + private static createAsyncProviders( + options: KyselyModuleAsyncOptions, + ): Provider[] { + if (options.useExisting || options.useFactory) { + return this.createAsyncOptionsProvider(options); + } + + if (options.useClass) { + return [ + ...this.createAsyncOptionsProvider(options), + { + provide: options.useClass, + useClass: options.useClass, + }, + ]; + } + + return [...this.createAsyncOptionsProvider(options)]; + } + + private static createAsyncOptionsProvider( + options: KyselyModuleAsyncOptions, + ): Provider[] { + if (options.useFactory) { + const config = options.useFactory(options.inject); + return [ + // { + // provide: KYSELY_CONFIG_TOKEN, + // useFactory: options.useFactory, + // inject: options.inject || [], + // }, + { + provide: KYSELY_CLIENT_PROVIDER_TOKEN, + useExisting: createKyselyProvider(config) + } + ]; + } + + if (options.useExisting) { + return [ + { + provide: KYSELY_CONFIG_TOKEN, + useFactory: async (optionsFactory: KyselyConfigFactory) => + await optionsFactory.createKyselyConfig(), + inject: [options.useExisting], + }, + ]; + } + + if (options.useClass) { + return [ + { + provide: KYSELY_CONFIG_TOKEN, + useFactory: async (optionsFactory: KyselyConfigFactory) => + await optionsFactory.createKyselyConfig(), + inject: [options.useClass], + }, + ]; + } + + return [ + { + provide: KYSELY_CONFIG_TOKEN, + useFactory: async (optionsFactory: KyselyConfigFactory) => + await optionsFactory.createKyselyConfig(), + inject: [], + }, + ]; + } } diff --git a/service/data-hub/data-connector/src/core/database/prisma-module.options.ts b/service/data-hub/data-connector/src/core/database/prisma-module.options.ts index 0085a10..bc8cf34 100644 --- a/service/data-hub/data-connector/src/core/database/prisma-module.options.ts +++ b/service/data-hub/data-connector/src/core/database/prisma-module.options.ts @@ -1,5 +1,5 @@ -import { ModuleMetadata, Type } from "@nestjs/common"; -import { Prisma } from "@prisma/client"; +import { ModuleMetadata, Type } from '@nestjs/common'; +import { Prisma } from '@prisma/client'; export interface PrismaModuleOptions { /** diff --git a/service/data-hub/data-connector/src/core/database/schema.gql b/service/data-hub/data-connector/src/core/database/schema.gql new file mode 100644 index 0000000..4f9ef11 --- /dev/null +++ b/service/data-hub/data-connector/src/core/database/schema.gql @@ -0,0 +1,293 @@ +# ------------------------------------------------------ +# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) +# ------------------------------------------------------ + +input AccountingFilterArgs { + deviation: [String!] + missing: [String!] +} + +enum AccountingState { + Billable + Billed + PreparedForBilling +} + +type Anomaly { + createdAt: DateTime! + description: String! + groupDescription: String! + groupKey: String! + id: String! + manuallyResolved: Boolean + resolvedAt: DateTime + sleepSince: DateTime + sleepTimer: Int! + solution: [SolutionTarget!]! + tour: Tour + type: String! + workedOnBy: String! +} + +type AnomalyGroup { + anomalies: [Anomaly!]! + key: String! +} + +type ApprovalInfo { + answeredFrom: String + isNeeded: Boolean + location: String + whoRequested: String +} + +input ApprovalInfoArgs { + answeredFrom: String + isNeeded: Boolean + location: String + whoRequested: String +} + +enum ApprovalState { + APPROVED + FREE + REQUESTED + REQUIRED + UNKNOWN +} + +""" +A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format. +""" +scalar DateTime + +type DocumentInfo { + approval: ApprovalInfo! + tdLocation: String +} + +type Interval { + end: DateTime! + start: DateTime! +} + +type KPI { + meta: KPIMetadata! + value: Float! +} + +type KPIByCar { + carName: String! + kpi: KPI! +} + +type KPIMetadata { + availableTime: Float! + baseValue: Float! + breakEven: Float! + tourAmount: Float! +} + +type Mutation { + acceptAnomaly(id: String!): Anomaly! + setAnomalyToSleep(id: String!): Anomaly! + setDocumentInfo(approval: ApprovalInfoArgs, tdLocation: String, ticketId: String!): Ticket! +} + +type PaginatedAnomaly { + endCursor: String! + hasNextPage: Boolean! + nodes: [Anomaly!]! + prevCursor: String! + totalCount: Int! +} + +type PaginatedAnomalyGroup { + endCursor: String! + hasNextPage: Boolean! + nodes: [AnomalyGroup!]! + prevCursor: String! + totalCount: Int! +} + +type PaginatedTicket { + endCursor: String! + hasNextPage: Boolean! + nodes: [Ticket!]! + prevCursor: String! + totalCount: Int! +} + +type PaginatedTour { + endCursor: String! + hasNextPage: Boolean! + nodes: [Tour!]! + prevCursor: String! + totalCount: Int! +} + +type Query { + accountingStateMeta(state: AccountingState!): TourStateMeta! + accountingTours(cursor: String, filters: AccountingFilterArgs, state: AccountingState!, take: Int! = 10): PaginatedTour! + anomalies(cursor: String, take: Int! = 10): PaginatedAnomaly! + anomaliesProto: [Anomaly!]! + anomaly(id: String!): Anomaly! + groupedAnomalies(cursor: String, take: Int! = 10): PaginatedAnomalyGroup! + searchTickets(query: String!): [Ticket!]! + ticket(id: String!): Ticket! + ticketStateKeyFigures(state: TicketValidationState!): TicketStateKeyFigures! + ticketStateMeta(state: TicketValidationState!): TicketStateMeta! + tickets(cursor: String, filters: TicketFilterArgs, state: TicketValidationState!, take: Int! = 10): PaginatedTicket! + timePerTourMetricForDay(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]! + timePerTourMetricForMonth(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]! + toursPerTimeKPI(ordinanceTypes: String!): [TimePerTourKPISummary!]! +} + +type SolutionTarget { + anomalyId: String! + id: String! + name: String! + steps: [SolutionTargetStep!]! +} + +type SolutionTargetStep { + description: String! + id: String! + solutionTargetId: String! +} + +type Subscription { + ticketsUpdated: TicketsUpdated! + timePerTourUpdated: TimePerTourUpdate! +} + +type Ticket { + approvalState: ApprovalState + createdAt: DateTime! + currentState: TicketValidationState! + documentInfo: DocumentInfo + errors: [String!]! + id: String! + isUrgent: Boolean! + notes: [String!]! + tours: [Tour!]! + urgency: DateTime! +} + +input TicketFilterArgs { + approvalStates: [ApprovalState!] + documentInfo: [String!] + errors: [String!] +} + +type TicketStateKeyFigures { + dwellTime: Int! +} + +type TicketStateMeta { + approvalNotCompanyCount: Int + approvalRequiredCount: Int + approvalUnknownCount: Int + count: Int! + tdNotCompanyCount: Int +} + +enum TicketValidationState { + ARCHIVED + CREATED + DOCUMENTS_MISSING + TD_UNCERTAIN + USER_INTERACTION_REQUIRED +} + +type TicketsUpdated { + count: Int! +} + +type TimePerTourKPISummary { + cars: [KPIByCar!]! + interval: Interval! + kpi: KPI! +} + +type TimePerTourMetric { + availableTime: Float! + averageRevenue: Float! + breakEven: Float! + categoryAmounts: [TimePerTourMetricCategoryAmount!]! + consumptionCost: Float! + forecast: Float! + forecastRevenue: Float! + interval: Interval! + personnelCost: Float! + revenue: Float! + tourAmount: Float! +} + +type TimePerTourMetricCategoryAmount { + amount: Int! + category: String! +} + +type TimePerTourUpdate { + job: String! +} + +type Tour { + billDate: DateTime + billNumber: String + carName: String + category: String + check: Int! + codriverId: String + codriverName: String + consumptionCosts: Float! + createdAt: DateTime + deletedAt: DateTime + direction: String! + done: Boolean! + driverId: String + driverName: String + empty: String + hasInfection: Boolean + healthInsurance: String + healthInsuranceNumber: String + id: String! + infectionName: String + occupiedKm: Float! + operationId: String! + ordinanceType: String + patientCity: String + patientId: String + patientName: String + patientStreet: String + patientSurname: String + patientZip: String + rangeEndDate: DateTime + revenue: Float! + revenueDeviation: Float! + revenueDispoLive: Float! + startBegin: String + startCity: String + startDate: DateTime + startEnd: String + startInstitution: String + startStreet: String + startZip: String + target: String + targetBegin: String + targetCity: String + targetInstitution: String + targetStreet: String + targetZip: String + ticket: Ticket + ticketId: String + totalKm: Float! + transportType: String + type: String! +} + +type TourStateMeta { + count: Int! + revenueSum: Float +} \ No newline at end of file diff --git a/service/data-hub/data-connector/src/instrument.js b/service/data-hub/data-connector/src/instrument.js new file mode 100644 index 0000000..a00f2b1 --- /dev/null +++ b/service/data-hub/data-connector/src/instrument.js @@ -0,0 +1,38 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/*instrumentation.ts*/ +const { NodeSDK } = require('@opentelemetry/sdk-node'); +const { + getNodeAutoInstrumentations, +} = require('@opentelemetry/auto-instrumentations-node'); +const { + OTLPTraceExporter, +} = require('@opentelemetry/exporter-trace-otlp-proto'); // Annahme: gRPC/Proto Exporter für Backend +const { + OTLPMetricExporter, +} = require('@opentelemetry/exporter-metrics-otlp-proto'); // Annahme: gRPC/Proto Exporter für Backend +const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { resourceFromAttributes } = require('@opentelemetry/resources'); +const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions'); + +const otelEndpoint = + process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ?? + 'http://monitoring_alloy:4318'; + +const sdk = new NodeSDK({ + resource: resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'avicenna.data-connector', + namespace: 'avicenna', + }), + traceExporter: new OTLPTraceExporter({ + url: `${otelEndpoint}/v1/traces`, + }), + metricReader: new PeriodicExportingMetricReader({ + exporter: new OTLPMetricExporter({ + url: `${otelEndpoint}/v1/metrics`, + }), + }), + instrumentations: [getNodeAutoInstrumentations()], +}); + +sdk.start(); +console.log('OTel SDK started'); diff --git a/service/data-hub/data-connector/src/main.ts b/service/data-hub/data-connector/src/main.ts index 13cad38..92220fd 100644 --- a/service/data-hub/data-connector/src/main.ts +++ b/service/data-hub/data-connector/src/main.ts @@ -3,6 +3,8 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.enableCors(); + await app.listen(3000); } bootstrap(); diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting-arg.types.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting-arg.types.ts new file mode 100644 index 0000000..c157862 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting-arg.types.ts @@ -0,0 +1,37 @@ +import { + ArgsType, + Field, + Float, + InputType, + Int, + ObjectType, +} from '@nestjs/graphql'; +import { PaginationArgs } from 'src/core/base/pagination.input'; +import { AccountingFilter } from './accounting-filter'; +import { AccountingState } from './accounting-state'; + +@InputType() +export class AccountingFilterArgs implements AccountingFilter { + @Field(() => [String], { nullable: true }) + deviation!: 'pos' | 'neg' | 'both'[]; + @Field(() => [String], { nullable: true }) + missing?: 'information' | 'documents'[]; + // @Field(() => IntervalObjectType, { nullable: true }) + // dateRange?: Interval; +} + +@ArgsType() +export class AccountingTourArgs extends PaginationArgs { + @Field(() => AccountingState) + state!: AccountingState; + @Field(() => AccountingFilterArgs, { nullable: true }) + filters!: AccountingFilterArgs; +} + +@ObjectType('TourStateMeta') +export class TourStateMetaObjectType { + @Field(() => Int) + count!: number; + @Field(() => Float, { nullable: true }) + revenueSum?: number; +} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting-filter.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting-filter.ts new file mode 100644 index 0000000..cce5ef2 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting-filter.ts @@ -0,0 +1,66 @@ +import { Prisma, TicketValidationState } from '@prisma/client'; +import { Interval } from 'date-fns'; + +export interface AccountingFilter { + deviation?: 'pos' | 'neg' | 'both'[]; + missing?: 'information' | 'documents'[]; + dateRange?: Interval; +} + +export function createPrismaQueryFromAccountingFilters( + filter: AccountingFilter, +): Prisma.TourWhereInput[] { + const conditions: Prisma.TourWhereInput[] = []; + + Object.entries(filter ?? {}).forEach(([property, args]) => { + switch (property) { + case 'deviation': + if (args.includes('pos')) + conditions.push({ + revenueDeviation: { gt: 0 }, + }); + else if (args.includes('neg')) + conditions.push({ + revenueDeviation: { lt: 0 }, + }); + else if (args.includes('both')) + conditions.push({ + revenueDeviation: { not: 0 }, + }); + return; + case 'missing': + if (args.includes('information')) + conditions.push({ + Ticket: { + currentState: { + in: [ + TicketValidationState.CREATED, + TicketValidationState.USER_INTERACTION_REQUIRED, + ], + }, + }, + }); + else if (args.includes('documents')) + conditions.push({ + Ticket: { + currentState: { + in: [ + TicketValidationState.TD_UNCERTAIN, + TicketValidationState.DOCUMENTS_MISSING, + ], + }, + }, + }); + return; + case 'dateRange': + conditions.push({ + AND: [ + { startDate: { gte: new Date(args.from) } }, + { startDate: { lt: new Date(args.to) } }, + ], + }); + return; + } + }); + return conditions; +} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting-state.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting-state.ts new file mode 100644 index 0000000..8689dc2 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting-state.ts @@ -0,0 +1,5 @@ +export enum AccountingState { + Billable = 'Billable', + PreparedForBilling = 'PreparedForBilling', + Billed = 'Billed', +} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.repository.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.repository.ts new file mode 100644 index 0000000..86a2715 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.repository.ts @@ -0,0 +1,211 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma } from '@prisma/client'; +import { PrismaService } from 'src/core/database/prisma.service'; +import { TourStateMetaObjectType } from './accounting-arg.types'; +import { + AccountingFilter, + createPrismaQueryFromAccountingFilters, +} from './accounting-filter'; +import { AccountingState } from './accounting-state'; + +@Injectable() +export class AccountingValidationRepository { + constructor(private readonly prisma: PrismaService) {} + + async findByState( + state: AccountingState, + filter: AccountingFilter, + take?: number, + skip?: number, + ) { + const checkValue = this.getCheckValueForAccountingState(state); + const query: Prisma.TourFindManyArgs = { + ...{ + ...(skip ? { skip, take: 10 } : {}), + ...(take ? { take } : {}), + ...(take && !skip ? { skip: 0 } : {}), + }, + where: { + check: checkValue, + ...(checkValue === 2 && !filter?.deviation + ? { revenueDeviation: { not: 0 } } + : {}), + empty: { + not: null, + }, + ticketId: { + not: null, + }, + AND: [ + ...createPrismaQueryFromAccountingFilters(filter), + { + OR: [ + { + carName: null, + }, + { + AND: [ + { + carName: { + not: 'Storno', + }, + }, + { + carName: { + not: 'Löschen', + }, + }, + { + carName: { + not: 'Abgabe', + }, + }, + ], + }, + ], + }, + { + deletedAt: null, + }, + { + OR: [ + { patientId: null }, + { + AND: [ + { + patientId: { + // Pause + not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979', + }, + }, + { + patientId: { + // Feierabend + not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a', + }, + }, + ], + }, + ], + }, + ], + }, + orderBy: { createdAt: 'desc' }, + }; + + const [tours, count] = await this.prisma.$transaction([ + this.prisma.tour.findMany({ ...query }), + this.prisma.tour.count({ where: query.where }), + ]); + return { + tours, + total: count, + }; + } + + async calculateTourStateMeta( + state: AccountingState, + ): Promise { + const checkValue = this.getCheckValueForAccountingState(state); + const ticketWithCurrentStateAndTours: Prisma.TourWhereInput = { + check: checkValue, + ...(checkValue === 2 ? { revenueDeviation: { not: 0 } } : {}), + empty: { + not: null, + }, + ticketId: { + not: null, + }, + AND: [ + { + OR: [ + { + carName: null, + }, + { + AND: [ + { + carName: { + not: 'Storno', + }, + }, + { + carName: { + not: 'Löschen', + }, + }, + { + carName: { + not: 'Abgabe', + }, + }, + ], + }, + ], + }, + { + deletedAt: null, + }, + { + OR: [ + { patientId: null }, + { + AND: [ + { + patientId: { + // Pause + not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979', + }, + }, + { + patientId: { + // Feierabend + not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a', + }, + }, + ], + }, + ], + }, + ], + }; + + const [count, revenueSum] = await this.prisma.$transaction([ + this.prisma.tour.count({ + where: { ...ticketWithCurrentStateAndTours }, + }), + this.prisma.tour.aggregate({ + _sum: { + revenue: true, + }, + where: { + ...ticketWithCurrentStateAndTours, + }, + }), + ]); + + return { + count, + revenueSum: revenueSum._sum.revenue ?? 0, + }; + } + + findTicketByTicketId(ticketId: string) { + return this.prisma.ticket.findFirst({ + where: { + id: ticketId, + }, + }); + } + + private getCheckValueForAccountingState(state: AccountingState): number { + switch (state) { + case AccountingState.Billable: + return 0; + case AccountingState.PreparedForBilling: + return 1; + case AccountingState.Billed: + return 2; + } + } +} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.resolver.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.resolver.ts new file mode 100644 index 0000000..fd81780 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.resolver.ts @@ -0,0 +1,35 @@ +import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'; +import { TicketObjectType } from '../app-ticket-system/ticket.object-type'; +import { + AccountingTourArgs, + TourStateMetaObjectType, +} from './accounting-arg.types'; +import { AccountingState } from './accounting-state'; +import { AccountingValidationService } from './accounting-validation.service'; +import { PaginatedTourObjectType } from './accounting.object-type'; +import { TourObjectType } from './tour.object-type'; + +@Resolver(() => TourObjectType) +export class AccountingValidationResolver { + constructor(private readonly service: AccountingValidationService) {} + + @Query(() => PaginatedTourObjectType) + accountingTours( + @Args() { state, filters, cursor, take }: AccountingTourArgs, + ) { + return this.service.paginateToursByState(state, filters, take, cursor); + } + + @Query(() => TourStateMetaObjectType) + accountingStateMeta( + @Args('state', { type: () => AccountingState }) + accountingState: AccountingState, + ) { + return this.service.getAccountingStateMeta(accountingState); + } + + @ResolveField(() => TicketObjectType, { nullable: true }) + ticket(@Parent() tour: TourObjectType) { + return this.service.findTicketByTicketId(tour.ticketId); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.service.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.service.ts new file mode 100644 index 0000000..6ecedce --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting-validation.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@nestjs/common'; +import { TourStateMetaObjectType } from './accounting-arg.types'; +import { AccountingFilter } from './accounting-filter'; +import { AccountingState } from './accounting-state'; +import { AccountingValidationRepository } from './accounting-validation.repository'; +import { PaginatedTourObjectType } from './accounting.object-type'; +import { + decodeOffsetCursor, + encodeCursor, +} from 'src/core/base/pagination.util'; + +@Injectable() +export class AccountingValidationService { + constructor(private readonly repository: AccountingValidationRepository) {} + + async paginateToursByState( + state: AccountingState, + filter: AccountingFilter, + take: number = 10, + cursor?: string, + ): Promise { + const skip = cursor ? decodeOffsetCursor(cursor) : 0; + const { tours, total } = await this.repository.findByState( + state, + filter, + take, + skip, + ); + + const prevPage = skip - take < 0 ? 0 : skip - take; + const nextPage = skip + take < total ? skip + take : skip; + + return { + nodes: tours, + prevCursor: encodeCursor(prevPage), + endCursor: encodeCursor(nextPage), + hasNextPage: nextPage !== skip, + totalCount: total, + }; + } + + async getAccountingStateMeta( + accountingState: AccountingState, + ): Promise { + return this.repository.calculateTourStateMeta(accountingState); + } + + findTicketByTicketId(ticketId?: string | null) { + if (!ticketId) return; + return this.repository.findTicketByTicketId(ticketId); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/accounting.object-type.ts b/service/data-hub/data-connector/src/modules/app-accounting/accounting.object-type.ts new file mode 100644 index 0000000..bfb06df --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/accounting.object-type.ts @@ -0,0 +1,11 @@ +import { ObjectType, registerEnumType } from '@nestjs/graphql'; +import { AccountingState } from './accounting-state'; +import { Paginated } from 'src/core/base/pagination.input'; +import { TourObjectType } from './tour.object-type'; + +@ObjectType('PaginatedTour') +export class PaginatedTourObjectType extends Paginated(TourObjectType) {} + +registerEnumType(AccountingState, { + name: 'AccountingState', +}); diff --git a/service/data-hub/data-connector/src/modules/app-accounting/app-accounting.module.ts b/service/data-hub/data-connector/src/modules/app-accounting/app-accounting.module.ts new file mode 100644 index 0000000..c2d942e --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/app-accounting.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/core/database/prisma.module'; +import { AccountingValidationRepository } from './accounting-validation.repository'; +import { AccountingValidationResolver } from './accounting-validation.resolver'; +import { AccountingValidationService } from './accounting-validation.service'; + +@Module({ + imports: [PrismaModule], + providers: [ + AccountingValidationRepository, + AccountingValidationResolver, + AccountingValidationService, + ], + exports: [], +}) +export class ApplicationAccountingModule {} diff --git a/service/data-hub/data-connector/src/modules/app-accounting/tour.object-type.ts b/service/data-hub/data-connector/src/modules/app-accounting/tour.object-type.ts new file mode 100644 index 0000000..fa28457 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-accounting/tour.object-type.ts @@ -0,0 +1,110 @@ +import { Field, Float, Int, ObjectType } from '@nestjs/graphql'; +import { Tour } from '@prisma/client'; + +@ObjectType('Tour') +export class TourObjectType implements Tour { + @Field() + id: string; + @Field() + done: boolean; + @Field() + direction: string; + @Field({ nullable: true }) + carName: string | null; + @Field() + operationId: string; + @Field({ nullable: true }) + driverId: string | null; + @Field({ nullable: true }) + driverName: string | null; + @Field({ nullable: true }) + codriverId: string | null; + @Field({ nullable: true }) + codriverName: string | null; + @Field({ nullable: true }) + patientId: string | null; + @Field({ nullable: true }) + patientName: string | null; + @Field({ nullable: true }) + patientSurname: string | null; + @Field({ nullable: true }) + patientStreet: string | null; + @Field({ nullable: true }) + patientZip: string | null; + @Field({ nullable: true }) + patientCity: string | null; + @Field({ nullable: true }) + healthInsurance: string | null; + @Field({ nullable: true }) + healthInsuranceNumber: string | null; + @Field({ nullable: true }) + category: string | null; + @Field({ nullable: true }) + transportType: string | null; + @Field({ nullable: true }) + ordinanceType: string | null; + @Field({ nullable: true }) + startInstitution: string | null; + @Field({ nullable: true }) + startStreet: string | null; + @Field({ nullable: true }) + startZip: string | null; + @Field({ nullable: true }) + startCity: string | null; + @Field({ nullable: true }) + targetInstitution: string | null; + @Field({ nullable: true }) + targetStreet: string | null; + @Field({ nullable: true }) + targetZip: string | null; + @Field({ nullable: true }) + targetCity: string | null; + @Field({ nullable: true }) + startDate: Date; + @Field({ nullable: true }) + startBegin: string | null; + @Field({ nullable: true }) + startEnd: string | null; + @Field({ nullable: true }) + target: string | null; + @Field({ nullable: true }) + targetBegin: string | null; + @Field({ nullable: true }) + empty: string | null; + @Field(() => Int) + check: number; + @Field(() => Float) + occupiedKm: number | null; + @Field(() => Float) + totalKm: number | null; + @Field() + type: string | null; + @Field({ nullable: true }) + rangeEndDate: Date | null; + + @Field(() => Float) + revenueDispoLive: number | null; + @Field(() => Float) + revenue: number | null; + @Field(() => Float) + consumptionCosts: number | null; + @Field(() => Float) + revenueDeviation: number; + @Field({ nullable: true }) + billDate: Date | null; + @Field({ nullable: true }) + billNumber: string | null; + + @Field({ nullable: true }) + createdAt: Date | null; + @Field({ nullable: true }) + deletedAt: Date | null; + + @Field(() => Boolean, { nullable: true }) + hasInfection: boolean | null; + @Field({ nullable: true }) + infectionName: string | null; + + @Field({ nullable: true }) + ticketId: string | null; +} diff --git a/service/data-hub/data-connector/src/modules/app-control-center/anomaly.object-type.ts b/service/data-hub/data-connector/src/modules/app-control-center/anomaly.object-type.ts new file mode 100644 index 0000000..61076be --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-control-center/anomaly.object-type.ts @@ -0,0 +1,65 @@ +import { Field, Int, ObjectType } from '@nestjs/graphql'; +import { Anomaly, SolutionTarget, SolutionTargetStep } from '@prisma/client'; +import { Paginated } from 'src/core/base/pagination.input'; + +@ObjectType('SolutionTarget') +export class SolutionTargetObjectType implements SolutionTarget { + @Field() + id: string; + @Field() + name: string; + @Field() + anomalyId: string; +} + +@ObjectType('SolutionTargetStep') +export class SolutionTargetStepObjectType implements SolutionTargetStep { + @Field() + id: string; + @Field() + description: string; + @Field() + solutionTargetId: string; +} + +@ObjectType('Anomaly') +export class AnomalyObjectType implements Anomaly { + @Field() + id: string; + @Field() + type: string; + @Field() + groupKey: string; + @Field() + groupDescription: string; + @Field() + workedOnBy: string; + @Field(() => Int) + sleepTimer: number; + @Field({ nullable: true }) + sleepSince: Date | null; + @Field() + description: string; + @Field({ nullable: true }) + resolvedAt: Date | null; + @Field({ nullable: true }) + manuallyResolved: boolean | null; + @Field() + createdAt: Date; +} + +@ObjectType('PaginatedAnomaly') +export class PaginatedAnomalyObjectType extends Paginated(AnomalyObjectType) {} + +@ObjectType('AnomalyGroup') +export class AnomalyGroupObjectType { + @Field() + key: string; + @Field(() => [AnomalyObjectType]) + anomalies: AnomalyObjectType[]; +} + +@ObjectType('PaginatedAnomalyGroup') +export class PaginatedAnomalyGroupObjectType extends Paginated( + AnomalyGroupObjectType, +) {} diff --git a/service/data-hub/data-connector/src/modules/app-control-center/app-control-center.module.ts b/service/data-hub/data-connector/src/modules/app-control-center/app-control-center.module.ts new file mode 100644 index 0000000..90d521c --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-control-center/app-control-center.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ControlCenterResolver } from './control-center.resolver'; +import { SolutionTargetsResolver } from './solution-targets.resolver'; +import { AnomalyDetectionModule } from '../feat-anomalies/anomaly-detection.module'; + +@Module({ + imports: [AnomalyDetectionModule], + providers: [ControlCenterResolver, SolutionTargetsResolver], +}) +export class ApplicationControlCenterModule {} diff --git a/service/data-hub/data-connector/src/modules/app-control-center/control-center.resolver.ts b/service/data-hub/data-connector/src/modules/app-control-center/control-center.resolver.ts new file mode 100644 index 0000000..0c0c733 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-control-center/control-center.resolver.ts @@ -0,0 +1,108 @@ +import { + Args, + Mutation, + Parent, + Query, + ResolveField, + Resolver, +} from '@nestjs/graphql'; + +import { PaginationArgs } from 'src/core/base/pagination.input'; +import { + decodeOffsetCursor, + encodeCursor, +} from 'src/core/base/pagination.util'; +import { TourObjectType } from '../app-accounting/tour.object-type'; +import { AnomaliesService } from '../feat-anomalies/anomalies.service'; +import { AnomalyTypes } from '../feat-anomalies/anomaly-detectors/anomaly-types.enum'; +import { + AnomalyObjectType, + PaginatedAnomalyGroupObjectType, + PaginatedAnomalyObjectType, + SolutionTargetObjectType, +} from './anomaly.object-type'; + +@Resolver(() => AnomalyObjectType) +export class ControlCenterResolver { + constructor(private readonly anomaliesService: AnomaliesService) {} + + @Query(() => AnomalyObjectType) + async anomaly(@Args('id') id: string) { + return this.anomaliesService.findById(id); + } + + @Query(() => [AnomalyObjectType]) + async anomaliesProto() { + return this.anomaliesService.findActiveAnomalies(); + } + + @Query(() => PaginatedAnomalyObjectType) + async anomalies(@Args() { cursor, take }: PaginationArgs) { + const skip = cursor ? decodeOffsetCursor(cursor) : 0; + const activeAnomalies = await this.anomaliesService.findActiveAnomalies( + skip, + take, + [AnomalyTypes.WorkingEmployeeNotInDistributionSystem], + ); + + const totalCount = await this.anomaliesService.countActiveAnomalies([ + AnomalyTypes.WorkingEmployeeNotInDistributionSystem, + ]); + + const prevPage = skip - take < 0 ? 0 : skip - take; + const nextPage = skip + take < totalCount ? skip + take : skip; + + return { + nodes: activeAnomalies, + prevCursor: encodeCursor(prevPage), + endCursor: encodeCursor(nextPage), + hasNextPage: nextPage !== skip, + totalCount, + }; + } + + @Query(() => PaginatedAnomalyGroupObjectType) + async groupedAnomalies(@Args() { cursor, take }: PaginationArgs) { + const skip = cursor ? decodeOffsetCursor(cursor) : 0; + + const { totalCount, anomalies: groupedActiveAnomalies } = + await this.anomaliesService.findActiveAnomaliesGroupedByGroupKey( + skip, + take, + ); + + const prevPage = skip - take < 0 ? 0 : skip - take; + const nextPage = skip + take < totalCount ? skip + take : skip; + + return { + nodes: groupedActiveAnomalies, + prevCursor: encodeCursor(prevPage), + endCursor: encodeCursor(nextPage), + hasNextPage: nextPage !== skip, + totalCount, + }; + } + + @Mutation(() => AnomalyObjectType) + async setAnomalyToSleep(@Args({ name: 'id' }) anomalyId: string) { + return this.anomaliesService.setAnomalyToSleep(anomalyId); + } + + @Mutation(() => AnomalyObjectType) + async acceptAnomaly(@Args({ name: 'id' }) anomalyId: string) { + return this.anomaliesService.acceptAnomaly(anomalyId); + } + + @ResolveField(() => TourObjectType, { nullable: true }) + tour(@Parent() anomaly: AnomalyObjectType) { + const [operationId] = anomaly.groupKey.split('-').reverse(); + return this.anomaliesService.findTourByOperationId(operationId); + } + + @ResolveField(() => [SolutionTargetObjectType]) + solution( + @Parent() anomaly: AnomalyObjectType, + ): Promise { + return this.anomaliesService.findSolutionTargetsByAnomalyId(anomaly.id); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-control-center/solution-targets.resolver.ts b/service/data-hub/data-connector/src/modules/app-control-center/solution-targets.resolver.ts new file mode 100644 index 0000000..09cd810 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-control-center/solution-targets.resolver.ts @@ -0,0 +1,21 @@ +import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; + +import { AnomaliesService } from '../feat-anomalies/anomalies.service'; +import { + SolutionTargetObjectType, + SolutionTargetStepObjectType, +} from './anomaly.object-type'; + +@Resolver(() => SolutionTargetObjectType) +export class SolutionTargetsResolver { + constructor(private readonly anomaliesService: AnomaliesService) {} + + @ResolveField(() => [SolutionTargetStepObjectType!]!) + steps( + @Parent() solutionTarget: SolutionTargetObjectType, + ): Promise { + return this.anomaliesService.findSolutionTargetStepsBySolutionTargetId( + solutionTarget.id, + ); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/app-manager.module.ts b/service/data-hub/data-connector/src/modules/app-manager/app-manager.module.ts new file mode 100644 index 0000000..818a3f4 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/app-manager.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/core/database/prisma.module'; +import { FeatureCalculationModule } from '../feat-calculation/feat-calculation.module'; +import { TourAmountForecastService } from './tour-amount-forecast.service'; +import { TimePerTourKpiService } from './time-per-tour-kpi.service'; +import { TimePerTourMetricsService } from './time-per-tour-metrics.service'; +import { ManagerResolver } from './manager.resolver'; +import { CarEntityService } from './car-entity/car-entity.service'; +import { CarEntityRepository } from './car-entity/car-entity.repository'; + +@Module({ + imports: [PrismaModule, FeatureCalculationModule], + providers: [ + TourAmountForecastService, + TimePerTourKpiService, + TimePerTourMetricsService, + CarEntityService, + CarEntityRepository, + ManagerResolver, + ], +}) +export class ApplicationManagerModule {} diff --git a/service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.repository.ts b/service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.repository.ts new file mode 100644 index 0000000..5f8ec93 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.repository.ts @@ -0,0 +1,130 @@ +import { Injectable } from '@nestjs/common'; +import { PlannedTime, Tour } from '@prisma/client'; +import { Interval } from 'date-fns'; +import { PrismaService } from 'src/core/database/prisma.service'; + +@Injectable() +export class CarEntityRepository { + constructor(private readonly prisma: PrismaService) {} + + findToursInInterval(interval: Interval): Promise { + return this.prisma.tour.findMany({ + where: { + AND: [ + { + startDate: { + gte: new Date(interval.start), + }, + }, + { + startDate: { + lt: new Date(interval.end), + }, + }, + { + OR: [ + { + carName: null, + }, + { + AND: [ + { + carName: { + not: 'Storno', + }, + }, + { + carName: { + not: 'Löschen', + }, + }, + { + carName: { + not: 'Abgabe', + }, + }, + ], + }, + ], + }, + { + check: { + not: 3, + }, + }, + { + deletedAt: null, + }, + { + OR: [ + { + patientId: null, + }, + { + patientId: { + // Pause + not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979', + }, + }, + { + patientId: { + // Feierabend + not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a', + }, + }, + ], + }, + ], + }, + }); + } + + findPlannedTimeByOrdinanceTypesInInterval( + ordinanceType: string, + interval: Interval, + ): Promise { + return this.prisma.plannedTime.findMany({ + where: { + AND: [ + { + startDate: { + gte: new Date(interval.start), + }, + }, + { + startDate: { + lte: new Date(interval.end), + }, + }, + { + AND: [ + { + department: { + contains: ordinanceType, + }, + }, + { + department: { + not: 'Leitstelle', + }, + }, + { + department: { + not: 'Büro', + }, + }, + ], + }, + { + note: null, + }, + { + userId: { + not: '0', + }, + }, + ], + }, + }); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.service.ts b/service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.service.ts new file mode 100644 index 0000000..d8320e0 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/car-entity/car-entity.service.ts @@ -0,0 +1,106 @@ +import { Injectable } from '@nestjs/common'; +import { PlannedTime, Tour } from '@prisma/client'; +import { CarEntityRepository } from './car-entity.repository'; +import { CarEntity } from './car.entity'; +import { Interval } from 'date-fns'; +import { generateIntervalForTimeRange } from 'src/utils'; + +@Injectable() +export class CarEntityService { + constructor(private readonly repository: CarEntityRepository) {} + + async getCarsInInterval( + interval: Interval, + ordinanceType: string, + ): Promise { + const carEntities = await this.generateCarEntitiesForInterval( + interval, + ordinanceType, + ); + return carEntities; + } + + async getCarsForOperationDay( + day: Date, + ordinanceType: string, + ): Promise { + const operationDayInterval = generateIntervalForTimeRange( + '05:00-04:59', + 'Europe/Berlin', + day, + ); + + const carEntities = await this.generateCarEntitiesForInterval( + operationDayInterval, + ordinanceType, + ); + return carEntities; + } + + private async generateCarEntitiesForInterval( + interval: Interval, + ordinanceType: string, + ): Promise { + const toursInFullRange = + await this.repository.findToursInInterval(interval); + const plannedTimeByOrdinanceType = + await this.repository.findPlannedTimeByOrdinanceTypesInInterval( + ordinanceType, + interval, + ); + return this.generateCarEntities( + toursInFullRange, + plannedTimeByOrdinanceType, + ordinanceType, + ); + } + + private generateCarEntities( + tours: Tour[], + plannedTimes: PlannedTime[], + ordinanceType: string, + ): CarEntity[] { + const departmentsAsCarNames = [ + ...new Set(plannedTimes.map(({ department }) => department)), + ]; + + const toursWithoutCarName = tours + .filter((tour) => tour?.ordinanceType?.includes(ordinanceType)) + .filter(({ carName }) => !carName); + + return [ + ...departmentsAsCarNames.map((departmentAsCarName) => { + const toursForCarName = tours.filter(({ carName }) => + this.isCarNameForDepartment(departmentAsCarName, carName), + ); + const plannedTimesForCarName = plannedTimes.filter( + ({ department }) => department === departmentAsCarName, + ); + + return new CarEntity( + toursForCarName, + plannedTimesForCarName, + departmentAsCarName, + ordinanceType, + ); + }), + ...(toursWithoutCarName.length + ? [new CarEntity(toursWithoutCarName, [], undefined, ordinanceType)] + : []), + ]; + } + + private isCarNameForDepartment(department: string, carName: string): boolean { + if (!department || !carName) return false; + const [ordinanceType, carNumber] = department + .replace('1/', '') + .replace('E', '') + .split('-') + .map((part) => part.trim()); + return ( + (carName.indexOf(ordinanceType) > -1 && + carName.indexOf(carNumber) > -1) || + carName === department + ); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/car-entity/car.entity.ts b/service/data-hub/data-connector/src/modules/app-manager/car-entity/car.entity.ts new file mode 100644 index 0000000..a8a34fd --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/car-entity/car.entity.ts @@ -0,0 +1,160 @@ +import { PlannedTime, Tour } from '@prisma/client'; +import { + areIntervalsOverlapping, + endOfDay, + format, + Interval, + isWithinInterval, + startOfDay, +} from 'date-fns'; +import { CalculationService } from 'src/modules/feat-calculation/calculation.service'; +import { clampInterval, getHoursInInterval } from 'src/utils'; + +const EMPLOYEE_AMOUNT_BY_ORDINANCE_TYPE = { + BTW: 1, + TSW: 2, + KTW: 2, +}; + +export class CarEntity { + constructor( + private readonly tours: Tour[], + private readonly plannedTimes: PlannedTime[], + public readonly carName: string, + private readonly ordinanceType: string, + ) {} + + availableTimeInInterval(interval: Interval): number { + return ( + this.plannedTimes + .map(({ startDate, endDate }) => ({ + start: new Date(startDate), + end: new Date(endDate), + })) + .filter((plannedTimeInterval) => + areIntervalsOverlapping(plannedTimeInterval, interval), + ) + .map((plannedTimeInterval) => + getHoursInInterval(plannedTimeInterval, interval), + ) + .sort() + .at(0) ?? 0 + ); + } + + tourAmountInInterval(interval: Interval): number { + return this.toursInInterval(interval).reduce( + (sum, { occupiedKm }) => (sum += occupiedKm > 30 ? occupiedKm / 30 : 1), + 0, + ); + } + + toursInInterval(interval: Interval): Tour[] { + return this.tours + .filter(({ startDate }) => + isWithinInterval(new Date(startDate), interval), + ) + .filter(({ occupiedKm }) => occupiedKm < 9999); + } + + async breakEvenInInterval( + interval: Interval, + calculationService: CalculationService, + ): Promise<{ + toursRevenue; + personnelCosts; + consumptionCost; + }> { + const toursInInterval = this.toursInInterval(interval); + + const toursRevenue = toursInInterval.reduce( + (sum, tour) => (sum += tour.revenue), + 0, + ); + + const consumptionCost = toursInInterval.reduce( + (sum, tour) => (sum += tour.consumptionCosts), + 0, + ); + + const plannedTimeIntervalInInterval = + this.plannedTimeIntervalInInterval(interval); + + if (!plannedTimeIntervalInInterval) { + return { + toursRevenue, + personnelCosts: 0, + consumptionCost, + }; + } + + const personnelCosts = + calculationService.calculatePersonnelCostInInterval( + clampInterval(plannedTimeIntervalInInterval, interval), + this.employeeAmount, + this.ordinanceType, + ) / 100; + return { + toursRevenue, + personnelCosts, + consumptionCost, + }; + } + + getTourAmountAtDateCreatedToday(date: Date): number { + const dateInterval: Interval = { + start: startOfDay(date), + end: endOfDay(date), + }; + + const todayInterval: Interval = { + start: startOfDay(new Date()), + end: endOfDay(new Date()), + }; + + return this.tours.filter( + ({ startDate, createdAt }) => + isWithinInterval(new Date(startDate), dateInterval) && + isWithinInterval(new Date(createdAt), todayInterval), + ).length; + } + + toString(): string { + return `${this.carName} + ${this.tours.length} Touren + ${this.plannedTimes.length} geplante Mitarbeiter + ${ + this.plannedTimes.length + ? this.plannedTimes + .map( + ({ startDate, endDate }) => + `${format(new Date(startDate), 'HH:mm')}-${format( + new Date(endDate), + 'HH:mm', + )}`, + ) + .join(', ') + : '' + }`; + } + + private plannedTimeIntervalInInterval(interval: Interval): Interval { + return this.plannedTimes + .map(({ startDate, endDate }) => ({ + start: new Date(startDate), + end: new Date(endDate), + })) + .filter((plannedTimeInterval) => + areIntervalsOverlapping(plannedTimeInterval, interval), + ) + .at(0); + } + + private hasPlannedTimeInInterval(interval: Interval): boolean { + return !!this.plannedTimeIntervalInInterval(interval); + } + + private get employeeAmount(): number { + return EMPLOYEE_AMOUNT_BY_ORDINANCE_TYPE[this.ordinanceType]; + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/manager.object-type.ts b/service/data-hub/data-connector/src/modules/app-manager/manager.object-type.ts new file mode 100644 index 0000000..ca7f507 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/manager.object-type.ts @@ -0,0 +1,86 @@ +import { Field, Float, Int, ObjectType } from '@nestjs/graphql'; +import { Interval } from 'date-fns'; +import { IntervalObjectType } from 'src/core/base/interval.object-type'; + +@ObjectType('KPI') +export class KPIObjectType { + @Field(() => Float) + value: number; + @Field(() => KPIMetadataObjectType) + meta: { + tourAmount: number; + availableTime: number; + baseValue: number; + breakEven: number; + }; +} + +@ObjectType('KPIByCar') +export class KPIByCarObjectType { + @Field() + carName: string; + @Field(() => KPIObjectType) + kpi: KPIObjectType; +} + +@ObjectType('TimePerTourKPISummary') +export class TimePerTourKPISummaryObjectType { + @Field(() => IntervalObjectType) + interval: Interval; + @Field(() => KPIObjectType) + kpi: KPIObjectType; + @Field(() => [KPIByCarObjectType]) + cars: KPIByCarObjectType[]; +} + +@ObjectType('KPIMetadata') +export class KPIMetadataObjectType { + @Field(() => Float) + tourAmount: number; + @Field(() => Float) + availableTime: number; + @Field(() => Float) + baseValue: number; + @Field(() => Float) + breakEven: number; +} + +@ObjectType('TimePerTourUpdate') +export class TimePerTourUpdateObjectType { + @Field() + job: string; +} + +@ObjectType('TimePerTourMetric') +export class TimePerTourMetricObjectType { + @Field(() => IntervalObjectType) + interval: Interval; + @Field(() => Float) + availableTime: number; + @Field(() => Float) + tourAmount: number; + @Field(() => Float) + breakEven: number; + @Field(() => Float) + revenue: number; + @Field(() => Float) + averageRevenue: number; + @Field(() => Float) + personnelCost: number; + @Field(() => Float) + consumptionCost: number; + @Field(() => Float) + forecast: number; + @Field(() => Float) + forecastRevenue: number; + @Field(() => [TimePerTourMetricCategoryAmountObjectType]) + categoryAmounts: TimePerTourMetricCategoryAmountObjectType[]; +} + +@ObjectType('TimePerTourMetricCategoryAmount') +export class TimePerTourMetricCategoryAmountObjectType { + @Field() + category: string; + @Field(() => Int) + amount: number; +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/manager.resolver.ts b/service/data-hub/data-connector/src/modules/app-manager/manager.resolver.ts new file mode 100644 index 0000000..b30f4f3 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/manager.resolver.ts @@ -0,0 +1,46 @@ +import { Args, Query, Resolver, Subscription } from '@nestjs/graphql'; +import { + TimePerTourKPISummaryObjectType, + TimePerTourMetricObjectType, + TimePerTourUpdateObjectType, +} from './manager.object-type'; +import { TimePerTourKpiService } from './time-per-tour-kpi.service'; +import { TimePerTourMetricsService } from './time-per-tour-metrics.service'; + +@Resolver() +export class ManagerResolver { + constructor( + private readonly timePerTourKpiService: TimePerTourKpiService, + private readonly timePerTourMetricService: TimePerTourMetricsService, + ) {} + + @Query(() => [TimePerTourKPISummaryObjectType]) + toursPerTimeKPI( + @Args('ordinanceTypes', { type: () => String }) ordinanceType: string, + ) { + return this.timePerTourKpiService.toursPerTimeKPI(ordinanceType); + } + + @Query(() => [TimePerTourMetricObjectType]) + timePerTourMetricForDay( + @Args('day', { type: () => Date }) day: Date, + @Args('ordinanceType', { type: () => String }) ordinanceType: string, + ) { + return this.timePerTourMetricService.forDay(day, ordinanceType); + } + + @Query(() => [TimePerTourMetricObjectType]) + timePerTourMetricForMonth( + @Args('day', { type: () => Date }) day: Date, + @Args('ordinanceType', { type: () => String }) ordinanceType: string, + ) { + return this.timePerTourMetricService.forMonth(day, ordinanceType); + } + + @Subscription(() => TimePerTourUpdateObjectType) + timePerTourUpdated() { + return this.timePerTourKpiService.pubSub.asyncIterator( + 'timePerTourUpdated', + ); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/time-per-tour-kpi.service.ts b/service/data-hub/data-connector/src/modules/app-manager/time-per-tour-kpi.service.ts new file mode 100644 index 0000000..6719c8c --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/time-per-tour-kpi.service.ts @@ -0,0 +1,145 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { addMilliseconds } from 'date-fns'; +import { PubSub } from 'graphql-subscriptions'; +import { generateIntervalForTimeRange } from 'src/utils'; +import { CarEntityService } from './car-entity/car-entity.service'; +import { CalculationService } from '../feat-calculation/calculation.service'; +import { ETLEventTypes } from '../feat-etl/feat-importers/events/event-types.enum'; +import { LoadingDoneEvent } from '../feat-etl/feat-importers/events/loading-done.event'; + +@Injectable() +export class TimePerTourKpiService { + public readonly pubSub = new PubSub(); + + @OnEvent(ETLEventTypes.LoadingDone, { async: true }) + notifyOnPlannedTimeUpdate(event: LoadingDoneEvent): void { + this.pubSub.publish('timePerTourUpdated', { + timePerTourUpdated: { job: event.jobId }, + }); + } + + constructor( + private readonly calculationService: CalculationService, + private readonly carEntityService: CarEntityService, + ) {} + + async toursPerTimeKPI(ordinanceType: string, date = new Date()) { + const fullRangeInterval = generateIntervalForTimeRange( + '05:00-01:00', + 'Europe/Berlin', + date, + ); + + const subIntervals = [ + generateIntervalForTimeRange('05:00-12:00', 'Europe/Berlin'), + generateIntervalForTimeRange('12:00-18:30', 'Europe/Berlin'), + generateIntervalForTimeRange('18:30-01:00', 'Europe/Berlin'), + ]; + + const carEntities = await this.carEntityService.getCarsInInterval( + fullRangeInterval, + ordinanceType, + ); + + return Promise.all( + subIntervals + .map(({ start, end }, index, array) => ({ + start, + end: index + 1 !== array.length ? addMilliseconds(end, -1) : end, + })) + .map(async (interval) => { + const cars = await Promise.all( + carEntities.map(async (carEntity) => { + const tourAmount = carEntity.tourAmountInInterval(interval); + const availableTime = carEntity.availableTimeInInterval(interval); + + const { personnelCosts, consumptionCost, toursRevenue } = + await carEntity.breakEvenInInterval( + interval, + this.calculationService, + ); + + const baseValue = this.calculateKPIBaseValue( + availableTime, + tourAmount, + ); + return { + carName: carEntity.carName ?? 'Touren ohne Zuweisung', + kpi: { + value: !carEntity.carName ? tourAmount : baseValue, + meta: { + tourAmount: Math.round(tourAmount * 100) / 100, + availableTime: Math.round(availableTime * 100) / 100, + baseValue, + breakEven: + Math.round( + (toursRevenue - personnelCosts - consumptionCost) * 100, + ) / 100, + }, + }, + }; + }), + ); + + const tourAmount = carEntities.reduce( + (sum, carEntity) => + (sum += carEntity.tourAmountInInterval(interval)), + 0, + ); + + const availableTime = carEntities.reduce( + (sum, carEntity) => + (sum += carEntity.availableTimeInInterval(interval)), + 0, + ); + + let revenue = 0; + let personnelCost = 0; + let consumptionCost = 0; + for (const carEntity of carEntities) { + const { + personnelCosts, + consumptionCost: cc, + toursRevenue, + } = await carEntity.breakEvenInInterval( + interval, + this.calculationService, + ); + revenue += toursRevenue; + personnelCost += personnelCosts; + consumptionCost += cc; + } + const baseValue = this.calculateKPIBaseValue( + availableTime, + tourAmount, + ); + return { + interval, + kpi: { + value: baseValue, + meta: { + tourAmount: Math.round(tourAmount * 100) / 100, + availableTime: Math.round(availableTime * 100) / 100, + baseValue, + breakEven: + Math.round( + (revenue - personnelCost - consumptionCost) * 100, + ) / 100, + }, + }, + cars, + }; + }), + ); + } + + calculateKPIBaseValue(availableTime: number, tourAmount: number): number { + if (tourAmount === 0 && availableTime > 0) { + return Math.round((availableTime / 0.01) * 1000) / 1000; + } else if (tourAmount === 0) { + return -1; + } + return Math.round((availableTime / tourAmount) * 1000) / 1000; + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/time-per-tour-metrics.service.ts b/service/data-hub/data-connector/src/modules/app-manager/time-per-tour-metrics.service.ts new file mode 100644 index 0000000..2e3cc88 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/time-per-tour-metrics.service.ts @@ -0,0 +1,214 @@ +import { Injectable } from '@nestjs/common'; +import { + addDays, + addMilliseconds, + areIntervalsOverlapping, + differenceInCalendarDays, + eachDayOfInterval, + eachHourOfInterval, + endOfDay, + endOfMonth, + getISODay, + Interval, + isSunday, + set, + startOfDay, + startOfMonth, +} from 'date-fns'; +import { CarEntityService } from './car-entity/car-entity.service'; +import { TourAmountForecastService } from './tour-amount-forecast.service'; +import { TimePerTourMetricObjectType } from './manager.object-type'; +import { CalculationService } from '../feat-calculation/calculation.service'; +import { generateIntervalForTimeRange, groupBy } from 'src/utils'; + +@Injectable() +export class TimePerTourMetricsService { + constructor( + private readonly calculationService: CalculationService, + private readonly tourAmountForecastService: TourAmountForecastService, + private readonly carEntityService: CarEntityService, + ) {} + + async forMonth(date: Date, ordinanceType: string) { + const fullRangeInterval = { + start: startOfMonth(date), + end: endOfMonth(date), + }; + + const subIntervals = eachDayOfInterval(fullRangeInterval) + .filter((day) => !isSunday(day)) + .reduce( + (subIntervals, day) => [ + ...subIntervals, + generateIntervalForTimeRange('05:00-04:59', 'Europe/Berlin', day), + ], + [] as Interval[], + ); + + return this.generateTimePerTourMetric( + fullRangeInterval, + subIntervals, + ordinanceType, + ); + } + + async forDay(date: Date, ordinanceType: string) { + const fullRangeInterval = generateIntervalForTimeRange( + '05:00-04:59', + 'Europe/Berlin', + date, + ); + const subIntervals = eachHourOfInterval(fullRangeInterval) + .reduce( + (subIntervals, hour, index, array) => [ + ...subIntervals, + ...(index === array.length - 1 + ? [] + : [{ start: hour, end: array[index + 1] }]), + ], + [] as Interval[], + ) + .map(({ start, end }, index, array) => ({ + start, + end: index + 1 !== array.length ? addMilliseconds(end, -1) : end, + })); + + return this.generateTimePerTourMetric( + fullRangeInterval, + subIntervals, + ordinanceType, + ); + } + + private async generateTimePerTourMetric( + intervalClamp: Interval, + subIntervals: Interval[], + ordinanceType: string, + ): Promise { + const carEntities = await this.carEntityService.getCarsInInterval( + intervalClamp, + ordinanceType, + ); + + const forecastPeriod: Interval = { + start: startOfDay(new Date()), + end: endOfDay(addDays(new Date(), 7)), + }; + const forecastForOrdinanceType = + await this.calculateForecastForOrdinanceType( + forecastPeriod, + intervalClamp, + ordinanceType, + ); + + return Promise.all( + subIntervals.map(async (interval) => { + const tourAmount = carEntities.reduce( + (sum, carEntity) => (sum += carEntity.tourAmountInInterval(interval)), + 0, + ); + const availableTime = carEntities.reduce( + (sum, carEntity) => + (sum += carEntity.availableTimeInInterval(interval)), + 0, + ); + let revenue = 0; + let personnelCost = 0; + let consumptionCost = 0; + + const categoryAmounts = Object.entries( + groupBy( + carEntities.flatMap((ce) => ce.toursInInterval(interval)), + ({ category }) => category, + ), + ).reduce( + (sum, [category, tours]) => [ + ...sum, + { category, amount: tours.length }, + ], + [], + ); + for (const carEntity of carEntities) { + const { + personnelCosts, + consumptionCost: cc, + toursRevenue, + } = await carEntity.breakEvenInInterval( + interval, + this.calculationService, + ); + revenue += toursRevenue; + personnelCost += personnelCosts; + consumptionCost += cc; + } + let forecast = 0; + let forecastRevenue = 0; + if (areIntervalsOverlapping(interval, forecastPeriod)) { + const dayDiffFromNow = differenceInCalendarDays( + interval.start, + new Date(), + ); + const weekday = getISODay(interval.start); + const tourAmountAtDateCreatedToday = carEntities.reduce( + (sum, carEntity) => + (sum += carEntity.getTourAmountAtDateCreatedToday( + new Date(interval.start), + )), + 0, + ); + forecast = + forecastForOrdinanceType[weekday] + ?.slice(0, dayDiffFromNow + 1) + .reduce((sum, tourAmount) => (sum += tourAmount), 0) - + (tourAmountAtDateCreatedToday ?? 0); + + if (forecast >= 0) { + const billingInformation = + this.calculationService.getModellBillingInformationByOrdinanceType( + ordinanceType, + set(new Date(), { + hours: 13, + minutes: 0, + seconds: 0, + milliseconds: 0, + }), + ); + forecastRevenue = + billingInformation.revenuePerTour * Math.round(forecast) - + billingInformation.consumptionCostsPerTour * Math.round(forecast); + } + } + + return { + interval, + tourAmount: Math.round(tourAmount * 100) / 100, + availableTime: Math.round(availableTime * 100) / 100, + breakEven: + Math.round((revenue - personnelCost - consumptionCost) * 100) / 100, + revenue, + averageRevenue: isNaN(revenue / tourAmount) + ? 0 + : revenue / tourAmount, + personnelCost, + consumptionCost, + forecast: forecast < 0 || isNaN(forecast) ? 0 : forecast, + forecastRevenue, + categoryAmounts, + }; + }), + ); + } + + private async calculateForecastForOrdinanceType( + forecastPeriod: Interval, + requestedInterval: Interval, + ordinanceType: string, + ): Promise> { + if (areIntervalsOverlapping(requestedInterval, forecastPeriod)) { + return this.tourAmountForecastService.calculateTourAmountForecastFourWeekPeriod( + ordinanceType, + ); + } + return {}; + } +} diff --git a/service/data-hub/data-connector/src/modules/app-manager/tour-amount-forecast.service.ts b/service/data-hub/data-connector/src/modules/app-manager/tour-amount-forecast.service.ts new file mode 100644 index 0000000..b51f613 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-manager/tour-amount-forecast.service.ts @@ -0,0 +1,243 @@ +import { Injectable } from '@nestjs/common'; +import { Tour } from '@prisma/client'; +import { + addDays, + differenceInCalendarDays, + eachDayOfInterval, + endOfDay, + format, + getISODay, + Interval, + isSameDay, + startOfDay, +} from 'date-fns'; +import { PrismaService } from 'src/core/database/prisma.service'; + +@Injectable() +export class TourAmountForecastService { + constructor(private readonly prisma: PrismaService) {} + + calculateTourAmountForecastFourWeekPeriod( + ordinanceType: string | undefined, + ): Promise> { + return this.calculateTourAmountForecastForInterval( + { + start: startOfDay(addDays(new Date(), -28)), + end: endOfDay(new Date()), + }, + ordinanceType, + ); + } + + async calculateTourAmountForecastForInterval( + interval: Interval, + ordinanceType: string | undefined, + ): Promise> { + const toursInInterval = await this.findToursInInterval( + interval, + ordinanceType, + ); + const toursGroupedByDay = this.groupToursByDay(interval, toursInInterval); + + const tourCreationToStartDiffByDay = Object.entries( + toursGroupedByDay, + ).reduce( + (group, [dateString, tours]) => ({ + ...group, + [dateString]: this.calculateCreationToStartDifferenceOfTours(tours), + }), + {} as Record>, + ); + + const tourCreationToStartDiffsByWeekday = Object.entries( + tourCreationToStartDiffByDay, + ).reduce( + (group, [dateString, diffs]) => { + const weekday = getISODay(new Date(dateString)); + const existingDiffs = group[weekday] ?? {}; + const dayDifferences = [0, 1, 2, 3, 4, 5, 6, 7]; + const diffsCombined = dayDifferences.reduce( + (combined, dayDifference) => { + const diffForDay = diffs[dayDifference]; + return { + ...combined, + [dayDifference]: [ + ...(existingDiffs[dayDifference] ?? []), + diffForDay, + ], + }; + }, + {} as Record, + ); + + return { + ...group, + [weekday]: diffsCombined, + }; + }, + {} as Record>, + ); + + const tourAmountForecastByWeekday = Object.entries( + tourCreationToStartDiffsByWeekday, + ).reduce( + (group, [weekday, diffs]) => { + const diffsMedian = Object.entries(diffs).reduce( + (combined, [, tourAmounts]) => { + const median = + tourAmounts.reduce( + (sum, tourAmount) => (sum += tourAmount), + 0.0, + ) / tourAmounts.length; + return [...combined, median]; + }, + [] as number[], + ); + return { + ...group, + [weekday]: diffsMedian, + }; + }, + {} as Record, + ); + + return tourAmountForecastByWeekday; + } + + private groupToursByDay( + interval: Interval, + tours: Tour[], + ): Record { + const daysInInterval = eachDayOfInterval(interval); + return daysInInterval.reduce( + (group, day) => { + return { + ...group, + [format(day, 'yyyy-MM-dd')]: tours.filter(({ startDate }) => + isSameDay(day, new Date(startDate)), + ), + }; + }, + {} as Record, + ); + } + + private calculateCreationToStartDifferenceOfTours( + tours: Tour[], + ): Record { + return tours.reduce( + (diffs, tour) => { + const tourCreationToStartDiff = differenceInCalendarDays( + new Date(tour.startDate), + new Date(tour.createdAt), + ); + if (tourCreationToStartDiff > 7 || tourCreationToStartDiff < 0) { + return diffs; + } + return { + ...diffs, + [tourCreationToStartDiff]: (diffs[tourCreationToStartDiff] ?? 0) + 1, + }; + }, + { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0 } as Record< + string, + number + >, + ); + } + + private findToursInInterval( + interval: Interval, + ordinanceType: string | undefined, + ) { + const ordinanceTypeToQuery = ordinanceType.split(' ')[0]; + return this.prisma.tour.findMany({ + where: { + AND: [ + { + startDate: { + gte: new Date(interval.start), + }, + }, + { + startDate: { + lt: new Date(interval.end), + }, + }, + ...(ordinanceType + ? [ + { + AND: [ + { + ordinanceType: ordinanceTypeToQuery, + }, + ...(ordinanceType.indexOf('TSW') > -1 + ? [ + { + transportType: + ordinanceType.indexOf('1') > -1 ? 'TS1' : 'TS2', + }, + ] + : []), + ], + }, + ] + : []), + { + OR: [ + { + carName: null, + }, + { + AND: [ + { + carName: { + not: 'Storno', + }, + }, + { + carName: { + not: 'Löschen', + }, + }, + { + carName: { + not: 'Abgabe', + }, + }, + ], + }, + ], + }, + { + check: { + not: 3, + }, + }, + { + deletedAt: null, + }, + { + OR: [ + { + patientId: null, + }, + { + patientId: { + // Pause + not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979', + }, + }, + { + patientId: { + // Feierabend + not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a', + }, + }, + ], + }, + ], + }, + }); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/app-ticket-system.module.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/app-ticket-system.module.ts new file mode 100644 index 0000000..95836c4 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/app-ticket-system.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/core/database/prisma.module'; +import { TicketLifecycleService } from './ticket-lifecycle.service'; +import { TicketRepository } from './ticket.repository'; +import { TicketsResolver } from './tickets.resolver'; +import { TicketService } from './tickets.service'; + +@Module({ + imports: [PrismaModule], + providers: [ + TicketRepository, + TicketService, + TicketLifecycleService, + TicketsResolver, + ], + exports: [TicketService], +}) +export class ApplicationTicketSystemModule {} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-arg.types.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-arg.types.ts new file mode 100644 index 0000000..5d92a4f --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-arg.types.ts @@ -0,0 +1,42 @@ +import { ArgsType, Field, InputType } from '@nestjs/graphql'; +import { ApprovalState, TicketValidationState } from '@prisma/client'; +import { PaginationArgs } from 'src/core/base/pagination.input'; +import { TicketFilter } from './ticket-filter'; + +@InputType() +export class TicketFilterArgs implements TicketFilter { + @Field(() => [String], { nullable: true }) + errors: string[]; + @Field(() => [ApprovalState], { nullable: true }) + approvalStates: ApprovalState[]; + @Field(() => [String], { nullable: true }) + documentInfo: ('approval' | 'transport_document')[]; +} + +@ArgsType() +export class TicketsArgs extends PaginationArgs { + @Field(() => TicketValidationState) + state: TicketValidationState; + @Field(() => TicketFilterArgs, { nullable: true }) + filters: TicketFilterArgs; +} + +@InputType() +export class ApprovalInfoArgs { + @Field({ nullable: true }) + isNeeded?: boolean; + @Field({ nullable: true }) + whoRequested?: string; + @Field({ nullable: true }) + answeredFrom?: string; + @Field({ nullable: true }) + location?: string; +} + +@ArgsType() +export class DocumentInfoArgs { + @Field(() => ApprovalInfoArgs, { nullable: true }) + approval: ApprovalInfoArgs; + @Field({ nullable: true }) + tdLocation?: string; +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-filter.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-filter.ts new file mode 100644 index 0000000..4179193 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-filter.ts @@ -0,0 +1,71 @@ +import { ApprovalState, Prisma } from '@prisma/client'; + +export interface TicketFilter { + errors?: string[]; + approvalStates?: ApprovalState[]; + documentInfo?: ('approval' | 'transport_document')[]; +} + +export function createPrismaQueryFromTicketFilters( + filter: TicketFilter, +): Prisma.TicketWhereInput[] { + const conditions: Prisma.TicketWhereInput[] = []; + + Object.entries(filter ?? {}).forEach(([property, args]) => { + switch (property) { + case 'errors': + if (args.length > 0) + conditions.push({ + errors: { + hasSome: [...args], + }, + }); + return; + case 'approvalStates': + if (args.length > 0) + conditions.push({ + approvalState: { + in: [...args], + }, + }); + return; + case 'documentInfo': + if (args.includes('approval')) { + conditions.push({ + OR: [ + { + documentInfo: { + path: ['approval', 'location'], + not: 'company', + }, + }, + { + documentInfo: { + path: ['approval', 'location'], + equals: Prisma.JsonNull, + }, + }, + ], + }); + } + if (args.includes('td_company')) { + conditions.push({ + documentInfo: { + path: ['tdLocation'], + equals: 'company', + }, + }); + } + if (args.includes('td_customer')) { + conditions.push({ + documentInfo: { + path: ['tdLocation'], + equals: 'customer', + }, + }); + } + return; + } + }); + return conditions; +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-lifecycle.service.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-lifecycle.service.ts new file mode 100644 index 0000000..8797e49 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-lifecycle.service.ts @@ -0,0 +1,240 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { + ApprovalState, + Ticket, + TicketValidationState, + Tour, +} from '@prisma/client'; +import { Interval } from 'date-fns'; +import { DocumentInfoArgs } from './ticket-arg.types'; +import { + ValidationResult, + validatePatient, + validatePatientDataOnTours, + validateTour, +} from './ticket-validation.functions'; +import { TicketRepository } from './ticket.repository'; + +@Injectable() +export class TicketLifecycleService { + private readonly logger = new Logger(TicketLifecycleService.name); + + constructor(private readonly repository: TicketRepository) {} + + async createTickets(interval: Interval): Promise { + const tours = await this.repository.findTours(interval); + + for (const tour of tours) { + if (tour.ticketId) { + continue; + } + const ticketId = await this.repository.searchTicketIDForTour(tour); + if (ticketId) { + await this.repository.addToursToTicket(ticketId, [tour]); + } else { + await this.repository.createTicket([tour]); + } + } + + await this.validateCreatedTickets(); + // await this.archiveClosedTickets(); + } + + async validateDocumentInfoUpdate( + ticketId: string, + documentInfo: DocumentInfoArgs, + ) { + const ticket = await this.repository.findById(ticketId); + + switch (ticket.currentState) { + case TicketValidationState.USER_INTERACTION_REQUIRED: + const approvalStateUpdate = documentInfo.approval.isNeeded + ? ApprovalState.REQUESTED + : ApprovalState.FREE; + + let stateUpdate: TicketValidationState = + TicketValidationState.USER_INTERACTION_REQUIRED; + if ( + documentInfo.tdLocation === 'company' && + approvalStateUpdate === ApprovalState.FREE + ) + stateUpdate = TicketValidationState.ARCHIVED; + else if (documentInfo.tdLocation === 'uncertain') + stateUpdate = TicketValidationState.TD_UNCERTAIN; + else stateUpdate = TicketValidationState.DOCUMENTS_MISSING; + + return this.repository.updateTicket(ticketId, { + documentInfo, + approvalState: approvalStateUpdate, + state: stateUpdate, + }); + + case TicketValidationState.TD_UNCERTAIN: + return this.repository.updateTicket(ticketId, { + documentInfo, + state: + ticket.approvalState === ApprovalState.FREE && + documentInfo.tdLocation === 'company' + ? TicketValidationState.ARCHIVED + : TicketValidationState.DOCUMENTS_MISSING, + }); + case TicketValidationState.DOCUMENTS_MISSING: + return this.repository.updateTicket(ticketId, { + documentInfo, + ...((ticket.approvalState === ApprovalState.FREE || + documentInfo.approval.location === 'company') && + documentInfo.tdLocation === 'company' + ? { state: TicketValidationState.ARCHIVED } + : {}), + }); + } + } + + private async validateCreatedTickets(): Promise { + const tickets = await this.repository.findAllCreated(); + this.logger.debug(`Found ${tickets.length} with state CREATED`); + + for (const ticket of tickets) { + const { + result: isValid, + errors, + notes, + } = await this.validateCreatedTicket(ticket); + + if (isValid) { + const { result: approvalResult } = await this.isApprovalNeeded(ticket); + if (approvalResult) { + await this.repository.updateTicket(ticket.id, { + errors, + notes, + state: TicketValidationState.USER_INTERACTION_REQUIRED, + approvalState: ticket.tours.every(({ patientId }) => !patientId) + ? ApprovalState.UNKNOWN + : ApprovalState.REQUIRED, + }); + } else { + await this.repository.updateTicket(ticket.id, { + errors, + notes, + state: TicketValidationState.USER_INTERACTION_REQUIRED, + approvalState: ApprovalState.FREE, + }); + } + } else { + await this.repository.setActionablesOnTicket(ticket.id, errors, notes); + } + } + } + + private async archiveClosedTickets() { + const closedTickets = await this.repository.findAllClosed(); + for (const closedTicket of closedTickets) { + await this.repository.setStateOfTicket( + closedTicket.id, + TicketValidationState.ARCHIVED, + ); + } + } + + private async validateCreatedTicket( + ticket: Ticket & { tours: Tour[] }, + ): Promise { + const patientIds = [ + ...new Set(ticket.tours.map(({ patientId }) => patientId)), + ]; + if (patientIds.length > 1) { + return { + result: false, + errors: ['Ticket: Die Touren umfassen mehr als einen Patienten'], + notes: [], + }; + } + const [patientId] = patientIds; + if (!patientId && ticket.tours.at(0)?.type !== 'Spontan') { + return { + result: false, + errors: [], + notes: ['Patient: Der Patient ist nicht in DispoLive hinterlegt'], + }; + } + + let patientResult: ValidationResult; + if (!patientId && ticket.tours.at(0)?.type === 'Spontan') { + patientResult = validatePatientDataOnTours(ticket.tours); + } else { + try { + const patient = await this.repository.findPatientById(patientId); + patientResult = validatePatient(patient); + } catch { + patientResult = validatePatientDataOnTours(ticket.tours); + } + } + const tourResults = ticket.tours.map((tour) => validateTour(tour)); + + return [patientResult, ...tourResults].reduce( + (result, results) => ({ + result: result.result && results.result, + errors: [...result.errors, ...results.errors], + notes: [...result.notes, ...results.notes], + }), + { result: true, errors: [], notes: [] } as ValidationResult, + ); + } + + private async isApprovalNeeded( + ticket: Ticket & { tours: Tour[] }, + ): Promise { + const approvalRequirements = await Promise.all( + ticket.tours.map((tour) => this.checkTourApprovalRequirement(tour)), + ); + const isApprovalRequired = approvalRequirements.reduce( + (result, requirement) => result || requirement, + false, + ); + + return { + result: isApprovalRequired, + errors: [], + notes: + isApprovalRequired && ticket.tours.every(({ patientId }) => !patientId) + ? [ + 'Patient nicht in Stammdaten: Prüfen Sie ob eine Genehmigung benötigt wird', + ] + : [], + }; + } + + private async checkTourApprovalRequirement(tour: Tour): Promise { + if ( + [ + 'Einweisung', + 'Entlassung', + 'Entlassung Vorbestellung', + 'Konsil KH - Dia', + 'Privatfahrt Rechnung', + 'Tagesklinik', + 'Verlegung', + ].includes(tour.category) || + tour.healthInsurance === 'KOSTENTRÄGER PATIENT' + ) { + return false; + } + + if ( + tour.patientId && + ['BTW', 'TSW'].includes(tour.ordinanceType) && + tour.category !== 'Dialyse' + ) { + const patient = await this.repository.findPatientById(tour.patientId); + if ( + ['3', '4', '5'].includes(patient.careDegree) && + (['aG', 'Bi', 'H'].includes(patient.disabilityMark1) || + ['aG', 'Bi', 'H'].includes(patient.disabilityMark2)) + ) { + return false; + } + } + + return true; + } +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-system.events.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-system.events.ts new file mode 100644 index 0000000..ec1e88a --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-system.events.ts @@ -0,0 +1,7 @@ +export enum TicketSystemEventTypes { + Validated = 'ticket-system.validated', +} + +export class TicketSystemValidatedEvent { + constructor(public readonly count: number) {} +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-validation.functions.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-validation.functions.ts new file mode 100644 index 0000000..7258667 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket-validation.functions.ts @@ -0,0 +1,153 @@ +import { Patient, Tour } from '@prisma/client'; + +export interface ValidationResult { + result: boolean; + notes: string[]; + errors: string[]; +} + +export function validatePatient(patient: Patient): ValidationResult { + const notes: string[] = []; + const errors: string[] = []; + let isValid = true; + + if (!patient?.name) { + isValid = false; + errors.push('Patient: Vorname ist nicht gefüllt'); + } + if (!patient?.surname) { + isValid = false; + errors.push('Patient: Nachname ist nicht gefüllt'); + } + if (!patient?.birthday) { + isValid = false; + notes.push('Patient: Geburtstag ist nicht gefüllt'); + } + if (!patient?.healthinsurance) { + isValid = false; + errors.push('Patient: Krankenkasse ist nicht gefüllt'); + } + if (!patient?.careDegree) { + notes.push('Patient: Pflegegrad ist nicht gefüllt'); + } + if (!patient?.disabilityMark1) { + notes.push('Patient: Merkzeichen 1 ist nicht gefüllt'); + } + if (!patient?.disabilityMark2) { + notes.push('Patient: Merkzeichen 2 ist nicht gefüllt'); + } + + return { + result: isValid, + errors, + notes, + }; +} + +export function validatePatientDataOnTours(tours: Tour[]): ValidationResult { + const errors: string[] = []; + let isValid = true; + + for (const tour of tours) { + if (!tour.patientName) { + isValid = false; + errors.push( + `Tour ${tour.operationId}: Patient Vorname ist nicht gefüllt`, + ); + } + if (!tour.patientSurname) { + isValid = false; + errors.push( + `Tour ${tour.operationId}: Patient Nachname ist nicht gefüllt`, + ); + } + if (!tour.healthInsurance) { + isValid = false; + errors.push( + `Tour ${tour.operationId}: Patient Krankenversicherung ist nicht gefüllt`, + ); + } + } + + return { + result: isValid, + errors, + notes: [], + }; +} + +export function validateTour(tour: Tour): ValidationResult { + const errors: string[] = []; + let isValid = true; + + if (!tour.ordinanceType) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Verordnungsart ist nicht gefüllt`); + } + if (!tour.transportType) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Transportart ist nicht gefüllt`); + } + if (!tour.category) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Kategorie ist nicht gefüllt`); + } + if (!tour.startDate) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Startzeit ist nicht gefüllt`); + } + if (!tour.startStreet || !tour.startZip || !tour.startCity) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Start Adresse ist nicht gefüllt`); + } + if (!tour.targetStreet || !tour.targetZip || !tour.targetCity) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Ziel Adresse ist nicht gefüllt`); + } + if (tour.occupiedKm > 9990) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Besetzte Kilometer sind zu hoch`); + } + + return { + result: isValid, + errors, + notes: [], + }; +} + +export function validateTransport(tour: Tour): ValidationResult { + const errors: string[] = []; + let isValid = true; + + if (!tour.carName) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Fahrzeugname ist nicht angegeben`); + } + + if (!tour.driverName) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Fahrer ist nicht angegeben`); + } + + if (!tour.codriverName) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Beifahrer ist nicht angegeben`); + } + + if (!tour.startBegin) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Anfahrt-Stempel fehlt`); + } + + if (!tour.empty) { + isValid = false; + errors.push(`Tour ${tour.operationId}: Frei-Stempel fehlt`); + } + + return { + result: isValid, + errors, + notes: [], + }; +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket.object-type.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket.object-type.ts new file mode 100644 index 0000000..f51fafa --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket.object-type.ts @@ -0,0 +1,87 @@ +import { Field, Int, ObjectType, registerEnumType } from '@nestjs/graphql'; +import { + ApprovalState, + Prisma, + Ticket, + TicketValidationState, +} from '@prisma/client'; +import { Paginated } from 'src/core/base/pagination.input'; + +@ObjectType('ApprovalInfo') +export class ApprovalInfoObjectType { + @Field({ nullable: true }) + isNeeded?: boolean; + @Field({ nullable: true }) + whoRequested?: string; + @Field({ nullable: true }) + answeredFrom?: string; + @Field({ nullable: true }) + location?: string; +} + +@ObjectType('DocumentInfo') +export class DocumentInfoObjectType { + @Field(() => ApprovalInfoObjectType) + approval: ApprovalInfoObjectType; + @Field({ nullable: true }) + tdLocation: string; +} + +@ObjectType('Ticket') +export class TicketObjectType implements Ticket { + @Field() + id: string; + @Field(() => TicketValidationState) + currentState: TicketValidationState; + @Field(() => ApprovalState, { nullable: true }) + approvalState: ApprovalState | null; + @Field(() => [String]) + errors: string[]; + @Field(() => [String]) + notes: string[]; + @Field(() => Boolean) + isUrgent: boolean; + @Field() + urgency: Date; + @Field(() => DocumentInfoObjectType, { nullable: true }) + documentInfo: Prisma.JsonValue; + @Field() + createdAt: Date; +} + +@ObjectType('PaginatedTicket') +export class PaginatedTicketObjectType extends Paginated(TicketObjectType) {} + +@ObjectType('TicketsUpdated') +export class TicketsUpdatedObjectType { + @Field(() => Int) + count: number; +} + +@ObjectType('TicketStateMeta') +export class TicketStateMetaObjectType { + @Field(() => Int) + count: number; + @Field(() => Int, { nullable: true }) + approvalRequiredCount?: number; + @Field(() => Int, { nullable: true }) + approvalUnknownCount?: number; + @Field(() => Int, { nullable: true }) + tdNotCompanyCount?: number; + @Field(() => Int, { nullable: true }) + approvalNotCompanyCount?: number; +} + +@ObjectType('TicketStateKeyFigures') +export class TicketStateKeyFiguresObjectType { + @Field(() => Int) + dwellTime: number; +} + +registerEnumType(TicketValidationState, { + name: 'TicketValidationState', +}); + +registerEnumType(ApprovalState, { + name: 'ApprovalState', +}); diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/ticket.repository.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket.repository.ts new file mode 100644 index 0000000..48128b9 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/ticket.repository.ts @@ -0,0 +1,619 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { + ApprovalState, + Patient, + Prisma, + TicketValidationState, + Tour, +} from '@prisma/client'; +import { + differenceInBusinessDays, + endOfDay, + Interval, + startOfDay, +} from 'date-fns'; +import { ExpressionBuilder, Kysely } from 'kysely'; +import { jsonArrayFrom } from 'kysely/helpers/postgres'; +import { KYSELY_CLIENT_PROVIDER_TOKEN } from 'src/core/database/database.module'; +import { PrismaService } from 'src/core/database/prisma.service'; +import { DB } from 'src/core/database/types'; +import { DocumentInfoArgs, TicketFilterArgs } from './ticket-arg.types'; +import { createPrismaQueryFromTicketFilters } from './ticket-filter'; + +function withTours(eb: ExpressionBuilder) { + return jsonArrayFrom( + eb + .selectFrom('Tour') + .selectAll('Tour') + .whereRef('Tour.ticketId', '=', 'Ticket.id') + .orderBy('Tour.operationId'), + ).as('tours'); +} + +@Injectable() +export class TicketRepository { + private logger = new Logger(TicketRepository.name); + + constructor( + private readonly prisma: PrismaService, + @Inject(KYSELY_CLIENT_PROVIDER_TOKEN) private readonly db: Kysely, + ) {} + + async createTicket(tours: Tour[]) { + const ticket = await this.db + .insertInto('Ticket') + .values({ + currentState: TicketValidationState.CREATED, + }) + .returningAll() + .executeTakeFirst(); + + await this.db + .insertInto('TicketStateHistory') + .values({ + state: TicketValidationState.CREATED, + ticketId: ticket.id, + }) + .executeTakeFirst(); + + await this.db + .updateTable('Tour') + .set({ + ticketId: ticket.id, + }) + .where( + 'id', + 'in', + tours.map(({ id }) => id), + ) + .execute(); + + return ticket; + } + + async addToursToTicket(ticketId: string, tours: Tour[]) { + await this.db + .updateTable('Tour') + .set({ + ticketId: ticketId, + }) + .where( + 'id', + 'in', + tours.map(({ id }) => id), + ) + .execute(); + + return this.db + .selectFrom('Ticket') + .where('id', '=', ticketId) + .selectAll() + .executeTakeFirst(); + } + + setActionablesOnTicket(ticketId: string, errors: string[], notes: string[]) { + return this.db + .updateTable('Ticket') + .set({ errors, notes }) + .where('id', '=', ticketId) + .returningAll() + .executeTakeFirst(); + } + + async setStateOfTicket(ticketId: string, state: TicketValidationState) { + await this.db + .updateTable('Ticket') + .set({ + currentState: state, + }) + .where('Ticket.id', '=', ticketId) + .execute(); + + return this.db + .insertInto('TicketStateHistory') + .values({ state, ticketId }) + .returningAll() + .executeTakeFirst(); + } + + async updateTicket( + ticketId: string, + data: { + state?: TicketValidationState; + errors?: string[]; + notes?: string[]; + isUrgent?: boolean; + urgency?: Date; + approvalState?: ApprovalState; + documentInfo?: DocumentInfoArgs; + approvalPath?: string; + }, + ) { + const { state, ...ticketData } = data; + + await this.db + .updateTable('Ticket') + .set({ ...ticketData }) + .where('id', '=', ticketId) + .execute(); + + if (state) { + await this.setStateOfTicket(ticketId, state); + } + + return this.db + .selectFrom('Ticket') + .where('id', '=', ticketId) + .selectAll('Ticket') + .select((eb) => withTours(eb)) + .executeTakeFirst(); + } + + findById(id: string) { + return this.prisma.ticket.findUnique({ + where: { + id, + }, + include: { + tours: true, + }, + }); + } + + findAll() { + return this.prisma.ticket.findMany({ + where: { + state: { + none: { + state: TicketValidationState.ARCHIVED, + }, + }, + }, + include: { + tours: true, + }, + }); + } + + findAllCreated() { + return this.prisma.ticket.findMany({ + where: { + state: { + none: { + state: TicketValidationState.USER_INTERACTION_REQUIRED, + }, + }, + }, + include: { + tours: true, + }, + }); + } + + findAllClosed() { + return this.prisma.ticket.findMany({ + where: { + tours: { + every: { + check: { + not: 0, + }, + }, + }, + }, + }); + } + + search(query: string) { + return this.prisma.ticket.findMany({ + where: { + state: { + none: { + state: TicketValidationState.ARCHIVED, + }, + }, + tours: { + some: { + OR: [ + ...query.split(' ').flatMap((word) => [ + { + patientName: { + contains: word, + mode: Prisma.QueryMode.insensitive, + }, + }, + { + patientSurname: { + contains: word, + mode: Prisma.QueryMode.insensitive, + }, + }, + { + operationId: { + contains: word, + mode: Prisma.QueryMode.insensitive, + }, + }, + ]), + ], + }, + }, + }, + take: 5, + include: { + tours: true, + }, + }); + } + + async findByState( + state: TicketValidationState, + filter: TicketFilterArgs, + take?: number, + skip?: number, + ) { + const query: Prisma.TicketFindManyArgs = { + ...{ + ...(skip ? { skip, take: 10 } : {}), + ...(take ? { take } : {}), + ...(take && !skip ? { skip: 0 } : {}), + }, + where: { + AND: createPrismaQueryFromTicketFilters(filter), + currentState: state, + tours: { + some: { + check: 0, + }, + }, + }, + orderBy: { createdAt: 'desc' }, + }; + + const [tickets, count] = await this.prisma.$transaction([ + this.prisma.ticket.findMany({ ...query }), + this.prisma.ticket.count({ where: query.where }), + ]); + return { + tickets, + total: count, + }; + } + + async calculateTicketStateMeta(ticketState: TicketValidationState) { + const ticketWithCurrentStateAndTours: Prisma.TicketWhereInput = { + currentState: ticketState, + tours: { some: { check: 0 } }, + }; + const [ + count, + approvalRequiredCount, + approvalUnknownCount, + tdNotCompanyCount, + approvalNotCompanyCount, + ] = await this.prisma.$transaction([ + this.prisma.ticket.count({ + where: { ...ticketWithCurrentStateAndTours }, + }), + this.prisma.ticket.aggregate({ + _count: { + id: true, + }, + where: { + ...ticketWithCurrentStateAndTours, + approvalState: ApprovalState.REQUIRED, + }, + }), + this.prisma.ticket.aggregate({ + _count: { + id: true, + }, + where: { + ...ticketWithCurrentStateAndTours, + approvalState: ApprovalState.UNKNOWN, + }, + }), + this.prisma.ticket.aggregate({ + _count: { + id: true, + }, + where: { + ...ticketWithCurrentStateAndTours, + documentInfo: { + path: ['approval', 'location'], + not: 'company', + }, + }, + }), + this.prisma.ticket.aggregate({ + _count: { + id: true, + }, + where: { + ...ticketWithCurrentStateAndTours, + documentInfo: { + path: ['tdLocation'], + not: 'company', + }, + }, + }), + ]); + + return { + count, + approvalRequiredCount: approvalRequiredCount._count.id, + approvalUnknownCount: approvalUnknownCount._count.id, + tdNotCompanyCount: tdNotCompanyCount._count.id, + approvalNotCompanyCount: approvalNotCompanyCount._count.id, + }; + } + + async calculateTicketStateKeyFigures(ticketState: TicketValidationState) { + const ticketWithCurrentStateAndTours: Prisma.TicketWhereInput = { + tours: { some: { check: 0 } }, + }; + + const ticketWithState = await this.prisma.ticket.findMany({ + where: { ...ticketWithCurrentStateAndTours }, + include: { + state: true, + }, + }); + + const dwellTimesForTicketState = ticketWithState + .map(({ state }) => { + console.log(state); + const stateToCalculateIdx = state.findIndex( + ({ state }) => state === ticketState, + ); + if (stateToCalculateIdx > -1 && state[stateToCalculateIdx + 1]) { + const nextState = state[stateToCalculateIdx + 1]; + return differenceInBusinessDays( + new Date(nextState.createdAt), + new Date(state[stateToCalculateIdx].createdAt), + ); + } else if (stateToCalculateIdx > -1) { + return differenceInBusinessDays( + new Date(), + new Date(state[stateToCalculateIdx].createdAt), + ); + } else { + return undefined; + } + }) + .filter(Boolean); + + return { + dwellTime: + dwellTimesForTicketState.reduce((a, b) => a + b, 0) / + dwellTimesForTicketState.length, + }; + } + + findPatientById(patientId: string): Promise { + return this.prisma.patient.findUnique({ where: { id: patientId } }); + } + + findStatesByTicketId( + ticketId: string, + ): Promise<{ state: TicketValidationState }[]> { + return this.prisma.ticketStateHistory.findMany({ + where: { + ticketId, + }, + select: { + state: true, + }, + }); + } + + findToursByTicketId(ticketId: string): Promise { + return this.prisma.tour.findMany({ + where: { + ticketId, + }, + }); + } + + async searchTicketIDForTour(tour: Tour): Promise { + const tours = await Promise.all([ + this.findFirstRecurringTour(tour), + this.findTourOnSameDayWithOppositeDirection(tour), + ]); + const firstFoundTour = tours.filter((tour) => !!tour)[0]; + if (firstFoundTour) { + const ticket = await this.prisma.ticket.findFirst({ + select: { + id: true, + }, + where: { + tours: { + some: { + id: firstFoundTour.id, + }, + }, + }, + }); + return ticket?.id; + } + return undefined; + } + + findFirstRecurringTour(tour: Tour): Promise { + const institutionStreet = + tour.direction === 'Hinfahrt' ? tour.targetStreet : tour.startStreet; + return this.prisma.tour.findFirst({ + where: { + type: 'Serie', + category: tour.category, + ordinanceType: tour.ordinanceType, + patientId: tour.patientId, + patientName: tour.patientName, + patientSurname: tour.patientSurname, + patientStreet: tour.patientStreet, + patientCity: tour.patientCity, + patientZip: tour.patientZip, + OR: [ + { targetStreet: institutionStreet }, + { startStreet: institutionStreet }, + ], + rangeEndDate: { + gte: tour.startDate, + }, + ticketId: { + not: null, + }, + }, + orderBy: { + startDate: 'desc', + }, + }); + } + + findTourOnSameDayWithOppositeDirection(tour: Tour): Promise { + return this.prisma.tour.findFirst({ + where: { + AND: [ + { + startDate: { + gt: startOfDay(new Date(tour.startDate)), + }, + }, + { + startDate: { + lt: endOfDay(new Date(tour.startDate)), + }, + }, + { + startStreet: tour.targetStreet, + }, + { + targetStreet: tour.startStreet, + }, + { + direction: tour.direction === 'Hinfahrt' ? 'Rückfahrt' : 'Hinfahrt', + }, + ], + }, + }); + } + + findToursWithTicket(interval: Interval): Promise { + return this.prisma.tour.findMany({ + where: { + AND: [ + { + ticketId: { + not: null, + }, + }, + { + startDate: { + gte: new Date(interval.start), + }, + }, + { + startDate: { + lt: new Date(interval.end), + }, + }, + ], + }, + }); + } + + removeTourFromTicket(tourId: string): Promise { + return this.prisma.tour.update({ + where: { + id: tourId, + }, + data: { + Ticket: { + disconnect: true, + }, + }, + }); + } + + findTours(interval: Interval): Promise { + return this.prisma.tour.findMany({ + where: { + AND: [ + { + startDate: { + gte: new Date(interval.start), + }, + }, + { + startDate: { + lt: new Date(interval.end), + }, + }, + { + OR: [ + { + carName: null, + }, + { + AND: [ + { + carName: { + not: 'Storno', + }, + }, + { + carName: { + not: 'Löschen', + }, + }, + { + carName: { + not: 'Abgabe', + }, + }, + ], + }, + ], + }, + { + check: { + not: 3, + }, + }, + { + deletedAt: null, + }, + { + ticketId: null, + }, + { + AND: [ + { + patientId: { + // Pause + not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979', + }, + }, + { + patientId: { + // Feierabend + not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a', + }, + }, + ], + }, + ], + }, + }); + } + + async deleteEmptyTickets(): Promise { + return this.prisma.ticket.deleteMany({ + where: { + tours: { + none: {}, + }, + }, + }); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/tickets.resolver.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/tickets.resolver.ts new file mode 100644 index 0000000..afb5931 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/tickets.resolver.ts @@ -0,0 +1,82 @@ +import { + Args, + Mutation, + Parent, + Query, + ResolveField, + Resolver, + Subscription, +} from '@nestjs/graphql'; +import { TicketValidationState } from '@prisma/client'; +import { TourObjectType } from '../app-accounting/tour.object-type'; +import { DocumentInfoArgs, TicketsArgs } from './ticket-arg.types'; +import { + PaginatedTicketObjectType, + TicketObjectType, + TicketStateKeyFiguresObjectType, + TicketStateMetaObjectType, + TicketsUpdatedObjectType, +} from './ticket.object-type'; +import { TicketService } from './tickets.service'; + +@Resolver(() => TicketObjectType) +export class TicketsResolver { + constructor(private readonly ticketsService: TicketService) {} + + @Query(() => TicketObjectType) + ticket(@Args('id') id: string) { + return this.ticketsService.findById(id); + } + + @Query(() => PaginatedTicketObjectType) + tickets(@Args() { state, filters, cursor, take }: TicketsArgs) { + return this.ticketsService.paginateTicketsByState( + state, + filters, + take, + cursor, + ); + } + + @Query(() => [TicketObjectType]) + searchTickets(@Args('query') query: string) { + return this.ticketsService.searchTickets(query); + } + + @Query(() => TicketStateMetaObjectType) + ticketStateMeta( + @Args('state', { type: () => TicketValidationState }) + ticketState: TicketValidationState, + ) { + return this.ticketsService.getTicketStateMeta(ticketState); + } + + @Query(() => TicketStateKeyFiguresObjectType) + ticketStateKeyFigures( + @Args('state', { type: () => TicketValidationState }) + ticketState: TicketValidationState, + ) { + return this.ticketsService.getTicketStateKeyFigures(ticketState); + } + + @Mutation(() => TicketObjectType) + setDocumentInfo( + @Args('ticketId', { type: () => String }) ticketId: string, + @Args() { approval, tdLocation }: DocumentInfoArgs, + ) { + return this.ticketsService.updateDocumentInfo(ticketId, { + approval, + tdLocation, + }); + } + + @ResolveField(() => [TourObjectType]) + tours(@Parent() ticket: TicketObjectType) { + return this.ticketsService.findToursByTicketId(ticket.id); + } + + @Subscription(() => TicketsUpdatedObjectType) + ticketsUpdated() { + return this.ticketsService.pubSub.asyncIterator('ticketsUpdated'); + } +} diff --git a/service/data-hub/data-connector/src/modules/app-ticket-system/tickets.service.ts b/service/data-hub/data-connector/src/modules/app-ticket-system/tickets.service.ts new file mode 100644 index 0000000..c848567 --- /dev/null +++ b/service/data-hub/data-connector/src/modules/app-ticket-system/tickets.service.ts @@ -0,0 +1,112 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; +import { Ticket, TicketValidationState, Tour } from '@prisma/client'; +import { Interval } from 'date-fns'; +import { PubSub } from 'graphql-subscriptions'; +import { + decodeOffsetCursor, + encodeCursor, +} from 'src/core/base/pagination.util'; +import { DocumentInfoArgs, TicketFilterArgs } from './ticket-arg.types'; +import { TicketLifecycleService } from './ticket-lifecycle.service'; +import { + TicketSystemEventTypes, + TicketSystemValidatedEvent, +} from './ticket-system.events'; +import { + PaginatedTicketObjectType, + TicketStateKeyFiguresObjectType, + TicketStateMetaObjectType, +} from './ticket.object-type'; +import { TicketRepository } from './ticket.repository'; + +@Injectable() +export class TicketService { + public readonly pubSub = new PubSub(); + + @OnEvent(TicketSystemEventTypes.Validated, { async: true }) + notifyOnTicketsValidated(event: TicketSystemValidatedEvent): void { + this.pubSub.publish('ticketsUpdated', { + ticketsUpdated: { count: event.count }, + }); + } + + constructor( + private readonly repository: TicketRepository, + private readonly lifecycle: TicketLifecycleService, + private readonly eventEmitter: EventEmitter2, + ) {} + + async createTickets(interval: Interval): Promise { + await this.lifecycle.createTickets(interval); + + this.eventEmitter.emit( + TicketSystemEventTypes.Validated, + new TicketSystemValidatedEvent(0), + ); + } + + async updateDocumentInfo(ticketId: string, documentInfo: DocumentInfoArgs) { + return this.lifecycle.validateDocumentInfoUpdate(ticketId, documentInfo); + } + + findById(id: string): Promise { + return this.repository.findById(id); + } + + findAll(): Promise { + return this.repository.findAll() as Promise; + } + + searchTickets(query: string): Promise { + return this.repository.search(query); + } + + async paginateTicketsByState( + state: TicketValidationState, + filter: TicketFilterArgs, + take?: number, + cursor?: string, + ): Promise { + const skip = cursor ? decodeOffsetCursor(cursor) : 0; + const { tickets, total } = await this.repository.findByState( + state, + filter, + take, + skip, + ); + + const prevPage = skip - take < 0 ? 0 : skip - take; + const nextPage = skip + take < total ? skip + take : skip; + + return { + nodes: tickets, + prevCursor: encodeCursor(prevPage), + endCursor: encodeCursor(nextPage), + hasNextPage: nextPage !== skip, + totalCount: total, + }; + } + + async getTicketStateMeta( + ticketState: TicketValidationState, + ): Promise { + return this.repository.calculateTicketStateMeta(ticketState); + } + + getTicketStateKeyFigures( + ticketState: TicketValidationState, + ): Promise { + return this.repository.calculateTicketStateKeyFigures(ticketState); + } + + findToursByTicketId(ticketId: string): Promise { + return this.repository.findToursByTicketId(ticketId); + } + + findStatesByTicketId(ticketId: string): Promise { + return this.repository + .findStatesByTicketId(ticketId) + .then((states) => states.map(({ state }) => state)); + } +} diff --git a/service/data-hub/data-connector/src/modules/feat-anomalies/anomalies.service.ts b/service/data-hub/data-connector/src/modules/feat-anomalies/anomalies.service.ts index 5a8da84..2c7fc8a 100644 --- a/service/data-hub/data-connector/src/modules/feat-anomalies/anomalies.service.ts +++ b/service/data-hub/data-connector/src/modules/feat-anomalies/anomalies.service.ts @@ -15,25 +15,28 @@ import { AnomalyTypes } from './anomaly-detectors/anomaly-types.enum'; import { DriverCertificateDetector } from './anomaly-detectors/driver-certificate.detector'; import { DriverCodriverSameDetector } from './anomaly-detectors/driver-codriver-same.detector'; import { EmployeesLoggedInDetector } from './anomaly-detectors/employees-logged-in.detector'; -import { OneTourAtATimeDetector } from './anomaly-detectors/one-tour-at-a-time.detector'; +// import { OneTourAtATimeDetector } from './anomaly-detectors/one-tour-at-a-time.detector'; import { WorkingEmployeeNotInDistributionSystemDetector } from './anomaly-detectors/working-employee-not-in-distribution-system.detector'; +import { AnomalyDetector } from './anomaly-detectors/anomaly-detector.class'; @Injectable() export class AnomaliesService { - private detectors = [ - new EmployeesLoggedInDetector(this.anomalyDetectorService), - new DriverCertificateDetector(this.anomalyDetectorService), - new DriverCodriverSameDetector(this.anomalyDetectorService), - new OneTourAtATimeDetector(this.anomalyDetectorService), - new WorkingEmployeeNotInDistributionSystemDetector( - this.anomalyDetectorService, - ), - ]; + private detectors: AnomalyDetector[]; constructor( private readonly prisma: PrismaService, private readonly anomalyDetectorService: AnomalyDetectorService, - ) {} + ) { + this.detectors = [ + new EmployeesLoggedInDetector(this.anomalyDetectorService), + new DriverCertificateDetector(this.anomalyDetectorService), + new DriverCodriverSameDetector(this.anomalyDetectorService), + // new OneTourAtATimeDetector(this.anomalyDetectorService), + new WorkingEmployeeNotInDistributionSystemDetector( + this.anomalyDetectorService, + ), + ]; + } detectAnomalies(interval: Interval): Promise { return Promise.all( diff --git a/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/anomaly-detector.service.ts b/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/anomaly-detector.service.ts index 47c1dee..a18e799 100644 --- a/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/anomaly-detector.service.ts +++ b/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/anomaly-detector.service.ts @@ -105,6 +105,9 @@ export class AnomalyDetectorService { }, ], }, + orderBy: { + startDate: 'asc', + }, }); } diff --git a/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/employees-logged-in.detector.ts b/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/employees-logged-in.detector.ts index 82a4d0d..5175906 100644 --- a/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/employees-logged-in.detector.ts +++ b/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/employees-logged-in.detector.ts @@ -267,19 +267,6 @@ export class EmployeesLoggedInDetector extends AnomalyDetector { const datetimeToCheckWithBuffer = addMinutes(datetimeToCheck, 5); - if (personnelNumber === '23') { - console.log( - datetimeToCheck, - { - start: new Date(workTime.startDate), - end: new Date(workTime.endDate), - }, - { - start: new Date(firstClockedIn.dateTime), - end: new Date(lastClockedOut.dateTime), - }, - ); - } try { if (workTime) { const interval = { diff --git a/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/one-tour-at-a-time.detector.ts b/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/one-tour-at-a-time.detector.ts index b834f65..f8ee07a 100644 --- a/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/one-tour-at-a-time.detector.ts +++ b/service/data-hub/data-connector/src/modules/feat-anomalies/anomaly-detectors/one-tour-at-a-time.detector.ts @@ -1,5 +1,5 @@ import { Logger } from '@nestjs/common'; -import { Anomaly, Prisma } from '@prisma/client'; +import { Anomaly, Prisma, Tour } from '@prisma/client'; import { addDays, areIntervalsOverlapping, @@ -7,11 +7,11 @@ import { format, Interval, isBefore, - isWithinInterval, + isSameDay, set, startOfDay, } from 'date-fns'; -import { toZonedTime, fromZonedTime } from 'date-fns-tz'; +import { fromZonedTime, toZonedTime } from 'date-fns-tz'; import { AnomalyDetector } from './anomaly-detector.class'; import { AnomalyTypes } from './anomaly-types.enum'; @@ -20,177 +20,209 @@ export class OneTourAtATimeDetector extends AnomalyDetector { private readonly logger = new Logger(OneTourAtATimeDetector.name); readonly type = AnomalyTypes.OneTourAtATime; + private groupToursByDay(tours: Tour[]): Record { + return tours.reduce( + (acc, cur) => { + const dayString = new Date(cur.startDate).toDateString(); + const group = acc[dayString] ?? []; + group.push(cur); + return { ...acc, [dayString]: group }; + }, + {} as Record, + ); + } + async detect(inInterval: Interval): Promise { const toursInRange = await this.dataService.findToursInInterval(inInterval); - return toursInRange - .map( - ( - { - operationId, - startDate, - startBegin, - startEnd, - targetBegin, - target, - empty, - carName, - driverId, - driverName, - codriverId, - codriverName, - }, - _, - tours, - ) => { - // somehow startBegin can be after empty, so we use startBegin and startEnd and take the lower value and hope for the best - const [start] = [startBegin, startEnd].sort(); - // somehow empty can be before everything else, so we use targetBegin, target and empty and take the higher value ... - const [end] = [targetBegin, target, empty] - .filter((val) => !!val) - .sort() - .reverse(); + const toursMap = this.groupToursByDay(toursInRange); + const result = []; + for (const [, tours] of Object.entries(toursMap)) { + const intermediateStep = tours + .map( + ( + { + operationId, + startDate, + startBegin, + startEnd, + targetBegin, + target, + empty, + carName, + driverId, + driverName, + codriverId, + codriverName, + }, + _, + tours, + ) => { + // somehow startBegin can be after empty, so we use startBegin and startEnd and take the lower value and hope for the best + const [start] = [startBegin, startEnd].sort(); + // somehow empty can be before everything else, so we use targetBegin, target and empty and take the higher value ... + const [end] = [targetBegin, target, empty] + .filter((val) => !!val) + .sort() + .reverse(); - if (!start || !end) { - if (isBefore(new Date(), startDate)) { - this.logger.warn( - `Tour ${operationId} started in the past, but is missing start or end time`, - ); - } - return []; - } - const tourInterval = this.getTourInterval(start, end, startDate); - - const toursInInterval = tours - .filter((tour) => - isWithinInterval(tour.startDate, { - start: startOfDay(startDate), - end: endOfDay(startDate), - }), - ) - .filter( - ({ - operationId: tourListOperationId, - carName: tourListCarName, - }) => - tourListOperationId !== operationId && - carName !== tourListCarName, - ) - .filter( - ({ - startDate: tourListStartDate, - startBegin: tourListStartBegin, - startEnd: tourListStartEnd, - targetBegin: tourListTargetBegin, - target: tourListTarget, - empty: tourListEmpty, - }) => { - // somehow startBegin can be after empty, so we use startBegin and startEnd and take the lower value and hope for the best - const [tourListStart] = [ - tourListStartBegin, - tourListStartEnd, - ].sort(); - // somehow empty can be before everything else, so we use targetBegin and empty and take the higher value ... - const [tourListEnd] = [ - tourListTargetBegin, - tourListTarget, - tourListEmpty, - ] - .filter((val) => !!val) - .sort() - .reverse(); - - if (!tourListStart || !tourListEnd) { - return false; - } - - const tourListInterval = this.getTourInterval( - tourListStart, - tourListEnd, - tourListStartDate, + if (!start || !end) { + if (isBefore(new Date(), startDate)) { + this.logger.warn( + `Tour ${operationId} started in the past, but is missing start or end time`, ); - try { - return areIntervalsOverlapping( - tourInterval, - tourListInterval, + } + return []; + } + const tourInterval = this.getTourInterval(start, end, startDate); + + const toursInInterval = tours + .filter( + (tour) => isSameDay(tour.startDate, startDate), + // isWithinInterval(tour.startDate, { + // start: startOfDay(startDate), + // end: endOfDay(startDate), + // }), + ) + .filter( + ({ + operationId: tourListOperationId, + carName: tourListCarName, + }) => + tourListOperationId !== operationId && + carName !== tourListCarName, + ) + .filter( + ({ + startDate: tourListStartDate, + startBegin: tourListStartBegin, + startEnd: tourListStartEnd, + targetBegin: tourListTargetBegin, + target: tourListTarget, + empty: tourListEmpty, + }) => { + // somehow startBegin can be after empty, so we use startBegin and startEnd and take the lower value and hope for the best + const [tourListStart] = [ + tourListStartBegin, + tourListStartEnd, + ].sort(); + // somehow empty can be before everything else, so we use targetBegin and empty and take the higher value ... + const [tourListEnd] = [ + tourListTargetBegin, + tourListTarget, + tourListEmpty, + ] + .filter((val) => !!val) + .sort() + .reverse(); + + if (!tourListStart || !tourListEnd) { + return false; + } + + const tourListInterval = this.getTourInterval( + tourListStart, + tourListEnd, + tourListStartDate, ); - } catch (error) { - this.logger.error( - 'tourListInterval', - error, - tourListInterval, - tourInterval, - ); - return false; - } - }, + try { + return areIntervalsOverlapping( + tourInterval, + tourListInterval, + ); + } catch (error) { + this.logger.error( + 'tourListInterval', + error, + tourListInterval, + tourInterval, + ); + return false; + } + }, + ); + + const toursInIntervalWithDriver = toursInInterval.filter( + ({ + driverId: tourListDriverId, + codriverId: tourListCodriverId, + }) => + driverId === tourListDriverId || + driverId === tourListCodriverId, + ); + const toursInIntervalWithCodriver = toursInInterval.filter( + ({ + driverId: tourListDriverId, + codriverId: tourListCodriverId, + }) => + codriverId === tourListDriverId || + codriverId === tourListCodriverId, ); - const toursInIntervalWithDriver = toursInInterval.filter( - ({ driverId: tourListDriverId, codriverId: tourListCodriverId }) => - driverId === tourListDriverId || driverId === tourListCodriverId, - ); - const toursInIntervalWithCodriver = toursInInterval.filter( - ({ driverId: tourListDriverId, codriverId: tourListCodriverId }) => - codriverId === tourListDriverId || - codriverId === tourListCodriverId, - ); - - const overlappingCarsWithDriver = toursInIntervalWithDriver.map( - ({ carName }) => carName, - ); - const overlappingTourInfoWithDriver = toursInIntervalWithDriver.map( - ({ operationId, startDate }) => ({ - startDate: format(new Date(startDate), 'HH:mm dd.MM.yy'), - operationId, - }), - ); - const overlappingCarsWithCodriver = toursInIntervalWithCodriver.map( - ({ carName }) => carName, - ); - const overlappingTourInfoWithCodriver = - toursInIntervalWithCodriver.map(({ operationId, startDate }) => ({ - startDate: format(new Date(startDate), 'HH:mm dd.MM.yy'), - operationId, - })); - return [ - toursInIntervalWithDriver.length - ? this.createAnomaly( - `${operationId}-${driverId}-${this.type}`, - operationId, - driverName, - startDate, - [carName, ...overlappingCarsWithDriver], - [ - { - operationId, - startDate: format(new Date(startDate), 'HH:mm dd.MM.yy'), - }, - ...overlappingTourInfoWithDriver, - ], - ) - : undefined, - toursInIntervalWithCodriver.length - ? this.createAnomaly( - `${operationId}-${codriverId}-${this.type}`, - operationId, - codriverName, - startDate, - [carName, ...overlappingCarsWithCodriver], - [ - { - operationId, - startDate: format(new Date(startDate), 'HH:mm dd.MM.yy'), - }, - ...overlappingTourInfoWithCodriver, - ], - ) - : undefined, - ]; - }, - ) - .flat() - .filter((anomaly) => !!anomaly); + const overlappingCarsWithDriver = toursInIntervalWithDriver.map( + ({ carName }) => carName, + ); + const overlappingTourInfoWithDriver = toursInIntervalWithDriver.map( + ({ operationId, startDate }) => ({ + startDate: format(new Date(startDate), 'HH:mm dd.MM.yy'), + operationId, + }), + ); + const overlappingCarsWithCodriver = toursInIntervalWithCodriver.map( + ({ carName }) => carName, + ); + const overlappingTourInfoWithCodriver = + toursInIntervalWithCodriver.map(({ operationId, startDate }) => ({ + startDate: format(new Date(startDate), 'HH:mm dd.MM.yy'), + operationId, + })); + return [ + toursInIntervalWithDriver.length + ? this.createAnomaly( + `${operationId}-${driverId}-${this.type}`, + operationId, + driverName, + startDate, + [carName, ...overlappingCarsWithDriver], + [ + { + operationId, + startDate: format( + new Date(startDate), + 'HH:mm dd.MM.yy', + ), + }, + ...overlappingTourInfoWithDriver, + ], + ) + : undefined, + toursInIntervalWithCodriver.length + ? this.createAnomaly( + `${operationId}-${codriverId}-${this.type}`, + operationId, + codriverName, + startDate, + [carName, ...overlappingCarsWithCodriver], + [ + { + operationId, + startDate: format( + new Date(startDate), + 'HH:mm dd.MM.yy', + ), + }, + ...overlappingTourInfoWithCodriver, + ], + ) + : undefined, + ]; + }, + ) + .flat() + .filter((anomaly) => !!anomaly); + result.push(intermediateStep); + } + return result.flat(); } async validate(anomaly: Anomaly): Promise { diff --git a/service/data-hub/data-connector/src/modules/feat-calculation/calculation.service.ts b/service/data-hub/data-connector/src/modules/feat-calculation/calculation.service.ts index 4cd9ec8..448d6a1 100644 --- a/service/data-hub/data-connector/src/modules/feat-calculation/calculation.service.ts +++ b/service/data-hub/data-connector/src/modules/feat-calculation/calculation.service.ts @@ -179,8 +179,7 @@ export class CalculationService { ): number { const highestZuschlag = this.getHighestZuschlag(tour, tariff); // if TariffGroup is VDEK KTW, sum up costTypes - const isVdek = - tariff.tariffGroup?.description?.indexOf('VDEK KTW') > -1 ?? false; + const isVdek = tariff.tariffGroup?.description?.indexOf('VDEK KTW') > -1; return tariff.costTypes.reduce((sum, costType) => { switch (costType.variant) { @@ -281,12 +280,19 @@ export class CalculationService { private calculateTSW(tour: Tour, costType: CostType): number { if ( - costType.description.includes('TSW 1') && + costType.description.includes('TS+R') && + tour.transportType === 'TS+R' + ) { + return costType.costAmount; + } else if ( + (costType.description.includes('TS 1') || + costType.description.includes('TSW 1')) && tour.driverId === tour.codriverId ) { return costType.costAmount; } else if ( - costType.description.includes('TSW 2') && + (costType.description.includes('TS 2') || + costType.description.includes('TSW 2')) && tour.driverId !== tour.codriverId ) { return costType.costAmount; @@ -389,9 +395,16 @@ export class CalculationService { } const tenant = await this.prisma.avicennaTenant.findFirst({ where: { - name: { - contains: healthInsurance, - }, + OR: [ + { + name: healthInsurance, + }, + { + name: { + contains: healthInsurance, + }, + }, + ], tariffGroups: { some: { ordinanceType, diff --git a/service/data-hub/data-connector/src/modules/feat-calculation/util-calculation.module.ts b/service/data-hub/data-connector/src/modules/feat-calculation/feat-calculation.module.ts similarity index 90% rename from service/data-hub/data-connector/src/modules/feat-calculation/util-calculation.module.ts rename to service/data-hub/data-connector/src/modules/feat-calculation/feat-calculation.module.ts index 647273f..4d5434d 100644 --- a/service/data-hub/data-connector/src/modules/feat-calculation/util-calculation.module.ts +++ b/service/data-hub/data-connector/src/modules/feat-calculation/feat-calculation.module.ts @@ -9,4 +9,4 @@ import { PersonnelCostCalculationService } from './personnel-cost-calculation.se providers: [CalculationService, PersonnelCostCalculationService], exports: [CalculationService], }) -export class UtilCalculationModule {} +export class FeatureCalculationModule {} diff --git a/service/data-hub/data-connector/src/modules/feat-calculation/infection-types.ts b/service/data-hub/data-connector/src/modules/feat-calculation/infection-types.ts index 1648f6a..653f830 100644 --- a/service/data-hub/data-connector/src/modules/feat-calculation/infection-types.ts +++ b/service/data-hub/data-connector/src/modules/feat-calculation/infection-types.ts @@ -19,4 +19,21 @@ export const INFECTION_TYPES = { Norovirus: '2.B', Sonstiges: '2.A', VRE: '2.A', + + // TODO: Veränderte Infektionstypen ergänzen + // Diphtherie: '', + // Harnwegsinfektion: '', + // 'Hepatitis A,E': '', + // 'Hepatitis B,C,D': '', + // Influenza: '', + // 'Krätze 3.1': '', + // Laus: '', + // MRGN: '', + // 'MRGN (MNR)': '', + // 'MRSA (MNR)': '', + // 'Röteln (Rubeolae)': '', + // Scharlach: '', + // 'TBC Lunge': '', + // 'TBC Organ': '', + // 'VRE (MNR)': '', }; diff --git a/service/data-hub/data-connector/src/modules/feat-etl/feat-etl.module.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-etl.module.ts new file mode 100644 index 0000000..883b4ff --- /dev/null +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-etl.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { FeatureImporterModule } from './feat-importers/feature-importer.module'; + +@Module({ + imports: [FeatureImporterModule], + exports: [FeatureImporterModule], +}) +export class FeatureEtlModule {} diff --git a/service/data-hub/data-connector/src/modules/feat-extractors/data-access-extractors.module.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/data-access-extractors.module.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-extractors/data-access-extractors.module.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/data-access-extractors.module.ts diff --git a/service/data-hub/data-connector/src/modules/feat-extractors/dispo-live-extractors.service.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/dispo-live-extractors.service.ts similarity index 98% rename from service/data-hub/data-connector/src/modules/feat-extractors/dispo-live-extractors.service.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/dispo-live-extractors.service.ts index 56531a1..250953c 100644 --- a/service/data-hub/data-connector/src/modules/feat-extractors/dispo-live-extractors.service.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/dispo-live-extractors.service.ts @@ -320,7 +320,11 @@ export class DispoLiveExtractorsService { surname, persId, importPersId, - qualifikation, + qualifikation: + !!qualifikation && typeof qualifikation === 'object' + ? //@ts-expect-error ts is wrong here + qualifikation?._id + : qualifikation ?? undefined, }; }), ), diff --git a/service/data-hub/data-connector/src/modules/feat-extractors/dyflexis-extractors.service.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/dyflexis-extractors.service.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-extractors/dyflexis-extractors.service.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/dyflexis-extractors.service.ts diff --git a/service/data-hub/data-connector/src/modules/feat-extractors/extractors.service.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/extractors.service.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-extractors/extractors.service.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/extractors.service.ts diff --git a/service/data-hub/data-connector/src/modules/feat-extractors/source.interface.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/source.interface.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-extractors/source.interface.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/source.interface.ts diff --git a/service/data-hub/data-connector/src/modules/feat-extractors/source.repository.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/source.repository.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-extractors/source.repository.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-extractors/source.repository.ts diff --git a/service/data-hub/data-connector/src/modules/feat-importers/events/event-types.enum.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/event-types.enum.ts similarity index 80% rename from service/data-hub/data-connector/src/modules/feat-importers/events/event-types.enum.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/event-types.enum.ts index 3c9c3e8..c324b2a 100644 --- a/service/data-hub/data-connector/src/modules/feat-importers/events/event-types.enum.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/event-types.enum.ts @@ -1,4 +1,4 @@ -export enum EventTypes { +export enum ETLEventTypes { ExtractionDone = 'etl.extraction', TransformationDone = 'etl.transformation', LoadingDone = 'etl.loading', diff --git a/service/data-hub/data-connector/src/modules/feat-importers/events/extraction-done.event.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/extraction-done.event.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-importers/events/extraction-done.event.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/extraction-done.event.ts diff --git a/service/data-hub/data-connector/src/modules/feat-importers/events/loading-done.event.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/loading-done.event.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-importers/events/loading-done.event.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/loading-done.event.ts diff --git a/service/data-hub/data-connector/src/modules/feat-importers/events/transformation-done.event.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/transformation-done.event.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-importers/events/transformation-done.event.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-importers/events/transformation-done.event.ts diff --git a/service/data-hub/data-connector/src/modules/feat-importers/feature-importer.module.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/feature-importer.module.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-importers/feature-importer.module.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-importers/feature-importer.module.ts diff --git a/service/data-hub/data-connector/src/modules/feat-importers/importer.service.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/importer.service.ts similarity index 95% rename from service/data-hub/data-connector/src/modules/feat-importers/importer.service.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-importers/importer.service.ts index 5fa56ac..c01b7e7 100644 --- a/service/data-hub/data-connector/src/modules/feat-importers/importer.service.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-importers/importer.service.ts @@ -7,7 +7,7 @@ import { Observable, combineLatest, from, map, switchMap, tap } from 'rxjs'; import { ExtractorsService } from '../feat-extractors/extractors.service'; import { LoadersService } from '../feat-loaders/loaders.service'; import { TransformersService } from '../feat-transformers/transformers.service'; -import { EventTypes } from './events/event-types.enum'; +import { ETLEventTypes } from './events/event-types.enum'; import { ExtractionDoneEvent } from './events/extraction-done.event'; import { LoadingDoneEvent } from './events/loading-done.event'; import { TransformationDoneEvent } from './events/transformation-done.event'; @@ -62,7 +62,7 @@ export class ImporterService { ]).pipe( tap(() => this.eventEmitter.emit( - EventTypes.ExtractionDone, + ETLEventTypes.ExtractionDone, new ExtractionDoneEvent(jobId), ), ), @@ -84,7 +84,7 @@ export class ImporterService { ]).pipe( tap(() => this.eventEmitter.emit( - EventTypes.TransformationDone, + ETLEventTypes.TransformationDone, new TransformationDoneEvent(jobId), ), ), @@ -104,7 +104,7 @@ export class ImporterService { switchMap(() => this.loadersService.loadWorkTimes()), tap(() => this.eventEmitter.emit( - EventTypes.LoadingDone, + ETLEventTypes.LoadingDone, new LoadingDoneEvent(jobId), ), ), diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/data-access-loaders.module.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/data-access-loaders.module.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/data-access-loaders.module.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/data-access-loaders.module.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/final.repository.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/final.repository.ts similarity index 92% rename from service/data-hub/data-connector/src/modules/feat-loaders/final.repository.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/final.repository.ts index 956a233..8a57e18 100644 --- a/service/data-hub/data-connector/src/modules/feat-loaders/final.repository.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/final.repository.ts @@ -12,6 +12,7 @@ import { import { Interval } from 'date-fns'; import { PrismaService } from 'src/core/database/prisma.service'; +import { chunk } from 'src/utils'; @Injectable() export class FinalRepository { @@ -50,16 +51,21 @@ export class FinalRepository { createTours( tourCreateInput: Prisma.TourCreateInput[], - ): Promise { - return this.prisma.tour.createMany({ - data: tourCreateInput, - skipDuplicates: true, - }); + ): Promise { + const chunks = chunk(tourCreateInput, 25000); + return Promise.all( + chunks.map((chunk) => + this.prisma.tour.createMany({ + data: chunk, + skipDuplicates: true, + }), + ), + ); } async updateTours( tourCreateInput: Prisma.TourCreateInput[], - ): Promise { + ): Promise { await this.prisma.tour.deleteMany({ where: { operationId: { @@ -70,17 +76,22 @@ export class FinalRepository { return this.createTours(tourCreateInput); } - deleteTours(operationIds: string[]): Promise { - return this.prisma.tour.updateMany({ - where: { - operationId: { - in: operationIds, - }, - }, - data: { - deletedAt: new Date(), - }, - }); + deleteTours(operationIds: string[]): Promise { + const chunks = chunk(operationIds, 20000); + return Promise.all( + chunks.map((chunk) => + this.prisma.tour.updateMany({ + where: { + operationId: { + in: chunk, + }, + }, + data: { + deletedAt: new Date(), + }, + }), + ), + ); } findPatients(): Promise { diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders.service.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders.service.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders.service.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders.service.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/attendance-registration.loader.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/attendance-registration.loader.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/attendance-registration.loader.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/attendance-registration.loader.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/employee-dispolive.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/employee-dispolive.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/employee-dispolive.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/employee-dispolive.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/employee-dyflexis.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/employee-dyflexis.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/employee-dyflexis.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/employee-dyflexis.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/patient.loader.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/patient.loader.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/patient.loader.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/patient.loader.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/planned-time.loader.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/planned-time.loader.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/planned-time.loader.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/planned-time.loader.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/tour.loader.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/tour.loader.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/tour.loader.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/tour.loader.ts diff --git a/service/data-hub/data-connector/src/modules/feat-loaders/loaders/work-time.loader.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/work-time.loader.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-loaders/loaders/work-time.loader.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-loaders/loaders/work-time.loader.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/adapter/tours-calculation.adapter.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/adapter/tours-calculation.adapter.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/adapter/tours-calculation.adapter.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/adapter/tours-calculation.adapter.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/data-access-transformers.module.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/data-access-transformers.module.ts similarity index 76% rename from service/data-hub/data-connector/src/modules/feat-transformers/data-access-transformers.module.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/data-access-transformers.module.ts index 38f1691..ee5e5ee 100644 --- a/service/data-hub/data-connector/src/modules/feat-transformers/data-access-transformers.module.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/data-access-transformers.module.ts @@ -1,13 +1,13 @@ import { Module } from '@nestjs/common'; import { PrismaModule } from 'src/core/database/prisma.module'; -import { UtilCalculationModule } from '../feat-calculation/util-calculation.module'; +import { FeatureCalculationModule } from 'src/modules/feat-calculation/feat-calculation.module'; import { DataAccessExtractorsModule } from '../feat-extractors/data-access-extractors.module'; import { ToursCalculationAdapater } from './adapter/tours-calculation.adapter'; import { StagingRepository } from './staging.repository'; import { TransformersService } from './transformers.service'; @Module({ - imports: [PrismaModule, DataAccessExtractorsModule, UtilCalculationModule], + imports: [PrismaModule, DataAccessExtractorsModule, FeatureCalculationModule], providers: [TransformersService, StagingRepository, ToursCalculationAdapater], exports: [TransformersService], }) diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/staging.interface.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/staging.interface.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/staging.interface.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/staging.interface.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/staging.repository.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/staging.repository.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/staging.repository.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/staging.repository.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformer.functions.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformer.functions.ts similarity index 96% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformer.functions.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformer.functions.ts index cc89a55..be75069 100644 --- a/service/data-hub/data-connector/src/modules/feat-transformers/transformer.functions.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformer.functions.ts @@ -2,7 +2,7 @@ import { parse, parseISO } from 'date-fns'; import { fromZonedTime } from 'date-fns-tz'; export const parseDateTime = (format: string) => (value: string) => - parse(value, format, new Date()); + !!value ? parse(value, format, new Date()) : value; export const parseIsoDateTime = (value: string) => parseISO(value); export const parseZonedDateTime = diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers.service.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers.service.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers.service.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers.service.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/attendance-registration.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/attendance-registration.transformer.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/attendance-registration.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/attendance-registration.transformer.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/employee-dispolive.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/employee-dispolive.transformer.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/employee-dispolive.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/employee-dispolive.transformer.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/employee-dyflexis.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/employee-dyflexis.transformer.ts similarity index 100% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/employee-dyflexis.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/employee-dyflexis.transformer.ts diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/patient.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/patient.transformer.ts similarity index 86% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/patient.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/patient.transformer.ts index 1393261..6e7b885 100644 --- a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/patient.transformer.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/patient.transformer.ts @@ -1,4 +1,5 @@ import { PatientSource } from '@prisma/client'; +import { fakerDE as faker } from '@faker-js/faker'; import { isValidDate } from 'src/utils'; import { PatientPreStaging } from '../staging.interface'; import { @@ -11,6 +12,9 @@ export class PatientTransformer { public static transform(patientSource: PatientSource): PatientPreStaging { const patientParsed = { ...patientSource, + surname: faker.person.lastName(), + name: faker.person.firstName(), + street: faker.location.streetAddress(), ...transformProperties(parseDateTime('yyyy-MM-dd'))( pick(['birthday'])(patientSource), ), diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/planned-time.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/planned-time.transformer.ts similarity index 90% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/planned-time.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/planned-time.transformer.ts index d73c1a7..e8c3300 100644 --- a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/planned-time.transformer.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/planned-time.transformer.ts @@ -1,4 +1,5 @@ import { PlannedTimeSource } from '@prisma/client'; +import { fakerDE as faker } from '@faker-js/faker'; import { PlannedTimePreStaging } from '../staging.interface'; import { parseBoolean, @@ -14,6 +15,8 @@ export class PlannedTimeTransformer { ): PlannedTimePreStaging { const plannedTimeParsed = { ...plannedTimeSource, + firstname: faker.person.firstName(), + surname: faker.person.lastName(), ...transformProperties(parseInteger)( pick(['id', 'user_id'])(plannedTimeSource), ), diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/tour.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/tour.transformer.ts similarity index 86% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/tour.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/tour.transformer.ts index 318fc50..2b2e879 100644 --- a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/tour.transformer.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/tour.transformer.ts @@ -1,4 +1,5 @@ import { TourSource } from '@prisma/client'; +import { fakerDE as faker } from '@faker-js/faker'; import { TourPreStaging } from '../staging.interface'; import { parseBoolean, @@ -15,6 +16,16 @@ export class TourTransformer { public static transform(tourSource: TourSource): TourPreStaging { const tourParsed = { ...tourSource, + operationId: faker.number.int({ min: 10000, max: 19999 }), + driverName: faker.person.fullName(), + codriverName: faker.person.fullName(), + patientName: faker.person.firstName(), + patientSurname: faker.person.lastName(), + patientStreet: faker.location.streetAddress(), + startInstitution: faker.company.name(), + startStreet: faker.location.streetAddress(), + targetInstitution: faker.company.name(), + targetStreet: faker.location.streetAddress(), ...transformProperties(parseInteger)(pick(['check'])(tourSource)), ...transformProperties(parseFloat)( pick(['besetztKm', 'gesamtPreis'])(tourSource), diff --git a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/work-time.transformer.ts b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/work-time.transformer.ts similarity index 88% rename from service/data-hub/data-connector/src/modules/feat-transformers/transformers/work-time.transformer.ts rename to service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/work-time.transformer.ts index 7e3d554..89806a3 100644 --- a/service/data-hub/data-connector/src/modules/feat-transformers/transformers/work-time.transformer.ts +++ b/service/data-hub/data-connector/src/modules/feat-etl/feat-transformers/transformers/work-time.transformer.ts @@ -1,4 +1,5 @@ import { WorkTimeSource } from '@prisma/client'; +import { fakerDE as faker } from '@faker-js/faker'; import { WorkTimePreStaging } from '../staging.interface'; import { parseCETDateTime, @@ -11,6 +12,8 @@ export class WorkTimeTransformer { public static transform(workTimeSource: WorkTimeSource): WorkTimePreStaging { const workTimeParsed = { ...workTimeSource, + firstname: faker.person.firstName(), + surname: faker.person.lastName(), ...transformProperties(parseInteger)( pick(['id', 'user_id'])(workTimeSource), ), diff --git a/service/data-hub/data-connector/src/modules/feat-tickets/ticket-lifecycle.service.ts b/service/data-hub/data-connector/src/modules/feat-tickets/ticket-lifecycle.service.ts index 03f1ade..b192513 100644 --- a/service/data-hub/data-connector/src/modules/feat-tickets/ticket-lifecycle.service.ts +++ b/service/data-hub/data-connector/src/modules/feat-tickets/ticket-lifecycle.service.ts @@ -22,22 +22,27 @@ export class TicketLifecycleService { constructor(private readonly repository: TicketRepository) {} async createTickets(interval: Interval): Promise { - const tours = await this.repository.findTours(interval); + try { + const tours = await this.repository.findTours(interval); - for (const tour of tours) { - if (tour.ticketId) { - continue; - } - const ticketId = await this.repository.searchTicketIDForTour(tour); - if (ticketId) { - await this.repository.addToursToTicket(ticketId, [tour]); - } else { - await this.repository.createTicket([tour]); + for (const tour of tours) { + if (tour.ticketId) { + continue; + } + + const ticketId = await this.repository.searchTicketIDForTour(tour); + if (ticketId) { + await this.repository.addToursToTicket(ticketId, [tour]); + } else { + await this.repository.createTicket([tour]); + } } + + await this.validateCreatedTickets(); + // await this.archiveClosedTickets(); + } catch (error) { + this.logger.error(error); } - - await this.validateCreatedTickets(); - // await this.archiveClosedTickets(); } async validateDocumentInfoUpdate( @@ -162,11 +167,11 @@ export class TicketLifecycleService { if (!patientId && ticket.tours.at(0)?.type === 'Spontan') { patientResult = validatePatientDataOnTours(ticket.tours); } else { - try { - const patient = await this.repository.findPatientById(patientId); - patientResult = validatePatient(patient); - } catch { + const patient = await this.repository.findPatientById(patientId); + if (!patient) { patientResult = validatePatientDataOnTours(ticket.tours); + } else { + patientResult = validatePatient(patient); } } const tourResults = ticket.tours.map((tour) => validateTour(tour)); diff --git a/service/data-hub/data-connector/src/modules/feat-tickets/ticket.repository.ts b/service/data-hub/data-connector/src/modules/feat-tickets/ticket.repository.ts index 61f5d0e..3d8b414 100644 --- a/service/data-hub/data-connector/src/modules/feat-tickets/ticket.repository.ts +++ b/service/data-hub/data-connector/src/modules/feat-tickets/ticket.repository.ts @@ -30,35 +30,40 @@ export class TicketRepository { ) {} async createTicket(tours: Tour[]) { - const ticket = await this.db - .insertInto('Ticket') - .values({ - currentState: TicketValidationState.CREATED, - }) - .returningAll() - .executeTakeFirst(); + this.logger.log(`Creating Ticket for ${tours.length} tours`); + try { + const ticket = await this.db + .insertInto('Ticket') + .values({ + currentState: TicketValidationState.CREATED, + }) + .returningAll() + .executeTakeFirst(); - await this.db - .insertInto('TicketStateHistory') - .values({ - state: TicketValidationState.CREATED, - ticketId: ticket.id, - }) - .executeTakeFirst(); + await this.db + .insertInto('TicketStateHistory') + .values({ + state: TicketValidationState.CREATED, + ticketId: ticket.id, + }) + .executeTakeFirst(); - await this.db - .updateTable('Tour') - .set({ - ticketId: ticket.id, - }) - .where( - 'id', - 'in', - tours.map(({ id }) => id), - ) - .execute(); + await this.db + .updateTable('Tour') + .set({ + ticketId: ticket.id, + }) + .where( + 'id', + 'in', + tours.map(({ id }) => id), + ) + .execute(); - return ticket; + return ticket; + } catch (error) { + throw error; + } } async addToursToTicket(ticketId: string, tours: Tour[]) { diff --git a/service/data-hub/data-connector/src/utils/group-by.spec.ts b/service/data-hub/data-connector/src/utils/group-by.spec.ts new file mode 100644 index 0000000..ea3b5cb --- /dev/null +++ b/service/data-hub/data-connector/src/utils/group-by.spec.ts @@ -0,0 +1,14 @@ +import { groupBy } from './group-by'; + +describe('groupBy', () => { + it('should group a list by keys', () => { + const given = ['one', 'two', 'three']; + + const when = groupBy(given, (item) => item.length); + + expect(when).toEqual({ + 3: ['one', 'two'], + 5: ['three'], + }); + }); +}); diff --git a/service/data-hub/data-connector/src/utils/group-by.ts b/service/data-hub/data-connector/src/utils/group-by.ts new file mode 100644 index 0000000..5b8fd52 --- /dev/null +++ b/service/data-hub/data-connector/src/utils/group-by.ts @@ -0,0 +1,12 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function groupBy( + list: T[], + getKey: (item: T) => K +) { + return list.reduce((previous, current) => { + const group = getKey(current); + if (!previous[group]) previous[group] = []; + previous[group].push(current); + return previous; + }, {} as Record); +} diff --git a/service/data-hub/data-connector/src/utils/helper.spec.ts b/service/data-hub/data-connector/src/utils/helper.spec.ts new file mode 100644 index 0000000..faa9992 --- /dev/null +++ b/service/data-hub/data-connector/src/utils/helper.spec.ts @@ -0,0 +1,63 @@ +import { addDays, Interval, set } from 'date-fns'; +import { fromZonedTime } from 'date-fns-tz'; +import { generateIntervalForTimeRange } from './helper'; + +describe('avicennaApiUtilHelper', () => { + describe('generateIntervalForTimeRange', () => { + it('should throw an error when the timeRange format is invalid', () => { + expect(() => generateIntervalForTimeRange('42:00;06:09')).toThrow(); + }); + it('should generate an interval for the specified time range on the current day', () => { + const result: Interval = { + start: fromZonedTime( + set(new Date(), { + hours: 12, + minutes: 0, + seconds: 0, + milliseconds: 0, + }), + 'Europe/Berlin', + ), + end: fromZonedTime( + set(new Date(), { + hours: 13, + minutes: 0, + seconds: 0, + milliseconds: 0, + }), + 'Europe/Berlin', + ), + }; + + expect( + generateIntervalForTimeRange('12:00-13:00', 'Europe/Berlin'), + ).toStrictEqual(result); + }); + it('should generate an interval which spans over two days, if the second time is lower than the first', () => { + const result: Interval = { + start: fromZonedTime( + set(new Date(), { + hours: 12, + minutes: 0, + seconds: 0, + milliseconds: 0, + }), + 'Europe/Berlin', + ), + end: fromZonedTime( + set(addDays(new Date(), 1), { + hours: 1, + minutes: 0, + seconds: 0, + milliseconds: 0, + }), + 'Europe/Berlin', + ), + }; + + expect( + generateIntervalForTimeRange('12:00-01:00', 'Europe/Berlin'), + ).toStrictEqual(result); + }); + }); +}); diff --git a/service/data-hub/data-connector/src/utils/index.ts b/service/data-hub/data-connector/src/utils/index.ts index 2c3535b..a9fe443 100644 --- a/service/data-hub/data-connector/src/utils/index.ts +++ b/service/data-hub/data-connector/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './chunk'; +export * from './group-by'; export * from './helper'; export * from './is-valid-date'; export * from './range'; diff --git a/service/data-hub/workflows/avic-etl.yml b/service/data-hub/workflows/avic-etl.yml new file mode 100644 index 0000000..5819676 --- /dev/null +++ b/service/data-hub/workflows/avic-etl.yml @@ -0,0 +1,63 @@ +id: avic-etl +namespace: dev +description: Run Avicenna ETL + +labels: + env: dev + project: avicenna + +triggers: + - id: schedule + type: io.kestra.plugin.core.trigger.Schedule + cron: "*/5 * * * *" + +tasks: + - id: getPartitions + type: io.kestra.plugin.scripts.python.Script + taskRunner: + type: io.kestra.plugin.scripts.runner.docker.Docker + containerImage: ghcr.io/kestra-io/pydata:latest + beforeCommands: + - pip install -U arrow + script: | + from kestra import Kestra + import arrow + partitions = [] + + now = arrow.utcnow() #.shift(months=-1) + partitions.append("from={}&to={}".format(now.span('month')[0].format('YYYY-MM-DD'), now.shift(days=14).format('YYYY-MM-DD'))) + lastMonth = now.shift(months=-1) + partitions.append("from={}&to={}".format(lastMonth.span('month')[0].format('YYYY-MM-DD'), lastMonth.span('month')[-1].format('YYYY-MM-DD'))) + monthBeforeLast = lastMonth.shift(months=-1) + partitions.append("from={}&to={}".format(monthBeforeLast.span('month')[0].format('YYYY-MM-DD'), monthBeforeLast.span('month')[-1].format('YYYY-MM-DD'))) + monthBeforeLast = monthBeforeLast.shift(months=-1) + partitions.append("from={}&to={}".format(monthBeforeLast.span('month')[0].format('YYYY-MM-DD'), monthBeforeLast.span('month')[-1].format('YYYY-MM-DD'))) + Kestra.outputs({'partitions': partitions}) + - id: loadThreeMonths + type: io.kestra.plugin.core.flow.EachSequential + value: '{{ outputs.getPartitions.vars.partitions }}' + tasks: + - id: "request" + type: "io.kestra.plugin.core.http.Request" + uri: "http://avicenna_data:3000/etl?{{ taskrun.value }}" + headers: + user-agent: "kestra-io" + options: + readTimeout: "PT180S" + method: "GET" + # - id: checkForAnomalies + # type: "io.kestra.plugin.core.http.Request" + # uri: "http://avicenna_avicenna-data:3000/anomalies?from=2024-07-01&to=2024-07-31" + # headers: + # user-agent: "kestra-io" + # options: + # readTimeout: "PT90S" + # method: "GET" + # - id: updateTickets + # type: "io.kestra.plugin.core.http.Request" + # uri: "http://avicenna_avicenna-data:3000/tickets?from=2024-07-01&to=2024-07-31" + # headers: + # user-agent: "kestra-io" + # options: + # readTimeout: "PT90S" + # method: "GET" diff --git a/service/data-hub/workflows/avic-tickets.yml b/service/data-hub/workflows/avic-tickets.yml new file mode 100644 index 0000000..c2b4e4b --- /dev/null +++ b/service/data-hub/workflows/avic-tickets.yml @@ -0,0 +1,55 @@ +id: avic-tickets +namespace: dev +description: Run Avicenna Ticket Creation + +labels: + env: dev + project: avicenna + +triggers: + - id: schedule + type: io.kestra.plugin.core.trigger.Schedule + cron: "*/5 * * * *" + +tasks: + - id: getPartitions + type: io.kestra.plugin.scripts.python.Script + taskRunner: + type: io.kestra.plugin.scripts.runner.docker.Docker + containerImage: ghcr.io/kestra-io/pydata:latest + beforeCommands: + - pip install -U arrow + script: | + from kestra import Kestra + import arrow + partitions = [] + + now = arrow.utcnow() #.shift(months=-1) + partitions.append("from={}&to={}".format(now.span('month')[0].format('YYYY-MM-DD'), now.format('YYYY-MM-DD'))) + lastMonth = now.shift(months=-1) + partitions.append("from={}&to={}".format(lastMonth.span('month')[0].format('YYYY-MM-DD'), lastMonth.span('month')[-1].format('YYYY-MM-DD'))) + monthBeforeLast = lastMonth.shift(months=-1) + partitions.append("from={}&to={}".format(monthBeforeLast.span('month')[0].format('YYYY-MM-DD'), monthBeforeLast.span('month')[-1].format('YYYY-MM-DD'))) + monthBeforeLast = monthBeforeLast.shift(months=-1) + partitions.append("from={}&to={}".format(monthBeforeLast.span('month')[0].format('YYYY-MM-DD'), monthBeforeLast.span('month')[-1].format('YYYY-MM-DD'))) + Kestra.outputs({'partitions': partitions}) + - id: loadThreeMonths + type: io.kestra.plugin.core.flow.EachSequential + value: '{{ outputs.getPartitions.vars.partitions }}' + tasks: + - id: updateTickets + type: "io.kestra.plugin.core.http.Request" + uri: "http://avicenna_data:3000/tickets?{{taskrun.value}}" + headers: + user-agent: "kestra-io" + options: + readTimeout: "PT90S" + method: "GET" + - id: checkForAnomalies + type: "io.kestra.plugin.core.http.Request" + uri: "http://avicenna_data:3000/anomalies?{{ taskrun.value }}" + headers: + user-agent: "kestra-io" + options: + readTimeout: "PT90S" + method: "GET"