From 6bfc3e7cee1965907ae9127460f85ea2d6cc9697 Mon Sep 17 00:00:00 2001 From: Niv Greenstein Date: Mon, 30 Sep 2024 19:09:12 +0300 Subject: [PATCH] Revert "0.1.0" --- .dockerignore | 1 - .github/workflows/pull_request.yaml | 196 +- config/custom-environment-variables.json | 43 +- config/default.json | 53 +- config/test.json | 54 +- dataSource.ts | 13 + devScripts/controlElasticsearchData.json | 325 +- devScripts/geotextElasticsearchData.json | 146 +- devScripts/importDataToElastic.ts | 77 +- devScripts/importDataToS3.ts | 39 - devScripts/importToPostgres.ts | 47 + devScripts/index.ts | 9 +- devScripts/latLonConvertions.json | 24 - helm/templates/configmap.yaml | 14 - helm/templates/deployment.yaml | 10 - helm/values.yaml | 14 - openapi3.yaml | 1172 ++-- package-lock.json | 5639 ++++------------- package.json | 17 +- src/app.ts | 8 +- src/common/constants.ts | 15 +- src/common/elastic/index.ts | 2 +- src/common/elastic/utils.ts | 27 +- src/common/errors.ts | 24 - src/common/interfaces.ts | 75 +- .../middlewares/feedbackApi.middleware.ts | 59 - src/common/middlewares/utils.ts | 5 - src/common/postgresql/index.ts | 68 + src/common/postgresql/promiseTimeout.ts | 14 + src/common/projections.ts | 1 - src/common/redis/index.ts | 50 - src/common/redis/interfaces.ts | 8 - src/common/s3/index.ts | 48 - src/common/s3/s3Repository.ts | 69 - src/common/utils.ts | 284 +- src/containerConfig.ts | 218 +- src/control/constants.ts | 22 - src/control/item/DAL/queries.ts | 59 - src/control/item/models/item.ts | 11 - src/control/item/models/itemManager.ts | 28 - src/control/route/DAL/queries.ts | 78 - src/control/route/models/route.ts | 11 - src/control/route/models/routeManager.ts | 36 - src/control/tile/DAL/queries.ts | 113 - src/control/tile/DAL/tileRepository.ts | 49 - src/control/tile/models/tile.ts | 11 - src/control/tile/models/tileManager.ts | 55 - src/control/utils.ts | 103 - .../DAL/geotextSearchRepository.ts} | 10 +- .../DAL/queries.ts | 77 +- .../controllers/geotextSearchController.ts} | 28 +- src/geotextSearch/interfaces.ts | 66 + .../models/elasticsearchHits.ts | 0 .../models/geotextSearchManager.ts} | 29 +- src/{location => geotextSearch}/parsing.ts | 23 +- .../routes/geotextSearchRouter.ts} | 5 +- src/geotextSearch/utils.ts | 139 + src/index.ts | 4 +- src/{control => }/item/DAL/itemRepository.ts | 13 +- src/item/DAL/queries.ts | 66 + .../item/controllers/itemController.ts | 30 +- src/item/models/item.ts | 3 + src/item/models/itemManager.ts | 34 + src/{control => }/item/routes/itemRouter.ts | 0 src/latLon/DAL/latLon.ts | 32 + src/latLon/DAL/latLonDAL.ts | 81 +- src/latLon/DAL/latLonRepository.ts | 24 + src/latLon/controllers/latLonController.ts | 91 +- src/latLon/models/latLon.ts | 1 + src/latLon/models/latLonManager.ts | 143 +- src/latLon/routes/latLonRouter.ts | 5 +- src/latLon/utlis/index.ts | 100 + src/location/interfaces.ts | 28 - src/location/utils.ts | 107 - src/mgrs/controllers/mgrsController.ts | 48 - src/mgrs/models/mgrsManager.ts | 54 - src/mgrs/routers/mgrsRouter.ts | 16 - src/route/DAL/queries.ts | 79 + .../route/DAL/routeRepository.ts | 18 +- .../route/controllers/routeController.ts | 28 +- src/route/models/route.ts | 3 + src/route/models/routeManager.ts | 44 + src/{control => }/route/routes/routeRouter.ts | 0 src/serverBuilder.ts | 44 +- src/tile/DAL/queries.ts | 54 + src/tile/DAL/tileRepository.ts | 39 + .../tile/controllers/tileController.ts | 27 +- src/tile/models/tile.ts | 3 + src/tile/models/tileManager.ts | 43 + src/{control => }/tile/routes/tileRouter.ts | 0 .../configurations/integration/jest.config.js | 2 - tests/configurations/unit/jest.config.js | 1 - tests/integration/control/item/item.spec.ts | 448 -- tests/integration/control/item/mockObjects.ts | 99 - .../integration/control/route/mockObjects.ts | 75 - tests/integration/control/route/route.spec.ts | 565 -- tests/integration/control/tile/mockObjects.ts | 85 - tests/integration/control/tile/tile.spec.ts | 580 -- tests/integration/control/utils.ts | 50 - tests/integration/docs/docs.spec.ts | 5 - .../integration/geotextSearch/geotext.spec.ts | 172 + .../geotextSearch/helpers/requestSender.ts | 25 + .../geotextSearch/possibleObjects.ts | 167 + tests/integration/globalSetup.ts | 39 - tests/integration/globalTeardown.ts | 84 - .../item/helpers/requestSender.ts | 6 +- tests/integration/item/item.spec.ts | 67 + .../latLon/helpers/requestSender.ts | 45 +- tests/integration/latLon/latLon.spec.ts | 274 +- .../location/helpers/requestSender.ts | 36 - tests/integration/location/location.spec.ts | 582 -- tests/integration/location/mockObjects.ts | 403 -- tests/integration/location/utils.ts | 46 - .../integration/mgrs/helpers/requestSender.ts | 16 - tests/integration/mgrs/mgrs.spec.ts | 112 - .../route/helpers/requestSender.ts | 6 +- tests/integration/route/route.spec.ts | 68 + .../tile/helpers/requestSender.ts | 6 +- tests/integration/tile/tile.spec.ts | 124 + tests/unit/common/objects.ts | 271 + tests/unit/common/utils.spec.ts | 74 + 121 files changed, 4258 insertions(+), 11358 deletions(-) create mode 100644 dataSource.ts delete mode 100644 devScripts/importDataToS3.ts create mode 100644 devScripts/importToPostgres.ts delete mode 100644 devScripts/latLonConvertions.json delete mode 100644 src/common/middlewares/feedbackApi.middleware.ts delete mode 100644 src/common/middlewares/utils.ts create mode 100644 src/common/postgresql/index.ts create mode 100644 src/common/postgresql/promiseTimeout.ts delete mode 100644 src/common/redis/index.ts delete mode 100644 src/common/redis/interfaces.ts delete mode 100644 src/common/s3/index.ts delete mode 100644 src/common/s3/s3Repository.ts delete mode 100644 src/control/constants.ts delete mode 100644 src/control/item/DAL/queries.ts delete mode 100644 src/control/item/models/item.ts delete mode 100644 src/control/item/models/itemManager.ts delete mode 100644 src/control/route/DAL/queries.ts delete mode 100644 src/control/route/models/route.ts delete mode 100644 src/control/route/models/routeManager.ts delete mode 100644 src/control/tile/DAL/queries.ts delete mode 100644 src/control/tile/DAL/tileRepository.ts delete mode 100644 src/control/tile/models/tile.ts delete mode 100644 src/control/tile/models/tileManager.ts delete mode 100644 src/control/utils.ts rename src/{location/DAL/locationRepository.ts => geotextSearch/DAL/geotextSearchRepository.ts} (89%) rename src/{location => geotextSearch}/DAL/queries.ts (66%) rename src/{location/controllers/locationController.ts => geotextSearch/controllers/geotextSearchController.ts} (57%) create mode 100644 src/geotextSearch/interfaces.ts rename src/{location => geotextSearch}/models/elasticsearchHits.ts (100%) rename src/{location/models/locationManager.ts => geotextSearch/models/geotextSearchManager.ts} (74%) rename src/{location => geotextSearch}/parsing.ts (55%) rename src/{location/routes/locationRouter.ts => geotextSearch/routes/geotextSearchRouter.ts} (72%) create mode 100644 src/geotextSearch/utils.ts rename src/{control => }/item/DAL/itemRepository.ts (78%) create mode 100644 src/item/DAL/queries.ts rename src/{control => }/item/controllers/itemController.ts (67%) create mode 100644 src/item/models/item.ts create mode 100644 src/item/models/itemManager.ts rename src/{control => }/item/routes/itemRouter.ts (100%) create mode 100644 src/latLon/DAL/latLon.ts create mode 100644 src/latLon/DAL/latLonRepository.ts create mode 100644 src/latLon/utlis/index.ts delete mode 100644 src/location/interfaces.ts delete mode 100644 src/location/utils.ts delete mode 100644 src/mgrs/controllers/mgrsController.ts delete mode 100644 src/mgrs/models/mgrsManager.ts delete mode 100644 src/mgrs/routers/mgrsRouter.ts create mode 100644 src/route/DAL/queries.ts rename src/{control => }/route/DAL/routeRepository.ts (71%) rename src/{control => }/route/controllers/routeController.ts (67%) create mode 100644 src/route/models/route.ts create mode 100644 src/route/models/routeManager.ts rename src/{control => }/route/routes/routeRouter.ts (100%) create mode 100644 src/tile/DAL/queries.ts create mode 100644 src/tile/DAL/tileRepository.ts rename src/{control => }/tile/controllers/tileController.ts (67%) create mode 100644 src/tile/models/tile.ts create mode 100644 src/tile/models/tileManager.ts rename src/{control => }/tile/routes/tileRouter.ts (100%) delete mode 100644 tests/integration/control/item/item.spec.ts delete mode 100644 tests/integration/control/item/mockObjects.ts delete mode 100644 tests/integration/control/route/mockObjects.ts delete mode 100644 tests/integration/control/route/route.spec.ts delete mode 100644 tests/integration/control/tile/mockObjects.ts delete mode 100644 tests/integration/control/tile/tile.spec.ts delete mode 100644 tests/integration/control/utils.ts create mode 100644 tests/integration/geotextSearch/geotext.spec.ts create mode 100644 tests/integration/geotextSearch/helpers/requestSender.ts create mode 100644 tests/integration/geotextSearch/possibleObjects.ts delete mode 100644 tests/integration/globalSetup.ts delete mode 100644 tests/integration/globalTeardown.ts rename tests/integration/{control => }/item/helpers/requestSender.ts (70%) create mode 100644 tests/integration/item/item.spec.ts delete mode 100644 tests/integration/location/helpers/requestSender.ts delete mode 100644 tests/integration/location/location.spec.ts delete mode 100644 tests/integration/location/mockObjects.ts delete mode 100644 tests/integration/location/utils.ts delete mode 100644 tests/integration/mgrs/helpers/requestSender.ts delete mode 100644 tests/integration/mgrs/mgrs.spec.ts rename tests/integration/{control => }/route/helpers/requestSender.ts (69%) create mode 100644 tests/integration/route/route.spec.ts rename tests/integration/{control => }/tile/helpers/requestSender.ts (70%) create mode 100644 tests/integration/tile/tile.spec.ts create mode 100644 tests/unit/common/objects.ts create mode 100644 tests/unit/common/utils.spec.ts diff --git a/.dockerignore b/.dockerignore index 4cb8c68a..bfbdee64 100644 --- a/.dockerignore +++ b/.dockerignore @@ -16,4 +16,3 @@ commitlint.config.json dist build reports -devScripts \ No newline at end of file diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index e248d2d5..af576d04 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -1,196 +1,8 @@ name: pull_request -on: [pull_request, workflow_dispatch] +on: [pull_request] jobs: - eslint: - name: Run eslint - runs-on: ubuntu-latest - - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v1 - with: - node-version: 12 - - - name: Install dependencies - run: npm ci - - - name: Run linters - uses: wearerequired/lint-action@v1 - with: - github_token: ${{ secrets.github_token }} - # Enable linters - eslint: true - prettier: true - eslint_extensions: ts - - - name: OpenAPI Lint Checks - uses: nwestfall/openapi-action@v1.0.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - file: ./openapi3.yaml - - integration-test: - name: Run Integration Tests - runs-on: ubuntu-latest - container: node:16 - - services: - # Label used to access the service container - elasticsearch: - # Docker Hub image - image: elasticsearch:8.13.0 - env: - discovery.type: single-node - xpack.security.enabled: false - xpack.security.enrollment.enabled: false - ES_JAVA_OPTS: "-Xms512m -Xmx512m" - ports: - - 9200:9200 - # Set health checks to wait until elastic has started - options: >- - --health-cmd "curl -f http://localhost:9200/_cluster/health || exit 1" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - minio: - # Docker Hub image - image: minio/minio:edge-cicd - env: - MINIO_ROOT_USER: minio - MINIO_ROOT_PASSWORD: minio123 - ports: - - 9000:9000 - # Set health checks to wait until elastic has started - options: >- - --health-cmd "curl -f http://localhost:9000/minio/health/live || exit 1" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - strategy: - matrix: - node: [18.x, 20.x] - - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node }} - - - name: Install Node.js dependencies - run: npm ci - - - name: Wait for Elasticsearch - run: | - until curl -s http://elasticsearch:9200/_cluster/health | grep '"status":"green"'; do - echo "Waiting for Elasticsearch..."; - sleep 5; - done - - - name: Run integration tests - run: npm run test:integration - - - uses: actions/upload-artifact@v4 - with: - name: Test Reporters - path: reports/** - - unit-test: - name: Run Unit Tests - runs-on: ubuntu-latest - container: node:16 - - services: - # Label used to access the service container - elasticsearch: - # Docker Hub image - image: elasticsearch:8.13.0 - env: - discovery.type: single-node - xpack.security.enabled: false - xpack.security.enrollment.enabled: false - ES_JAVA_OPTS: "-Xms512m -Xmx512m" - ports: - - 9200:9200 - # Set health checks to wait until elastic has started - options: >- - --health-cmd "curl -f http://localhost:9200/_cluster/health || exit 1" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - minio: - # Docker Hub image - image: minio/minio:edge-cicd - env: - MINIO_ROOT_USER: minio - MINIO_ROOT_PASSWORD: minio123 - ports: - - 9000:9000 - # Set health checks to wait until elastic has started - options: >- - --health-cmd "curl -f http://localhost:9000/minio/health/live || exit 1" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - strategy: - matrix: - node: [18.x, 20.x] - - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node }} - - - name: Install Node.js dependencies - run: npm ci - - - name: Wait for Elasticsearch - run: | - until curl -s http://elasticsearch:9200/_cluster/health | grep '"status":"green"'; do - echo "Waiting for Elasticsearch..."; - sleep 5; - done - - - name: Run unit tests - run: npm run test:unit - - - uses: actions/upload-artifact@v4 - with: - name: Test Reporters - path: reports/** - - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/node@master - continue-on-error: true - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - - build_image: - name: Build Image - runs-on: ubuntu-latest - - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - - name: build Docker image - run: docker build -t test-build:latest . + pull_request: + uses: MapColonies/shared-workflows/.github/workflows/pull_request.yaml@master + secrets: inherit diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 4a8fdc81..ed72d6e5 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -67,39 +67,25 @@ } } }, - "s3": { - "endpoint": "S3_ENDPOINT", - "credentials": { - "accessKeyId": "S3_ACCESS_KEY", - "secretAccessKey": "S3_SECRET_KEY" - }, - "region": "S3_REGION", - "files": { - "__name": "S3_FILES_DATA", - "__format": "json" - } - }, - "redis": { - "host": "REDIS_HOST", + "postgresql": { + "host": "POSTGRES_URL", "port": { - "__name": "REDIS_PORT", + "__name": "POSTGRES_PORT", "__format": "number" }, - "username": "REDIS_USERNAME", - "password": "REDIS_PASSWORD", + "username": "POSTGRES_USERNAME", + "password": "POSTGRES_PASSWORD", "enableSslAuth": { - "__name": "REDIS_ENABLE_SSL_AUTH", + "__name": "POSTGRES_ENABLE_SLL_AUTH", "__format": "boolean" }, "sslPaths": { - "ca": "REDIS_CA_PATH", - "key": "REDIS_KEY_PATH", - "cert": "REDIS_CERT_PATH" + "ca": "POSTGRES_CA_PATH", + "key": "POSTGRES_KEY_PATH", + "cert": "POSTGRES_CERT_PATH" }, - "database": { - "__name": "REDIS_DATABASE", - "__format": "number" - } + "database": "POSTGRES_DB_NAME", + "schema": "POSTGRES_DB_SCHEMA" } }, "application": { @@ -118,11 +104,6 @@ "__name": "NAME_TRANSLATION_KEYS", "__format": "json" }, - "controlObjectDisplayNamePrefixes": { - "__name": "CONTROL_DISPLAY_KEY_TRANSLATIONS", - "__format": "json" - }, - "mainLanguageRegex": "MAIN_LANGUAGE_REGEX", - "site": "SITE" + "mainLanguageRegex": "MAIN_LANGUAGE_REGEX" } } diff --git a/config/default.json b/config/default.json index 9c165b10..cf9186b0 100644 --- a/config/default.json +++ b/config/default.json @@ -28,21 +28,22 @@ "db": { "elastic": { "control": { - "node": "http://control_elastic:9200", + "node": "http://localhost:9200", "auth": { - "username": "elastic", - "password": "changeme" + "username": "control", + "password": "password" }, "requestTimeout": 60000, "properties": { - "index": "control_index" + "index": "control_index", + "defaultResponseLimit": 3 } }, "geotext": { - "node": "http://geotext_elastic:9200", + "node": "http://localhost:9200", "auth": { - "username": "elastic", - "password": "changeme" + "username": "geotext", + "password": "password" }, "requestTimeout": 60000, "properties": { @@ -51,37 +52,25 @@ "placetypes": "placetypes_index", "hierarchies": "hierarchies_index" }, + "defaultResponseLimit": 3, "textTermLanguage": "en" } } }, - "s3": { - "endpoint": "http://s3:9000", - "credentials": { - "accessKeyId": "accessKeyId", - "secretAccessKey": "secretAccessKey" - }, - "forcePathStyle": true, - "region": "local", - "files": { - "latLonConvertionTable": { - "bucket": "geocoding", - "fileName": "table.json" - } - } - }, - "redis": { - "host": "REDIS_HOST", - "port": 6379, - "username": "", - "password": "", + "postgresql": { + "type": "postgres", + "host": "localhost", + "port": 5432, + "username": "postgres", + "password": "postgres", "enableSslAuth": false, "sslPaths": { "ca": "", "key": "", "cert": "" }, - "database": 0 + "database": "postgres", + "schema": "geocoder" } }, "application": { @@ -96,10 +85,12 @@ "hierarchy": 1.1, "viewbox": 1.1 }, - "sources": {}, + "sources": { + "SOURCE_A": "a", + "SOURCE_B": "b" + }, "regions": {}, - "controlObjectDisplayNamePrefixes": {}, - "nameTranslationsKeys": [], + "nameTranslationsKeys": ["en", "fr"], "mainLanguageRegex": "[a-zA-Z]" } } diff --git a/config/test.json b/config/test.json index 8305cd32..6c4f3c2f 100644 --- a/config/test.json +++ b/config/test.json @@ -2,18 +2,19 @@ "db": { "elastic": { "control": { - "node": "http://elasticsearch:9200", + "node": "http://localhost:9200", "auth": { "username": "elastic", "password": "changeme" }, "requestTimeout": 60000, "properties": { - "index": "control_gil_v5_test" + "index": "control_index", + "defaultResponseLimit": 3 } }, "geotext": { - "node": "http://elasticsearch:9200", + "node": "http://localhost:9200", "auth": { "username": "elastic", "password": "changeme" @@ -21,41 +22,29 @@ "requestTimeout": 60000, "properties": { "index": { - "geotext": "geotext_index_test", - "placetypes": "placetypes_index_test", - "hierarchies": "hierarchies_index_test" + "geotext": "geotext_index", + "placetypes": "placetypes_index", + "hierarchies": "hierarchies_index" }, + "defaultResponseLimit": 3, "textTermLanguage": "en" } } }, - "s3": { - "endpoint": "http://minio:9000", - "credentials": { - "accessKeyId": "minio", - "secretAccessKey": "minio123" - }, - "region": "local", - "forcePathStyle": true, - "files": { - "latLonConvertionTable": { - "bucket": "geocoding-test", - "fileName": "table.json" - } - } - }, - "redis": { + "postgresql": { + "type": "postgres", "host": "localhost", - "port": 6379, - "username": "", - "password": "", + "port": 5432, + "username": "postgres", + "password": "postgres", "enableSslAuth": false, "sslPaths": { "ca": "", "key": "", "cert": "" }, - "database": 1 + "database": "postgres", + "schema": "geocoder" } }, "application": { @@ -71,19 +60,10 @@ "viewbox": 1.1 }, "sources": { - "OSM": "OSM", - "GOOGLE": "GOOGLE" + "OSM": "OSM" }, "regions": { - "USA": ["New York", "Los Angeles"], - "FRANCE": ["Paris"] - }, - "controlObjectDisplayNamePrefixes": { - "TILE": "Tile", - "SUB_TILE": "Sub Tile", - "ROUTE": "Route", - "ITEM": "Item", - "CONTROL_POINT": "Control Point" + "USA": ["New York", "Los Angeles"] }, "nameTranslationsKeys": ["en", "fr"], "mainLanguageRegex": "[a-zA-Z]" diff --git a/dataSource.ts b/dataSource.ts new file mode 100644 index 00000000..ad31f0f6 --- /dev/null +++ b/dataSource.ts @@ -0,0 +1,13 @@ +import config from 'config'; +import { DataSource } from 'typeorm'; +import { createConnectionOptions } from './src/common/postgresql'; +import { PostgresDbConfig } from './src/common/interfaces'; + +const connectionOptions = config.get('db.postgresql'); + +export const appDataSource = new DataSource({ + ...createConnectionOptions(connectionOptions), + entities: ['src/**/DAL/*.ts'], + migrationsTableName: 'migrations_table', + migrations: ['db/migrations/*.ts'], +}); diff --git a/devScripts/controlElasticsearchData.json b/devScripts/controlElasticsearchData.json index 73da7efd..856ca077 100644 --- a/devScripts/controlElasticsearchData.json +++ b/devScripts/controlElasticsearchData.json @@ -1,6 +1,7 @@ [ { "_id": "CONTROL.ITEMS", + "_score": 1, "_source": { "type": "Feature", "id": 27, @@ -52,322 +53,70 @@ } }, { - "_id": "CONTROL.ITEMS1", - "_source": { - "type": "Feature", - "id": 211, - "geometry": { - "coordinates": [ - [ - [12.432792582620323, 41.9327692706986], - [12.432648028923637, 41.93209008126263], - [12.43295235249525, 41.93189198298137], - [12.435105441764364, 41.93235609798671], - [12.435516278586334, 41.93274663116725], - [12.43637599267521, 41.93308056343986], - [12.436026020567567, 41.93386161853829], - [12.432792582620323, 41.9327692706986] - ] - ], - "type": "Polygon" - }, - "properties": { - "OBJECTID": 211, - "F_CODE": 23000, - "F_ATT": 6020, - "VIEW_SCALE_50K_CONTROL": 6040, - "NAME": null, - "GFID": "{656eb8b8-1056-4eb5-955e-b670996cbb78}", - "OBJECT_COMMAND_NAME": "1234", - "SUB_TILE_NAME": null, - "TILE_NAME": "RIT", - "TILE_ID": "36, 7300, 3560", - "SUB_TILE_ID": "37", - "SHAPE.AREA": 2.65, - "SHAPE.LEN": 0.0044192097656775, - "LAYER_NAME": "CONTROL.ITEMS", - "ENTITY_HEB": "hotel", - "TYPE": "ITEM" - } - } - }, - { - "_id": "CONTROL.ITEMS2", - "_source": { - "type": "Feature", - "id": 212, - "geometry": { - "coordinates": [ - [ - [12.454224879037952, 41.93533128513971], - [12.4536294370983, 41.93493944442386], - [12.453354617740985, 41.934479454425286], - [12.453308814515196, 41.93386612926582], - [12.453789748389482, 41.933065390320394], - [12.454660009686364, 41.93255427509931], - [12.455667680662373, 41.93265649847106], - [12.456217319376208, 41.933355020461335], - [12.456217319376208, 41.934309086916585], - [12.45569058227531, 41.935092773686364], - [12.454957730656645, 41.93534832163783], - [12.454224879037952, 41.93533128513971] - ] - ], - "type": "Polygon" - }, - "properties": { - "OBJECTID": 212, - "F_CODE": 23001, - "F_ATT": 6021, - "VIEW_SCALE_50K_CONTROL": 6041, - "NAME": null, - "GFID": "{69a2805c-3977-4123-b97d-c149287af9d4}", - "OBJECT_COMMAND_NAME": "1235", - "SUB_TILE_NAME": null, - "TILE_NAME": "RIT", - "TILE_ID": "36, 7310, 3561", - "SUB_TILE_ID": "37", - "SHAPE.AREA": 2.65, - "SHAPE.LEN": 0.0044192097656775, - "LAYER_NAME": "CONTROL.ITEMS", - "ENTITY_HEB": "oplympic stadium", - "TYPE": "ITEM" - } - } - }, - { - "_id": "CONTROL.ITEMS3", + "_id": "CONTROL.SUB_TILES", + "_score": 1, "_source": { "type": "Feature", - "id": 213, + "id": 13668, "geometry": { "coordinates": [ [ - [12.508591887289015, 41.908719199148294], - [12.5109416268715, 41.90414674778023], - [12.515619548794433, 41.905558627360875], - [12.514541686600694, 41.906697734719614], - [12.514929038978266, 41.9065233923005], - [12.515546733877699, 41.90711121610428], - [12.51436197480865, 41.90815873484965], - [12.513612640013008, 41.90876915151631], - [12.512974692823434, 41.90872393566693], - [12.512863305218872, 41.90807583830912], - [12.513561316395936, 41.90754911551221], - [12.513962675013943, 41.907221129768516], - [12.51419876831929, 41.906986853205694], - [12.513954805237745, 41.906893142339726], - [12.512516460769376, 41.90804005137895], - [12.511855399516293, 41.90857302052004], - [12.51176096219396, 41.908660872149284], - [12.508762577222939, 41.908924426310676], - [12.508591887289015, 41.908719199148294] + [27.149158174343427, 35.63159611670335], + [27.149274355343437, 35.64061707270338], + [27.138786228343463, 35.640716597703374], + [27.13867103934342, 35.631695606703374], + [27.149158174343427, 35.63159611670335] ] ], "type": "Polygon" }, - "properties": { - "OBJECTID": 213, - "F_CODE": 23002, - "F_ATT": 6022, - "VIEW_SCALE_50K_CONTROL": 6042, - "NAME": null, - "GFID": "{935265e6-402f-4824-a193-e8725f73212e}", - "OBJECT_COMMAND_NAME": "1236", - "SUB_TILE_NAME": null, - "TILE_NAME": "RIT", - "TILE_ID": "36, 7310, 3581", - "SUB_TILE_ID": "38", - "SHAPE.AREA": 2.65, - "SHAPE.LEN": 0.0044192097656775, - "LAYER_NAME": "CONTROL.ITEMS", - "ENTITY_HEB": "hospital", - "TYPE": "ITEM" - } - } - }, - { - "_id": "CONTROL.SUB_TILES", - "_source": { - "type": "Feature", "properties": { "OBJECTID": 13668, "SUB_TILE_ID": "65", - "TILE_NAME": "RIT", + "TILE_NAME": "GRC", "NAME": "somePlace", - "ZON": 37, + "ZON": 35, "LAYER_NAME": "CONTROL.SUB_TILES", "TYPE": "SUB_TILE" - }, - "geometry": { - "coordinates": [ - [ - [12.439530324602458, 41.93031190061167], - [12.439646505602468, 41.9393328566117], - [12.429158378602494, 41.939432381611695], - [12.429043189602453, 41.930411390611695], - [12.439530324602458, 41.93031190061167] - ] - ], - "type": "Polygon" - } - } - }, - { - "_id": "CONTROL.SUB_TILES1", - "_source": { - "type": "Feature", - "properties": { - "OBJECTID": 13669, - "SUB_TILE_ID": "66", - "TILE_NAME": "RIT", - "NAME": "somePlace", - "ZON": 37, - "LAYER_NAME": "CONTROL.SUB_TILES", - "TYPE": "SUB_TILE" - }, - "geometry": { - "coordinates": [ - [ - [12.44999804325252, 41.930226156898485], - [12.45011422425253, 41.939247112898514], - [12.439626097252557, 41.93934663789851], - [12.439510908252515, 41.93032564689851], - [12.44999804325252, 41.930226156898485] - ] - ], - "type": "Polygon" } } }, { "_id": "CONTROL.ROUTES", + "_score": 1, "_source": { "type": "Feature", "id": 2, "geometry": { "coordinates": [ - [12.443243654365062, 41.93890891937724], - [12.442636325462843, 41.93804302794496], - [12.442828646282095, 41.93725242115204], - [12.443405608739027, 41.93556576056804], - [12.44388471483822, 41.93370245333628], - [12.445132826188399, 41.93084467089824], - [12.445812354584433, 41.93031849813403], - [12.445819951137906, 41.930296776658196] + [13.448493352142947, 52.31016611400918], + [13.447219581381603, 52.313370282889224], + [13.448088381125075, 52.31631514453963], + [13.450458681234068, 52.31867376333767], + [13.451112278530388, 52.32227665244022], + [13.449728938644029, 52.32463678850752], + [13.445021899434977, 52.32863442881066], + [13.444723882330948, 52.340023400115086], + [13.446229682887974, 52.34532799609971] ], "type": "LineString" }, "properties": { "OBJECTID": 2, - "OBJECT_COMMAND_NAME": "via camillucciaA", + "OBJECT_COMMAND_NAME": "route96", "FIRST_GFID": null, "SHAPE_Length": 4.1, "F_CODE": 8754, "F_ATT": 951, "LAYER_NAME": "CONTROL.ROUTES", - "ENTITY_HEB": "route", - "TYPE": "ROUTE" - } - } - }, - { - "_id": "CONTROL.ROUTES2", - "_source": { - "type": "Feature", - "id": 3, - "geometry": { - "coordinates": [ - [12.445818466287847, 41.93029376141277], - [12.446047161911423, 41.930040913942264], - [12.446171038707206, 41.9297762500957], - [12.446167862379838, 41.92945014491775], - [12.446075748864388, 41.9286136066203] - ], - "type": "LineString" - }, - "properties": { - "OBJECTID": 3, - "OBJECT_COMMAND_NAME": "via camillucciaB", - "FIRST_GFID": null, - "SHAPE_Length": 4.1, - "F_CODE": 8754, - "F_ATT": 951, - "LAYER_NAME": "CONTROL.ROUTES", - "ENTITY_HEB": "route", - "TYPE": "ROUTE" - } - } - }, - { - "_id": "CONTROL.ROUTES1", - "_source": { - "type": "Feature", - "id": 3, - "geometry": { - "coordinates": [ - [12.472759211857749, 41.932073169074016], - [12.475880836863752, 41.93239148035457] - ], - "type": "LineString" - }, - "properties": { - "OBJECTID": 3, - "OBJECT_COMMAND_NAME": "olimpiade", - "FIRST_GFID": null, - "SHAPE_Length": 4.1, - "F_CODE": 87541, - "F_ATT": 9511, - "LAYER_NAME": "CONTROL.ROUTES", - "ENTITY_HEB": "route", + "ENTITY_HEB": "route96", "TYPE": "ROUTE" } } }, - { - "_id": "CONTROL.ROUTES2", - "_source": { - "type": "Feature", - "id": 4, - "geometry": { - "coordinates": [12.475638293442415, 41.932360642739155], - "type": "Point" - }, - "properties": { - "OBJECTID": 4, - "OBJECT_COMMAND_NAME": "111", - "TIED_TO": "olimpiade", - "F_CODE": 8754111, - "F_ATT": 951111, - "ENTITY_HEB": "control point", - "LAYER_NAME": "CONTROL_GIL_GDB.CTR_CONTROL_POINT_CROSS_N", - "TYPE": "ITEM" - } - } - }, - { - "_id": "CONTROL.ROUTES3", - "_source": { - "type": "Feature", - "id": 5, - "geometry": { - "coordinates": [12.474175672012962, 41.932217551210556], - "type": "Point" - }, - "properties": { - "OBJECTID": 4, - "OBJECT_COMMAND_NAME": "112", - "TIED_TO": "olimpiade", - "F_CODE": 875411, - "F_ATT": 95111, - "ENTITY_HEB": "control point", - "LAYER_NAME": "CONTROL_GIL_GDB.CTR_CONTROL_POINT_CROSS_N", - "TYPE": "ITEM" - } - } - }, { "_id": "CONTROL.TILES", + "_score": 1, "_source": { "type": "Feature", "id": 52, @@ -392,31 +141,5 @@ "TYPE": "TILE" } } - }, - { - "_id": "CONTROL.TILES1", - "_source": { - "type": "Feature", - "properties": { - "OBJECTID": 53, - "ZONE": "37", - "TILE_NAME": "RIC", - "TILE_ID": null, - "LAYER_NAME": "CONTROL.TILES", - "TYPE": "TILE" - }, - "geometry": { - "coordinates": [ - [ - [12.64750356570994, 41.854073129598774], - [12.64478277570987, 41.94417235759876], - [12.536787035709892, 41.941850298598766], - [12.539620755710018, 41.85175890959876], - [12.64750356570994, 41.854073129598774] - ] - ], - "type": "Polygon" - } - } } ] diff --git a/devScripts/geotextElasticsearchData.json b/devScripts/geotextElasticsearchData.json index 0581ea2c..3d0bddda 100644 --- a/devScripts/geotextElasticsearchData.json +++ b/devScripts/geotextElasticsearchData.json @@ -1,7 +1,7 @@ [ { "_id": "12cd98ee-82eb-415e-a1d8-e1667a6f2e7f", - "_index": "geotext", + "_index": "geotext_index", "_source": { "source": "OSM", "layer_name": "osm_airports", @@ -66,7 +66,7 @@ }, { "_id": "8b5009b4-b643-4856-905d-ac95a2040aca", - "_index": "geotext", + "_index": "geotext_index", "_source": { "source": "OSM", "layer_name": "osm_airports", @@ -117,7 +117,7 @@ }, { "_id": "2bd0d4bb-af93-4211-9499-174c56feddb5", - "_index": "geotext", + "_index": "geotext_index", "_source": { "source": "OSM", "layer_name": "osm_airports", @@ -170,7 +170,7 @@ }, { "_id": "ba951f2f-08c2-446e-845d-e2b0547d5d8c", - "_index": "geotext", + "_index": "geotext_index", "_source": { "source": "OSM", "layer_name": "osm_ports", @@ -222,63 +222,9 @@ "text_language": "en" } }, - { - "_id": "1bb11f54-939e-457b-bf68-a3920ccf629c", - "_index": "geotext", - "_source": { - "source": "GOOGLE", - "layer_name": "google_ports", - "ingestion_timestamp": "2024-07-17 00:00:00", - "timestamp": "11/12/2023 00:00:00", - "source_id": ["{1bb11f54-939e-457b-bf68-a3920ccf629c}"], - "sensitivity": "non-sensitive", - "placetype": "transportation", - "sub_placetype": "port", - "wkt": "POLYGON ((-118.2505781304088 33.7502674389752, -118.25604403409116 33.76075151051916, -118.27057180697577 33.748593059782564, -118.27503083555426 33.741097783576635, -118.2747911028351 33.734798055529765, -118.27215404292296 33.73136889520775, -118.26807858669537 33.720881194108856, -118.26424286318695 33.721997816398385, -118.26640045650717 33.72901625632974, -118.2431463824787 33.735794882347946, -118.24492040460113 33.739303607948656, -118.25072193640723 33.73794798097781, -118.25220827926702 33.74193505797223, -118.24937943317966 33.74508471776615, -118.24798898340768 33.74783559181691, -118.24909175391655 33.74803492708783, -118.25096166912684 33.74600168558719, -118.25326310323155 33.745363795966625, -118.25278363779321 33.74687877606813, -118.2505781304088 33.7502674389752))", - "geo_json": { - "type": "Polygon", - "coordinates": [ - [ - [-118.2505781304088, 33.7502674389752], - [-118.25604403409116, 33.76075151051916], - [-118.27057180697577, 33.748593059782564], - [-118.27503083555426, 33.741097783576635], - [-118.2747911028351, 33.734798055529765], - [-118.27215404292296, 33.73136889520775], - [-118.26807858669537, 33.720881194108856], - [-118.26424286318695, 33.721997816398385], - [-118.26640045650717, 33.72901625632974], - [-118.2431463824787, 33.735794882347946], - [-118.24492040460113, 33.739303607948656], - [-118.25072193640723, 33.73794798097781], - [-118.25220827926702, 33.74193505797223], - [-118.24937943317966, 33.74508471776615], - [-118.24798898340768, 33.74783559181691], - [-118.24909175391655, 33.74803492708783], - [-118.25096166912684, 33.74600168558719], - [-118.25326310323155, 33.745363795966625], - [-118.25278363779321, 33.74687877606813], - [-118.2505781304088, 33.7502674389752] - ] - ] - }, - "centroid": { - "type": "Point", - "coordinates": [-118.25908860901649, 33.740816352314006] - }, - "geometry_type": "Polygon", - "geometry_hash": "ffbc4f66c802719b5bcffaca238ee1d3", - "region": ["USA"], - "sub_region": ["Los Angeles"], - "name": "Port of Los Angeles", - "text": ["Port of Los Angeles"], - "translated_text": ["Puerto de Los Ángeles"], - "text_language": "en" - } - }, { "_id": "2b3dd420-d265-4665-94a6-3e13650b7a2d", - "_index": "geotext", + "_index": "geotext_index", "_source": { "source": "OSM", "layer_name": "osm_schools", @@ -287,7 +233,7 @@ "source_id": ["{1a5b981b-bb0e-44dd-b9e2-424b92f2de49}"], "sensitivity": "non-sensitive", "placetype": "education", - "sub_placetype": "school", + "sub_placetype": "elementary school", "wkt": "POLYGON ((-118.30812263653988 33.71684417247593, -118.30861990876181 33.71674433152869, -118.30879709771484 33.71635922964194, -118.30619642115158 33.71550819588987, -118.30586490633668 33.715921827872904, -118.30587062210924 33.716183318328746, -118.30812263653988 33.71684417247593))", "geo_json": { "coordinates": [ @@ -317,48 +263,9 @@ "text_language": "en" } }, - { - "_id": "dc02a3f9-156a-4f61-85bd-fd040cd322a3", - "_index": "geotext", - "_source": { - "source": "OSM", - "layer_name": "osm_schools", - "ingestion_timestamp": "2024-07-17 00:00:00", - "timestamp": "11/12/2023 00:00:00", - "source_id": ["{dc02a3f9-156a-4f61-85bd-fd040cd322a3}"], - "sensitivity": "non-sensitive", - "placetype": "education", - "sub_placetype": "school", - "wkt": "POLYGON ((2.346441270696971 48.88088750665477, 2.3462780852304945 48.88018258877358, 2.347503576087604 48.87999951892243, 2.347737155284733 48.88070864783427, 2.346441270696971 48.88088750665477))", - "geo_json": { - "coordinates": [ - [ - [2.346441270696971, 48.88088750665477], - [2.3462780852304945, 48.88018258877358], - [2.347503576087604, 48.87999951892243], - [2.347737155284733, 48.88070864783427], - [2.346441270696971, 48.88088750665477] - ] - ], - "type": "Polygon" - }, - "centroid": { - "type": "Point", - "coordinates": [2.346966023961727, 48.88044772323158] - }, - "geometry_type": "Polygon", - "geometry_hash": "791f6b24002bbc1d50cdea5015244da9 ", - "region": ["FRANCE"], - "sub_region": ["Paris"], - "name": "Wi School Paris 9", - "text": ["Wi School Paris 9"], - "translated_text": ["Ecole Wi Paris 9"], - "text_language": "en" - } - }, { "_id": "5a54ff54-53f6-4ad1-9d2a-cd20f333ee2b", - "_index": "hierarchies", + "_index": "hierarchies_index", "_source": { "geo_json": { "coordinates": [ @@ -384,7 +291,7 @@ }, { "_id": "a3f38afc-1ce6-443c-a302-a0103c2bcb7d", - "_index": "hierarchies", + "_index": "hierarchies_index", "_source": { "geo_json": { "coordinates": [ @@ -407,34 +314,9 @@ "text": "Los Angeles" } }, - { - "_id": "a3f38afc-1ce6-443c-a302-a0103c2bcb7d", - "_index": "hierarchies", - "_source": { - "geo_json": { - "coordinates": [ - [ - [2.226678539753607, 49.06838747927134], - [1.9344166918067742, 48.906487548202136], - [2.014124468519668, 48.56855190252173], - [2.6536844864307625, 48.53463335095324], - [2.902296837606201, 48.82159183126478], - [2.6460932696008683, 49.0124047223114], - [2.384196288972504, 49.05097737411208], - [2.226678539753607, 49.06838747927134] - ] - ], - "type": "Polygon" - }, - "hierarchy": "city", - "placetype": "city", - "region": "FRANCE", - "text": "Paris" - } - }, { "_id": "430ddc04-2370-415f-8761-b8c66a142f0a", - "_index": "placetypes", + "_index": "placetypes_index", "_source": { "placetype": "transportation", "sub_placetype": "airport", @@ -443,7 +325,7 @@ }, { "_id": "6941ab8e-7503-4f8a-94f2-4750b2698db9", - "_index": "placetypes", + "_index": "placetypes_index", "_source": { "placetype": "transportation", "sub_placetype": "port", @@ -452,11 +334,11 @@ }, { "_id": "6941ab8e-7503-4f8a-94f2-4750b2698db9", - "_index": "placetypes", + "_index": "placetypes_index", "_source": { - "placetype": "education", - "sub_placetype": "school", - "sub_placetype_keyword": "school" + "placetype": "transportation", + "sub_placetype": "education", + "sub_placetype_keyword": "elementary school" } } ] diff --git a/devScripts/importDataToElastic.ts b/devScripts/importDataToElastic.ts index 84a5d85b..f23aefb0 100644 --- a/devScripts/importDataToElastic.ts +++ b/devScripts/importDataToElastic.ts @@ -1,86 +1,27 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import crypto from 'node:crypto'; -import httpStatusCodes from 'http-status-codes'; -import { Client, estypes } from '@elastic/elasticsearch'; -import { ElasticDbClientsConfig } from '../src/common/elastic/interfaces'; -import { elasticConfigPath } from '../src/common/constants'; -import { IConfig } from '../src/common/interfaces'; +import { Client } from '@elastic/elasticsearch'; +import config from '../config/default.json'; import controlData from './controlElasticsearchData.json'; import geotextData from './geotextElasticsearchData.json'; -const main = async (config: IConfig): Promise => { - const elasticConfig = config.get(elasticConfigPath); - const controlClient = new Client({ ...elasticConfig.control }); - const geotextClient = new Client({ ...elasticConfig.geotext }); - - for (const { client, indices } of [ - { - client: controlClient, - indices: [elasticConfig.control.properties.index] as string[], - }, - { - client: geotextClient, - indices: Object.values(elasticConfig.geotext.properties.index as { [key: string]: string }), - }, - ]) { - for (const index of indices) { - try { - await client.indices.delete({ index }); - } catch (error) { - if ((error as estypes.ErrorCause).meta.statusCode !== httpStatusCodes.NOT_FOUND) { - throw error; - } - console.error(error); - } - } - } - - for (const { client, index, key } of [ - { - client: controlClient, - index: elasticConfig.control.properties.index as string, - key: 'geometry', - }, - { - client: geotextClient, - index: (elasticConfig.geotext.properties.index as { [key: string]: string }).geotext, - key: 'geo_json', - }, - { - client: geotextClient, - index: (elasticConfig.geotext.properties.index as { [key: string]: string }).hierarchies, - key: 'geo_json', - }, - ]) { - await client.indices.create({ - index: index, - body: { - mappings: { - properties: { - [key]: { - type: 'geo_shape', - }, - }, - }, - }, - }); - } +const main = async (): Promise => { + const controlClient = new Client({ ...config.db.elastic.control }); + const geotextClient = new Client({ ...config.db.elastic.geotext }); for (const item of controlData) { await controlClient.index({ - index: elasticConfig.control.properties.index as string, - id: crypto.randomUUID(), + index: config.db.elastic.control.properties.index, + id: item._id, body: item._source, }); } for (const item of geotextData) { await geotextClient.index({ - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - index: (elasticConfig.geotext.properties.index as { [key: string]: string })[item._index] as string, - id: crypto.randomUUID(), + index: item._index, + id: item._id, body: { ...item._source, }, diff --git a/devScripts/importDataToS3.ts b/devScripts/importDataToS3.ts deleted file mode 100644 index 74aacee0..00000000 --- a/devScripts/importDataToS3.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { CreateBucketCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; -import { S3Config, s3ConfigPath } from '../src/common/s3'; -import { IConfig } from '../src/common/interfaces'; -import mockDataJson from './latLonConvertions.json'; - -const main = async (config: IConfig): Promise => { - const s3Config = config.get(s3ConfigPath); - const s3Client = new S3Client({ ...s3Config }); - - if (!s3Config.files.latLonConvertionTable) { - throw new Error('No latLonConvertionTable file path provided'); - } - - const { bucket: Bucket, fileName: Key } = s3Config.files.latLonConvertionTable; - - try { - await s3Client.send(new CreateBucketCommand({ Bucket, ACL: 'public-read' })); - } catch (error) { - console.error(error); - } - - try { - await s3Client.send( - new PutObjectCommand({ - Bucket, - Key, - Body: Buffer.from(JSON.stringify(mockDataJson), 'utf-8'), - }) - ); - } catch (error) { - console.error(error); - throw error; - } -}; - -export default main; diff --git a/devScripts/importToPostgres.ts b/devScripts/importToPostgres.ts new file mode 100644 index 00000000..9e81b7a6 --- /dev/null +++ b/devScripts/importToPostgres.ts @@ -0,0 +1,47 @@ +import { DataSource } from 'typeorm'; +import { createConnectionOptions } from '../src/common/postgresql'; +import config from '../config/test.json'; +import { PostgresDbConfig } from '../src/common/interfaces'; + +export default async function createDatabaseClient(): Promise { + const connectionOptions = config.db.postgresql as PostgresDbConfig; + const connection = new DataSource({ + ...createConnectionOptions(connectionOptions), + }); + + await connection.initialize(); + + await connection.query(`CREATE SCHEMA IF NOT EXISTS ${config.db.postgresql.schema};`); + + await connection.query(` + CREATE TABLE IF NOT EXISTS ${config.db.postgresql.schema}.tile_lat_lon + ( + pk integer NOT NULL, + tile_name text COLLATE pg_catalog."default", + zone text COLLATE pg_catalog."default", + min_x text COLLATE pg_catalog."default", + min_y text COLLATE pg_catalog."default", + ext_min_x integer, + ext_min_y integer, + ext_max_x integer, + ext_max_y integer, + CONSTRAINT tile_lat_lon_pkey PRIMARY KEY (pk) + ) + `); + + await connection.query(` + INSERT INTO ${config.db.postgresql.schema}.tile_lat_lon( + pk, tile_name, zone, min_x, min_y, ext_min_x, ext_min_y, ext_max_x, ext_max_y) + VALUES (1, 'BRN', '33', '360000', '5820000', 360000, 5820000, 370000, 5830000); + `); + + await connection.query(` + INSERT INTO ${config.db.postgresql.schema}.tile_lat_lon( + pk, tile_name, zone, min_x, min_y, ext_min_x, ext_min_y, ext_max_x, ext_max_y) + VALUES (2, 'BMN', '32', '480000', '5880000', 480000, 5880000, 490000, 5890000); + `); + + console.log('PGSQL: Table created and data inserted'); + + await connection.destroy(); +} diff --git a/devScripts/index.ts b/devScripts/index.ts index 7a759df3..1ec5acdc 100644 --- a/devScripts/index.ts +++ b/devScripts/index.ts @@ -1,11 +1,10 @@ -import config from 'config'; import importDataToElastic from './importDataToElastic'; -import importDataToS3 from './importDataToS3'; +import importToPostgres from './importToPostgres'; -importDataToElastic(config) +importDataToElastic() .then(() => console.log('Success import data to elastic')) .catch(console.error); -importDataToS3(config) - .then(() => console.log('Success import data to s3')) +importToPostgres() + .then(() => console.log('Success import data to postgres')) .catch(console.error); diff --git a/devScripts/latLonConvertions.json b/devScripts/latLonConvertions.json deleted file mode 100644 index c406ea34..00000000 --- a/devScripts/latLonConvertions.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "items": [ - { - "tile_name": "BRN", - "zone": "33", - "min_x": "360000", - "min_y": "5820000", - "ext_min_x": 360000, - "ext_min_y": 5820000, - "ext_max_x": 370000, - "ext_max_y": 5830000 - }, - { - "tile_name": "BMN", - "zone": "32", - "min_x": "480000", - "min_y": "5880000", - "ext_min_x": 480000, - "ext_min_y": 5880000, - "ext_max_x": 490000, - "ext_max_y": 5890000 - } - ] -} diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml index a43002d5..69202f7f 100644 --- a/helm/templates/configmap.yaml +++ b/helm/templates/configmap.yaml @@ -10,7 +10,6 @@ data: RESPONSE_COMPRESSION_ENABLED: {{ .Values.env.responseCompressionEnabled | quote }} LOG_LEVEL: {{ .Values.env.logLevel | quote }} LOG_PRETTY_PRINT_ENABLED: {{ .Values.env.logPrettyPrintEnabled | quote }} - SITE: {{ .Values.env.site | quote }} {{ if .Values.env.tracing.enabled }} TELEMETRY_TRACING_ENABLED: 'true' TELEMETRY_TRACING_URL: {{ $tracingUrl }} @@ -19,18 +18,5 @@ data: TELEMETRY_METRICS_ENABLED: 'true' TELEMETRY_METRICS_URL: {{ $metricsUrl }} {{ end }} - {{- with .Values.redisConfig }} - REDIS_HOST: {{ .host }} - REDIS_DATABASE: {{ .database | quote}} - REDIS_PORT: {{ .port | quote }} - {{- if .sslAuth.enabled }} - REDIS_ENABLE_SSL_AUTH: "true" - REDIS_CERT_PATH: /tmp/certs/{{ .sslAuth.certFileName }} - REDIS_KEY_PATH: /tmp/certs/{{ .sslAuth.keyFileName }} - REDIS_CA_PATH: /tmp/certs/{{ .sslAuth.caFileName }} - {{- else }} - REDIS_ENABLE_SSL_AUTH: "false" - {{- end }} - {{- end }} npm_config_cache: /tmp/ {{- end }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 5484cfd7..007efa2a 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -53,11 +53,6 @@ spec: name: root-ca subPath: {{ quote .Values.caKey }} {{- end }} - {{- if .Values.redisConfig.sslAuth.enabled }} - - name: cert-conf - mountPath: /tmp/certs - readOnly: true - {{- end }} {{- if .Values.extraVolumeMounts -}} {{ toYaml .Values.extraVolumeMounts | nindent 12 }} {{- end }} @@ -114,11 +109,6 @@ spec: secret: secretName: {{ .Values.caSecretName }} {{- end }} - {{- if .Values.redisConfig.sslAuth.enabled }} - - name: cert-conf - secret: - secretName: {{ .Values.redisConfig.sslAuth.secretName }} - {{- end }} {{- if .Values.extraVolumes -}} {{ tpl (toYaml .Values.extraVolumes) . | nindent 8 }} {{- end }} diff --git a/helm/values.yaml b/helm/values.yaml index 5fd8cc0e..25fd7688 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -62,7 +62,6 @@ env: protocol: TCP logLevel: info logPrettyPrintEnabled: false - site: main responseCompressionEnabled: true requestPayloadLimit: 1mb tracing: @@ -72,19 +71,6 @@ env: enabled: false url: http://localhost:55681/v1/metrics -redisConfig: - host: localhost - username: "" - password: "" - database: 0 - port: 6379 - sslAuth: - enabled: false - secretName: secret-name - certFileName: postgresql.crt - keyFileName: postgresql.key - caFileName: root.crt - resources: enabled: true value: diff --git a/openapi3.yaml b/openapi3.yaml index 1d92fd88..8a19c7db 100644 --- a/openapi3.yaml +++ b/openapi3.yaml @@ -1,23 +1,22 @@ -openapi: '3.0.1' +openapi: "3.0.1" info: - version: '0.1.0' - title: 'Geocoding' - description: |- - MapColonies Vector Geocoding api provides custom geodata search engine uniting multiple sources of data, query tiles, routes, items; Convertion functions - tile to/from WGS84 lat lng, WGS84 to/from US Army MGRS, WGS84 to/from UTM. + version: "1.0.0" + title: "New Geocoding" + description: "Geocoding api" license: name: MIT url: https://opensource.org/licenses/MIT tags: - - name: Location Name Based Search - - name: Control - - name: Convertions + - name: Query Based Search + - name: Exact Searches paths: - /search/query: + /v1/query: get: - operationId: GetSmartQuery - summary: 'Search anything' - description: |- - This is for general queries. the services will make a sophisticated guess. + operationId: searchByQuery + tags: + - Query Based Search + summary: "Search anything by query" + description: "This is for general queries. If known regex is found, the server will return results as if you searched for exact search. Else, the server will search using geotext engine to find a match." parameters: - name: query in: query @@ -25,890 +24,619 @@ paths: schema: type: string minLength: 3 - maxLength: 100 - title: 'Query' + maxLength: 1000 + description: Text to search + title: Query description: Text to search allowReserved: true - - $ref: '#/components/parameters/geo_context' - - $ref: '#/components/parameters/geo_context_mode' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/disable_fuzziness' - responses: - 200: - description: 'OK
Will return valid GeoJSON FeatureCollection' - headers: - x-request-id: - schema: - type: string - minLength: 1 - maxLength: 36 - content: - application/json: - schema: - $ref: '#/components/schemas/genericGeocodingResponse' - 400: - '$ref': '#/components/responses/BadRequest' - 401: - '$ref': '#/components/responses/Unauthorized' - 403: - '$ref': '#/components/responses/Forbidden' - 500: - '$ref': '#/components/responses/InternalError' - security: - - x-api-key: [] - x-user-id: [] - - /search/location/query: - get: - operationId: locationGetQuery - summary: 'Search engine' - description: 'Search for geo-data. It serves data from multiple diverse mapcolonies vector sources and partners.' - tags: - - Location Name Based Search - parameters: - - name: query + - name: limit in: query - required: true + required: false schema: - type: string - minLength: 3 - maxLength: 100 - title: 'Query' - description: Text to search - allowReserved: true + type: integer + maximum: 1000 + minimum: 0 + description: Maximum results the service should return + default: 10 + title: Limit + description: Maximum results the service should return - name: source in: query required: false schema: type: array items: - $ref: '#/components/schemas/Source' + $ref: "#/components/schemas/Source" + description: Sources to include (if not specified, all sources will be queried) title: Source - description: | - Sources to include (if not specified, all sources will be queried) + description: Sources to include (if not specified, all sources will be queried) + - name: viewbox + in: query + required: false + allowReserved: true + schema: + type: string + description: |- + User's viewbox, used for biasing the results, or preferring results closer to the user + + Results close to this geometry will be preferred + + When providing a non-zero-area entity (e.g. polygon or bbox), results inside the entity will be most preferred + + Possible formats include a valid geoJSON, a valid WKT, an "x,y" point, or a "xmin,ymin,xmax,ymax" bounding box + title: Viewbox + description: |- + User's viewbox, used for biasing the results, or preferring results closer to the user + + Results close to this geometry will be preferred + + When providing a non-zero-area entity (e.g. polygon or bbox), results inside the entity will be most preferred + + Possible formats include a valid geoJSON, a valid WKT, an "x,y" point, or a "xmin,ymin,xmax,ymax" bounding box + - name: boundary + in: query + required: false + allowReserved: true + schema: + type: string + description: |- + A filter on the results. + + Results not touching this geometry will not be returned + + Possible formats include a valid geoJSON, a valid WKT, an "x,y" point, or a "xmin,ymin,xmax,ymax" bounding box + title: Boundary + description: |- + A filter on the results. + + Results not touching this geometry will not be returned + + Possible formats include a valid geoJSON, a valid WKT, an "x,y" point, or a "xmin,ymin,xmax,ymax" bounding box - name: region in: query required: false schema: type: array items: - $ref: '#/components/schemas/Region' + $ref: "#/components/schemas/Region" description: Regions to include (if not specified, all Regions will be queried) title: Region description: Regions to include (if not specified, all Regions will be queried) - - $ref: '#/components/parameters/geo_context' - - $ref: '#/components/parameters/geo_context_mode' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/disable_fuzziness' responses: 200: - description: 'OK' - x-request-id: - schema: - type: string - minLength: 1 - maxLength: 36 + description: "OK (can be one of the following: tilesSchema, routesSchema, itemsSchema, subTileSchema)

WIP: Find out returned schema from geotext" content: application/json: schema: - allOf: - - "$ref": "#/components/schemas/genericGeocodingResponse" - - type: "object" - required: - - "type" - - "features" - properties: - type: - type: "string" - enum: - - "FeatureCollection" - features: - type: "array" - items: - type: "object" - properties: - geometry: - oneOf: - - $ref: "#/components/schemas/Point" - - $ref: "#/components/schemas/Polygon" - properties: - type: "object" - properties: - placetype: - type: "string" - sub_placetype: - type: "string" + anyOf: + - $ref: "#/components/schemas/tilesSchema" + - $ref: "#/components/schemas/routesSchema" + - $ref: "#/components/schemas/itemsSchema" + - $ref: "#/components/schemas/subTileSchema" 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - - /search/location/regions: + - X-API-Key: [] + X-User-ID: [] + /v1/query/regions: get: - operationId: locationGetRegions + operationId: getRegions tags: - - Location Name Based Search - summary: 'Get all available regions to filter on using location query' + - Query Based Search + summary: "Get regions" responses: 200: - description: 'All regions' + description: "All regions" content: application/json: schema: type: array items: - type: string # return geojson with name property + type: string 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - - /search/location/sources: + - X-API-Key: [] + X-User-ID: [] + /v1/search/tiles: get: - operationId: locationGetSources + operationId: getTilesByQueryParams tags: - - Location Name Based Search - summary: 'Get all available sources to filter on using location query' + - Exact Searches + summary: "Search tiles and sub tiles" + description: "Tiles are consist of 3 characters.

You can query and get results from 2 characters.

If you define a tile (full name of it) you can query the sub tile associated with it." + parameters: + - name: "tile" + in: "query" + description: "Tile name" + schema: + type: "string" + required: true + - name: "sub_tile" + in: "query" + description: "Sub tile number" + schema: + type: "number" + - $ref: "#/components/parameters/reduce_fuzzy_match" + - $ref: "#/components/parameters/size" responses: 200: - description: 'All sources' + description: "OK" content: application/json: schema: - type: array - items: - type: string + $ref: "#/components/schemas/tilesSchema" 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - - /search/control/tiles: + - X-API-Key: [] + X-User-ID: [] + /v1/search/items: get: - operationId: controlGetTilesByQueryParams + operationId: getItemsByQueryParams tags: - - Control - summary: 'Search tiles and sub tiles' - description: |- - Tiles are consisted of 3 characters.
- You can query and get results from 2 characters.

If you define a tile (full name of it) you can query the sub tile associated with it.
- You may also search tile based on 1 meter precision MGRS tile. + - Exact Searches + summary: "Search control items" + description: "" parameters: - - name: 'tile' - in: 'query' - description: 'Tile name' + - name: "command_name" + in: "query" + description: "Object command name of the item" schema: - type: 'string' - minLength: 2 - maxLength: 3 - - name: 'sub_tile' - in: 'query' - description: 'Sub tile number' + type: "string" + required: true + + - name: "tile" + in: "query" + description: "The tile the item in it (full name of it)" schema: - type: 'string' - pattern: '^[1-9][0-9]*$' - - name: mgrs - description: '1 meters MGRS Tile' - example: '18SUJ2338907395' - in: query + type: "string" + required: false + + - name: "sub_tile" + in: "query" + description: "The sub tile the item in it (required if tile is defined)" schema: - type: string - - $ref: '#/components/parameters/geo_context' - - $ref: '#/components/parameters/geo_context_mode' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/disable_fuzziness' + type: "number" + - $ref: "#/components/parameters/geo_context" + - $ref: "#/components/parameters/reduce_fuzzy_match" + - $ref: "#/components/parameters/size" responses: 200: - description: 'OK' + description: "OK" content: application/json: schema: - $ref: '#/components/schemas/tilesSchema' + $ref: "#/components/schemas/itemsSchema" 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - /search/control/items: + - X-API-Key: [] + X-User-ID: [] + /v1/search/routes: get: - operationId: controlGetItemsByQueryParams + operationId: getRoutesByQueryParams tags: - - Control - summary: 'Search control items' - description: '' + - Exact Searches + summary: "Search routes and report control points" + description: "You can query and get control routes.

If you define a route (full name of it) you can query the control points associated with it. " parameters: - - name: 'command_name' - in: 'query' - description: 'Object command name of the item' + - name: "command_name" + in: "query" + description: "Object command name of the item" schema: - type: 'string' + type: "string" required: true - - - name: 'tile' - in: 'query' - description: 'The tile the item in it (full name of it)' + - name: "control_point" + in: "query" + description: "The associated report control point of the route" schema: - type: 'string' - minLength: 3 - maxLength: 3 - required: false - - - name: 'sub_tile' - in: 'query' - description: 'The sub tile the item in it (required if tile is defined)' - schema: - type: 'string' - pattern: '^[1-9][0-9]*$' - example: '66' - - $ref: '#/components/parameters/geo_context' - - $ref: '#/components/parameters/geo_context_mode' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/disable_fuzziness' + type: "number" + - $ref: "#/components/parameters/geo_context" + - $ref: "#/components/parameters/reduce_fuzzy_match" + - $ref: "#/components/parameters/size" responses: 200: - description: 'OK' + description: "OK (can be routesSchema or itemsSchema)" content: application/json: schema: - $ref: '#/components/schemas/itemsSchema' + anyOf: + - $ref: "#/components/schemas/routesSchema" + - $ref: "#/components/schemas/itemsSchema" 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - /search/control/routes: + - X-API-Key: [] + X-User-ID: [] + /v1/lookup/latlonToTile: get: - operationId: controlGetRoutesByQueryParams + operationId: convertLatlonToTile tags: - - Control - summary: 'Search routes and report control points' - description: 'You can query and get control routes.

If you define a route (full name of it) you can query the control points associated with it. ' + - Exact Searches + summary: "Convert a WGS84 coordinate to a tile" parameters: - - name: 'command_name' - in: 'query' - description: 'Object command name of the item' + - name: "lat" + in: "query" + description: "Latitude of the coordinate" schema: - type: 'string' + type: "number" + minimum: -90 + maximum: 90 required: true - - name: 'control_point' - in: 'query' - description: 'The associated report control point of the route' + - name: "lon" + in: "query" + description: "Longitude of the coordinate" schema: - type: 'string' - pattern: '^[1-9][0-9]*$' - - $ref: '#/components/parameters/geo_context' - - $ref: '#/components/parameters/geo_context_mode' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/disable_fuzziness' + type: "number" + minimum: -180 + maximum: 180 + required: true responses: 200: - description: 'OK (can be routesSchema or itemsSchema)' + description: "OK" content: application/json: schema: - anyOf: - - $ref: '#/components/schemas/routesSchema' - - $ref: '#/components/schemas/itemsSchema' + $ref: "#/components/schemas/subTileSchema" 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - /search/MGRS/tiles: + - X-API-Key: [] + X-User-ID: [] + /v1/lookup/tileToLatLon: get: - operationId: getMGRSToGeom + operationId: convertTileToLatlon tags: - - MGRS - summary: 'Convert a MGRS string to Geometry in GeoJSON' + - Exact Searches + summary: "Convert a tile to a geojson tile" parameters: - - name: 'tile' - in: 'query' - description: 'MGRS tile string' + - name: "tile" + in: "query" + description: "Tile name" schema: - type: 'string' + type: "string" + required: true + - name: "sub_tile_number" + in: "query" + description: "An array describing sub tile number" + schema: + type: "array" + items: + type: "number" + minimum: 0 + maximum: 99 + minItems: 3 + maxItems: 3 required: true responses: 200: - description: 'OK' + description: "OK" content: application/json: schema: - $ref: '#/components/schemas/mgrsTileSchema' + $ref: "#/components/schemas/tilesSchema" 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - /lookup/coordinates: + - X-API-Key: [] + X-User-ID: [] + /v1/lookup/latlonToMgrs: get: - operationId: convertionGetLatLonToMgrs + operationId: convertLatLonToMgrs tags: - - Convertions - summary: 'Convert a WGS84 coordinate to US Army MGRS / Control Grid' + - Exact Searches + summary: "Convert a WGS84 coordinate to US Army MGRS" parameters: - - name: 'lat' - in: 'query' - description: 'Latitude of the coordinate' + - name: "lat" + in: "query" + description: "Latitude of the coordinate" schema: - type: 'number' + type: "number" minimum: -90 maximum: 90 required: true - - name: 'lon' - in: 'query' - description: 'Longitude of the coordinate' + - name: "lon" + in: "query" + description: "Longitude of the coordinate" schema: - type: 'number' + type: "number" minimum: -180 maximum: 180 required: true - - name: 'target_grid' - in: query - description: Choose target grid + - name: "accuracy" + in: "query" + description: "Accuracy in digits (5 for 1 m, 4 for 10 m, 3 for 100 m, 2 for 1 km, 1 for 10 km or 0 for 100 km). Optional, default is 5." schema: - type: string - enum: ['control', 'MGRS'] - required: true + type: "number" + minimum: 0 + maximum: 5 responses: 200: - description: 'OK' + description: "OK" content: application/json: schema: type: object properties: - type: - type: "string" - enum: - - "Feature" - geocoding: - type: "object" - properties: - query: - type: "object" - properties: - lat: - type: number - lon: - type: number - response: - type: "object" - properties: - max_score: - type: number - results_count: - type: number - match_latency_ms: - type: number - bbox: - $ref: "#/components/schemas/BoundingBox" - geometry: - oneOf: - - $ref: "#/components/schemas/Point" - - $ref: "#/components/schemas/Polygon" - properties: - type: object - properties: - name: - type: string - additionalProperty1: - type: object + mgrs: + type: string + pattern: ^\d{1,2}[^ABIOYZabioyz][A-Za-z]{2}([0-9][0-9])+$ 400: - '$ref': '#/components/responses/BadRequest' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - - /searches/feedback: - post: - operationId: postSearchFeedback + - X-API-Key: [] + X-User-ID: [] + /v1/lookup/mgrsToLatLon: + get: + operationId: convertMgrsToLatLon tags: - - Feedback - summary: 'Retrieve feedback from services about quality of results' + - Exact Searches + summary: "Convert a US Army MGRS to WGS84 coordinate" parameters: - - name: choosen_result_id - in: 'query' - description: 'The result ID of chosen search' + - name: "mgrs" + in: "query" + description: "MGRS string" schema: - type: string - minimum: 1 - maximum: 100 - required: true - - name: request_id - in: query - description: The request ID retrieved from headers of search API response - schema: - type: string - minimum: 1 - maximum: 100 + type: "string" + pattern: ^\d{1,2}[^ABIOYZabioyz][A-Za-z]{2}([0-9][0-9])+$ required: true responses: - 204: - description: 'OK - No Content' + 200: + description: "OK" + content: + application/json: + schema: + type: object + properties: + lat: + type: number + lon: + type: number 400: - '$ref': '#/components/responses/BadRequest' - 404: - $ref: '#/components/responses/NotFound' + "$ref": "#/components/responses/BadRequest" 401: - '$ref': '#/components/responses/Unauthorized' + "$ref": "#/components/responses/Unauthorized" 403: - '$ref': '#/components/responses/Forbidden' + "$ref": "#/components/responses/Forbidden" 500: - '$ref': '#/components/responses/InternalError' + "$ref": "#/components/responses/InternalError" security: - - x-api-key: [] - x-user-id: [] - + - X-API-Key: [] + X-User-ID: [] components: responses: BadRequest: - description: 'Invalid Request' + description: "Invalid Request" content: application/json: schema: - $ref: '#/components/schemas/errorSchema' + $ref: "#/components/schemas/errorSchema" Unauthorized: - description: 'Please provide a valid token' - content: - application/json: - schema: - $ref: '#/components/schemas/errorSchema' - NotFound: - description: 'Resource Not Found' + description: "Please provide a valid token" content: application/json: schema: - $ref: '#/components/schemas/errorSchema' + $ref: "#/components/schemas/errorSchema" Forbidden: - description: 'Token is not valid' + description: "Token is not valid" content: application/json: schema: - $ref: '#/components/schemas/errorSchema' + $ref: "#/components/schemas/errorSchema" InternalError: - description: 'Invalid Request' + description: "Invalid Request" content: application/json: schema: - $ref: '#/components/schemas/errorSchema' + $ref: "#/components/schemas/errorSchema" parameters: - disable_fuzziness: - name: 'disable_fuzziness' - in: 'query' - description: |- - If an accurate result is obtained, only it will be returned - schema: - $ref: '#/components/schemas/disable_fuzziness' - limit: - name: 'limit' - in: 'query' - description: 'Maximum number of results' + reduce_fuzzy_match: + name: "reduce_fuzzy_match" + in: "query" + description: "If an accurate result is obtained, only it will be returned (default is false)" schema: - default: 5 - minimum: 1 - maximum: 15 - type: 'number' - geo_context: - in: 'query' - name: 'geo_context' - description: |- - Geo context of search. + type: "boolean" - Supported queries: - * bounding box:
- {"bbox":[minX,minY,maxX,maxY]} - * By point and radius (WGS84 and UTM are supported)
- {"lon":value,"lat":value,"radius":value}
- {"x":value,"y":value,"zone": number, "radius":value} + size: + name: "size" + in: "query" + description: "Maximum number of results (default is 3, range: 1-100)" schema: - $ref: '#/components/schemas/geo_context' - geo_context_mode: - name: 'geo_context_mode' - in: 'query' - description: 'Choose whether geo_context query parameter will be a filter or a bias value' + type: "number" + + geo_context: + name: "geo_context" + in: "query" + description: 'Geo context of search.

Supported queries:

*By bounding box, object with these keys: {"bbox":[minX,minY,maxX,maxY]}

*By point and radius, object with these keys: {"lon":value,"lat":value,"radius":value}' schema: - type: string - enum: ['filter', 'bias'] + type: "string" schemas: errorSchema: - type: 'object' + type: "object" required: - - 'message' + - "message" properties: message: - type: 'string' + type: "string" status: - type: 'number' + type: "number" subTileSchema: - type: 'object' - description: 'An object represnting a sub tile' + type: "object" + description: "An object represnting a sub tile" required: - tileName - subTileNumber properties: tileName: - type: 'string' + type: "string" subTileNumber: - type: 'array' + type: "array" items: - type: 'number' + type: "number" minimum: 0 maximum: 100 minItems: 3 maxItems: 3 - tileSchema: - type: 'object' + tilesSchema: + type: "object" + description: "GeoJson feature collection representing a tile (Polygon)" + required: + - "type" + - "features" properties: type: - type: 'string' - enum: ['Feature'] - geometry: - $ref: '#/components/schemas/Polygon' - properties: - type: 'object' - required: - - 'TYPE' - - 'tile_name' - properties: - TYPE: - type: 'string' - tile_name: - type: 'string' - sub_tile_id: - type: 'string' - SUB_TILE_NUMBER: - type: 'array' - items: - type: 'number' - minimum: 0 - maximum: 100 - minItems: 3 - maxItems: 3 - mgrsTileSchema: - type: 'object' + type: "string" + enum: + - "FeatureCollection" + features: + type: "array" + items: + type: "object" + properties: + type: + type: "string" + enum: ["Feature"] + geometry: + $ref: "#/components/schemas/Geometry" + properties: + type: "object" + required: + - "TYPE" + - "TILE_NAME" + properties: + TYPE: + type: "string" + TILE_NAME: + type: "string" + SUB_TILE_ID: + type: "string" + SUB_TILE_NUMBER: + type: "array" + items: + type: "number" + minimum: 0 + maximum: 100 + minItems: 3 + maxItems: 3 + itemsSchema: + type: "object" + description: "GeoJson feature collection representing an item" + required: + - "type" + - "features" properties: type: type: "string" enum: - - "Feature" - geocoding: - type: "object" - properties: - query: - type: "object" - properties: - tile: - type: string - example: "18SUJ2339007393" - response: - type: "object" + - "FeatureCollection" + features: + type: "array" + items: + type: "object" + properties: + geometry: + $ref: "#/components/schemas/Geometry" properties: - max_score: - type: number - results_count: - type: number - match_latency_ms: - type: number - - bbox: - $ref: "#/components/schemas/BoundingBox" - geometry: - oneOf: - - $ref: "#/components/schemas/Point" - - $ref: "#/components/schemas/Polygon" - properties: - type: object - properties: - score: - type: number - tilesSchema: - allOf: - - $ref: "#/components/schemas/genericGeocodingResponse" - - type: "object" - description: "GeoJson feature collection representing a tile (Polygon)" - required: - - "type" - - "features" - properties: - type: - type: "string" - enum: - - "FeatureCollection" - features: - type: "array" - items: - $ref: "#/components/schemas/tileSchema" - itemsSchema: - allOf: - - $ref: "#/components/schemas/genericGeocodingResponse" - - type: "object" - description: "GeoJson feature collection representing an item" - required: - - "type" - - "features" - properties: - type: - type: "string" - enum: - - "FeatureCollection" - features: - type: "array" - items: type: "object" + required: + - "TYPE" + - "OBJECT_COMMAND_NAME" + - "ENTITY_HEB" + - "SUB_TILE_ID" properties: - geometry: - oneOf: - - $ref: "#/components/schemas/Point" - - $ref: "#/components/schemas/Polygon" - properties: - type: "object" - required: - - "TYPE" - - "object_command_name" - - "entity_heb" - - "sub_tile_id" - properties: - TYPE: - type: "string" - object_command_name: - type: "string" - entity_heb: - type: "string" - tile_name: - type: "string" - sub_tile_id: - type: "string" + TYPE: + type: "string" + OBJECT_COMMAND_NAME: + type: "string" + ENTITY_HEB: + type: "string" + TILE_NAME: + type: "string" + SUB_TILE_ID: + type: "string" routesSchema: - allOf: - - $ref: "#/components/schemas/genericGeocodingResponse" - - type: "object" - description: "GeoJson feature collection representing a route (MultiLineString, LineString)" - required: - - "type" - - "features" - properties: - type: - type: "string" - enum: - - "FeatureCollection" - features: - type: "array" - items: - type: "object" - properties: - geometry: - oneOf: - - $ref: "#/components/schemas/LineString" - - $ref: "#/components/schemas/MultiLineString" - properties: - type: "object" - required: - - "TYPE" - - "object_command_name" - - "entity_heb" - properties: - TYPE: - type: "string" - object_command_name: - type: "string" - entity_heb: - type: "string" - SECTION: - type: "string" - genericGeocodingResponse: # we need discriminator for Control and Location - type: 'object' - description: 'GeoJson feature collection representing an item' + type: "object" + description: "GeoJson feature collection representing a route (Line)" required: - - 'type' - - 'features' - - 'geocoding' + - "type" + - "features" properties: type: - type: 'string' + type: "string" enum: - - 'FeatureCollection' - geocoding: - type: object - required: - - 'query' - - 'response' - properties: - version: - type: string - pattern: '^\d{3}\.\d{3}\.\d{3}$' - example: 1.5.3 - query: - type: object - required: - - 'text' - - 'limit' - - 'geo_context' - - 'disable_fuzziness' - properties: - text: - type: string - minLength: 1 - maxLength: 100 - limit: - type: integer - minimum: 1 - maximum: 10 - geo_context: - $ref: '#/components/schemas/geo_context' - disable_fuzziness: - $ref: '#/components/schemas/disable_fuzziness' - response: - type: object - required: - - 'max_score' - - 'results_count' - - 'match_latency' - properties: - max_score: - type: number - minimum: 0 - maximum: 100 - results_count: - type: integer - minimum: 0 - maximum: 10 - match_latency_ms: - type: integer - minimum: 0 - maximum: 5000 + - "FeatureCollection" features: - type: 'array' + type: "array" items: - type: 'object' + type: "object" properties: geometry: - $ref: '#/components/schemas/Geometry' + $ref: "#/components/schemas/Geometry" properties: - type: 'object' - additionalProperties: true + type: "object" required: - - id - - score - - matches - - regions - - display_name - - names + - "TYPE" + - "OBJECT_COMMAND_NAME" + - "ENTITY_HEB" properties: - score: - type: number - minimum: 0 - maximum: 100 - matches: - type: array - minItems: 1 - maxItems: 100 - items: - type: object - required: - - id - - source - - layer - properties: - layer: - type: string - source: - type: string - source_id: - type: array - items: - type: string - names: - type: object - required: - - display - - default - additionalProperties: - type: array - minItems: 1 - maxItems: 100 - items: - type: string - properties: - display: - type: string - default: - type: array - minItems: 1 - items: - type: string - regions: - type: array - minItems: 1 - maxItems: 100 - items: - type: object - required: - - name - - sub_regions_names - properties: - name: - type: string - sub_regions_names: - type: array - minItems: 0 - maxItems: 100 - items: - type: string - minLength: 1 - maxLength: 100 - + TYPE: + type: "string" + OBJECT_COMMAND_NAME: + type: "string" + ENTITY_HEB: + type: "string" + SECTION: + type: "string" Geometry: type: object description: Geojson geometry @@ -944,69 +672,69 @@ components: externalDocs: url: http://geojson.org/geojson-spec.html#id2 allOf: - - $ref: '#/components/schemas/Geometry' + - $ref: "#/components/schemas/Geometry" - properties: coordinates: - $ref: '#/components/schemas/Point3D' + $ref: "#/components/schemas/Point3D" LineString: type: object description: Geojson geometry externalDocs: url: http://geojson.org/geojson-spec.html#id3 allOf: - - $ref: '#/components/schemas/Geometry' + - $ref: "#/components/schemas/Geometry" - properties: coordinates: type: array items: - $ref: '#/components/schemas/Point3D' + $ref: "#/components/schemas/Point3D" Polygon: type: object description: Geojson geometry externalDocs: url: http://geojson.org/geojson-spec.html#id4 allOf: - - $ref: '#/components/schemas/Geometry' + - $ref: "#/components/schemas/Geometry" - properties: coordinates: type: array items: type: array items: - $ref: '#/components/schemas/Point3D' + $ref: "#/components/schemas/Point3D" MultiPoint: type: object description: Geojson geometry externalDocs: url: http://geojson.org/geojson-spec.html#id5 allOf: - - $ref: '#/components/schemas/Geometry' + - $ref: "#/components/schemas/Geometry" - properties: coordinates: type: array items: - $ref: '#/components/schemas/Point3D' + $ref: "#/components/schemas/Point3D" MultiLineString: type: object description: Geojson geometry externalDocs: url: http://geojson.org/geojson-spec.html#id6 allOf: - - $ref: '#/components/schemas/Geometry' + - $ref: "#/components/schemas/Geometry" - properties: coordinates: type: array items: type: array items: - $ref: '#/components/schemas/Point3D' + $ref: "#/components/schemas/Point3D" MultiPolygon: type: object description: Geojson geometry externalDocs: url: http://geojson.org/geojson-spec.html#id6 allOf: - - $ref: '#/components/schemas/Geometry' + - $ref: "#/components/schemas/Geometry" - properties: coordinates: type: array @@ -1015,61 +743,19 @@ components: items: type: array items: - $ref: '#/components/schemas/Point3D' - + $ref: "#/components/schemas/Point3D" Source: type: string title: Source Region: type: string title: Region - BoundingBox: - type: array - description: 'Bounding box array that contains [minX,minY,maxX,maxY]' - example: '[-74.382527,40.477003,-73.322346,40.916383]' - items: - type: number - minLength: 4 - maxLength: 4 - WGS84Circle: - type: object - properties: - lat: - type: number - lon: - type: number - radius: - type: number - UTMCircle: - type: object - properties: - x: - type: number - y: - type: number - zone: - type: number - minimum: 1 - maximum: 60 - radius: - type: number - geo_context: - anyOf: - - type: object - properties: - bbox: - $ref: '#/components/schemas/BoundingBox' - - $ref: '#/components/schemas/WGS84Circle' - - $ref: '#/components/schemas/UTMCircle' - disable_fuzziness: - type: boolean - default: false securitySchemes: - x-api-key: - type: 'apiKey' - name: 'x-api-key' - in: 'header' - x-user-id: - type: 'apiKey' - name: 'x-user-id' - in: 'header' + X-API-Key: + type: "apiKey" + name: "X-API-Key" + in: "header" + X-User-ID: + type: "apiKey" + name: "X-User-ID" + in: "header" diff --git a/package-lock.json b/package-lock.json index 5eb0f3f2..a0ae8830 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,17 @@ { "name": "geocoding", - "version": "0.1.0-prealpha.4", + "version": "0.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "geocoding", - "version": "0.1.0-prealpha.4", + "version": "0.5.0", "hasInstallScript": true, "license": "ISC", "dependencies": { - "@aws-sdk/client-s3": "^3.637.0", "@elastic/elasticsearch": "^8.13.1", "@godaddy/terminus": "^4.12.1", - "@map-colonies/cleanup-registry": "^1.1.0", "@map-colonies/error-express-handler": "^2.1.0", "@map-colonies/express-access-log-middleware": "^2.0.1", "@map-colonies/js-logger": "^1.0.1", @@ -24,8 +22,6 @@ "@opentelemetry/api-metrics": "0.23.0", "@opentelemetry/instrumentation-express": "0.32.1", "@opentelemetry/instrumentation-http": "0.35.1", - "@smithy/node-http-handler": "^3.1.4", - "ajv": "^8.17.1", "axios": "^0.21.1", "compression": "^1.7.4", "config": "^3.3.9", @@ -34,15 +30,14 @@ "fast-xml-parser": "^4.4.0", "geojson-validation": "^1.0.2", "http-status-codes": "^2.2.0", - "ioredis": "^5.4.1", "mgrs": "^2.0.0", "node-cron": "^3.0.3", "node-fetch-commonjs": "^3.3.2", + "pg": "^8.11.5", "proj4": "^2.11.0", - "redis": "^4.7.0", "reflect-metadata": "^0.1.13", "tsyringe": "^4.8.0", - "utm-latlng": "^1.0.8", + "typeorm": "^0.3.20", "wellknown": "^0.5.0" }, "devDependencies": { @@ -108,1051 +103,6 @@ "js-yaml": "^4.1.0" } }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/crc32c/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.637.0.tgz", - "integrity": "sha512-y6UC94fsMvhKbf0dzfnjVP1HePeGjplfcYfilZU1COIJLyTkMcUv4XcT4I407CGIrvgEafONHkiC09ygqUauNA==", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.637.0", - "@aws-sdk/client-sts": "3.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-bucket-endpoint": "3.620.0", - "@aws-sdk/middleware-expect-continue": "3.620.0", - "@aws-sdk/middleware-flexible-checksums": "3.620.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-location-constraint": "3.609.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@aws-sdk/xml-builder": "3.609.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-blob-browser": "^3.1.2", - "@smithy/hash-node": "^3.0.3", - "@smithy/hash-stream-node": "^3.1.2", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-s3/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.637.0.tgz", - "integrity": "sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.637.0.tgz", - "integrity": "sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/client-sso/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.637.0.tgz", - "integrity": "sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/core": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", - "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", - "dependencies": { - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", - "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", - "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz", - "integrity": "sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.637.0.tgz", - "integrity": "sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-ini": "3.637.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", - "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.637.0.tgz", - "integrity": "sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==", - "dependencies": { - "@aws-sdk/client-sso": "3.637.0", - "@aws-sdk/token-providers": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.621.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", - "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.621.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", - "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", - "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", - "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-sdk/types": "3.609.0", - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", - "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz", - "integrity": "sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", - "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", - "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", - "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", - "dependencies": { - "@aws-sdk/core": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-stream": "^3.1.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", - "integrity": "sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.637.0.tgz", - "integrity": "sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", - "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", - "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", - "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.614.0" - } - }, - "node_modules/@aws-sdk/token-providers/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/types": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", - "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/types/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", - "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.637.0.tgz", - "integrity": "sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "@smithy/util-endpoints": "^2.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", - "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", - "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", - "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-user-agent-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz", - "integrity": "sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/xml-builder/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, "node_modules/@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", @@ -3157,6 +2107,28 @@ "node": ">=v14" } }, + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/@commitlint/ensure": { "version": "17.4.4", "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.4.4.tgz", @@ -3647,7 +2619,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -3659,7 +2631,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -3755,23 +2727,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -3787,13 +2742,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -3823,28 +2771,6 @@ "ajv": "^6.12.6" } }, - "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -4027,16 +2953,10 @@ "node": ">=6.9.0" } }, - "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -4053,7 +2973,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -4065,7 +2984,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -4076,14 +2994,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4100,7 +3016,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -4115,7 +3030,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -4835,7 +3749,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.0.0" } @@ -4853,7 +3767,7 @@ "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.17", @@ -4870,15 +3784,6 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, - "node_modules/@map-colonies/cleanup-registry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@map-colonies/cleanup-registry/-/cleanup-registry-1.1.0.tgz", - "integrity": "sha512-/lhIGklWPZSY37JwzhFJEtBlqwXDRhHSeCBwpPaGMxpycpt5ZRIVQxUt6Og4mt6c5GoRoX9dZYHY0qV3UMGvtQ==", - "dependencies": { - "nanoid": "^3.3.4", - "tiny-typed-emitter": "^2.1.0" - } - }, "node_modules/@map-colonies/error-express-handler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@map-colonies/error-express-handler/-/error-express-handler-2.1.0.tgz", @@ -7318,7 +6223,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -7378,59 +6282,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", - "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", - "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", - "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", - "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, "node_modules/@redocly/ajv": { "version": "8.6.4", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.4.tgz", @@ -7447,6 +6298,12 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/@redocly/openapi-cli": { "version": "1.0.0-beta.95", "resolved": "https://registry.npmjs.org/@redocly/openapi-cli/-/openapi-cli-1.0.0-beta.95.tgz", @@ -7604,914 +6461,34 @@ "@sinonjs/commons": "^2.0.0" } }, - "node_modules/@smithy/abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", - "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/abort-controller/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz", - "integrity": "sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz", - "integrity": "sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==", - "dependencies": { - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/chunked-blob-reader-native/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/chunked-blob-reader/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/config-resolver": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", - "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/config-resolver/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", - "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/core/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", - "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/eventstream-codec": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", - "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/eventstream-codec/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", - "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", - "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz", - "integrity": "sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", - "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", - "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", - "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", - "dependencies": { - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", - "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", - "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/fetch-http-handler/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz", - "integrity": "sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==", - "dependencies": { - "@smithy/chunked-blob-reader": "^3.0.0", - "@smithy/chunked-blob-reader-native": "^3.0.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/hash-blob-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/hash-node": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", - "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", - "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/hash-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/hash-stream-node": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz", - "integrity": "sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==", - "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/hash-stream-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/invalid-dependency": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", - "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/invalid-dependency/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/is-array-buffer/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/md5-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", - "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", - "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/md5-js/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/middleware-content-length": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", - "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", - "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-content-length/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", - "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", - "dependencies": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/middleware-retry": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", - "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-retry/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/middleware-serde": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", - "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-serde/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/middleware-stack": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", - "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-stack/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/node-config-provider": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", - "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", - "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-config-provider/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/node-http-handler": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", - "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", - "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-http-handler/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/property-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", - "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/property-provider/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/protocol-http": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", - "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/protocol-http/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/querystring-builder": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", - "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", - "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/querystring-builder/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/querystring-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", - "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/querystring-parser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/service-error-classification": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", - "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", - "dependencies": { - "@smithy/types": "^3.3.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", - "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/signature-v4": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", - "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/signature-v4/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/smithy-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", - "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/smithy-client/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", - "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/types/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/url-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", - "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", - "dependencies": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/url-parser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-base64/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/util-body-length-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-body-length-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-buffer-from/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-config-provider/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", - "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", - "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", - "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", - "dependencies": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-endpoints": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", - "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-endpoints/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", - "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-middleware/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-retry": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", - "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", - "dependencies": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-retry/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-stream": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", - "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", - "dependencies": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-stream/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-uri-escape/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-utf8/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/@smithy/util-waiter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", - "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", - "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-waiter/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "devOptional": true }, "node_modules/@types/accepts": { "version": "1.3.5", @@ -9416,7 +7393,7 @@ "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -9437,7 +7414,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -9449,15 +7426,14 @@ "dev": true }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { "type": "github", @@ -9480,6 +7456,26 @@ } } }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -9527,6 +7523,11 @@ "node": ">=4" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -9540,6 +7541,14 @@ "node": ">= 8" } }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -9554,7 +7563,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "2.0.1", @@ -10123,11 +8132,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -10431,6 +8435,107 @@ "node": ">=8" } }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/cli-highlight/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cli-spinners": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", @@ -10471,14 +8576,6 @@ "node": ">=0.8" } }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -11398,13 +9495,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -11457,6 +9553,11 @@ "node": "*" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -11570,14 +9671,6 @@ "node": ">=0.4.0" } }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -11636,7 +9729,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -11686,6 +9779,17 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dotgitignore": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", @@ -11759,8 +9863,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ee-first": { "version": "1.1.1", @@ -12392,23 +10495,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -12559,13 +10645,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -12880,6 +10959,21 @@ "path-to-regexp": "^6.2.0" } }, + "node_modules/express-openapi-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/express-openapi-validator/node_modules/ajv-draft-04": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", @@ -12893,6 +10987,11 @@ } } }, + "node_modules/express-openapi-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/express-openapi-validator/node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -12996,28 +11095,6 @@ "node": ">= 10.0.0" } }, - "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -13037,16 +11114,10 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "license": "MIT" - }, "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", + "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", "funding": [ { "type": "github", @@ -13329,7 +11400,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -13345,7 +11415,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true, "engines": { "node": ">=14" }, @@ -13999,6 +12068,14 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -14312,29 +12389,6 @@ "node": ">= 0.4" } }, - "node_modules/ioredis": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", - "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", - "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -14693,8 +12747,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -14796,7 +12849,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -16695,10 +14747,9 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -16830,6 +14881,26 @@ "set-cookie-parser": "^2.4.1" } }, + "node_modules/light-my-request/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/light-my-request/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/light-my-request/node_modules/process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", @@ -16912,21 +14983,11 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -17159,7 +15220,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -17365,7 +15426,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -17448,21 +15508,14 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, "node_modules/natural-compare": { @@ -17820,6 +15873,28 @@ "openapi-types": "^9.3.1" } }, + "node_modules/openapi-response-validator/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-response-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/openapi-schema-validator": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-schema-validator/-/openapi-schema-validator-9.3.1.tgz", @@ -17832,6 +15907,28 @@ "openapi-types": "^9.3.1" } }, + "node_modules/openapi-schema-validator/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-schema-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/openapi-types": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz", @@ -18065,6 +16162,24 @@ "node": ">=0.10.0" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -18095,7 +16210,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -18119,7 +16233,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", - "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -18135,7 +16248,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, "engines": { "node": "14 || >=16.14" } @@ -18154,6 +16266,43 @@ "node": ">=8" } }, + "node_modules/pg": { + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" + }, "node_modules/pg-int8": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", @@ -18162,6 +16311,14 @@ "node": ">=4.0.0" } }, + "node_modules/pg-pool": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, "node_modules/pg-protocol": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", @@ -18182,6 +16339,14 @@ "node": ">=4" } }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -19066,38 +17231,6 @@ "node": ">=8" } }, - "node_modules/redis": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", - "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.0", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -19590,11 +17723,22 @@ "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", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -19606,7 +17750,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -19783,11 +17926,6 @@ "node": ">=8" } }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, "node_modules/standard-version": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", @@ -19975,7 +18113,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -20054,7 +18191,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -20247,6 +18383,25 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/thread-stream": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", @@ -20278,11 +18433,6 @@ "node": ">=6" } }, - "node_modules/tiny-typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", - "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -20413,7 +18563,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20562,11 +18712,316 @@ "integrity": "sha1-nIRAPyMjrlOZFnJ1SXY46h0vJEA=", "dev": true }, + "node_modules/typeorm": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.2.1", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/typeorm/node_modules/cliui": { + "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==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/typeorm/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/typeorm/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + }, + "node_modules/typeorm/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typeorm/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/typeorm/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/typeorm/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20709,11 +19164,6 @@ "node": ">= 0.4.0" } }, - "node_modules/utm-latlng": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/utm-latlng/-/utm-latlng-1.0.8.tgz", - "integrity": "sha512-jQ5l1BNTzNgas7jXOIh0jU0m5PU5LtmjYvRiLwR4iymYYAhEhQ6N0MGCnLjv5iO958b9tLMMSmzdbIIhLtXqCg==" - }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -20730,7 +19180,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "9.1.0", @@ -20858,7 +19308,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -20926,7 +19375,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", @@ -20943,7 +19391,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -20958,7 +19405,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -20969,8 +19415,7 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", @@ -21116,7 +19561,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -21156,983 +19601,6 @@ "js-yaml": "^4.1.0" } }, - "@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "requires": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "requires": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "requires": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "requires": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "requires": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "requires": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "requires": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "requires": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "requires": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "requires": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "requires": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "requires": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/client-s3": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.637.0.tgz", - "integrity": "sha512-y6UC94fsMvhKbf0dzfnjVP1HePeGjplfcYfilZU1COIJLyTkMcUv4XcT4I407CGIrvgEafONHkiC09ygqUauNA==", - "requires": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.637.0", - "@aws-sdk/client-sts": "3.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-bucket-endpoint": "3.620.0", - "@aws-sdk/middleware-expect-continue": "3.620.0", - "@aws-sdk/middleware-flexible-checksums": "3.620.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-location-constraint": "3.609.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@aws-sdk/xml-builder": "3.609.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-blob-browser": "^3.1.2", - "@smithy/hash-node": "^3.0.3", - "@smithy/hash-stream-node": "^3.1.2", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.2", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/client-sso": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.637.0.tgz", - "integrity": "sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/client-sso-oidc": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.637.0.tgz", - "integrity": "sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/client-sts": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.637.0.tgz", - "integrity": "sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/core": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", - "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", - "requires": { - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", - "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-http": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", - "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz", - "integrity": "sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==", - "requires": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.637.0.tgz", - "integrity": "sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==", - "requires": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-ini": "3.637.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.620.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", - "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.637.0.tgz", - "integrity": "sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==", - "requires": { - "@aws-sdk/client-sso": "3.637.0", - "@aws-sdk/token-providers": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.621.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", - "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-bucket-endpoint": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", - "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-expect-continue": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", - "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-flexible-checksums": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", - "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", - "requires": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-sdk/types": "3.609.0", - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", - "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-location-constraint": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz", - "integrity": "sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", - "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.620.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", - "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-sdk-s3": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", - "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", - "requires": { - "@aws-sdk/core": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-arn-parser": "3.568.0", - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-stream": "^3.1.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-ssec": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", - "integrity": "sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.637.0.tgz", - "integrity": "sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/region-config-resolver": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", - "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/signature-v4-multi-region": { - "version": "3.635.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", - "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", - "requires": { - "@aws-sdk/middleware-sdk-s3": "3.635.0", - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/token-providers": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", - "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/types": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", - "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/util-arn-parser": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", - "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.637.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.637.0.tgz", - "integrity": "sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "@smithy/util-endpoints": "^2.0.5", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/util-locate-window": { - "version": "3.568.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", - "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", - "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", - "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@aws-sdk/xml-builder": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz", - "integrity": "sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, "@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", @@ -23531,6 +20999,26 @@ "requires": { "@commitlint/types": "^17.4.4", "ajv": "^8.11.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "@commitlint/ensure": { @@ -23896,7 +21384,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -23905,7 +21393,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -23986,18 +21474,6 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -24007,12 +21483,6 @@ "type-fest": "^0.20.2" } }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -24033,24 +21503,6 @@ "integrity": "sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg==", "requires": { "ajv": "^6.12.6" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - } } }, "@fastify/busboy": { @@ -24206,16 +21658,10 @@ "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", "dev": true }, - "@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" - }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "requires": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -24228,26 +21674,22 @@ "ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" }, "ansi-styles": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "requires": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -24258,7 +21700,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "requires": { "ansi-regex": "^6.0.1" } @@ -24267,7 +21708,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "requires": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -24817,7 +22257,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true + "devOptional": true }, "@jridgewell/set-array": { "version": "1.1.1", @@ -24829,7 +22269,7 @@ "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "devOptional": true }, "@jridgewell/trace-mapping": { "version": "0.3.17", @@ -24846,15 +22286,6 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, - "@map-colonies/cleanup-registry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@map-colonies/cleanup-registry/-/cleanup-registry-1.1.0.tgz", - "integrity": "sha512-/lhIGklWPZSY37JwzhFJEtBlqwXDRhHSeCBwpPaGMxpycpt5ZRIVQxUt6Og4mt6c5GoRoX9dZYHY0qV3UMGvtQ==", - "requires": { - "nanoid": "^3.3.4", - "tiny-typed-emitter": "^2.1.0" - } - }, "@map-colonies/error-express-handler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@map-colonies/error-express-handler/-/error-express-handler-2.1.0.tgz", @@ -26590,7 +24021,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true }, "@protobufjs/aspromise": { @@ -26647,46 +24077,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "requires": {} - }, - "@redis/client": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", - "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", - "requires": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - } - }, - "@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", - "requires": {} - }, - "@redis/json": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", - "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", - "requires": {} - }, - "@redis/search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", - "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", - "requires": {} - }, - "@redis/time-series": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", - "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", - "requires": {} - }, "@redocly/ajv": { "version": "8.6.4", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.4.tgz", @@ -26697,6 +24087,14 @@ "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" + }, + "dependencies": { + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "@redocly/openapi-cli": { @@ -26836,889 +24234,34 @@ "@sinonjs/commons": "^2.0.0" } }, - "@smithy/abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", - "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/chunked-blob-reader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz", - "integrity": "sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/chunked-blob-reader-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz", - "integrity": "sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==", - "requires": { - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/config-resolver": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", - "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", - "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", - "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", - "requires": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/credential-provider-imds": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", - "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", - "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/eventstream-codec": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", - "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", - "requires": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/eventstream-serde-browser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", - "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", - "requires": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/eventstream-serde-config-resolver": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz", - "integrity": "sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/eventstream-serde-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", - "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", - "requires": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/eventstream-serde-universal": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", - "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", - "requires": { - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/fetch-http-handler": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", - "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", - "requires": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/hash-blob-browser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz", - "integrity": "sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==", - "requires": { - "@smithy/chunked-blob-reader": "^3.0.0", - "@smithy/chunked-blob-reader-native": "^3.0.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/hash-node": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", - "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", - "requires": { - "@smithy/types": "^3.3.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/hash-stream-node": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz", - "integrity": "sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==", - "requires": { - "@smithy/types": "^3.3.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/invalid-dependency": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", - "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/md5-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", - "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", - "requires": { - "@smithy/types": "^3.3.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/middleware-content-length": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", - "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", - "requires": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/middleware-endpoint": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", - "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", - "requires": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/middleware-retry": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", - "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", - "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/middleware-serde": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", - "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/middleware-stack": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", - "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/node-config-provider": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", - "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", - "requires": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/node-http-handler": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", - "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", - "requires": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/property-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", - "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/protocol-http": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", - "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/querystring-builder": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", - "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", - "requires": { - "@smithy/types": "^3.3.0", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/querystring-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", - "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/service-error-classification": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", - "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", - "requires": { - "@smithy/types": "^3.3.0" - } - }, - "@smithy/shared-ini-file-loader": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", - "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/signature-v4": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", - "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", - "requires": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/smithy-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", - "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", - "requires": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", - "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/url-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", - "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", - "requires": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "requires": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "requires": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", - "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", - "requires": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-defaults-mode-node": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", - "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", - "requires": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-endpoints": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", - "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", - "requires": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", - "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", - "requires": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-retry": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", - "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", - "requires": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-stream": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", - "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", - "requires": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "requires": { - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "requires": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } - }, - "@smithy/util-waiter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", - "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", - "requires": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - } - } + "@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "devOptional": true }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true + "devOptional": true }, "@types/accepts": { "version": "1.3.5", @@ -28475,7 +25018,7 @@ "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true + "devOptional": true }, "acorn-jsx": { "version": "5.3.2", @@ -28488,7 +25031,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true + "devOptional": true }, "add-stream": { "version": "1.0.0", @@ -28497,14 +25040,14 @@ "dev": true }, "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ajv-formats": { @@ -28513,6 +25056,24 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "requires": { "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } } }, "ansi-escapes": { @@ -28546,6 +25107,11 @@ "color-convert": "^1.9.0" } }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -28556,6 +25122,11 @@ "picomatch": "^2.0.4" } }, + "app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==" + }, "append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -28570,7 +25141,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "argparse": { "version": "2.0.1", @@ -29020,11 +25591,6 @@ } } }, - "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -29225,6 +25791,78 @@ "restore-cursor": "^3.1.0" } }, + "cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "requires": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, "cli-spinners": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", @@ -29253,11 +25891,6 @@ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, - "cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -30011,13 +26644,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -30056,6 +26688,11 @@ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==" }, + "dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -30136,11 +26773,6 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -30183,7 +26815,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "devOptional": true }, "diff-sequences": { "version": "29.4.3", @@ -30218,6 +26850,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, "dotgitignore": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", @@ -30278,8 +26915,7 @@ "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "ee-first": { "version": "1.1.1", @@ -30444,18 +27080,6 @@ "text-table": "^0.2.0" }, "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -30555,12 +27179,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -31124,12 +27742,28 @@ "path-to-regexp": "^6.2.0" }, "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, "ajv-draft-04": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", "requires": {} }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -31200,24 +27834,6 @@ "deepmerge": "^4.2.2", "rfdc": "^1.2.0", "string-similarity": "^4.0.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - } } }, "fast-levenshtein": { @@ -31236,15 +27852,10 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, - "fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, "fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", + "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", "requires": { "strnum": "^1.0.5" } @@ -31470,7 +28081,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "requires": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -31479,8 +28089,7 @@ "signal-exit": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==" } } }, @@ -31966,6 +28575,11 @@ "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", "dev": true }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -32188,22 +28802,6 @@ "side-channel": "^1.0.4" } }, - "ioredis": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", - "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", - "requires": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - } - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -32439,8 +29037,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "istanbul-lib-coverage": { "version": "3.2.0", @@ -32522,7 +29119,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", "@pkgjs/parseargs": "^0.11.0" @@ -33938,9 +30534,9 @@ "dev": true }, "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -34043,6 +30639,22 @@ "set-cookie-parser": "^2.4.1" }, "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", @@ -34117,21 +30729,11 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -34323,7 +30925,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "makeerror": { "version": "1.0.12", @@ -34473,8 +31075,7 @@ "minipass": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", - "dev": true + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==" }, "mkdirp": { "version": "0.5.6", @@ -34541,10 +31142,15 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } }, "natural-compare": { "version": "1.4.0", @@ -34811,6 +31417,26 @@ "requires": { "ajv": "^8.4.0", "openapi-types": "^9.3.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "openapi-schema-validator": { @@ -34823,6 +31449,26 @@ "ajv-formats": "^2.0.2", "lodash.merge": "^4.6.1", "openapi-types": "^9.3.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "openapi-types": { @@ -35002,6 +31648,26 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + } + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -35022,8 +31688,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -35044,7 +31709,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", - "dev": true, "requires": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -35053,8 +31717,7 @@ "lru-cache": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" } } }, @@ -35069,11 +31732,41 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pg": { + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "requires": { + "pg-cloudflare": "^1.1.1", + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" + }, "pg-int8": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" }, + "pg-pool": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "requires": {} + }, "pg-protocol": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", @@ -35091,6 +31784,14 @@ "postgres-interval": "^1.1.0" } }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -35755,32 +32456,6 @@ "strip-indent": "^3.0.0" } }, - "redis": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", - "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", - "requires": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.0", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - } - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "requires": { - "redis-errors": "^1.0.0" - } - }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -36149,11 +32824,19 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -36161,8 +32844,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "shimmer": { "version": "1.2.1", @@ -36303,11 +32985,6 @@ } } }, - "standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, "standard-version": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", @@ -36447,7 +33124,6 @@ "version": "npm:string-width@4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -36510,7 +33186,6 @@ "version": "npm:strip-ansi@6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -36651,6 +33326,22 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "thread-stream": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", @@ -36679,11 +33370,6 @@ "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" }, - "tiny-typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", - "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -36766,7 +33452,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "devOptional": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -36868,11 +33554,164 @@ "integrity": "sha1-nIRAPyMjrlOZFnJ1SXY46h0vJEA=", "dev": true }, + "typeorm": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", + "requires": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.2.1", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "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==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" + }, + "mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==" + }, + "reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + } + } + }, "typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", - "dev": true + "devOptional": true }, "uglify-js": { "version": "3.15.5", @@ -36971,11 +33810,6 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, - "utm-latlng": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/utm-latlng/-/utm-latlng-1.0.8.tgz", - "integrity": "sha512-jQ5l1BNTzNgas7jXOIh0jU0m5PU5LtmjYvRiLwR4iymYYAhEhQ6N0MGCnLjv5iO958b9tLMMSmzdbIIhLtXqCg==" - }, "uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -36985,7 +33819,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "v8-to-istanbul": { "version": "9.1.0", @@ -37100,7 +33934,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -37172,7 +34005,6 @@ "version": "npm:wrap-ansi@7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -37183,7 +34015,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -37192,7 +34023,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -37200,8 +34030,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -37286,7 +34115,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "devOptional": true }, "yocto-queue": { "version": "0.1.0", diff --git a/package.json b/package.json index 81a0a0bd..0d74c749 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "geocoding", - "version": "0.1.0-prealpha.4", + "version": "0.5.0", "description": "Geocoding service for MapColonies", "main": "./src/index.ts", "scripts": { @@ -23,7 +23,11 @@ "assets:copy": "copyfiles -f ./config/* ./dist/config && copyfiles -f ./openapi3.yaml ./dist/ && copyfiles ./package.json dist", "clean": "rimraf dist", "install": "npx husky install", - "dev:scripts": "npx ts-node ./devScripts/index.ts" + "dev:scripts": "npx ts-node ./devScripts/index.ts", + "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js -d dataSource.ts", + "migration:create": "npm run typeorm migration:generate", + "migration:run": "npm run typeorm migration:run", + "migration:revert": "npm run typeorm migration:revert" }, "directories": { "test": "tests" @@ -42,10 +46,8 @@ } }, "dependencies": { - "@aws-sdk/client-s3": "^3.637.0", "@elastic/elasticsearch": "^8.13.1", "@godaddy/terminus": "^4.12.1", - "@map-colonies/cleanup-registry": "^1.1.0", "@map-colonies/error-express-handler": "^2.1.0", "@map-colonies/express-access-log-middleware": "^2.0.1", "@map-colonies/js-logger": "^1.0.1", @@ -56,8 +58,6 @@ "@opentelemetry/api-metrics": "0.23.0", "@opentelemetry/instrumentation-express": "0.32.1", "@opentelemetry/instrumentation-http": "0.35.1", - "@smithy/node-http-handler": "^3.1.4", - "ajv": "^8.17.1", "axios": "^0.21.1", "compression": "^1.7.4", "config": "^3.3.9", @@ -66,15 +66,14 @@ "fast-xml-parser": "^4.4.0", "geojson-validation": "^1.0.2", "http-status-codes": "^2.2.0", - "ioredis": "^5.4.1", "mgrs": "^2.0.0", "node-cron": "^3.0.3", "node-fetch-commonjs": "^3.3.2", + "pg": "^8.11.5", "proj4": "^2.11.0", - "redis": "^4.7.0", "reflect-metadata": "^0.1.13", "tsyringe": "^4.8.0", - "utm-latlng": "^1.0.8", + "typeorm": "^0.3.20", "wellknown": "^0.5.0" }, "devDependencies": { diff --git a/src/app.ts b/src/app.ts index 57da10f1..805d3dfc 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,10 +1,12 @@ import { Application } from 'express'; import { DependencyContainer } from 'tsyringe'; -import { registerExternalValues, RegisterOptions } from './containerConfig'; +import { RegisterOptions, registerExternalValues } from './containerConfig'; import { ServerBuilder } from './serverBuilder'; -export const getApp = async (registerOptions?: RegisterOptions): Promise<{ app: Application; container: DependencyContainer }> => { +async function getApp(registerOptions?: RegisterOptions): Promise<{ app: Application; container: DependencyContainer }> { const container = await registerExternalValues(registerOptions); const app = container.resolve(ServerBuilder).build(); return { app, container }; -}; +} + +export { getApp }; diff --git a/src/common/constants.ts b/src/common/constants.ts index ab5493a1..f5cf88cd 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -14,14 +14,21 @@ export const SERVICES: Record = { METER: Symbol('Meter'), APPLICATION: Symbol('Application'), ELASTIC_CLIENTS: Symbol('ElasticClients'), - REDIS: Symbol('Redis'), - S3_CLIENT: Symbol('S3Client'), - CLEANUP_REGISTRY: Symbol('CleanupRegistry'), }; /* eslint-enable @typescript-eslint/naming-convention */ export const ON_SIGNAL = Symbol('onSignal'); export const HEALTHCHECK = Symbol('healthcheck'); -export const siteConfig = 'application.site'; +export const CONTROL_FIELDS = [ + 'type', + 'geometry', + 'properties.OBJECT_COMMAND_NAME', + 'properties.TILE_NAME', + 'properties.TYPE', + 'properties.ENTITY_HEB', + 'properties.SUB_TILE_ID', + 'properties.SECTION', +]; + export const elasticConfigPath = 'db.elastic'; diff --git a/src/common/elastic/index.ts b/src/common/elastic/index.ts index da80c831..51645492 100644 --- a/src/common/elastic/index.ts +++ b/src/common/elastic/index.ts @@ -19,7 +19,7 @@ const initElasticsearchClient = (clientOptions: ClientOptions): ElasticClient => return client; }; -export const elasticClientsFactory: FactoryFunction = (container: DependencyContainer): ElasticClients => { +export const elasticClientFactory: FactoryFunction = (container: DependencyContainer): ElasticClients => { const config = container.resolve(SERVICES.CONFIG); const logger = container.resolve(SERVICES.LOGGER); diff --git a/src/common/elastic/utils.ts b/src/common/elastic/utils.ts index da798cba..5e7e6278 100644 --- a/src/common/elastic/utils.ts +++ b/src/common/elastic/utils.ts @@ -1,13 +1,24 @@ import { estypes } from '@elastic/elasticsearch'; -import { BBox } from 'geojson'; -import { WGS84Coordinate } from '../interfaces'; -import { ServiceUnavailableError } from '../errors'; -import { ElasticClient } from './index'; +import { IConfig, WGS84Coordinate } from '../interfaces'; +import { InternalServerError } from '../errors'; +import { elasticConfigPath, CONTROL_FIELDS } from '../constants'; +import { ElasticDbClientsConfig } from './interfaces'; +import { ElasticClient, ElasticClients } from './index'; + +export const getElasticClientQuerySize = (config: IConfig, key: keyof ElasticClients): number => { + return config.get(elasticConfigPath)[key].properties.defaultResponseLimit; +}; + +/* eslint-disable @typescript-eslint/naming-convention */ +export const additionalControlSearchProperties = (config: IConfig, size: number): { size: number; index: string; _source: string[] } => ({ + size, + index: config.get(elasticConfigPath).control.properties.index as string, + _source: CONTROL_FIELDS, +}); +/* eslint-enable @typescript-eslint/naming-convention */ export const queryElastic = async (client: ElasticClient, body: estypes.SearchRequest): Promise> => { - const clientNotAvailableError = new ServiceUnavailableError( - 'Elasticsearch client is not available. As for, the search request cannot be executed.' - ); + const clientNotAvailableError = new InternalServerError('Elasticsearch client is not available'); try { if (!(await client.ping())) { throw clientNotAvailableError; @@ -21,7 +32,7 @@ export const queryElastic = async (client: ElasticClient, body: estypes.Searc /* eslint-disable @typescript-eslint/naming-convention */ export const boundingBox = ( - bbox: BBox + bbox: number[] ): { geo_bounding_box: { geometry: { diff --git a/src/common/errors.ts b/src/common/errors.ts index f23ed0cd..c93dc054 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -26,14 +26,6 @@ export class InternalServerError extends Error implements HttpError { } } -export class ServiceUnavailableError extends Error implements HttpError { - public readonly status = httpStatus.SERVICE_UNAVAILABLE; - - public constructor(message: string) { - super(message); - } -} - export class NotFoundError extends Error implements HttpError { public readonly status = httpStatus.NOT_FOUND; @@ -41,19 +33,3 @@ export class NotFoundError extends Error implements HttpError { super(message); } } - -export class NotImplementedError extends Error implements HttpError { - public readonly status = httpStatus.NOT_IMPLEMENTED; - - public constructor(message: string) { - super(message); - } -} - -export class TimeoutError extends Error implements HttpError { - public readonly status = httpStatus.REQUEST_TIMEOUT; - - public constructor(message: string) { - super(message); - } -} diff --git a/src/common/interfaces.ts b/src/common/interfaces.ts index 861f2aec..cadc78ee 100644 --- a/src/common/interfaces.ts +++ b/src/common/interfaces.ts @@ -1,6 +1,6 @@ -import { estypes } from '@elastic/elasticsearch'; -import { BBox, Feature, FeatureCollection as GeoJSONFeatureCollection, GeoJsonProperties } from 'geojson'; -import { RemoveUnderscore } from './utils'; +import { Client, ClientOptions } from '@elastic/elasticsearch'; +import { DataSourceOptions } from 'typeorm'; +import { Feature, FeatureCollection as GeoJSONFeatureCollection } from 'geojson'; export interface IConfig { get: (setting: string) => T; @@ -14,14 +14,21 @@ export interface OpenApiConfig { uiPath: string; } +export type PostgresDbConfig = { + enableSslAuth: boolean; + sslPaths: { ca: string; cert: string; key: string }; +} & DataSourceOptions; + export interface GeoContext { - bbox?: BBox; + bbox?: number[]; radius?: number; lon?: number; lat?: number; - x?: number; - y?: number; - zone?: number; +} + +export interface Geometry { + type: string; + coordinates: number[][][]; } export interface FeatureCollection extends GeoJSONFeatureCollection { @@ -53,58 +60,4 @@ export interface IApplication { }; nameTranslationsKeys: string[]; mainLanguageRegex: string; - controlObjectDisplayNamePrefixes: { - [key: string]: string; - }; -} - -export enum GeoContextMode { - FILTER = 'filter', - BIAS = 'bias', -} - -export interface FeebackApiGeocodingResponse { - userId: string; - apiKey: string; - site: string; - response: JSON; - respondedAt: Date; -} - -/* eslint-disable @typescript-eslint/naming-convention */ -export interface CommonRequestParameters { - geo_context?: GeoContext; - geo_context_mode?: GeoContextMode; - limit: number; - disable_fuzziness: boolean; -} -/* eslint-enable @typescript-eslint/naming-convention */ - -export interface GenericGeocodingResponse extends FeatureCollection { - geocoding: { - version?: string; - query: G & CommonRequestParameters; - response: Pick & { - /* eslint-disable @typescript-eslint/naming-convention */ - results_count?: estypes.SearchHitsMetadata['total']; - match_latency_ms?: estypes.SearchResponse['took']; - /* eslint-enable @typescript-eslint/naming-convention */ - } & { [key: string]: unknown }; - }; - features: (T & { - properties: RemoveUnderscore, '_score'>> & - GeoJsonProperties & { - matches: { - layer: string; - source: string; - // eslint-disable-next-line @typescript-eslint/naming-convention - source_id: string[]; - }[]; - names: { - [key: string]: string | string[] | undefined; - display: string; - default: string[]; - }; - }; - })[]; } diff --git a/src/common/middlewares/feedbackApi.middleware.ts b/src/common/middlewares/feedbackApi.middleware.ts deleted file mode 100644 index c44db663..00000000 --- a/src/common/middlewares/feedbackApi.middleware.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as crypto from 'node:crypto'; -import { Request, Response, NextFunction } from 'express'; -import { Logger } from '@map-colonies/js-logger'; -import { inject, injectable } from 'tsyringe'; -import { SERVICES, siteConfig } from '../constants'; -import { RedisClient } from '../redis'; -import { FeebackApiGeocodingResponse, IConfig } from '../interfaces'; -import { XApi } from './utils'; - -const REDIS_TTL = 300; - -@injectable() -export class FeedbackApiMiddlewareManager { - public constructor( - @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(SERVICES.REDIS) private readonly redis: RedisClient, - @inject(SERVICES.CONFIG) private readonly config: IConfig - ) {} - - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - public saveResponses = (req: Request, res: Response, next: NextFunction) => { - const reqId = res.getHeader(XApi.REQUEST); - const redisClient = this.redis; - const logger = this.logger; - - const drSite = this.config.get(siteConfig); - - logger.info({ msg: 'saving response to redis' }); - const geocodingResponseDetails: FeebackApiGeocodingResponse = { - userId: req.headers[XApi.USER] as string, - apiKey: req.headers[XApi.KEY] as string, - site: drSite, - response: JSON.parse('{}') as JSON, - respondedAt: new Date(), - }; - - const originalJson = res.json; - const logJson = function (this: Response, body: JSON): Response { - geocodingResponseDetails.response = body; - redisClient - .setEx(reqId as string, REDIS_TTL, JSON.stringify(geocodingResponseDetails)) - .then(() => { - logger.info({ msg: `response ${reqId?.toString() ?? ''} saved to redis` }); - }) - .catch((error) => logger.error('Error setting key:', error)); - - return originalJson.call(this, body); - }; - res.json = logJson; - next(); - }; - - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - public setRequestId = (req: Request, res: Response, next: NextFunction) => { - const reqId = crypto.randomUUID(); - res.append(XApi.REQUEST, reqId); - next(); - }; -} diff --git a/src/common/middlewares/utils.ts b/src/common/middlewares/utils.ts deleted file mode 100644 index 31b6288c..00000000 --- a/src/common/middlewares/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum XApi { - REQUEST = 'x-request-id', - USER = 'x-user-id', - KEY = 'x-api-key', -} diff --git a/src/common/postgresql/index.ts b/src/common/postgresql/index.ts new file mode 100644 index 00000000..23f4c835 --- /dev/null +++ b/src/common/postgresql/index.ts @@ -0,0 +1,68 @@ +import { readFileSync } from 'fs'; +import { HealthCheck } from '@godaddy/terminus'; +import { DataSource, DataSourceOptions, QueryFailedError } from 'typeorm'; +import { PostgresDbConfig } from '../interfaces'; +import { LatLon } from '../../latLon/DAL/latLon'; +import { promiseTimeout } from './promiseTimeout'; + +let connectionSingleton: DataSource | undefined; + +const DB_TIMEOUT = 5000; + +enum TransactionFailure { + SERIALIZATION_FAILURE = '40001', + DEADLOCK_DETECTED = '40P01', +} + +interface QueryFailedErrorWithCode extends QueryFailedError { + code: string | undefined; +} + +export enum TransactionName { + TRY_CLOSING_FILE = 'TryClosingFile', + CREATE_RERUN = 'CreateRerun', + TRY_CLOSING_CHANGESET = 'TryClosingChangeset', + TRY_CLOSING_CHANGESETS = 'TryClosingChangesets', +} + +export const isTransactionFailure = (error: unknown): boolean => { + if (error instanceof QueryFailedError) { + const code = (error as QueryFailedErrorWithCode).code; + return code === TransactionFailure.SERIALIZATION_FAILURE || code === TransactionFailure.DEADLOCK_DETECTED; + } + return false; +}; + +export const DB_ENTITIES = [LatLon]; + +export const createConnectionOptions = (dbConfig: PostgresDbConfig): DataSourceOptions => { + const { enableSslAuth, sslPaths, ...connectionOptions } = dbConfig; + if (enableSslAuth && connectionOptions.type === 'postgres') { + connectionOptions.password = undefined; + connectionOptions.ssl = { key: readFileSync(sslPaths.key), cert: readFileSync(sslPaths.cert), ca: readFileSync(sslPaths.ca) }; + } + return { entities: [...DB_ENTITIES, '**/DAL/*.js'], ...connectionOptions }; +}; + +export const initDataSource = async (dbConfig: PostgresDbConfig): Promise => { + if (connectionSingleton === undefined || !connectionSingleton.isInitialized) { + connectionSingleton = new DataSource(createConnectionOptions(dbConfig)); + await connectionSingleton.initialize(); + } + return connectionSingleton; +}; + +export const getDbHealthCheckFunction = (connection: DataSource): HealthCheck => { + return async (): Promise => { + const check = connection.query('SELECT 1').then(() => { + return; + }); + return promiseTimeout(DB_TIMEOUT, check); + }; +}; + +export interface ReturningId { + id: string; +} + +export type ReturningResult = [T[], number]; diff --git a/src/common/postgresql/promiseTimeout.ts b/src/common/postgresql/promiseTimeout.ts new file mode 100644 index 00000000..7f6255c4 --- /dev/null +++ b/src/common/postgresql/promiseTimeout.ts @@ -0,0 +1,14 @@ +export class TimeoutError extends Error {} + +export const promiseTimeout = async (ms: number, promise: Promise): Promise => { + // Create a promise that rejects in milliseconds + const timeout = new Promise((_, reject) => { + const id = setTimeout(() => { + clearTimeout(id); + reject(new TimeoutError(`Timed out in + ${ms} + ms.`)); + }, ms); + }); + + // Returns a race between our timeout and the passed in promise + return Promise.race([promise, timeout]); +}; diff --git a/src/common/projections.ts b/src/common/projections.ts index 71824891..f36a7501 100644 --- a/src/common/projections.ts +++ b/src/common/projections.ts @@ -1,4 +1,3 @@ -//TODO: REMOVE THIS FILE - if proj4 is not in use? export const wgs84Projection = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'; export const utmProjection = (zone: number): string => `+proj=utm +zone=${zone} +ellps=WGS84 +datum=WGS84 +units=m +no_defs`; diff --git a/src/common/redis/index.ts b/src/common/redis/index.ts deleted file mode 100644 index 2642959c..00000000 --- a/src/common/redis/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { readFileSync } from 'fs'; -import { Logger } from '@map-colonies/js-logger'; -import { createClient, RedisClientOptions } from 'redis'; -import { DependencyContainer, FactoryFunction } from 'tsyringe'; -import { SERVICES } from '../constants'; -import { IConfig } from '../interfaces'; -import { RedisConfig } from './interfaces'; - -const DEFAULT_LIMIT_FROM = 0; -const DEFAULT_LIMIT_SIZE = 1000; - -const createConnectionOptions = (redisConfig: RedisConfig): Partial => { - const { host, port, enableSslAuth, sslPaths, ...clientOptions } = redisConfig; - clientOptions.socket = { host, port }; - if (enableSslAuth) { - clientOptions.socket = { - ...clientOptions.socket, - tls: true, - key: sslPaths.key !== '' ? readFileSync(sslPaths.key) : undefined, - cert: sslPaths.cert !== '' ? readFileSync(sslPaths.cert) : undefined, - ca: sslPaths.ca !== '' ? readFileSync(sslPaths.ca) : undefined, - }; - } - - return clientOptions; -}; - -export const CONNECTION_TIMEOUT = 5000; - -export const DEFAULT_LIMIT = { from: DEFAULT_LIMIT_FROM, size: DEFAULT_LIMIT_SIZE }; - -export type RedisClient = ReturnType; - -export const redisClientFactory: FactoryFunction = (container: DependencyContainer): RedisClient | undefined => { - const logger = container.resolve(SERVICES.LOGGER); - const config = container.resolve(SERVICES.CONFIG); - const dbConfig = config.get('db.redis'); - const connectionOptions = createConnectionOptions(dbConfig); - try { - const redisClient = createClient(connectionOptions) - // .on('error', (error: Error) => logger.error({ msg: 'redis client errored', err: error })) - // .on('reconnecting', (...args) => logger.warn({ msg: 'redis client reconnecting', ...args })) - .on('end', (...args) => logger.info({ msg: 'redis client end', ...args })) - .on('connect', (...args) => logger.debug({ msg: 'redis client connected', ...args })) - .on('ready', (...args) => logger.debug({ msg: 'redis client is ready', ...args })); - return redisClient; - } catch (error) { - logger.error('Connection to Redis was unsuccessful', error); - } -}; diff --git a/src/common/redis/interfaces.ts b/src/common/redis/interfaces.ts deleted file mode 100644 index 145a202b..00000000 --- a/src/common/redis/interfaces.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RedisClientOptions } from 'redis'; - -export type RedisConfig = { - host: string; - port: number; - enableSslAuth: boolean; - sslPaths: { ca: string; cert: string; key: string }; -} & RedisClientOptions; diff --git a/src/common/s3/index.ts b/src/common/s3/index.ts deleted file mode 100644 index 4b145662..00000000 --- a/src/common/s3/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as https from 'https'; -import * as http from 'http'; -import { S3Client, S3ClientConfig } from '@aws-sdk/client-s3'; -import { NodeHttpHandler } from '@smithy/node-http-handler'; -import { Logger } from '@map-colonies/js-logger'; -import { DependencyContainer, FactoryFunction } from 'tsyringe'; -import { IConfig } from '../interfaces'; -import { SERVICES } from '../constants'; - -const createConnectionOptions = (clientOptions: S3ClientConfig): S3ClientConfig => ({ - ...clientOptions, - forcePathStyle: true, - requestHandler: new NodeHttpHandler({ - httpsAgent: new https.Agent({ - rejectUnauthorized: false, - }), - httpAgent: new http.Agent(), - }), -}); - -const initS3Client = (clientOptions: S3ClientConfig): S3Client => { - const client = new S3Client(createConnectionOptions(clientOptions)); - return client; -}; - -export const s3ClientFactory: FactoryFunction = (container: DependencyContainer): S3Client => { - const config = container.resolve(SERVICES.CONFIG); - const logger = container.resolve(SERVICES.LOGGER); - - const s3Config = config.get(s3ConfigPath); - - const client = initS3Client(s3Config); - logger.info(`S3 Client is initialized`); - - return client; -}; - -export type S3Config = S3ClientConfig & { bucketName: string } & { - files: { - [key: string]: - | { - bucket: string; - fileName: string; - } - | undefined; - }; -}; -export const s3ConfigPath = 'db.s3'; diff --git a/src/common/s3/s3Repository.ts b/src/common/s3/s3Repository.ts deleted file mode 100644 index 71e24ede..00000000 --- a/src/common/s3/s3Repository.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import fs from 'fs'; -import path from 'path'; -import { Logger } from '@map-colonies/js-logger'; -import { FactoryFunction } from 'tsyringe'; -import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; -import { SERVICES } from '../constants'; -import { IConfig } from '../interfaces'; -import { S3Config, s3ConfigPath } from '.'; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createS3Repository = (s3Client: S3Client, config: IConfig, logger: Logger) => { - return { - async downloadFile(key: keyof S3Config['files']): Promise { - try { - const fileData = config.get(s3ConfigPath).files[key]; - if (!fileData) { - throw new Error(`${key} data is missing in the configuration`); - } - const { bucket: Bucket, fileName: Key } = fileData; - logger.info(`Downloading ${Key} file from S3`); - - const command = new GetObjectCommand({ - Bucket, - Key, - }); - - const { Body } = await s3Client.send(command); - - const filePath = path.join(__dirname, 'downloads', Key); - - try { - await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); - } catch { - //folder already exists - } - - await new Promise((resolve, reject) => { - (Body as NodeJS.ReadableStream) - .pipe(fs.createWriteStream(filePath)) - .on('error', (err: Error) => { - reject(err); - }) - .on('close', () => { - logger.info('table.json file was downloaded successfully'); - resolve(); - }); - }); - - return filePath; - } catch (error) { - logger.error(`Error while downloading ${key}'s data from S3. Error: ${(error as Error).message}`); - throw error; - } - }, - }; -}; - -export const s3RepositoryFactory: FactoryFunction = (depContainer) => { - return createS3Repository( - depContainer.resolve(SERVICES.S3_CLIENT), - depContainer.resolve(SERVICES.CONFIG), - depContainer.resolve(SERVICES.LOGGER) - ); -}; - -export type S3Repository = ReturnType; - -export const S3_REPOSITORY_SYMBOL = Symbol('S3_REPOSITORY_SYMBOL'); diff --git a/src/common/utils.ts b/src/common/utils.ts index 94f9873d..11359d34 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -1,61 +1,32 @@ -import * as Ajv from 'ajv'; -import utm from 'utm-latlng'; -import { BBox, Geometry, Point } from 'geojson'; import { estypes } from '@elastic/elasticsearch'; -import { ListBucketsCommand, S3Client } from '@aws-sdk/client-s3'; -import { DependencyContainer, FactoryFunction } from 'tsyringe'; -import { Logger } from '@map-colonies/js-logger'; -import { ELASTIC_KEYWORDS } from '../control/constants'; -import { TimeoutError } from './errors'; -import { RedisClient } from './redis'; -import { GeoContext, GeoContextMode, WGS84Coordinate } from './interfaces'; -import { SERVICES } from './constants'; +import proj4 from 'proj4'; +import { Item } from '../item/models/item'; +import { Tile } from '../tile/models/tile'; +import { Route } from '../route/models/route'; +import { elasticConfigPath } from './constants'; +import { utmProjection, wgs84Projection } from './projections'; +import { FeatureCollection, IConfig, WGS84Coordinate } from './interfaces'; import { ElasticClients } from './elastic'; -import { BadRequestError } from './errors'; - -type SnakeToCamelCase = S extends `${infer T}_${infer U}` ? `${T}${Capitalize>}` : S; - -type CamelToSnakeCase = S extends `${infer T}${infer U}` - ? U extends Uncapitalize - ? `${Lowercase}${CamelToSnakeCase}` - : `${Lowercase}_${CamelToSnakeCase>}` - : S; - -const ajv = new Ajv.Ajv(); - -const parsePoint = (split: string[] | number[]): Geometry => ({ - type: 'Point', - coordinates: split.map(Number), +import { ElasticDbClientsConfig } from './elastic/interfaces'; + +export const formatResponse = (elasticResponse: estypes.SearchResponse): FeatureCollection => ({ + type: 'FeatureCollection', + features: [ + ...(elasticResponse.hits.hits.map((item) => { + const source = item._source; + if (source?.properties) { + Object.keys(source.properties).forEach((key) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (source.properties !== null && source.properties[key as keyof typeof source.properties] == null) { + delete source.properties[key as keyof typeof source.properties]; + } + }); + } + return source; + }) as T[]), + ], }); -const parseBbox = (split: [string, string, string, string] | BBox): Geometry => { - const [xMin, yMin, xMax, yMax] = split.map(Number); - return { - type: 'Polygon', - coordinates: [ - [ - [xMin, yMin], - [xMin, yMax], - [xMax, yMax], - [xMax, yMin], - [xMin, yMin], - ], - ], - }; -}; - -export type ConvertSnakeToCamelCase = { - [K in keyof T as SnakeToCamelCase]: T[K]; -}; - -export type ConvertCamelToSnakeCase = { - [K in keyof T as CamelToSnakeCase]: T[K]; -}; - -export type RemoveUnderscore = { - [K in keyof T as K extends `_${infer Rest}` ? Rest : K]: T[K]; -}; - export const validateWGS84Coordinate = (coordinate: { lon: number; lat: number }): boolean => { // eslint-disable-next-line @typescript-eslint/no-magic-numbers const [min, max] = [0, 180]; @@ -78,7 +49,8 @@ export const validateWGS84Coordinate = (coordinate: { lon: number; lat: number } /* eslint-disable @typescript-eslint/naming-convention */ export const convertWgs84ToUTM = ( - { longitude, latitude }: { longitude: number; latitude: number }, + latitude: number, + longitude: number, utmPrecision = 0 ): | string @@ -87,197 +59,33 @@ export const convertWgs84ToUTM = ( Northing: number; ZoneNumber: number; } => { - //@ts-expect-error: utm has problem with types. Need to ignore ts error here - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const UTMcoordinates = new utm().convertLatLngToUtm(latitude, longitude, utmPrecision) as { - Easting: number; - Northing: number; - ZoneNumber: number; - ZoneLetter: string; - }; + const zone = Math.floor((longitude + 180) / 6) + 1; + + const [easting, northing] = proj4(wgs84Projection, utmProjection(zone), [longitude, latitude]); - return UTMcoordinates; + return { + Easting: +easting.toFixed(utmPrecision), + Northing: +northing.toFixed(utmPrecision), + ZoneNumber: zone, + }; }; /* eslint-enable @typescript-eslint/naming-convention */ export const convertUTMToWgs84 = (x: number, y: number, zone: number): WGS84Coordinate => { - //TODO: change ZONE Letter to relevent letter. Currently it is hardcoded to 'N' - const { lat, lng: lon } = new utm().convertUtmToLatLng(x, y, zone, 'N') as { lat: number; lng: number }; - return { lat, lon }; -}; - -export const healthCheckFactory: FactoryFunction = (container: DependencyContainer): void => { - const logger = container.resolve(SERVICES.LOGGER); - const elasticClients = container.resolve(SERVICES.ELASTIC_CLIENTS); - const s3Client = container.resolve(SERVICES.S3_CLIENT); - const redis = container.resolve(SERVICES.REDIS); - - for (const [key, client] of Object.entries(elasticClients)) { - client.cluster - .health({}) - .then(() => { - return; - }) - .catch((error) => { - logger.error(`Healthcheck failed for ${key}. Error: ${(error as Error).message}`); - }); - } - - s3Client - .send(new ListBucketsCommand({})) - .then(() => { - return; - }) - .catch((error) => { - logger.error(`Healthcheck failed for S3. Error: ${(error as Error).message}`); - }); - - redis - .ping() - .then(() => { - return; - }) - .catch((error) => { - logger.error(`Healthcheck failed for Redis. Error: ${(error as Error).message}`); - }); -}; - -export const promiseTimeout = async (ms: number, promise: Promise): Promise => { - // create a promise that rejects in milliseconds - const timeout = new Promise((_, reject) => { - const id = setTimeout(() => { - clearTimeout(id); - reject(new TimeoutError(`Timed out in + ${ms} + ms.`)); - }, ms); - }); - - // returns a race between our timeout and the passed in promise - return Promise.race([promise, timeout]); + const [longitude, latitude] = proj4(utmProjection(zone), wgs84Projection, [x, y]); + return { lat: latitude, lon: longitude }; }; -export const parseGeo = (input: GeoContext): Geometry | undefined => { - //TODO: Add geojson validation - //TODO: refactor this function - if (input.bbox !== undefined) { - return parseBbox(input.bbox); - } else if ( - (input.x !== undefined && input.y !== undefined && input.zone !== undefined && input.zone !== undefined) || - (input.lon !== undefined && input.lat !== undefined) - ) { - const { x, y, zone, radius } = input; - const { lon, lat } = x && y && zone ? convertUTMToWgs84(x, y, zone) : (input as Required>); - - return { type: 'Circle', coordinates: (parsePoint([lon, lat]) as Point).coordinates, radius: `${radius ?? ''}` } as unknown as Geometry; +export const validateTile = (tile: { tileName: string; subTileNumber: number[] }): boolean => { + if (!tile.tileName || !Array.isArray(tile.subTileNumber) || tile.subTileNumber.length !== 3) { + return false; } -}; - -export const validateGeoContext = (geoContext: GeoContext): boolean => { - const geoCOntextSchema: Ajv.Schema = { - oneOf: [ - { - type: 'object', - properties: { - bbox: { - oneOf: [ - { - type: 'array', - minItems: 4, - maxItems: 4, - items: { - type: 'number', - }, - }, - { - type: 'array', - minItems: 6, - maxItems: 6, - items: { - type: 'number', - }, - }, - ], - }, - }, - required: ['bbox'], - additionalProperties: false, - }, - { - type: 'object', - properties: { - lat: { - type: 'number', - }, - lon: { - type: 'number', - }, - radius: { - type: 'number', - }, - }, - required: ['lat', 'lon', 'radius'], - additionalProperties: false, - }, - { - type: 'object', - properties: { - x: { - type: 'number', - }, - y: { - type: 'number', - }, - zone: { - type: 'number', - }, - radius: { - type: 'number', - }, - }, - required: ['x', 'y', 'zone', 'radius'], - additionalProperties: false, - }, - ], - }; - - const validate = ajv.compile(geoCOntextSchema); - const isValid = validate(geoContext); - const messagePrefix = 'geo_context validation: '; - - if (!isValid) { - throw new BadRequestError( - messagePrefix + - 'geo_context must contain one of the following: {"bbox": [number,number,number,number] | [number,number,number,number,number,number]}, {"lat": number, "lon": number, "radius": number}, or {"x": number, "y": number, "zone": number, "radius": number}' - ); + //regex = /^-?d+$/; + const regex = /^(\d\d)$/; + for (const subTileNumber of tile.subTileNumber) { + if (!regex.test(`${subTileNumber}`)) { + return false; + } } - return true; }; - -export const geoContextQuery = ( - geoContext?: GeoContext, - geoContextMode?: GeoContextMode, - elasticGeoShapeField = ELASTIC_KEYWORDS.geometry -): { [key in 'filter' | 'should']?: estypes.QueryDslQueryContainer[] } => { - if (geoContext === undefined && geoContextMode === undefined) { - return {}; - } - if ((geoContext !== undefined && geoContextMode === undefined) || (geoContext === undefined && geoContextMode !== undefined)) { - throw new BadRequestError('/control/utils/geoContextQuery: geo_context and geo_context_mode must be both defined or both undefined'); - } - - validateGeoContext(geoContext!); - - return { - [geoContextMode === GeoContextMode.FILTER ? 'filter' : 'should']: [ - { - // eslint-disable-next-line @typescript-eslint/naming-convention - geo_shape: { - [elasticGeoShapeField]: { - shape: parseGeo(geoContext!), - }, - boost: geoContextMode === GeoContextMode.BIAS ? 1.1 : 1, //TODO: change magic number - }, - }, - ], - }; -}; diff --git a/src/containerConfig.ts b/src/containerConfig.ts index a365a4b5..e3b4d6e5 100644 --- a/src/containerConfig.ts +++ b/src/containerConfig.ts @@ -1,33 +1,28 @@ import config from 'config'; import { getOtelMixin } from '@map-colonies/telemetry'; -import { ListBucketsCommand, S3Client } from '@aws-sdk/client-s3'; +import { DataSource } from 'typeorm'; import { instancePerContainerCachingFactory } from 'tsyringe'; import { trace, metrics as OtelMetrics } from '@opentelemetry/api'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; import { DependencyContainer } from 'tsyringe/dist/typings/types'; import jsLogger, { LoggerOptions } from '@map-colonies/js-logger'; import { Metrics } from '@map-colonies/telemetry'; -import { ScheduledTask } from 'node-cron'; -import { HEALTHCHECK, ON_SIGNAL, SERVICES, SERVICE_NAME } from './common/constants'; +import { SERVICES, SERVICE_NAME } from './common/constants'; import { tracing } from './common/tracing'; import { InjectionObject, registerDependencies } from './common/dependencyRegistration'; -import { elasticClientsFactory, ElasticClients } from './common/elastic'; -import { IApplication } from './common/interfaces'; -import { TILE_REPOSITORY_SYMBOL, tileRepositoryFactory } from './control/tile/DAL/tileRepository'; -import { TILE_ROUTER_SYMBOL, tileRouterFactory } from './control/tile/routes/tileRouter'; -import { ITEM_ROUTER_SYMBOL, itemRouterFactory } from './control/item/routes/itemRouter'; -import { ROUTE_REPOSITORY_SYMBOL, routeRepositoryFactory } from './control/route/DAL/routeRepository'; -import { ROUTE_ROUTER_SYMBOL, routeRouterFactory } from './control/route/routes/routeRouter'; +import { elasticClientFactory, ElasticClients } from './common/elastic'; +import { IApplication, PostgresDbConfig } from './common/interfaces'; +import { TILE_REPOSITORY_SYMBOL, tileRepositoryFactory } from './tile/DAL/tileRepository'; +import { TILE_ROUTER_SYMBOL, tileRouterFactory } from './tile/routes/tileRouter'; +import { ITEM_REPOSITORY_SYMBOL, itemRepositoryFactory } from './item/DAL/itemRepository'; +import { ITEM_ROUTER_SYMBOL, itemRouterFactory } from './item/routes/itemRouter'; +import { ROUTE_REPOSITORY_SYMBOL, routeRepositoryFactory } from './route/DAL/routeRepository'; +import { ROUTE_ROUTER_SYMBOL, routeRouterFactory } from './route/routes/routeRouter'; +import { initDataSource } from './common/postgresql'; +import { LATLON_CUSTOM_REPOSITORY_SYMBOL, latLonRepositoryFactory } from './latLon/DAL/latLonRepository'; import { LAT_LON_ROUTER_SYMBOL, latLonRouterFactory } from './latLon/routes/latLonRouter'; -import { GEOTEXT_REPOSITORY_SYMBOL, geotextRepositoryFactory } from './location/DAL/locationRepository'; -import { GEOTEXT_SEARCH_ROUTER_SYMBOL, geotextSearchRouterFactory } from './location/routes/locationRouter'; -import { cronLoadTileLatLonDataFactory, cronLoadTileLatLonDataSymbol, latLonDalSymbol, latLonSignletonFactory } from './latLon/DAL/latLonDAL'; -import { ITEM_REPOSITORY_SYMBOL, itemRepositoryFactory } from './control/item/DAL/itemRepository'; -import { RedisClient, redisClientFactory } from './common/redis'; -import { s3ClientFactory } from './common/s3'; -import { S3_REPOSITORY_SYMBOL, s3RepositoryFactory } from './common/s3/s3Repository'; -import { healthCheckFactory } from './common/utils'; -import { MGRS_ROUTER_SYMBOL, mgrsRouterFactory } from './mgrs/routers/mgrsRouter'; +import { GEOTEXT_REPOSITORY_SYMBOL, geotextRepositoryFactory } from './geotextSearch/DAL/geotextSearchRepository'; +import { GEOTEXT_SEARCH_ROUTER_SYMBOL, geotextSearchRouterFactory } from './geotextSearch/routes/geotextSearchRouter'; +import { cronLoadTileLatLonDataFactory, cronLoadTileLatLonDataSymbol } from './latLon/DAL/latLonDAL'; export interface RegisterOptions { override?: InjectionObject[]; @@ -35,139 +30,72 @@ export interface RegisterOptions { } export const registerExternalValues = async (options?: RegisterOptions): Promise => { - const cleanupRegistry = new CleanupRegistry(); - try { - const loggerConfig = config.get('telemetry.logger'); - const logger = jsLogger({ ...loggerConfig, prettyPrint: loggerConfig.prettyPrint, mixin: getOtelMixin() }); - const cleanupRegistryLogger = logger.child({ subComponent: 'cleanupRegistry' }); + const loggerConfig = config.get('telemetry.logger'); + const logger = jsLogger({ ...loggerConfig, prettyPrint: loggerConfig.prettyPrint, mixin: getOtelMixin() }); - cleanupRegistry.on('itemFailed', (id, error, msg) => cleanupRegistryLogger.error({ msg, itemId: id, err: error })); - cleanupRegistry.on('finished', (status) => cleanupRegistryLogger.info({ msg: `cleanup registry finished cleanup`, status })); + const metrics = new Metrics(); + metrics.start(); - const metrics = new Metrics(); - cleanupRegistry.register({ func: metrics.stop.bind(metrics), id: SERVICES.METER }); - metrics.start(); + tracing.start(); + const tracer = trace.getTracer(SERVICE_NAME); - cleanupRegistry.register({ func: tracing.stop.bind(tracing), id: SERVICES.TRACER }); - tracing.start(); - const tracer = trace.getTracer(SERVICE_NAME); + const applicationConfig: IApplication = config.get('application'); - const applicationConfig: IApplication = config.get('application'); + const postgresqlDataSourceOptions = config.get('db.postgresql'); - const dependencies: InjectionObject[] = [ - { token: SERVICES.CONFIG, provider: { useValue: config } }, - { token: SERVICES.LOGGER, provider: { useValue: logger } }, - { token: SERVICES.TRACER, provider: { useValue: tracer } }, - { token: SERVICES.METER, provider: { useValue: OtelMetrics.getMeterProvider().getMeter(SERVICE_NAME) } }, - { token: SERVICES.APPLICATION, provider: { useValue: applicationConfig } }, - { token: HEALTHCHECK, provider: { useFactory: healthCheckFactory } }, - { - token: ON_SIGNAL, - provider: { - useFactory: instancePerContainerCachingFactory((container) => { - const cleanupRegistry = container.resolve(SERVICES.CLEANUP_REGISTRY); - return cleanupRegistry.trigger.bind(cleanupRegistry); - }), - }, - }, - { - token: SERVICES.ELASTIC_CLIENTS, - provider: { useFactory: instancePerContainerCachingFactory(elasticClientsFactory) }, - postInjectionHook: async (deps: DependencyContainer): Promise => { - const elasticClients = deps.resolve(SERVICES.ELASTIC_CLIENTS); - try { - const response = await Promise.all([elasticClients.control?.ping(), elasticClients.geotext?.ping()]); - response.forEach((res) => { - if (!res) { - logger.error('Failed to connect to Elasticsearch', res); - } - }); - cleanupRegistry.register({ - func: async () => { - await elasticClients.control.close(); - await elasticClients.geotext.close(); - }, - id: SERVICES.ELASTIC_CLIENTS, - }); - } catch (err) { - logger.error('Failed to connect to Elasticsearch', err); - } - }, - }, - { - token: SERVICES.S3_CLIENT, - provider: { useFactory: s3ClientFactory }, - postInjectionHook: async (deps: DependencyContainer): Promise => { - const s3Client = deps.resolve(SERVICES.S3_CLIENT); - try { - await s3Client.send(new ListBucketsCommand({})); - logger.info('Connected to S3'); - } catch (err) { - logger.error('Failed to connect to S3', err); - } - cleanupRegistry.register({ - func: async () => { - s3Client.destroy(); - return Promise.resolve(); - }, - id: SERVICES.S3_CLIENT, + const postgresqlConnection = await initDataSource(postgresqlDataSourceOptions); + + const dependencies: InjectionObject[] = [ + { token: SERVICES.CONFIG, provider: { useValue: config } }, + { token: SERVICES.LOGGER, provider: { useValue: logger } }, + { token: SERVICES.TRACER, provider: { useValue: tracer } }, + { token: SERVICES.METER, provider: { useValue: OtelMetrics.getMeterProvider().getMeter(SERVICE_NAME) } }, + { token: SERVICES.APPLICATION, provider: { useValue: applicationConfig } }, + { + token: SERVICES.ELASTIC_CLIENTS, + provider: { useFactory: instancePerContainerCachingFactory(elasticClientFactory) }, + postInjectionHook: async (deps: DependencyContainer): Promise => { + const elasticClients = deps.resolve(SERVICES.ELASTIC_CLIENTS); + try { + const response = await Promise.all([elasticClients.control?.ping(), elasticClients.geotext?.ping()]); + response.forEach((res) => { + if (!res) { + logger.error('Failed to connect to Elasticsearch', res); + } }); - }, + } catch (err) { + logger.error('Failed to connect to Elasticsearch', err); + } }, - { - token: S3_REPOSITORY_SYMBOL, - provider: { useFactory: s3RepositoryFactory }, - }, - { - token: SERVICES.CLEANUP_REGISTRY, - provider: { useValue: cleanupRegistry }, - }, - { token: TILE_REPOSITORY_SYMBOL, provider: { useFactory: tileRepositoryFactory } }, - { token: TILE_ROUTER_SYMBOL, provider: { useFactory: tileRouterFactory } }, - { token: ITEM_REPOSITORY_SYMBOL, provider: { useFactory: itemRepositoryFactory } }, - { token: ITEM_ROUTER_SYMBOL, provider: { useFactory: itemRouterFactory } }, - { token: ROUTE_REPOSITORY_SYMBOL, provider: { useFactory: routeRepositoryFactory } }, - { token: ROUTE_ROUTER_SYMBOL, provider: { useFactory: routeRouterFactory } }, - { token: LAT_LON_ROUTER_SYMBOL, provider: { useFactory: latLonRouterFactory } }, - { token: GEOTEXT_REPOSITORY_SYMBOL, provider: { useFactory: geotextRepositoryFactory } }, - { token: GEOTEXT_SEARCH_ROUTER_SYMBOL, provider: { useFactory: geotextSearchRouterFactory } }, - { token: MGRS_ROUTER_SYMBOL, provider: { useFactory: mgrsRouterFactory } }, - { token: latLonDalSymbol, provider: { useFactory: latLonSignletonFactory } }, - { - token: cronLoadTileLatLonDataSymbol, - provider: { - useFactory: cronLoadTileLatLonDataFactory, - }, - postInjectionHook: async (deps: DependencyContainer): Promise => { - const cronLoadTileLatLonData = deps.resolve(cronLoadTileLatLonDataSymbol); - cronLoadTileLatLonData.start(); - cleanupRegistry.register({ - func: async () => { - cronLoadTileLatLonData.stop(); - return Promise.resolve(); - }, - id: cronLoadTileLatLonDataSymbol, - }); - }, + }, + { token: DataSource, provider: { useValue: postgresqlConnection } }, + { token: TILE_REPOSITORY_SYMBOL, provider: { useFactory: tileRepositoryFactory } }, + { token: TILE_ROUTER_SYMBOL, provider: { useFactory: tileRouterFactory } }, + { token: ITEM_REPOSITORY_SYMBOL, provider: { useFactory: itemRepositoryFactory } }, + { token: ITEM_ROUTER_SYMBOL, provider: { useFactory: itemRouterFactory } }, + { token: ROUTE_REPOSITORY_SYMBOL, provider: { useFactory: routeRepositoryFactory } }, + { token: ROUTE_ROUTER_SYMBOL, provider: { useFactory: routeRouterFactory } }, + { token: LATLON_CUSTOM_REPOSITORY_SYMBOL, provider: { useFactory: latLonRepositoryFactory } }, + { token: LAT_LON_ROUTER_SYMBOL, provider: { useFactory: latLonRouterFactory } }, + { token: GEOTEXT_REPOSITORY_SYMBOL, provider: { useFactory: geotextRepositoryFactory } }, + { token: GEOTEXT_SEARCH_ROUTER_SYMBOL, provider: { useFactory: geotextSearchRouterFactory } }, + { + token: cronLoadTileLatLonDataSymbol, + provider: { + useFactory: cronLoadTileLatLonDataFactory, }, - { - token: SERVICES.REDIS, - provider: { useFactory: instancePerContainerCachingFactory(redisClientFactory) }, - postInjectionHook: async (deps: DependencyContainer): Promise => { - try { - const redis = deps.resolve(SERVICES.REDIS); - cleanupRegistry.register({ func: redis.disconnect.bind(redis), id: SERVICES.REDIS }); - await redis.connect(); - } catch (error) { - logger.error('Connection to redis failed', error); - } + }, + { + token: 'onSignal', + provider: { + useValue: { + useValue: async (): Promise => { + await Promise.all([tracing.stop(), metrics.stop()]); + }, }, }, - ]; - const container = await registerDependencies(dependencies, options?.override, options?.useChild); - return container; - } catch (error) { - await cleanupRegistry.trigger(); - throw error; - } + }, + ]; + const container = await registerDependencies(dependencies, options?.override, options?.useChild); + return container; }; diff --git a/src/control/constants.ts b/src/control/constants.ts deleted file mode 100644 index 620d816e..00000000 --- a/src/control/constants.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const ELASTIC_KEYWORDS = { - type: 'properties.TYPE.keyword', - tileName: 'properties.TILE_NAME.keyword', - subTileId: 'properties.SUB_TILE_ID.keyword', - geometry: 'geometry', - objectCommandName: 'properties.OBJECT_COMMAND_NAME.keyword', - layerName: 'properties.LAYER_NAME.keyword', - tiedTo: 'properties.TIED_TO', -}; - -export const CONTROL_FIELDS = [ - 'type', - 'geometry', - 'properties.OBJECT_COMMAND_NAME', - 'properties.TILE_NAME', - 'properties.TYPE', - 'properties.ENTITY_HEB', - 'properties.SUB_TILE_ID', - 'properties.SECTION', - 'properties.TIED_TO', - 'properties.LAYER_NAME', -]; diff --git a/src/control/item/DAL/queries.ts b/src/control/item/DAL/queries.ts deleted file mode 100644 index 9e612b70..00000000 --- a/src/control/item/DAL/queries.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { estypes } from '@elastic/elasticsearch'; -import { CommonRequestParameters } from '../../../common/interfaces'; -import { ELASTIC_KEYWORDS } from '../../constants'; -import { ConvertSnakeToCamelCase, geoContextQuery } from '../../../common/utils'; - -export interface ItemQueryParams extends ConvertSnakeToCamelCase { - commandName: string; - tile?: string; - subTile?: string; -} -/* eslint-disable @typescript-eslint/naming-convention */ -export const queryForItems = ({ - commandName, - tile, - subTile, - geoContext, - geoContextMode, - disableFuzziness, -}: ItemQueryParams): estypes.SearchRequest => ({ - query: { - bool: { - must: [ - { - term: { - [ELASTIC_KEYWORDS.type]: 'ITEM', - }, - }, - { - match: { - [ELASTIC_KEYWORDS.objectCommandName]: { - query: commandName, - fuzziness: disableFuzziness ? undefined : 1, - prefix_length: 1, - }, - }, - }, - ...(tile ?? '' - ? [ - { - term: { - [ELASTIC_KEYWORDS.tileName]: tile, - }, - }, - ] - : []), - ...(subTile ?? '' - ? [ - { - term: { - [ELASTIC_KEYWORDS.subTileId]: subTile, - }, - }, - ] - : []), - ], - ...geoContextQuery(geoContext, geoContextMode), - }, - }, -}); diff --git a/src/control/item/models/item.ts b/src/control/item/models/item.ts deleted file mode 100644 index 5b57db3a..00000000 --- a/src/control/item/models/item.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Feature, GeoJsonProperties } from 'geojson'; - -export interface Item extends Feature { - properties: GeoJsonProperties & { - TYPE: 'ITEM'; - OBJECT_COMMAND_NAME: string; - LAYER_NAME: string; - TIED_TO?: string; - }; -} diff --git a/src/control/item/models/itemManager.ts b/src/control/item/models/itemManager.ts deleted file mode 100644 index b751e8a9..00000000 --- a/src/control/item/models/itemManager.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { IConfig } from 'config'; -import { Logger } from '@map-colonies/js-logger'; -import { inject, injectable } from 'tsyringe'; -import { estypes } from '@elastic/elasticsearch'; -import { SERVICES } from '../../../common/constants'; -import { ITEM_REPOSITORY_SYMBOL, ItemRepository } from '../DAL/itemRepository'; -import { ItemQueryParams } from '../DAL/queries'; -import { formatResponse } from '../../utils'; -import { FeatureCollection, IApplication } from '../../../common/interfaces'; -import { Item } from './item'; - -@injectable() -export class ItemManager { - public constructor( - @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(SERVICES.CONFIG) private readonly config: IConfig, - @inject(SERVICES.APPLICATION) private readonly application: IApplication, - @inject(ITEM_REPOSITORY_SYMBOL) private readonly itemRepository: ItemRepository - ) {} - - public async getItems(itemQueryParams: ItemQueryParams): Promise> { - const { limit } = itemQueryParams; - let elasticResponse: estypes.SearchResponse | undefined = undefined; - elasticResponse = await this.itemRepository.getItems(itemQueryParams, limit); - - return formatResponse(elasticResponse, itemQueryParams, this.application.controlObjectDisplayNamePrefixes); - } -} diff --git a/src/control/route/DAL/queries.ts b/src/control/route/DAL/queries.ts deleted file mode 100644 index b278ee23..00000000 --- a/src/control/route/DAL/queries.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { estypes } from '@elastic/elasticsearch'; -import { CommonRequestParameters } from '../../../common/interfaces'; -import { ELASTIC_KEYWORDS } from '../../constants'; -import { ConvertSnakeToCamelCase, geoContextQuery } from '../../../common/utils'; - -export interface RouteQueryParams extends ConvertSnakeToCamelCase { - commandName: string; - controlPoint?: string; -} - -/* eslint-disable @typescript-eslint/naming-convention */ -export const queryForRoute = ({ geoContext, geoContextMode, commandName, disableFuzziness }: RouteQueryParams): estypes.SearchRequest => ({ - query: { - bool: { - must: [ - { - term: { - [ELASTIC_KEYWORDS.type]: 'ROUTE', - }, - }, - { - match: { - [ELASTIC_KEYWORDS.objectCommandName]: { - query: commandName, - fuzziness: disableFuzziness ? undefined : 1, - prefix_length: 1, - }, - }, - }, - ], - ...geoContextQuery(geoContext, geoContextMode), - }, - }, -}); - -export const queryForControlPointInRoute = ({ - controlPoint, - commandName, - geoContext, - geoContextMode, - disableFuzziness, -}: RouteQueryParams & Required>): estypes.SearchRequest => { - const geoContextOperation = geoContextQuery(geoContext, geoContextMode); - - const esQuery: estypes.SearchRequest = { - query: { - bool: { - must: [ - { - match: { - [ELASTIC_KEYWORDS.objectCommandName]: { - query: controlPoint, - fuzziness: disableFuzziness ? undefined : 1, - prefix_length: 1, - }, - }, - }, - ], - filter: [ - { - terms: { - [ELASTIC_KEYWORDS.layerName]: ['CONTROL_GIL_GDB.CTR_CONTROL_POINT_N', 'CONTROL_GIL_GDB.CTR_CONTROL_POINT_CROSS_N'], - }, - }, - { - term: { - [ELASTIC_KEYWORDS.tiedTo]: commandName, - }, - }, - ...(geoContextOperation.filter ?? []), - ], - should: geoContextOperation.should ?? [], - }, - }, - }; - - return esQuery; -}; diff --git a/src/control/route/models/route.ts b/src/control/route/models/route.ts deleted file mode 100644 index 607fcd12..00000000 --- a/src/control/route/models/route.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Feature, GeoJsonProperties } from 'geojson'; - -export interface Route extends Feature { - properties: GeoJsonProperties & { - TYPE: 'ROUTE'; - OBJECT_COMMAND_NAME: string; - ENTITY_HEB: string; - LAYER_NAME: string; - }; -} diff --git a/src/control/route/models/routeManager.ts b/src/control/route/models/routeManager.ts deleted file mode 100644 index ccc5934f..00000000 --- a/src/control/route/models/routeManager.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { IConfig } from 'config'; -import { Logger } from '@map-colonies/js-logger'; -import { inject, injectable } from 'tsyringe'; -import { estypes } from '@elastic/elasticsearch'; -import { SERVICES } from '../../../common/constants'; -import { ROUTE_REPOSITORY_SYMBOL, RouteRepository } from '../DAL/routeRepository'; -import { RouteQueryParams } from '../DAL/queries'; -import { formatResponse } from '../../utils'; -import { FeatureCollection, IApplication } from '../../../common/interfaces'; -import { Route } from './route'; - -@injectable() -export class RouteManager { - public constructor( - @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(SERVICES.CONFIG) private readonly config: IConfig, - @inject(SERVICES.APPLICATION) private readonly application: IApplication, - @inject(ROUTE_REPOSITORY_SYMBOL) private readonly routeRepository: RouteRepository - ) {} - - public async getRoutes(routeQueryParams: RouteQueryParams): Promise> { - const { limit } = routeQueryParams; - - let elasticResponse: estypes.SearchResponse | undefined = undefined; - if (routeQueryParams.controlPoint ?? '') { - elasticResponse = await this.routeRepository.getControlPointInRoute( - routeQueryParams as RouteQueryParams & Required>, - limit - ); - } else { - elasticResponse = await this.routeRepository.getRoutes(routeQueryParams, limit); - } - - return formatResponse(elasticResponse, routeQueryParams, this.application.controlObjectDisplayNamePrefixes); - } -} diff --git a/src/control/tile/DAL/queries.ts b/src/control/tile/DAL/queries.ts deleted file mode 100644 index 35dadf7a..00000000 --- a/src/control/tile/DAL/queries.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { estypes } from '@elastic/elasticsearch'; -import { BBox } from 'geojson'; -import { CommonRequestParameters } from '../../../common/interfaces'; -import { ELASTIC_KEYWORDS } from '../../constants'; -import { ConvertSnakeToCamelCase, geoContextQuery, parseGeo } from '../../../common/utils'; - -export interface TileQueryParams extends ConvertSnakeToCamelCase { - tile?: string; - mgrs?: string; - subTile?: string; -} - -export const queryForTiles = ({ - tile, - geoContext, - geoContextMode, - disableFuzziness, -}: Omit & Required>): estypes.SearchRequest => ({ - query: { - bool: { - must: [ - { - term: { - [ELASTIC_KEYWORDS.type]: 'TILE', - }, - }, - { - match: { - [ELASTIC_KEYWORDS.tileName]: { - query: tile, - fuzziness: disableFuzziness ? undefined : 1, - // eslint-disable-next-line @typescript-eslint/naming-convention - prefix_length: 1, - }, - }, - }, - ], - ...geoContextQuery(geoContext, geoContextMode), - }, - }, -}); - -export const queryForSubTiles = ({ - tile, - geoContext, - geoContextMode, - subTile, - disableFuzziness, -}: Omit, 'mgrs'>): estypes.SearchRequest => ({ - query: { - bool: { - must: [ - { - term: { - [ELASTIC_KEYWORDS.type]: 'SUB_TILE', - }, - }, - { - term: { - [ELASTIC_KEYWORDS.tileName]: tile, - }, - }, - { - match: { - [ELASTIC_KEYWORDS.subTileId]: { - query: subTile, - fuzziness: disableFuzziness ? undefined : 1, - // eslint-disable-next-line @typescript-eslint/naming-convention - prefix_length: 1, - }, - }, - }, - ], - ...geoContextQuery(geoContext, geoContextMode), - }, - }, -}); - -export const queryForTilesByBbox = ({ - bbox, - geoContext, - geoContextMode, -}: { bbox: BBox } & ConvertSnakeToCamelCase): estypes.SearchRequest => { - const { filter: geoCOntextQueryFilter, should } = geoContextQuery(geoContext, geoContextMode); - - const query: estypes.SearchRequest = { - query: { - bool: { - must: [ - { - term: { - ['properties.TYPE.keyword']: 'TILE', - }, - }, - ], - filter: [ - { - // eslint-disable-next-line @typescript-eslint/naming-convention - geo_shape: { - [ELASTIC_KEYWORDS.geometry]: { - shape: parseGeo({ bbox }), - relation: 'intersects', - }, - }, - }, - ...(geoCOntextQueryFilter ?? []), - ], - should, - }, - }, - }; - return query; -}; diff --git a/src/control/tile/DAL/tileRepository.ts b/src/control/tile/DAL/tileRepository.ts deleted file mode 100644 index 336818e4..00000000 --- a/src/control/tile/DAL/tileRepository.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { estypes } from '@elastic/elasticsearch'; -import { FactoryFunction } from 'tsyringe'; -import { BBox } from 'geojson'; -import { ElasticClient, ElasticClients } from '../../../common/elastic'; -import { Tile } from '../models/tile'; -import { queryElastic } from '../../../common/elastic/utils'; -import { IConfig } from '../../../common/interfaces'; -import { SERVICES } from '../../../common/constants'; -import { additionalControlSearchProperties } from '../../utils'; -import { queryForTiles, queryForSubTiles, TileQueryParams, queryForTilesByBbox } from './queries'; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createTileRepository = (client: ElasticClient, config: IConfig) => { - return { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async getTiles(tileQueryParams: TileQueryParams & Required>): Promise> { - const response = await queryElastic(client, { - ...additionalControlSearchProperties(config, tileQueryParams.limit), - ...queryForTiles(tileQueryParams), - }); - - return response; - }, - - async getSubTiles(tileQueryParams: Required): Promise> { - const response = await queryElastic(client, { - ...additionalControlSearchProperties(config, tileQueryParams.limit), - ...queryForSubTiles(tileQueryParams), - }); - - return response; - }, - async getTilesByBbox(searchParams: { bbox: BBox } & Omit): Promise> { - const response = await queryElastic(client, { - ...additionalControlSearchProperties(config, searchParams.limit), - ...queryForTilesByBbox(searchParams), - }); - return response; - }, - }; -}; - -export type TileRepository = ReturnType; - -export const tileRepositoryFactory: FactoryFunction = (depContainer) => { - return createTileRepository(depContainer.resolve(SERVICES.ELASTIC_CLIENTS).control, depContainer.resolve(SERVICES.CONFIG)); -}; - -export const TILE_REPOSITORY_SYMBOL = Symbol('TILE_REPOSITORY_SYMBOL'); diff --git a/src/control/tile/models/tile.ts b/src/control/tile/models/tile.ts deleted file mode 100644 index 5b58d885..00000000 --- a/src/control/tile/models/tile.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Feature, GeoJsonProperties } from 'geojson'; - -export interface Tile extends Feature { - properties: GeoJsonProperties & { - TYPE: 'TILE' | 'SUB_TILE'; - TILE_NAME?: string; - SUB_TILE_ID?: string; - LAYER_NAME?: string; - }; -} diff --git a/src/control/tile/models/tileManager.ts b/src/control/tile/models/tileManager.ts deleted file mode 100644 index 0825d120..00000000 --- a/src/control/tile/models/tileManager.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { IConfig } from 'config'; -import { Logger } from '@map-colonies/js-logger'; -import { inject, injectable } from 'tsyringe'; -import { estypes } from '@elastic/elasticsearch'; -import * as mgrs from 'mgrs'; -import { BBox } from 'geojson'; -import { SERVICES } from '../../../common/constants'; -import { TILE_REPOSITORY_SYMBOL, TileRepository } from '../DAL/tileRepository'; -import { formatResponse } from '../../utils'; -import { TileQueryParams } from '../DAL/queries'; -import { FeatureCollection, IApplication } from '../../../common/interfaces'; -import { BadRequestError } from '../../../common/errors'; -import { Tile } from './tile'; - -@injectable() -export class TileManager { - public constructor( - @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(SERVICES.CONFIG) private readonly config: IConfig, - @inject(SERVICES.APPLICATION) private readonly application: IApplication, - @inject(TILE_REPOSITORY_SYMBOL) private readonly tileRepository: TileRepository - ) {} - - public async getTiles(tileQueryParams: TileQueryParams): Promise> { - if ( - (tileQueryParams.tile === undefined && tileQueryParams.mgrs === undefined) || - (tileQueryParams.tile !== undefined && tileQueryParams.mgrs !== undefined) - ) { - throw new BadRequestError("/control/tiles: only one of 'tile' or 'mgrs' query parameter must be defined"); - } - - let elasticResponse: estypes.SearchResponse | undefined = undefined; - - if (tileQueryParams.mgrs !== undefined) { - let bbox: BBox = [0, 0, 0, 0]; - try { - bbox = mgrs.inverse(tileQueryParams.mgrs); - bbox.forEach((coord) => { - if (isNaN(coord)) { - throw new Error('Invalid MGRS'); - } - }); - } catch (error) { - throw new BadRequestError(`Invalid MGRS: ${tileQueryParams.mgrs}`); - } - elasticResponse = await this.tileRepository.getTilesByBbox({ bbox, ...tileQueryParams }); - } else if (tileQueryParams.subTile ?? '') { - elasticResponse = await this.tileRepository.getSubTiles(tileQueryParams as Required); - } else { - elasticResponse = await this.tileRepository.getTiles(tileQueryParams as TileQueryParams & Required>); - } - - return formatResponse(elasticResponse, tileQueryParams, this.application.controlObjectDisplayNamePrefixes); - } -} diff --git a/src/control/utils.ts b/src/control/utils.ts deleted file mode 100644 index cabace51..00000000 --- a/src/control/utils.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions */ -/* eslint-disable @typescript-eslint/naming-convention */ -import { estypes } from '@elastic/elasticsearch'; -import { CommonRequestParameters, GenericGeocodingResponse, IApplication, IConfig } from '../common/interfaces'; -import { elasticConfigPath } from '../common/constants'; -import { ElasticDbClientsConfig } from '../common/elastic/interfaces'; -import { Item } from '../control/item/models/item'; -import { Tile } from '../control/tile/models/tile'; -import { Route } from '../control/route/models/route'; -import { ConvertSnakeToCamelCase } from '../common/utils'; -import { CONTROL_FIELDS } from './constants'; - -const LAST_ELEMENT_INDEX = -1; - -const generateDisplayName = ( - source: T, - displayNamePrefixes: IApplication['controlObjectDisplayNamePrefixes'] -): (string | undefined)[] => { - const sourceType = - source.properties.TYPE === 'ITEM' && (source as Item).properties.TIED_TO !== undefined ? 'CONTROL_POINT' : source.properties.TYPE; - - const name: (string | undefined)[] = []; - sourceType === 'TILE' && name.unshift((source as Tile).properties.TILE_NAME); - sourceType === 'SUB_TILE' && name.unshift((source as Tile).properties.SUB_TILE_ID); - sourceType === 'ITEM' && name.unshift((source as Item).properties.OBJECT_COMMAND_NAME); - sourceType === 'ROUTE' && name.unshift((source as Route).properties.OBJECT_COMMAND_NAME); - sourceType === 'CONTROL_POINT' && name.unshift((source as Item).properties.OBJECT_COMMAND_NAME); - - name.unshift(displayNamePrefixes[sourceType]); - - if (sourceType === 'SUB_TILE') { - name.unshift((source as Tile).properties.TILE_NAME); - name.unshift(displayNamePrefixes['TILE']); - } - - if (sourceType === 'CONTROL_POINT') { - name.unshift((source as Item).properties.TIED_TO); - name.unshift(displayNamePrefixes['ROUTE']); - } - - return name; -}; - -export const convertCamelToSnakeCase = (obj: Record): Record => { - const snakeCaseObj: Record = {}; - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - const snakeCaseKey = key.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`); - snakeCaseObj[snakeCaseKey] = obj[key]; - } - } - return snakeCaseObj; -}; - -export const formatResponse = ( - elasticResponse: estypes.SearchResponse, - requestParams: CommonRequestParameters | ConvertSnakeToCamelCase, - displayNamePrefixes: IApplication['controlObjectDisplayNamePrefixes'] -): GenericGeocodingResponse => ({ - type: 'FeatureCollection', - geocoding: { - version: process.env.npm_package_version, - query: convertCamelToSnakeCase(requestParams as Record), - response: { - results_count: elasticResponse.hits.hits.length, - max_score: elasticResponse.hits.max_score ?? 0, - match_latency_ms: elasticResponse.took, - }, - }, - features: [ - ...(elasticResponse.hits.hits.map(({ _source: source, _score: score, _index: index }) => { - if (source !== undefined) { - const generatedDisplayName = generateDisplayName(source, displayNamePrefixes); - return { - ...source, - properties: { - ...source.properties, - matches: [ - { - layer: source.properties.LAYER_NAME, - source: index, - source_id: [], - }, - ], - names: { - default: [generatedDisplayName.at(LAST_ELEMENT_INDEX)], - display: generatedDisplayName.join(' '), - }, - score, - }, - }; - } - }) as GenericGeocodingResponse['features']), - ], -}); - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const additionalControlSearchProperties = (config: IConfig, size: number): Pick => ({ - size, - index: config.get(elasticConfigPath).control.properties.index as string, - // eslint-disable-next-line @typescript-eslint/naming-convention - _source: CONTROL_FIELDS, -}); diff --git a/src/location/DAL/locationRepository.ts b/src/geotextSearch/DAL/geotextSearchRepository.ts similarity index 89% rename from src/location/DAL/locationRepository.ts rename to src/geotextSearch/DAL/geotextSearchRepository.ts index 72354959..540b0f1a 100644 --- a/src/location/DAL/locationRepository.ts +++ b/src/geotextSearch/DAL/geotextSearchRepository.ts @@ -23,7 +23,7 @@ const createGeotextRepository = (client: ElasticClient, logger: Logger) => { const { tokens, prediction } = response[0]; - if (!tokens || !tokens.length || !prediction || !prediction.length) { + if (!tokens || !prediction) { logger.error('No tokens or prediction'); throw new BadRequestError('No tokens or prediction'); } @@ -33,10 +33,10 @@ const createGeotextRepository = (client: ElasticClient, logger: Logger) => { return nameTokens.join(' '); }, - async generatePlacetype(index: string, query: string, disableFuzziness: boolean): Promise<{ placeTypes: string[]; subPlaceTypes: string[] }> { + async generatePlacetype(index: string, query: string): Promise<{ placeTypes: string[]; subPlaceTypes: string[] }> { const { hits: { hits }, - } = await queryElastic(client, { index, ...placetypeQuery(query, disableFuzziness) }); + } = await queryElastic(client, { index, ...placetypeQuery(query) }); if (hits.length == 2 && hits[0]._score! - hits[1]._score! > 0.5) { hits.pop(); @@ -50,10 +50,10 @@ const createGeotextRepository = (client: ElasticClient, logger: Logger) => { return { placeTypes, subPlaceTypes }; }, - async extractHierarchy(index: string, query: string, hierarchyBoost: number, disableFuzziness: boolean): Promise { + async extractHierarchy(index: string, query: string, hierarchyBoost: number): Promise { const { hits: { hits }, - } = await queryElastic(client, { index, ...hierarchyQuery(query, disableFuzziness) }); + } = await queryElastic(client, { index, ...hierarchyQuery(query) }); const filteredHits = hits.length > 3 ? hits.filter((hit) => hit._score! >= hits[2]._score!) : hits; diff --git a/src/location/DAL/queries.ts b/src/geotextSearch/DAL/queries.ts similarity index 66% rename from src/location/DAL/queries.ts rename to src/geotextSearch/DAL/queries.ts index 4d41b827..f700acc5 100644 --- a/src/location/DAL/queries.ts +++ b/src/geotextSearch/DAL/queries.ts @@ -1,12 +1,9 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions */ /* eslint-disable @typescript-eslint/naming-convention */ import WKT, { GeoJSONPolygon } from 'wellknown'; import { estypes } from '@elastic/elasticsearch'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { TextSearchParams } from '../interfaces'; -import { GeoContextMode, IApplication } from '../../common/interfaces'; -import { BadRequestError } from '../../common/errors'; -import { geoContextQuery } from '../../common/utils'; +import { IApplication } from '../../common/interfaces'; const TEXT_FIELD = 'text'; const PLACETYPE_FIELD = 'placetype.keyword'; @@ -18,26 +15,10 @@ const HIERARCHY_FIELD = 'heirarchy'; const PLACETYPE_SEARCH_FIELD = 'sub_placetype_keyword'; export const geotextQuery = ( - { - query, - limit: size, - name, - placeTypes, - subPlaceTypes, - hierarchies, - region, - source, - geoContext, - geoContextMode, - disableFuzziness, - }: TextSearchParams, + { query, limit: size, name, placeTypes, subPlaceTypes, hierarchies, regions, viewbox, boundary, sources }: TextSearchParams, textLanguage: string, boosts: IApplication['elasticQueryBoosts'] ): estypes.SearchRequest => { - if ((geoContext !== undefined && geoContextMode === undefined) || (geoContext === undefined && geoContextMode !== undefined)) { - throw new BadRequestError('/location/geotextQuery: geo_context and geo_context_mode must be both defined or both undefined'); - } - const esQuery: estypes.SearchRequest = { query: { function_score: { @@ -56,19 +37,6 @@ export const geotextQuery = ( size, }; - if (geoContext && geoContextMode) { - const geoContextQueryFilter = geoContextQuery(geoContext, GeoContextMode.FILTER, GEOJSON_FIELD).filter![0]; - - if (geoContextMode === GeoContextMode.FILTER) { - (esQuery.query?.function_score?.query?.bool?.filter as QueryDslQueryContainer[]).push(geoContextQueryFilter); - } else { - esQuery.query?.function_score?.functions?.push({ - weight: boosts.viewbox, - filter: geoContextQueryFilter, - }); - } - } - if (!name && subPlaceTypes?.length) { (esQuery.query?.function_score?.query?.bool?.must as QueryDslQueryContainer[]).push( ...[ @@ -89,23 +57,32 @@ export const geotextQuery = ( match: { [TEXT_FIELD]: { query, - fuzziness: disableFuzziness ? undefined : 'AUTO:3,4', + fuzziness: 'AUTO:3,4', }, }, }); } - source?.length && + boundary && + (esQuery.query?.function_score?.query?.bool?.filter as QueryDslQueryContainer[]).push({ + geo_shape: { + [GEOJSON_FIELD]: { + shape: boundary, + }, + }, + }); + + sources?.length && (esQuery.query?.function_score?.query?.bool?.filter as QueryDslQueryContainer[]).push({ terms: { - [SOURCE_FIELD]: source.map((s) => s.toLowerCase()), + [SOURCE_FIELD]: sources, }, }); - region?.length && + regions?.length && (esQuery.query?.function_score?.query?.bool?.filter as QueryDslQueryContainer[]).push({ terms: { - [REGION_FIELD]: region.map((r) => r.toLowerCase()), + [REGION_FIELD]: regions, }, }); @@ -139,6 +116,18 @@ export const geotextQuery = ( }, }); + viewbox && + esQuery.query?.function_score?.functions?.push({ + weight: boosts.viewbox, + filter: { + geo_shape: { + [GEOJSON_FIELD]: { + shape: viewbox, + }, + }, + }, + }); + hierarchies.forEach((hierarchy) => { const hierarchyShape = typeof hierarchy.geo_json === 'string' ? WKT.parse(hierarchy.geo_json) : hierarchy.geo_json; esQuery.query?.function_score?.functions?.push({ @@ -161,14 +150,14 @@ export const geotextQuery = ( return esQuery; }; -export const placetypeQuery = (query: string, disableFuzziness: boolean): estypes.SearchRequest => ({ +export const placetypeQuery = (query: string): estypes.SearchRequest => ({ query: { bool: { should: { match: { [PLACETYPE_SEARCH_FIELD]: { query, - fuzziness: disableFuzziness ? undefined : 'AUTO:3,4', + fuzziness: 'AUTO:3,4', }, }, }, @@ -177,7 +166,7 @@ export const placetypeQuery = (query: string, disableFuzziness: boolean): estype size: 2, }); -export const hierarchyQuery = (query: string, disableFuzziness: boolean): estypes.SearchRequest => ({ +export const hierarchyQuery = (query: string): estypes.SearchRequest => ({ query: { function_score: { functions: [ @@ -187,7 +176,7 @@ export const hierarchyQuery = (query: string, disableFuzziness: boolean): estype match: { [HIERARCHY_FIELD]: { query, - fuzziness: disableFuzziness ? undefined : 'AUTO:3,4', + fuzziness: 'AUTO:3,4', }, }, }, @@ -199,7 +188,7 @@ export const hierarchyQuery = (query: string, disableFuzziness: boolean): estype match: { text: { query, - fuzziness: disableFuzziness ? undefined : 'AUTO:3,4', + fuzziness: 'AUTO:3,4', }, }, }, diff --git a/src/location/controllers/locationController.ts b/src/geotextSearch/controllers/geotextSearchController.ts similarity index 57% rename from src/location/controllers/locationController.ts rename to src/geotextSearch/controllers/geotextSearchController.ts index 491c06b5..f8b776d7 100644 --- a/src/location/controllers/locationController.ts +++ b/src/geotextSearch/controllers/geotextSearchController.ts @@ -2,24 +2,20 @@ import { Logger } from '@map-colonies/js-logger'; import { BoundCounter, Meter } from '@opentelemetry/api-metrics'; import { RequestHandler } from 'express'; import httpStatus from 'http-status-codes'; -import { Feature } from 'geojson'; import { injectable, inject } from 'tsyringe'; import { SERVICES } from '../../common/constants'; -import { GeotextSearchManager } from '../models/locationManager'; -import { GetGeotextSearchParams } from '../interfaces'; -import { GenericGeocodingResponse } from '../../common/interfaces'; +import { GeotextSearchManager } from '../models/geotextSearchManager'; +import { GetGeotextSearchParams, QueryResult } from '../interfaces'; type GetGeotextSearchHandler = RequestHandler< unknown, - GenericGeocodingResponse | { message: string; error: string }, //response + QueryResult | { message: string; error: string }, //response undefined, GetGeotextSearchParams >; type GetRegionshHandler = RequestHandler; -type GetSourcesHandler = RequestHandler; - @injectable() export class GeotextSearchController { private readonly createdResourceCounter: BoundCounter; @@ -33,18 +29,8 @@ export class GeotextSearchController { } public getGeotextSearch: GetGeotextSearchHandler = async (req, res, next) => { - const { - disable_fuzziness: disableFuzziness, - geo_context: geoContext, - geo_context_mode: geoContextMode, - query, - region, - source, - limit, - } = req.query; - try { - const response = await this.manager.search({ query, region, source, disableFuzziness, geoContext, geoContextMode, limit }); + const response = await this.manager.search(req.query); return res.status(httpStatus.OK).json(response); } catch (error: unknown) { this.logger.error('Error in getGeotextSearch', error); @@ -52,11 +38,7 @@ export class GeotextSearchController { } }; - public getRegions: GetRegionshHandler = (req, res) => { + public getRegions: GetRegionshHandler = (req, res, next) => { return res.status(httpStatus.OK).json(this.manager.regions()); }; - - public getSources: GetSourcesHandler = (req, res) => { - return res.status(httpStatus.OK).json(this.manager.sources()); - }; } diff --git a/src/geotextSearch/interfaces.ts b/src/geotextSearch/interfaces.ts new file mode 100644 index 00000000..b57fc493 --- /dev/null +++ b/src/geotextSearch/interfaces.ts @@ -0,0 +1,66 @@ +import type { GeoJSON } from 'geojson'; +import { HierarchySearchHit } from './models/elasticsearchHits'; + +export interface PlaceType { + placetype: string; + confidence: number; +} + +export interface TokenResponse { + tokens: string[]; + prediction: string[]; +} + +export interface TextSearchParams { + query: string; + viewbox?: GeoJSON; + boundary?: GeoJSON; + sources?: string[]; + regions?: string[]; + name?: string; + placeTypes?: string[]; + subPlaceTypes?: string[]; + hierarchies: HierarchySearchHit[]; + limit: number; +} + +export interface GetGeotextSearchParams { + query: string; + limit: number; + source?: string[]; + viewbox?: string; + boundary?: string; + region?: string[]; +} + +/* eslint-disable @typescript-eslint/naming-convention */ + +export interface QueryResult { + type: string; + geocoding: { version?: string; query: TextSearchParams; name?: string }; + features: { + type: string; + geometry?: GeoJSON; + properties: { + rank: number; + source?: string; + source_id?: string[]; + layer?: string; + name: { + [key: string]: string | string[] | undefined; + }; + highlight?: Record; + placetype?: string; + sub_placetype?: string; + region?: string[]; + sub_region?: string[]; + regions?: { region: string; sub_regions: string[] }[]; + }; + }[]; +} +/* eslint-enable @typescript-eslint/naming-convention */ + +//Defenitions +export const POINT_LENGTH = 2; +export const BBOX_LENGTH = 4; +export const INDEX_NOT_FOUND = -1; diff --git a/src/location/models/elasticsearchHits.ts b/src/geotextSearch/models/elasticsearchHits.ts similarity index 100% rename from src/location/models/elasticsearchHits.ts rename to src/geotextSearch/models/elasticsearchHits.ts diff --git a/src/location/models/locationManager.ts b/src/geotextSearch/models/geotextSearchManager.ts similarity index 74% rename from src/location/models/locationManager.ts rename to src/geotextSearch/models/geotextSearchManager.ts index 867656a7..1626406e 100644 --- a/src/location/models/locationManager.ts +++ b/src/geotextSearch/models/geotextSearchManager.ts @@ -1,14 +1,12 @@ import { IConfig } from 'config'; import { Logger } from '@map-colonies/js-logger'; import { inject, injectable } from 'tsyringe'; -import { Feature } from 'geojson'; import { SERVICES, elasticConfigPath } from '../../common/constants'; -import { GEOTEXT_REPOSITORY_SYMBOL, GeotextRepository } from '../DAL/locationRepository'; -import { GetGeotextSearchParams, TextSearchParams } from '../interfaces'; -import { convertResult } from '../utils'; -import { GenericGeocodingResponse, IApplication } from '../../common/interfaces'; +import { GEOTEXT_REPOSITORY_SYMBOL, GeotextRepository } from '../DAL/geotextSearchRepository'; +import { GetGeotextSearchParams, QueryResult, TextSearchParams } from '../interfaces'; +import { convertResult, parseGeo } from '../utils'; +import { IApplication } from '../../common/interfaces'; import { ElasticDbClientsConfig } from '../../common/elastic/interfaces'; -import { ConvertSnakeToCamelCase } from '../../common/utils'; @injectable() export class GeotextSearchManager { @@ -19,7 +17,7 @@ export class GeotextSearchManager { @inject(GEOTEXT_REPOSITORY_SYMBOL) private readonly geotextRepository: GeotextRepository ) {} - public async search(params: ConvertSnakeToCamelCase): Promise> { + public async search(params: GetGeotextSearchParams): Promise { const extractNameEndpoint = this.appConfig.services.tokenTypesUrl; const { geotext: geotextIndex, @@ -34,18 +32,23 @@ export class GeotextSearchManager { const promises = Promise.all([ this.geotextRepository.extractName(extractNameEndpoint, query), - this.geotextRepository.generatePlacetype(placetypesIndex, query, params.disableFuzziness), - this.geotextRepository.extractHierarchy(hierarchiesIndex, hierarchyQuery.join(','), hierarchyBoost, params.disableFuzziness), + this.geotextRepository.generatePlacetype(placetypesIndex, query), + this.geotextRepository.extractHierarchy(hierarchiesIndex, hierarchyQuery.join(','), hierarchyBoost), ]); const [name, { placeTypes, subPlaceTypes }, hierarchies] = await promises; const searchParams: TextSearchParams = { - ...params, + query, + limit: params.limit, + sources: params.source ? (params.source instanceof Array ? params.source : [params.source]) : undefined, + viewbox: params.viewbox ? parseGeo(params.viewbox) : undefined, + boundary: params.boundary ? parseGeo(params.boundary) : undefined, name, placeTypes, subPlaceTypes, hierarchies, + regions: params.region, }; const esResult = await this.geotextRepository.geotextSearch( @@ -55,7 +58,7 @@ export class GeotextSearchManager { this.appConfig.elasticQueryBoosts ); - return convertResult(searchParams, esResult, { + return convertResult(searchParams, esResult.hits.hits, { sources: this.appConfig.sources, regionCollection: this.appConfig.regions, nameKeys: this.appConfig.nameTranslationsKeys, @@ -66,8 +69,4 @@ export class GeotextSearchManager { public regions(): string[] { return Object.keys(this.appConfig.regions ?? {}); } - - public sources(): string[] { - return Object.keys(this.appConfig.sources ?? {}); - } } diff --git a/src/location/parsing.ts b/src/geotextSearch/parsing.ts similarity index 55% rename from src/location/parsing.ts rename to src/geotextSearch/parsing.ts index 99657aa9..29172a94 100644 --- a/src/location/parsing.ts +++ b/src/geotextSearch/parsing.ts @@ -1,17 +1,18 @@ -//This file might be deprecated in the future -/* istanbul ignore file */ -import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import { XMLParser } from 'fast-xml-parser'; +type Highlight = { + em: string | string[]; + '#text': string; +}; const HIERARCHY_OF_INTEREST = 3; const HIGHLIGHT_XML_REGEX = /|<\/em>/gi; -const untagHighlight = (highlight: string): string => highlight.replace(HIGHLIGHT_XML_REGEX, ''); +const untagHighlight = (highlight: string) => highlight.replace(HIGHLIGHT_XML_REGEX, ''); -const calculateHighlightQuality = (highlight: string, queryWordCount: number): number => { +const calculateHighlightQuality = (highlight: string, queryWordCount: number) => { const parser = new XMLParser({ numberParseOptions: { skipLike: /[0-9]+/, hex: false, leadingZeros: false } }); - const parsed = parser.parse(highlight) as NonNullable; + const parsed = parser.parse(highlight) as Highlight; if (!(parsed.em instanceof Array)) { parsed.em = [parsed.em]; @@ -31,18 +32,18 @@ const compareQualityThenLength = ( highlight: string; quality: number; } -): number => a.quality - b.quality || a.highlight.length - b.highlight.length; +) => a.quality - b.quality || a.highlight.length - b.highlight.length; -export const generateDisplayName = ({ text: highlights }: NonNullable, queryWordCount: number, name?: string): string => { +export const generateDisplayName = (highlights: string[], queryWordCount: number, name?: string) => { const scored = highlights.map((highlight) => ({ highlight, quality: calculateHighlightQuality(highlight, queryWordCount), })); - const filtered = scored.filter(name !== undefined ? ({ highlight }): boolean => highlight.includes(name) : ({ quality }): boolean => quality < 1); + const filtered = scored.filter(name ? ({ highlight }) => highlight.includes(name) : ({ quality }) => quality < 1); - const chosen = (filtered.length ? filtered : scored).sort(compareQualityThenLength).pop()!.highlight; + const chosen = (filtered.length ? filtered : scored).sort(compareQualityThenLength).pop()?.highlight; - return untagHighlight(chosen); + return chosen && untagHighlight(chosen); }; export const getHierarchyOfInterest = (hierarchy: string): string => hierarchy.split('/')[HIERARCHY_OF_INTEREST]; diff --git a/src/location/routes/locationRouter.ts b/src/geotextSearch/routes/geotextSearchRouter.ts similarity index 72% rename from src/location/routes/locationRouter.ts rename to src/geotextSearch/routes/geotextSearchRouter.ts index 430e42fe..2c72e165 100644 --- a/src/location/routes/locationRouter.ts +++ b/src/geotextSearch/routes/geotextSearchRouter.ts @@ -1,14 +1,13 @@ import { Router } from 'express'; import { FactoryFunction } from 'tsyringe'; -import { GeotextSearchController } from '../controllers/locationController'; +import { GeotextSearchController } from '../controllers/geotextSearchController'; const geotextSearchRouterFactory: FactoryFunction = (dependencyContainer) => { const router = Router(); const controller = dependencyContainer.resolve(GeotextSearchController); router.get('/regions', controller.getRegions); - router.get('/query', controller.getGeotextSearch); - router.get('/sources', controller.getSources); + router.get('/', controller.getGeotextSearch); return router; }; diff --git a/src/geotextSearch/utils.ts b/src/geotextSearch/utils.ts new file mode 100644 index 00000000..7324f961 --- /dev/null +++ b/src/geotextSearch/utils.ts @@ -0,0 +1,139 @@ +// import fetch, { Response } from "node-fetch-commonjs"; +import { GeoJSON } from 'geojson'; +import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; +import { StatusCodes } from 'http-status-codes'; +import axios, { AxiosResponse as Response } from 'axios'; +import { InternalServerError } from '../common/errors'; +import { IApplication } from '../common/interfaces'; +import { BBOX_LENGTH, POINT_LENGTH, QueryResult, TextSearchParams } from './interfaces'; +import { generateDisplayName } from './parsing'; +import { TextSearchHit } from './models/elasticsearchHits'; + +const FIND_QUOTES = /["']/g; + +const FIND_SPECIAL = /[`!@#$%^&*()_\-+=|\\/,.<>:[\]{}\n\t\r\s;؛]+/g; + +export const fetchNLPService = async (endpoint: string, requestData: object): Promise => { + let res: Response | null = null, + data: T[] | undefined | null = null; + try { + res = await axios.post(endpoint, requestData); + } catch (err: unknown) { + if (axios.isAxiosError(err)) { + throw new InternalServerError(`NLP analyser is not available - ${err.message}`); + } + throw new InternalServerError('fetchNLPService: Unknown error' + JSON.stringify(err)); + } + + try { + // data = (await res.json()) as T[] | undefined; + data = res?.data as T[] | undefined; + } catch (_) { + throw new InternalServerError("Couldn't convert the response from NLP service to JSON"); + } + + if (res?.status !== StatusCodes.OK || !data || data.length < 1 || !data[0]) { + throw new InternalServerError(JSON.stringify(data)); + } + return data; +}; + +export const cleanQuery = (query: string): string[] => query.replace(FIND_QUOTES, '').split(FIND_SPECIAL); + +export const parsePoint = (split: string[]): GeoJSON => ({ + type: 'Point', + coordinates: split.map(Number), +}); + +export const parseBbox = (split: string[]): GeoJSON => { + const [xMin, yMin, xMax, yMax] = split.map(Number); + return { + type: 'Polygon', + coordinates: [ + [ + [xMin, yMin], + [xMin, yMax], + [xMax, yMax], + [xMax, yMin], + [xMin, yMin], + ], + ], + }; +}; + +export const parseGeo = (input: string | GeoJSON): GeoJSON | undefined => { + if (typeof input === 'string') { + const splitted = input.split(','); + const converted = splitted.map(Number); + + if (converted.findIndex((x) => isNaN(x)) < 0) { + switch (splitted.length) { + case POINT_LENGTH: + // Point + return parsePoint(splitted); + case BBOX_LENGTH: + //BBOX + return parseBbox(splitted); + default: + return undefined; + } + } + } + + return input as GeoJSON; +}; + +/* eslint-disable @typescript-eslint/naming-convention */ +export const convertResult = ( + params: TextSearchParams, + results: SearchHit[], + { + sources, + regionCollection, + nameKeys, + mainLanguageRegex, + }: { + sources?: IApplication['sources']; + regionCollection?: IApplication['regions']; + nameKeys: IApplication['nameTranslationsKeys']; + mainLanguageRegex: IApplication['mainLanguageRegex']; + } = { nameKeys: [], mainLanguageRegex: '' } +): QueryResult => ({ + type: 'FeatureCollection', + geocoding: { + version: process.env.npm_package_version, + query: { + ...params, + }, + name: params.name, + }, + features: results.map(({ highlight, _source: feature }, index): QueryResult['features'][number] => { + const allNames = [feature!.text, feature!.translated_text || []]; + return { + type: 'Feature', + geometry: feature?.geo_json, + properties: { + rank: index + 1, + source: (sources ?? {})[feature?.source ?? ''] ?? feature?.source, + layer: feature?.layer_name, + source_id: feature?.source_id.map((id) => id.replace(/(^\{)|(\}$)/g, '')), // TODO: check if to remove this + name: { + [nameKeys[0]]: new RegExp(mainLanguageRegex).test(feature!.text[0]) ? allNames.shift() : allNames.pop(), + [nameKeys[1]]: allNames.pop(), + ['default']: [feature!.name], + display: highlight ? generateDisplayName(highlight.text, params.query!.split(' ').length, params.name) : feature!.name, + }, + highlight, + placetype: feature?.placetype, // TODO: check if to remove this + sub_placetype: feature?.sub_placetype, + regions: feature?.region.map((region) => ({ + region: region, + sub_regions: feature.sub_region.filter((sub_region) => (regionCollection ?? {})[region ?? '']?.includes(sub_region)), + })), + region: feature?.region, + sub_region: feature?.sub_region, + }, + }; + }), +}); +/* eslint-enable @typescript-eslint/naming-convention */ diff --git a/src/index.ts b/src/index.ts index a2f0f74b..4ee6fbac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,8 +21,8 @@ void getApp() const logger = container.resolve(SERVICES.LOGGER); const server = createTerminus(createServer(app), { // eslint-disable-next-line @typescript-eslint/naming-convention - healthChecks: { '/liveness': container.resolve(HEALTHCHECK) }, - onSignal: container.resolve(ON_SIGNAL), + // healthChecks: { '/liveness': container.resolve(HEALTHCHECK) }, + // onSignal: container.resolve(ON_SIGNAL), }); server.listen(port, () => { diff --git a/src/control/item/DAL/itemRepository.ts b/src/item/DAL/itemRepository.ts similarity index 78% rename from src/control/item/DAL/itemRepository.ts rename to src/item/DAL/itemRepository.ts index b7070433..b7df1f41 100644 --- a/src/control/item/DAL/itemRepository.ts +++ b/src/item/DAL/itemRepository.ts @@ -1,15 +1,16 @@ import { Logger } from '@map-colonies/js-logger'; import { estypes } from '@elastic/elasticsearch'; import { FactoryFunction } from 'tsyringe'; -import { IConfig } from '../../../common/interfaces'; -import { ElasticClients } from '../../../common/elastic'; -import { queryElastic } from '../../../common/elastic/utils'; -import { ElasticClient } from '../../../common/elastic'; +import { IConfig } from '../../common/interfaces'; +import { ElasticClients } from '../../common/elastic'; +import { additionalControlSearchProperties, queryElastic } from '../../common/elastic/utils'; +import { ElasticClient } from '../../common/elastic'; import { Item } from '../models/item'; -import { SERVICES } from '../../../common/constants'; -import { additionalControlSearchProperties } from '../../utils'; +import { SERVICES } from '../../common/constants'; import { ItemQueryParams, queryForItems } from './queries'; +/* eslint-enable @typescript-eslint/naming-convention */ + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const createItemRepository = (client: ElasticClient, config: IConfig, logger: Logger) => { return { diff --git a/src/item/DAL/queries.ts b/src/item/DAL/queries.ts new file mode 100644 index 00000000..ec81de64 --- /dev/null +++ b/src/item/DAL/queries.ts @@ -0,0 +1,66 @@ +import { estypes } from '@elastic/elasticsearch'; +import { boundingBox, geoDistance } from '../../common/elastic/utils'; +import { GeoContext } from '../../common/interfaces'; + +export interface ItemQueryParams { + commandName: string; + tile?: string; + subTile?: number; + geo?: GeoContext; +} + +/* eslint-disable @typescript-eslint/naming-convention */ +export const queryForItems = (params: ItemQueryParams): estypes.SearchRequest => ({ + query: { + bool: { + should: [ + { + term: { + 'properties.TYPE.keyword': 'ITEM', + }, + }, + ], + must: [ + { + match: { + 'properties.OBJECT_COMMAND_NAME.keyword': { + query: params.commandName, + fuzziness: 1, + prefix_length: 1, + }, + }, + }, + ...(params.tile ?? '' + ? [ + { + term: { + 'properties.TILE_NAME.keyword': params.tile, + }, + }, + ] + : []), + ...(params.subTile ?? 0 + ? [ + { + term: { + 'properties.SUB_TILE_ID.keyword': params.subTile, + }, + }, + ] + : []), + ...(params.geo?.bbox ? [boundingBox(params.geo.bbox)] : []), + ...((params.geo?.radius ?? 0) && (params.geo?.lat ?? 0) && (params.geo?.lon ?? 0) + ? [ + geoDistance( + params.geo as { + radius: number; + lon: number; + lat: number; + } + ), + ] + : []), + ], + }, + }, +}); diff --git a/src/control/item/controllers/itemController.ts b/src/item/controllers/itemController.ts similarity index 67% rename from src/control/item/controllers/itemController.ts rename to src/item/controllers/itemController.ts index 1d76bd7a..1a8f5776 100644 --- a/src/control/item/controllers/itemController.ts +++ b/src/item/controllers/itemController.ts @@ -4,17 +4,20 @@ import { BoundCounter, Meter } from '@opentelemetry/api-metrics'; import { RequestHandler } from 'express'; import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; -import { SERVICES } from '../../../common/constants'; +import { SERVICES } from '../../common/constants'; import { ItemManager } from '../models/itemManager'; import { Item } from '../models/item'; -import { CommonRequestParameters, FeatureCollection } from '../../../common/interfaces'; +import { FeatureCollection, GeoContext } from '../../common/interfaces'; type GetItemsHandler = RequestHandler, undefined, GetItemsQueryParams>; -export interface GetItemsQueryParams extends CommonRequestParameters { +export interface GetItemsQueryParams { command_name: string; tile?: string; sub_tile?: string; + geo_context?: string; + reduce_fuzzy_match?: string; + size?: string; } @injectable() @@ -31,16 +34,17 @@ export class ItemController { public getItems: GetItemsHandler = async (req, res, next) => { try { - const { command_name: commandName, tile, sub_tile, geo_context, geo_context_mode, disable_fuzziness, limit } = req.query; - const response = await this.manager.getItems({ - tile, - subTile: sub_tile, - commandName, - geoContext: geo_context, - geoContextMode: geo_context_mode, - limit, - disableFuzziness: disable_fuzziness, - }); + const { command_name: commandName, tile, sub_tile, geo_context, reduce_fuzzy_match, size } = req.query; + const response = await this.manager.getItems( + { + commandName, + tile, + subTile: sub_tile ? parseInt(sub_tile) : undefined, + geo: geo_context ? (JSON.parse(geo_context) as GeoContext) : undefined, + }, + reduce_fuzzy_match == 'true', + size ? parseInt(size) : undefined + ); return res.status(httpStatus.OK).json(response); } catch (error: unknown) { this.logger.warn('itemController.getItems Error:', error); diff --git a/src/item/models/item.ts b/src/item/models/item.ts new file mode 100644 index 00000000..3869c449 --- /dev/null +++ b/src/item/models/item.ts @@ -0,0 +1,3 @@ +import { Feature } from 'geojson'; + +export interface Item extends Feature {} diff --git a/src/item/models/itemManager.ts b/src/item/models/itemManager.ts new file mode 100644 index 00000000..8c17dbac --- /dev/null +++ b/src/item/models/itemManager.ts @@ -0,0 +1,34 @@ +import { IConfig } from 'config'; +import { Logger } from '@map-colonies/js-logger'; +import { inject, injectable } from 'tsyringe'; +import { estypes } from '@elastic/elasticsearch'; +import { SERVICES } from '../../common/constants'; +import { ITEM_REPOSITORY_SYMBOL, ItemRepository } from '../DAL/itemRepository'; +import { ItemQueryParams } from '../DAL/queries'; +import { formatResponse } from '../../common/utils'; +import { FeatureCollection } from '../../common/interfaces'; +import { getElasticClientQuerySize } from '../../common/elastic/utils'; +import { Item } from './item'; + +@injectable() +export class ItemManager { + public constructor( + @inject(SERVICES.LOGGER) private readonly logger: Logger, + @inject(SERVICES.CONFIG) private readonly config: IConfig, + @inject(ITEM_REPOSITORY_SYMBOL) private readonly itemRepository: ItemRepository + ) {} + + public async getItems(itemQueryParams: ItemQueryParams, reduceFuzzyMatch = false, size?: number): Promise> { + let elasticResponse: estypes.SearchResponse | undefined = undefined; + elasticResponse = await this.itemRepository.getItems(itemQueryParams, size ?? getElasticClientQuerySize(this.config, 'control')); + + const formattedResponse = formatResponse(elasticResponse); + + if (reduceFuzzyMatch && formattedResponse.features.length > 0) { + const filterFunction = (hit: Item | undefined): hit is Item => hit?.properties?.OBJECT_COMMAND_NAME === itemQueryParams.commandName; + formattedResponse.features = formattedResponse.features.filter(filterFunction); + } + + return formattedResponse; + } +} diff --git a/src/control/item/routes/itemRouter.ts b/src/item/routes/itemRouter.ts similarity index 100% rename from src/control/item/routes/itemRouter.ts rename to src/item/routes/itemRouter.ts diff --git a/src/latLon/DAL/latLon.ts b/src/latLon/DAL/latLon.ts new file mode 100644 index 00000000..7f057e7b --- /dev/null +++ b/src/latLon/DAL/latLon.ts @@ -0,0 +1,32 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { LatLon as ILatLon } from '../models/latLon'; + +@Entity({ name: 'tile_lat_lon' }) +export class LatLon implements ILatLon { + @PrimaryColumn({ name: 'pk' }) + public primaryKey!: number; + + @Column({ name: 'tile_name' }) + public tileName!: string; + + @Column({ name: 'zone' }) + public zone!: string; + + @Column({ name: 'min_x' }) + public minX!: string; + + @Column({ name: 'min_y' }) + public minY!: string; + + @Column({ name: 'ext_min_x' }) + public extMinX!: number; + + @Column({ name: 'ext_min_y' }) + public extMinY!: number; + + @Column({ name: 'ext_max_x' }) + public extMaxX!: number; + + @Column({ name: 'ext_max_y' }) + public extMaxY!: number; +} diff --git a/src/latLon/DAL/latLonDAL.ts b/src/latLon/DAL/latLonDAL.ts index fff62f1b..7faf6c6b 100644 --- a/src/latLon/DAL/latLonDAL.ts +++ b/src/latLon/DAL/latLonDAL.ts @@ -1,22 +1,15 @@ -import fs from 'fs'; import { Logger } from '@map-colonies/js-logger'; import cron from 'node-cron'; import { FactoryFunction, inject, injectable } from 'tsyringe'; import { InternalServerError } from '../../common/errors'; import { IApplication } from '../../common/interfaces'; import { SERVICES } from '../../common/constants'; -import { LatLon as ILatLon } from '../models/latLon'; -import { ConvertCamelToSnakeCase } from '../../common/utils'; -import { S3_REPOSITORY_SYMBOL, S3Repository } from '../../common/s3/s3Repository'; - -type LatLon = ConvertCamelToSnakeCase; -let scheduledTask: cron.ScheduledTask | null = null; - -let latLonDALInstance: LatLonDAL | null = null; +import { LATLON_CUSTOM_REPOSITORY_SYMBOL, LatLonRepository } from './latLonRepository'; +import { LatLon as LatLonDb } from './latLon'; @injectable() export class LatLonDAL { - private readonly latLonMap: Map; + private readonly latLonMap: Map; private onGoingUpdate: boolean; private dataLoad: | { @@ -29,13 +22,12 @@ export class LatLonDAL { public constructor( @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(S3_REPOSITORY_SYMBOL) private readonly latLonRepository: S3Repository + @inject(LATLON_CUSTOM_REPOSITORY_SYMBOL) private readonly latLonRepository: LatLonRepository ) { - this.latLonMap = new Map(); + this.latLonMap = new Map(); this.onGoingUpdate = true; this.dataLoad = undefined; this.dataLoadError = false; - this.init().catch((error) => { this.logger.error('Failed to initialize lat-lon data', error); this.dataLoadError = true; @@ -46,10 +38,6 @@ export class LatLonDAL { public getOnGoingUpdate(): boolean { return this.onGoingUpdate; } - - public getIsDataLoadError(): boolean { - return this.dataLoadError; - } /* istanbul ignore end */ public async init(): Promise { @@ -74,7 +62,7 @@ export class LatLonDAL { this.logger.debug('latLonData initialized'); } catch (error) { - this.logger.error(`Failed to initialize latLon data. Error: ${(error as Error).message}`); + this.logger.error('Failed to initialize latLon data', error); this.dataLoadError = true; } finally { this.onGoingUpdate = false; @@ -82,14 +70,22 @@ export class LatLonDAL { } } - public async latLonToTile({ x, y, zone }: { x: number; y: number; zone: number }): Promise { - if (this.getIsDataLoadError()) { + public async latLonToTile({ x, y, zone }: { x: number; y: number; zone: number }): Promise { + if (this.dataLoadError) { throw new InternalServerError('Lat-lon to tile data currently not available'); } await this.dataLoad?.promise; return this.latLonMap.get(`${x},${y},${zone}`); } + public async tileToLatLon(tileName: string): Promise { + if (this.dataLoadError) { + throw new InternalServerError('Tile to lat-lon data currently not available'); + } + await this.dataLoad?.promise; + return this.latLonMap.get(tileName); + } + private clearLatLonMap(): void { this.logger.debug('Clearing latLon data'); this.latLonMap.clear(); @@ -100,40 +96,19 @@ export class LatLonDAL { this.clearLatLonMap(); - const latLonDataPath = await this.latLonRepository.downloadFile('latLonConvertionTable'); - - const { items: latLonData } = JSON.parse(await fs.promises.readFile(latLonDataPath, 'utf8')) as { items: LatLon[] }; - + const latLonData = await this.latLonRepository.getAll(); latLonData.forEach((latLon) => { - this.latLonMap.set(`${latLon.min_x},${latLon.min_y},${latLon.zone}`, latLon); + this.latLonMap.set(latLon.tileName, latLon); + this.latLonMap.set(`${latLon.minX},${latLon.minY},${latLon.zone}`, latLon); }); - - try { - await fs.promises.unlink(latLonDataPath); - } catch (error) { - this.logger.error(`Failed to delete latLonData file ${latLonDataPath}. Error: ${(error as Error).message}`); - } - this.logger.info('loadLatLonData: update completed'); + this.logger.debug('latLon data loaded'); } } export const cronLoadTileLatLonDataSymbol = Symbol('cronLoadTileLatLonDataSymbol'); -export const latLonDalSymbol = Symbol('latLonDalSymbol'); -export const latLonSignletonFactory: FactoryFunction = (dependencyContainer) => { - const logger = dependencyContainer.resolve(SERVICES.LOGGER); - const s3Repository = dependencyContainer.resolve(S3_REPOSITORY_SYMBOL); - - if (latLonDALInstance !== null) { - return latLonDALInstance; - } - - latLonDALInstance = new LatLonDAL(logger, s3Repository); - return latLonDALInstance; -}; - -export const cronLoadTileLatLonDataFactory: FactoryFunction = (dependencyContainer) => { - const latLonDAL = dependencyContainer.resolve(latLonDalSymbol); +export const cronLoadTileLatLonDataFactory: FactoryFunction = (dependencyContainer) => { + const latLonDAL = dependencyContainer.resolve(LatLonDAL); const logger = dependencyContainer.resolve(SERVICES.LOGGER); const cronPattern: string | undefined = dependencyContainer.resolve(SERVICES.APPLICATION).cronLoadTileLatLonDataPattern; @@ -143,16 +118,18 @@ export const cronLoadTileLatLonDataFactory: FactoryFunction } /* istanbul ignore next */ - scheduledTask = cron.schedule(cronPattern, () => { + cron.schedule(cronPattern, () => { if (!latLonDAL.getOnGoingUpdate()) { logger.info('cronLoadTileLatLonData: starting update'); - latLonDAL.init().catch((error) => { - logger.error('cronLoadTileLatLonData: update failed', error); - }); + latLonDAL + .init() + .then(() => logger.info('cronLoadTileLatLonData: update completed')) + .catch((error) => { + logger.error('cronLoadTileLatLonData: update failed', error); + }); } else { logger.info('cronLoadTileLatLonData: update is already in progress'); } }); - return scheduledTask; /* istanbul ignore end */ }; diff --git a/src/latLon/DAL/latLonRepository.ts b/src/latLon/DAL/latLonRepository.ts new file mode 100644 index 00000000..6dc7dcab --- /dev/null +++ b/src/latLon/DAL/latLonRepository.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Logger } from '@map-colonies/js-logger'; +import { DataSource } from 'typeorm'; +import { FactoryFunction } from 'tsyringe'; +import { SERVICES } from '../../common/constants'; +import { LatLon as LatLonDb } from './latLon'; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const createLatLonRepository = (dataSource: DataSource, logger: Logger) => { + return dataSource.getRepository(LatLonDb).extend({ + async getAll(): Promise { + const result = await this.find(); + return result; + }, + }); +}; + +export type LatLonRepository = ReturnType; + +export const latLonRepositoryFactory: FactoryFunction = (depContainer) => { + return createLatLonRepository(depContainer.resolve(DataSource), depContainer.resolve(SERVICES.LOGGER)); +}; + +export const LATLON_CUSTOM_REPOSITORY_SYMBOL = Symbol('LATLON_CUSTOM_REPOSITORY_SYMBOL'); diff --git a/src/latLon/controllers/latLonController.ts b/src/latLon/controllers/latLonController.ts index c16b601f..00633d6a 100644 --- a/src/latLon/controllers/latLonController.ts +++ b/src/latLon/controllers/latLonController.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Feature } from 'geojson'; import { Logger } from '@map-colonies/js-logger'; import { BoundCounter, Meter } from '@opentelemetry/api-metrics'; import { RequestHandler } from 'express'; @@ -7,12 +6,38 @@ import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; import { SERVICES } from '../../common/constants'; import { LatLonManager } from '../models/latLonManager'; -import { WGS84Coordinate } from '../../common/interfaces'; -/* istanbul ignore file */ +import { Tile } from '../../tile/models/tile'; +import { FeatureCollection, WGS84Coordinate } from '../../common/interfaces'; -type GetCoordinatesHandler = RequestHandler; +type GetLatLonToTileHandler = RequestHandler< + undefined, + { + tileName: string; + subTileNumber: number[]; + }, + undefined, + GetLatLonToTileQueryParams +>; -export type GetCoordinatesRequestParams = WGS84Coordinate & { target_grid: 'control' | 'MGRS' }; +type GetTileToLatLonHandler = RequestHandler, undefined, GetTileToLatLonQueryParams>; + +type GetLatLonToMgrsHandler = RequestHandler; + +type GetMgrsToLatLonHandler = RequestHandler; + +export interface GetLatLonToTileQueryParams extends WGS84Coordinate {} + +export interface GetTileToLatLonQueryParams { + tile: string; + sub_tile_number: number[]; +} + +export interface GetLatLonToMgrsQueryParams extends WGS84Coordinate { + accuracy?: number; +} +export interface GetMgrsToLatLonQueryParams { + mgrs: string; +} @injectable() export class LatLonController { @@ -26,25 +51,55 @@ export class LatLonController { this.createdResourceCounter = meter.createCounter('created_resource'); } - public getCoordinates: GetCoordinatesHandler = async (req, res, next) => { + public latlonToTile: GetLatLonToTileHandler = async (req, res, next) => { + try { + const { lat, lon } = req.query; + + const response = await this.manager.latLonToTile({ lat, lon }); + return res.status(httpStatus.OK).json(response); + } catch (error: unknown) { + this.logger.warn('latLonController.latlonToTile Error:', error); + next(error); + } + }; + + public tileToLatLon: GetTileToLatLonHandler = async (req, res, next) => { try { - const { target_grid: targetGrid } = req.query; + const { tile: tileName, sub_tile_number } = req.query; - let response: - | ({ - [key: string]: unknown; - } & Feature) - | undefined = undefined; + const response = await this.manager.tileToLatLon({ + tileName, + subTileNumber: sub_tile_number, + }); + return res.status(httpStatus.OK).json(response); + } catch (error: unknown) { + this.logger.warn('latLonController.tileToLatLon Error:', error); + next(error); + } + }; + + public latlonToMgrs: GetLatLonToMgrsHandler = (req, res, next) => { + try { + const { lat, lon, accuracy } = req.query; + + const response = this.manager.latLonToMGRS({ lat, lon, accuracy }); + + return res.status(httpStatus.OK).json(response); + } catch (error: unknown) { + this.logger.warn('latLonController.latlonToMgrs Error:', error); + next(error); + } + }; + + public mgrsToLatlon: GetMgrsToLatLonHandler = (req, res, next) => { + try { + const { mgrs } = req.query; - if (targetGrid === 'control') { - response = await this.manager.latLonToTile({ ...req.query, targetGrid }); - } else { - response = this.manager.latLonToMGRS({ ...req.query, targetGrid }); - } + const response = this.manager.mgrsToLatLon(mgrs); return res.status(httpStatus.OK).json(response); } catch (error: unknown) { - this.logger.warn('latLonController.getCoordinates Error:', error); + this.logger.warn('latLonController.mgrsToLatlon Error:', error); next(error); } }; diff --git a/src/latLon/models/latLon.ts b/src/latLon/models/latLon.ts index 3fc70537..ab1176ba 100644 --- a/src/latLon/models/latLon.ts +++ b/src/latLon/models/latLon.ts @@ -1,4 +1,5 @@ export interface LatLon { + primaryKey: number; tileName: string; zone: string; minX: string; diff --git a/src/latLon/models/latLonManager.ts b/src/latLon/models/latLonManager.ts index c8db9eff..ee6732a0 100644 --- a/src/latLon/models/latLonManager.ts +++ b/src/latLon/models/latLonManager.ts @@ -1,30 +1,37 @@ import { IConfig } from 'config'; import { Logger } from '@map-colonies/js-logger'; import { inject, injectable } from 'tsyringe'; -import { BBox, Feature } from 'geojson'; import * as mgrs from 'mgrs'; import { SERVICES } from '../../common/constants'; -import { LatLonDAL, latLonDalSymbol } from '../DAL/latLonDAL'; -import { convertUTMToWgs84, convertWgs84ToUTM, parseGeo, validateWGS84Coordinate } from '../../common/utils'; +import { LatLonDAL } from '../DAL/latLonDAL'; +import { convertWgs84ToUTM, validateTile, validateWGS84Coordinate } from '../../common/utils'; +import { convertTilesToUTM, getSubTileByBottomLeftUtmCoor, validateResult } from '../utlis'; import { BadRequestError } from '../../common/errors'; -import { WGS84Coordinate } from '../../common/interfaces'; -import { convertCamelToSnakeCase } from '../../control/utils'; +import { Tile } from '../../tile/models/tile'; +import { FeatureCollection, WGS84Coordinate } from '../../common/interfaces'; @injectable() export class LatLonManager { + private readonly dbSchema: string; + public constructor( @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(latLonDalSymbol) private readonly latLonDAL: LatLonDAL, + @inject(LatLonDAL) private readonly latLonDAL: LatLonDAL, @inject(SERVICES.CONFIG) private readonly config: IConfig - ) {} + ) { + this.dbSchema = this.config.get('db.postgresql.schema'); + } - public async latLonToTile({ lat, lon, targetGrid }: WGS84Coordinate & { targetGrid: string }): Promise<{ [key: string]: unknown } & Feature> { + public async latLonToTile({ lat, lon }: WGS84Coordinate): Promise<{ + tileName: string; + subTileNumber: number[]; + }> { if (!validateWGS84Coordinate({ lat, lon })) { this.logger.warn("LatLonManager.latLonToTile: Invalid lat lon, check 'lat' and 'lon' keys exists and their values are legal"); throw new BadRequestError("Invalid lat lon, check 'lat' and 'lon' keys exists and their values are legal"); } - const utm = convertWgs84ToUTM({ longitude: lon, latitude: lat }); + const utm = convertWgs84ToUTM(lat, lon); if (typeof utm === 'string') { this.logger.warn('LatLonManager.latLonToTile: utm is string'); @@ -44,99 +51,51 @@ export class LatLonManager { throw new BadRequestError('The coordinate is outside the grid extent'); } - const xNumber = Math.abs(Math.trunc((utm.Easting % 10000) / 10) * 10) + const xNumber = Math.abs(Math.trunc((coordinatesUTM.x % 10000) / 10) * 10) .toString() .padStart(4, '0'); - const yNumber = Math.abs(Math.trunc((utm.Northing % 10000) / 10) * 10) + const yNumber = Math.abs(Math.trunc((coordinatesUTM.y % 10000) / 10) * 10) .toString() .padStart(4, '0'); - const bbox = [ - ...( - Object.values(convertUTMToWgs84(tileCoordinateData.ext_min_x, tileCoordinateData.ext_min_y, +tileCoordinateData.zone)) as number[] - ).reverse(), - ...( - Object.values(convertUTMToWgs84(tileCoordinateData.ext_max_x, tileCoordinateData.ext_max_y, +tileCoordinateData.zone)) as number[] - ).reverse(), - ] as BBox; - return { - type: 'Feature', - geocoding: { - version: process.env.npm_package_version, - query: { - lat, - lon, - ...convertCamelToSnakeCase({ targetGrid }), - }, - response: convertCamelToSnakeCase({ - maxScore: 1, - resultsCount: 1, - matchLatencyMs: 0, - }), - }, - bbox, - geometry: parseGeo({ - bbox, - }) ?? { - type: 'Point', - coordinates: [lon, lat], - }, - properties: { - name: tileCoordinateData.tile_name, - tileName: tileCoordinateData.tile_name, - subTileNumber: new Array(3).fill('').map(function (value, i) { - return xNumber[i] + yNumber[i]; - }), - }, + tileName: tileCoordinateData.tileName, + subTileNumber: new Array(3).fill('').map(function (value, i) { + return +(xNumber[i] + yNumber[i]); + }), }; } - public latLonToMGRS({ - lat, - lon, - accuracy = 5, - targetGrid, - }: { - lat: number; - lon: number; - accuracy?: number; - targetGrid: string; - }): { [key: string]: unknown } & Feature { - const accuracyString: Record = { - [0]: '100km', - [1]: '10km', - [2]: '1km', - [3]: '100m', - [4]: '10m', - [5]: '1m', - }; - const mgrsStr = mgrs.forward([lon, lat], accuracy); + public async tileToLatLon({ tileName, subTileNumber }: { tileName: string; subTileNumber: number[] }): Promise> { + if (!validateTile({ tileName, subTileNumber })) { + const message = "Invalid tile, check that 'tileName' and 'subTileNumber' exists and subTileNumber is array of size 3 with positive integers"; + this.logger.warn(`LatLonManager.tileToLatLon: ${message}`); + throw new BadRequestError(message); + } + + const tile = await this.latLonDAL.tileToLatLon(tileName); + + if (!tile) { + const meessage = 'Tile not found'; + this.logger.warn(`LatLonManager.tileToLatLon: ${meessage}`); + throw new BadRequestError(meessage); + } + + const utmCoor = convertTilesToUTM({ tileName, subTileNumber }, tile); + validateResult(tile, utmCoor); + + const geojsonRes = getSubTileByBottomLeftUtmCoor(utmCoor, { tileName, subTileNumber }); + return geojsonRes; + } + + public latLonToMGRS({ lat, lon, accuracy = 5 }: { lat: number; lon: number; accuracy?: number }): { mgrs: string } { return { - type: 'Feature', - geocoding: { - version: process.env.npm_package_version, - query: { - lat, - lon, - ...convertCamelToSnakeCase({ targetGrid }), - }, - response: convertCamelToSnakeCase({ - maxScore: 1, - resultsCount: 1, - matchLatencyMs: 0, - }), - }, - bbox: mgrs.inverse(mgrsStr), - geometry: { - type: 'Point', - coordinates: [lon, lat], - }, - properties: { - name: mgrsStr, - accuracy: accuracyString[accuracy], - mgrs: mgrsStr, - }, + mgrs: mgrs.forward([lon, lat], accuracy), }; } + + public mgrsToLatLon(mgrsStr: string): { lat: number; lon: number } { + const [lon, lat] = mgrs.toPoint(mgrsStr); + return { lat, lon }; + } } diff --git a/src/latLon/routes/latLonRouter.ts b/src/latLon/routes/latLonRouter.ts index 517178d0..534f765e 100644 --- a/src/latLon/routes/latLonRouter.ts +++ b/src/latLon/routes/latLonRouter.ts @@ -6,7 +6,10 @@ const latLonRouterFactory: FactoryFunction = (dependencyContainer) => { const router = Router(); const controller = dependencyContainer.resolve(LatLonController); - router.get('/coordinates', controller.getCoordinates); + router.get('/latlonToTile', controller.latlonToTile); + router.get('/tileToLatLon', controller.tileToLatLon); + router.get('/latlonToMgrs', controller.latlonToMgrs); + router.get('/mgrsToLatlon', controller.mgrsToLatlon); return router; }; diff --git a/src/latLon/utlis/index.ts b/src/latLon/utlis/index.ts new file mode 100644 index 00000000..aedc39ef --- /dev/null +++ b/src/latLon/utlis/index.ts @@ -0,0 +1,100 @@ +import { Polygon } from 'geojson'; +import { BadRequestError } from '../../common/errors'; +import { convertUTMToWgs84 } from '../../common/utils'; +import { LatLon } from '../DAL/latLon'; +import { FeatureCollection } from '../../common/interfaces'; +import { Tile } from '../../tile/models/tile'; + +/* eslint-disable @typescript-eslint/naming-convention */ +const geoJsonObjectTemplate = (): FeatureCollection => ({ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [[]], + }, + properties: { + TYPE: 'TILE', + }, + }, + ], +}); +/* eslint-enable @typescript-eslint/naming-convention */ + +export const convertTilesToUTM = ( + tile: { tileName: string; subTileNumber: number[] }, + tileObject: LatLon +): { + x: number; + y: number; + zone: number; +} => { + const xCoordinate = parseInt(tileObject.minX); + const yCoordinate = parseInt(tileObject.minY); + + const xCoordinatePart = tile.subTileNumber + .map((x) => { + return x.toString()[0]; + }) + .join(''); + const yCoordinatePart = tile.subTileNumber + .map(function (y) { + return y.toString()[1]; + }) + .join(''); + + return { x: xCoordinate + parseInt(xCoordinatePart) * 10, y: yCoordinate + parseInt(yCoordinatePart) * 10, zone: parseInt(tileObject.zone) }; +}; + +export const validateResult = ( + tile: LatLon, + utmCoor: { + x: number; + y: number; + zone: number; + } +): void => { + if (tile.extMinX > utmCoor.x || tile.extMaxX < utmCoor.x || tile.extMinY > utmCoor.y || tile.extMaxY < utmCoor.y) { + throw new BadRequestError("Tile is found, sub tile is not in tile's extent"); + } +}; + +export const getSubTileByBottomLeftUtmCoor = ( + utmCoor: { + x: number; + y: number; + zone: number; + }, + tile: { tileName: string; subTileNumber: number[] } +): FeatureCollection => { + const result = geoJsonObjectTemplate(); + + const multiplyByOrder = [ + [0, 0], + [1, 0], + [1, 1], + [0, 1], + [0, 0], + ]; // bottom left -> bottom right -> top right -> top left -> bottom left + const distance = 10; // 10 meters + const polygon: Polygon = { + type: 'Polygon', + coordinates: [[]], + }; + + for (const multiply of multiplyByOrder) { + const coordiante = convertUTMToWgs84(utmCoor.x + distance * multiply[0], utmCoor.y + distance * multiply[1], utmCoor.zone); + if (typeof coordiante === 'string') { + throw new Error('coordinate is string'); + } + polygon.coordinates[0].push([coordiante.lon, coordiante.lat]); + } + + result.features[0].geometry = polygon; + // eslint-disable-next-line @typescript-eslint/naming-convention + result.features[0].properties = { ...result.features[0].properties, TILE_NAME: tile.tileName, SUB_TILE_NUMBER: tile.subTileNumber }; + + return result; +}; diff --git a/src/location/interfaces.ts b/src/location/interfaces.ts deleted file mode 100644 index 87d6f138..00000000 --- a/src/location/interfaces.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { GeoJSON } from 'geojson'; -import { estypes } from '@elastic/elasticsearch'; -import { CommonRequestParameters } from '../common/interfaces'; -import { ConvertCamelToSnakeCase, ConvertSnakeToCamelCase, RemoveUnderscore } from '../common/utils'; -import { HierarchySearchHit } from './models/elasticsearchHits'; - -export interface PlaceType { - placetype: string; - confidence: number; -} - -export interface TokenResponse { - tokens: string[]; - prediction: string[]; -} - -export interface TextSearchParams extends ConvertSnakeToCamelCase { - name?: string; - placeTypes?: string[]; - subPlaceTypes?: string[]; - hierarchies: HierarchySearchHit[]; -} - -export interface GetGeotextSearchParams extends CommonRequestParameters { - query: string; - source?: string[]; - region?: string[]; -} diff --git a/src/location/utils.ts b/src/location/utils.ts deleted file mode 100644 index 559d7770..00000000 --- a/src/location/utils.ts +++ /dev/null @@ -1,107 +0,0 @@ -import https from 'https'; -import { Feature, Geometry } from 'geojson'; -import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import { StatusCodes } from 'http-status-codes'; -import axios, { AxiosError, AxiosResponse as Response } from 'axios'; -import { InternalServerError } from '../common/errors'; -import { GenericGeocodingResponse, IApplication } from '../common/interfaces'; -import { TextSearchParams } from './interfaces'; -import { TextSearchHit } from './models/elasticsearchHits'; -import { generateDisplayName } from './parsing'; - -const FIND_QUOTES = /["']/g; - -const FIND_SPECIAL = /[`!@#$%^&*()_\-+=|\\/,.<>:[\]{}\n\t\r\s;؛]+/g; - -const axiosInstance = axios.create({ - httpsAgent: new https.Agent({ rejectUnauthorized: false }), -}); - -export const fetchNLPService = async (endpoint: string, requestData: object): Promise => { - let res: Response | null = null, - data: T[] | undefined | null = null; - try { - res = await axiosInstance.post(endpoint, requestData); - } catch (err: unknown) { - throw new InternalServerError(`NLP analyser is not available - ${(err as AxiosError).message}`); - } - - data = res?.data as T[] | undefined; - - if (res?.status !== StatusCodes.OK || !data || data.length < 1 || !data[0]) { - throw new InternalServerError(`NLP analyser unexpected response: ${JSON.stringify(data)}`); - } - return data; -}; - -export const cleanQuery = (query: string): string[] => query.replace(FIND_QUOTES, '').split(FIND_SPECIAL); - -/* eslint-disable @typescript-eslint/naming-convention */ -export const convertResult = ( - params: TextSearchParams, - results: SearchResponse, - { - sources, - regionCollection, - nameKeys, - mainLanguageRegex, - }: { - sources?: IApplication['sources']; - regionCollection?: IApplication['regions']; - nameKeys: IApplication['nameTranslationsKeys']; - mainLanguageRegex: IApplication['mainLanguageRegex']; - } = { nameKeys: [], mainLanguageRegex: '' } -): GenericGeocodingResponse => ({ - type: 'FeatureCollection', - geocoding: { - version: process.env.npm_package_version, - query: { - query: params.query, - region: params.region, - source: params.source, - geo_context: params.geoContext, - geo_context_mode: params.geoContextMode, - disable_fuzziness: params.disableFuzziness, - limit: params.limit, - }, - response: { - results_count: results.hits.hits.length, - max_score: results.hits.max_score ?? 0, - match_latency_ms: results.took, - name: params.name ?? undefined, - place_types: params.placeTypes, - sub_place_types: params.subPlaceTypes, - hierarchies: params.hierarchies, - }, - }, - features: results.hits.hits.map(({ _source: feature, _score: score, highlight }, index): GenericGeocodingResponse['features'][number] => { - const allNames = [feature!.text, feature!.translated_text || []]; - return { - type: 'Feature', - geometry: feature!.geo_json as Geometry, - properties: { - score, - matches: [ - { - layer: feature!.layer_name, - source: (sources ?? {})[feature?.source ?? ''] ?? feature?.source, - source_id: feature?.source_id.map((id) => id.replace(/(^\{)|(\}$)/g, '')) ?? [], - }, - ], - names: { - [nameKeys[0]]: new RegExp(mainLanguageRegex).test(feature!.text[0]) ? allNames.shift() : allNames.pop(), - [nameKeys[1]]: allNames.pop(), - ['default']: [feature!.name], - display: highlight ? generateDisplayName(highlight, params.query.split(' ').length, params.name) : feature!.name, - }, - placetype: feature?.placetype, // TODO: check if to remove this - sub_placetype: feature?.sub_placetype, - regions: feature?.region.map((region) => ({ - region: region, - sub_region_names: feature.sub_region.filter((sub_region) => (regionCollection ?? {})[region ?? '']?.includes(sub_region)), - })), - }, - }; - }), -}); -/* eslint-enable @typescript-eslint/naming-convention */ diff --git a/src/mgrs/controllers/mgrsController.ts b/src/mgrs/controllers/mgrsController.ts deleted file mode 100644 index c482fc51..00000000 --- a/src/mgrs/controllers/mgrsController.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Logger } from '@map-colonies/js-logger'; -import { BoundCounter, Meter } from '@opentelemetry/api-metrics'; -import { RequestHandler } from 'express'; -import { Feature } from 'geojson'; -import httpStatus from 'http-status-codes'; -import { injectable, inject } from 'tsyringe'; -import { SERVICES } from '../../common/constants'; -import { MgrsManager } from '../models/mgrsManager'; - -type GetTilesHandler = RequestHandler< - undefined, - | Feature - | { - type: string; - message: string; - }, - undefined, - GetTileQueryParams ->; - -export interface GetTileQueryParams { - tile: string; -} - -@injectable() -export class MgrsController { - private readonly createdResourceCounter: BoundCounter; - - public constructor( - @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(MgrsManager) private readonly manager: MgrsManager, - @inject(SERVICES.METER) private readonly meter: Meter - ) { - this.createdResourceCounter = meter.createCounter('created_resource'); - } - - public getTile: GetTilesHandler = (req, res, next) => { - try { - const { tile } = req.query; - const response = this.manager.getTile({ tile }); - return res.status(httpStatus.OK).json(response); - } catch (error: unknown) { - this.logger.error(`MgrsController.getTile Error: ${(error as Error).message}`); - next(error); - } - }; -} diff --git a/src/mgrs/models/mgrsManager.ts b/src/mgrs/models/mgrsManager.ts deleted file mode 100644 index b340b5d6..00000000 --- a/src/mgrs/models/mgrsManager.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { IConfig } from 'config'; -import { Logger } from '@map-colonies/js-logger'; -import { BBox, Feature, Geometry } from 'geojson'; -import { inject, injectable } from 'tsyringe'; -import * as mgrs from 'mgrs'; -import { SERVICES } from '../../common/constants'; -import { IApplication } from '../../common/interfaces'; -import { GetTileQueryParams } from '../controllers/mgrsController'; -import { BadRequestError } from '../../common/errors'; -import { parseGeo } from '../../common/utils'; - -@injectable() -export class MgrsManager { - public constructor( - @inject(SERVICES.LOGGER) private readonly logger: Logger, - @inject(SERVICES.CONFIG) private readonly config: IConfig, - @inject(SERVICES.APPLICATION) private readonly application: IApplication - ) {} - - public getTile({ tile }: GetTileQueryParams): Feature & { geocoding: { [key: string]: unknown } } { - let bbox: BBox | undefined; - try { - bbox = mgrs.inverse(tile); - } catch (error) { - if ((error as Error).message.includes('MGRSPoint bad conversion')) { - throw new BadRequestError('Invalid MGRS tile'); - } - this.logger.error(`Failed to convert MGRS tile to bbox. Error: ${(error as Error).message}`); - throw error; - } - - const geometry = parseGeo({ bbox }) as Geometry; - - return { - type: 'Feature', - geocoding: { - query: { - tile, - }, - response: { - max_score: 1, - results_count: 1, - match_latency_ms: 0, - }, - }, - bbox, - geometry, - properties: { - score: 1, - }, - }; - } -} diff --git a/src/mgrs/routers/mgrsRouter.ts b/src/mgrs/routers/mgrsRouter.ts deleted file mode 100644 index 77ac0714..00000000 --- a/src/mgrs/routers/mgrsRouter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Router } from 'express'; -import { FactoryFunction } from 'tsyringe'; -import { MgrsController } from '../controllers/mgrsController'; - -const mgrsRouterFactory: FactoryFunction = (dependencyContainer) => { - const router = Router(); - const controller = dependencyContainer.resolve(MgrsController); - - router.get('/tiles', controller.getTile); - - return router; -}; - -export const MGRS_ROUTER_SYMBOL = Symbol('mgrsRouterFactory'); - -export { mgrsRouterFactory }; diff --git a/src/route/DAL/queries.ts b/src/route/DAL/queries.ts new file mode 100644 index 00000000..c07f5309 --- /dev/null +++ b/src/route/DAL/queries.ts @@ -0,0 +1,79 @@ +import { estypes } from '@elastic/elasticsearch'; +import { boundingBox, geoDistance } from '../../common/elastic/utils'; +import { GeoContext, WGS84Coordinate } from '../../common/interfaces'; + +export interface RouteQueryParams { + commandName: string; + controlPoint?: number; + geo?: GeoContext; +} + +/* eslint-disable @typescript-eslint/naming-convention */ +export const queryForRoute = (params: RouteQueryParams): estypes.SearchRequest => ({ + query: { + bool: { + should: [ + { + term: { + 'properties.TYPE.keyword': 'ROUTE', + }, + }, + ], + must: [ + { + match: { + 'properties.OBJECT_COMMAND_NAME.keyword': { + query: params.commandName, + fuzziness: 1, + prefix_length: 1, + }, + }, + }, + ...(params.geo?.bbox ? [boundingBox(params.geo.bbox)] : []), + ...(params.geo?.radius + ? [ + geoDistance( + params.geo as { + radius: number; + lon: number; + lat: number; + } + ), + ] + : []), + ], + }, + }, +}); + +export const queryForControlPointInRoute = (params: RouteQueryParams & Required>): estypes.SearchRequest => ({ + query: { + bool: { + must: [ + { + match: { + 'properties.OBJECT_COMMAND_NAME.keyword': { + query: params.controlPoint, + fuzziness: 1, + prefix_length: 1, + }, + }, + }, + ...(params.geo?.bbox ? [boundingBox(params.geo.bbox)] : []), + ...(params.geo?.radius ?? 0 ? [geoDistance(params.geo as WGS84Coordinate & { radius: number })] : []), + ], + filter: [ + { + terms: { + 'properties.LAYER_NAME.keyword': ['CONTROL_GIL_GDB.CTR_CONTROL_POINT_N', 'CONTROL_GIL_GDB.CTR_CONTROL_POINT_CROSS_N'], + }, + }, + { + term: { + 'properties.TIED_TO': params.commandName, + }, + }, + ], + }, + }, +}); diff --git a/src/control/route/DAL/routeRepository.ts b/src/route/DAL/routeRepository.ts similarity index 71% rename from src/control/route/DAL/routeRepository.ts rename to src/route/DAL/routeRepository.ts index 34da5b2e..40c013a4 100644 --- a/src/control/route/DAL/routeRepository.ts +++ b/src/route/DAL/routeRepository.ts @@ -1,16 +1,17 @@ +import { Logger } from '@map-colonies/js-logger'; import { estypes } from '@elastic/elasticsearch'; import { FactoryFunction } from 'tsyringe'; -import { ElasticClient } from '../../../common/elastic'; -import { SERVICES } from '../../../common/constants'; +import { ElasticClient } from '../../common/elastic'; +import { elasticConfigPath, SERVICES } from '../../common/constants'; import { Route } from '../models/route'; -import { IConfig } from '../../../common/interfaces'; -import { ElasticClients } from '../../../common/elastic'; -import { queryElastic } from '../../../common/elastic/utils'; -import { additionalControlSearchProperties } from '../../utils'; +import { IConfig } from '../../common/interfaces'; +import { ElasticClients } from '../../common/elastic'; +import { ElasticDbClientsConfig } from '../../common/elastic/interfaces'; +import { additionalControlSearchProperties, queryElastic } from '../../common/elastic/utils'; import { RouteQueryParams, queryForControlPointInRoute, queryForRoute } from './queries'; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createRouteRepository = (client: ElasticClient, config: IConfig) => { +const createRouteRepository = (client: ElasticClient, config: IConfig, logger: Logger) => { return { async getRoutes(routeQueryParams: RouteQueryParams, size: number): Promise> { const response = await queryElastic(client, { ...additionalControlSearchProperties(config, size), ...queryForRoute(routeQueryParams) }); @@ -37,7 +38,8 @@ export type RouteRepository = ReturnType; export const routeRepositoryFactory: FactoryFunction = (depContainer) => { return createRouteRepository( depContainer.resolve(SERVICES.ELASTIC_CLIENTS).control, - depContainer.resolve(SERVICES.CONFIG) + depContainer.resolve(SERVICES.CONFIG), + depContainer.resolve(SERVICES.LOGGER) ); }; diff --git a/src/control/route/controllers/routeController.ts b/src/route/controllers/routeController.ts similarity index 67% rename from src/control/route/controllers/routeController.ts rename to src/route/controllers/routeController.ts index 87983fd3..ce388fd3 100644 --- a/src/control/route/controllers/routeController.ts +++ b/src/route/controllers/routeController.ts @@ -4,16 +4,19 @@ import { BoundCounter, Meter } from '@opentelemetry/api-metrics'; import { RequestHandler } from 'express'; import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; -import { SERVICES } from '../../../common/constants'; +import { SERVICES } from '../../common/constants'; import { RouteManager } from '../models/routeManager'; import { Route } from '../models/route'; -import { CommonRequestParameters, FeatureCollection } from '../../../common/interfaces'; +import { FeatureCollection, GeoContext } from '../../common/interfaces'; type GetRoutesHandler = RequestHandler, undefined, GetRoutesQueryParams>; -export interface GetRoutesQueryParams extends CommonRequestParameters { +export interface GetRoutesQueryParams { command_name: string; control_point?: string; + geo_context?: string; + reduce_fuzzy_match?: string; + size?: string; } @injectable() @@ -30,15 +33,16 @@ export class RouteController { public getRoutes: GetRoutesHandler = async (req, res, next) => { try { - const { command_name: commandName, control_point, geo_context, geo_context_mode, disable_fuzziness, limit } = req.query; - const response = await this.manager.getRoutes({ - commandName, - controlPoint: control_point, - geoContext: geo_context, - geoContextMode: geo_context_mode, - disableFuzziness: disable_fuzziness, - limit, - }); + const { command_name: commandName, control_point, geo_context, reduce_fuzzy_match, size } = req.query; + const response = await this.manager.getRoutes( + { + commandName, + controlPoint: control_point ? parseInt(control_point) : undefined, + geo: geo_context ? (JSON.parse(geo_context) as GeoContext) : undefined, + }, + reduce_fuzzy_match == 'true', + size ? parseInt(size) : undefined + ); return res.status(httpStatus.OK).json(response); } catch (error: unknown) { this.logger.warn('routeController.getRoutes Error:', error); diff --git a/src/route/models/route.ts b/src/route/models/route.ts new file mode 100644 index 00000000..4860b131 --- /dev/null +++ b/src/route/models/route.ts @@ -0,0 +1,3 @@ +import { Feature } from 'geojson'; + +export interface Route extends Feature {} diff --git a/src/route/models/routeManager.ts b/src/route/models/routeManager.ts new file mode 100644 index 00000000..619b9a69 --- /dev/null +++ b/src/route/models/routeManager.ts @@ -0,0 +1,44 @@ +import { IConfig } from 'config'; +import { Logger } from '@map-colonies/js-logger'; +import { inject, injectable } from 'tsyringe'; +import { estypes } from '@elastic/elasticsearch'; +import { SERVICES } from '../../common/constants'; +import { ROUTE_REPOSITORY_SYMBOL, RouteRepository } from '../DAL/routeRepository'; +import { RouteQueryParams } from '../DAL/queries'; +import { formatResponse } from '../../common/utils'; +import { FeatureCollection } from '../../common/interfaces'; +import { getElasticClientQuerySize } from '../../common/elastic/utils'; +import { Route } from './route'; + +@injectable() +export class RouteManager { + public constructor( + @inject(SERVICES.LOGGER) private readonly logger: Logger, + @inject(SERVICES.CONFIG) private readonly config: IConfig, + @inject(ROUTE_REPOSITORY_SYMBOL) private readonly routeRepository: RouteRepository + ) {} + + public async getRoutes(routeQueryParams: RouteQueryParams, reduceFuzzyMatch = false, size?: number): Promise> { + let elasticResponse: estypes.SearchResponse | undefined = undefined; + if (routeQueryParams.controlPoint ?? 0) { + elasticResponse = await this.routeRepository.getControlPointInRoute( + routeQueryParams as RouteQueryParams & Required>, + size ?? getElasticClientQuerySize(this.config, 'control') + ); + } else { + elasticResponse = await this.routeRepository.getRoutes(routeQueryParams, size ?? getElasticClientQuerySize(this.config, 'control')); + } + + const formattedResponse = formatResponse(elasticResponse); + + if (reduceFuzzyMatch && formattedResponse.features.length > 0) { + const filterFunction = + routeQueryParams.controlPoint ?? 0 + ? (hit: Route | undefined): hit is Route => hit?.properties?.OBJECT_COMMAND_NAME === routeQueryParams.controlPoint + : (hit: Route | undefined): hit is Route => hit?.properties?.OBJECT_COMMAND_NAME === routeQueryParams.commandName; + formattedResponse.features = formattedResponse.features.filter(filterFunction); + } + + return formattedResponse; + } +} diff --git a/src/control/route/routes/routeRouter.ts b/src/route/routes/routeRouter.ts similarity index 100% rename from src/control/route/routes/routeRouter.ts rename to src/route/routes/routeRouter.ts diff --git a/src/serverBuilder.ts b/src/serverBuilder.ts index 6a533d37..9e8b387c 100644 --- a/src/serverBuilder.ts +++ b/src/serverBuilder.ts @@ -10,14 +10,12 @@ import httpLogger from '@map-colonies/express-access-log-middleware'; import { defaultMetricsMiddleware, getTraceContexHeaderMiddleware } from '@map-colonies/telemetry'; import { SERVICES } from './common/constants'; import { IConfig } from './common/interfaces'; -import { TILE_ROUTER_SYMBOL } from './control/tile/routes/tileRouter'; -import { ITEM_ROUTER_SYMBOL } from './control/item/routes/itemRouter'; -import { ROUTE_ROUTER_SYMBOL } from './control/route/routes/routeRouter'; +import { TILE_ROUTER_SYMBOL } from './tile/routes/tileRouter'; +import { ITEM_ROUTER_SYMBOL } from './item/routes/itemRouter'; +import { ROUTE_ROUTER_SYMBOL } from './route/routes/routeRouter'; import { LAT_LON_ROUTER_SYMBOL } from './latLon/routes/latLonRouter'; -import { GEOTEXT_SEARCH_ROUTER_SYMBOL } from './location/routes/locationRouter'; +import { GEOTEXT_SEARCH_ROUTER_SYMBOL } from './geotextSearch/routes/geotextSearchRouter'; import { cronLoadTileLatLonDataSymbol } from './latLon/DAL/latLonDAL'; -import { FeedbackApiMiddlewareManager } from './common/middlewares/feedbackApi.middleware'; -import { MGRS_ROUTER_SYMBOL } from './mgrs/routers/mgrsRouter'; @injectable() export class ServerBuilder { @@ -31,18 +29,17 @@ export class ServerBuilder { @inject(ROUTE_ROUTER_SYMBOL) private readonly routeRouter: Router, @inject(LAT_LON_ROUTER_SYMBOL) private readonly latLonRouter: Router, @inject(GEOTEXT_SEARCH_ROUTER_SYMBOL) private readonly geotextRouter: Router, - @inject(cronLoadTileLatLonDataSymbol) private readonly cronLoadTileLatLonData: void, - @inject(FeedbackApiMiddlewareManager) private readonly feedbackApiMiddleware: FeedbackApiMiddlewareManager, - @inject(MGRS_ROUTER_SYMBOL) private readonly mgrsRouter: Router + @inject(cronLoadTileLatLonDataSymbol) private readonly cronLoadTileLatLonData: void ) { this.serverInstance = express(); // eslint-disable-next-line @typescript-eslint/no-unused-expressions + this.cronLoadTileLatLonData; } public build(): express.Application { this.registerPreRoutesMiddleware(); this.buildDocsRoutes(); - this.buildRoutes(); + this.buildRoutesV1(); this.registerPostRoutesMiddleware(); return this.serverInstance; @@ -57,27 +54,14 @@ export class ServerBuilder { this.serverInstance.use(this.config.get('openapiConfig.basePath'), openapiRouter.getRouter()); } - private buildRoutes(): void { - this.serverInstance.use(this.feedbackApiMiddleware.saveResponses); + private buildRoutesV1(): void { const router = Router(); - + router.use('/search/tiles', this.tileRouter); + router.use('/search/items', this.itemRouter); + router.use('/search/routes', this.routeRouter); router.use('/lookup', this.latLonRouter); - router.use('/location', this.geotextRouter); - router.use('/control', this.buildControlRoutes()); - router.use('/MGRS', this.mgrsRouter); - - this.serverInstance.use('/search', router); - this.serverInstance.use('/lookup', this.latLonRouter); - } - - private buildControlRoutes(): Router { - const router = Router(); - - router.use('/tiles', this.tileRouter); - router.use('/items', this.itemRouter); - router.use('/routes', this.routeRouter); - - return router; + router.use('/query', this.geotextRouter); + this.serverInstance.use('/v1', router); } private registerPreRoutesMiddleware(): void { @@ -96,8 +80,6 @@ export class ServerBuilder { const apiSpecPath = this.config.get('openapiConfig.filePath'); this.serverInstance.use(OpenApiMiddleware({ apiSpec: apiSpecPath, validateRequests: true, ignorePaths: ignorePathRegex })); this.serverInstance.disable('x-powered-by'); - - this.serverInstance.use(this.feedbackApiMiddleware.setRequestId); } private registerPostRoutesMiddleware(): void { diff --git a/src/tile/DAL/queries.ts b/src/tile/DAL/queries.ts new file mode 100644 index 00000000..d3dfeb92 --- /dev/null +++ b/src/tile/DAL/queries.ts @@ -0,0 +1,54 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { estypes } from '@elastic/elasticsearch'; + +export interface TileQueryParams { + tile: string; + subTile?: number; +} + +export const queryForTiles = (params: Omit): estypes.SearchRequest => ({ + query: { + bool: { + must: [ + { + term: { + 'properties.TYPE.keyword': 'TILE', + }, + }, + { + match: { + 'properties.TILE_NAME.keyword': { + query: params.tile, + fuzziness: 1, + prefix_length: 1, + }, + }, + }, + ], + }, + }, +}); + +export const queryForSubTiles = (params: Required): estypes.SearchRequest => ({ + query: { + bool: { + must: [ + { + term: { + 'properties.TYPE.keyword': 'SUB_TILE', + }, + }, + { + term: { + 'properties.TILE_NAME.keyword': params.tile, + }, + }, + { + match: { + 'properties.SUB_TILE_ID.keyword': params.subTile, + }, + }, + ], + }, + }, +}); diff --git a/src/tile/DAL/tileRepository.ts b/src/tile/DAL/tileRepository.ts new file mode 100644 index 00000000..1b1de8b3 --- /dev/null +++ b/src/tile/DAL/tileRepository.ts @@ -0,0 +1,39 @@ +import { Logger } from '@map-colonies/js-logger'; +import { estypes } from '@elastic/elasticsearch'; +import { FactoryFunction } from 'tsyringe'; +import { ElasticClient, ElasticClients } from '../../common/elastic'; +import { Tile } from '../models/tile'; +import { additionalControlSearchProperties, queryElastic } from '../../common/elastic/utils'; +import { IConfig } from '../../common/interfaces'; +import { SERVICES } from '../../common/constants'; +import { queryForTiles, queryForSubTiles, TileQueryParams } from './queries'; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const createTileRepository = (client: ElasticClient, config: IConfig, logger: Logger) => { + return { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + async getTiles(tileQueryParams: TileQueryParams, size: number): Promise> { + const response = await queryElastic(client, { ...additionalControlSearchProperties(config, size), ...queryForTiles(tileQueryParams) }); + + return response; + }, + + async getSubTiles(tileQueryParams: Required, size: number): Promise> { + const response = await queryElastic(client, { ...additionalControlSearchProperties(config, size), ...queryForSubTiles(tileQueryParams) }); + + return response; + }, + }; +}; + +export type TileRepository = ReturnType; + +export const tileRepositoryFactory: FactoryFunction = (depContainer) => { + return createTileRepository( + depContainer.resolve(SERVICES.ELASTIC_CLIENTS).control, + depContainer.resolve(SERVICES.CONFIG), + depContainer.resolve(SERVICES.LOGGER) + ); +}; + +export const TILE_REPOSITORY_SYMBOL = Symbol('TILE_REPOSITORY_SYMBOL'); diff --git a/src/control/tile/controllers/tileController.ts b/src/tile/controllers/tileController.ts similarity index 67% rename from src/control/tile/controllers/tileController.ts rename to src/tile/controllers/tileController.ts index 1bd158e1..3e86c335 100644 --- a/src/control/tile/controllers/tileController.ts +++ b/src/tile/controllers/tileController.ts @@ -4,10 +4,10 @@ import { BoundCounter, Meter } from '@opentelemetry/api-metrics'; import { RequestHandler } from 'express'; import httpStatus from 'http-status-codes'; import { injectable, inject } from 'tsyringe'; -import { SERVICES } from '../../../common/constants'; +import { SERVICES } from '../../common/constants'; import { TileManager } from '../models/tileManager'; import { Tile } from '../models/tile'; -import { CommonRequestParameters, FeatureCollection } from '../../../common/interfaces'; +import { FeatureCollection } from '../../common/interfaces'; type GetTilesHandler = RequestHandler< undefined, @@ -20,10 +20,11 @@ type GetTilesHandler = RequestHandler< GetTilesQueryParams >; -export interface GetTilesQueryParams extends CommonRequestParameters { - tile?: string; - mgrs?: string; +export interface GetTilesQueryParams { + tile: string; sub_tile?: string; + reduce_fuzzy_match?: string; + size?: string; } @injectable() @@ -40,16 +41,12 @@ export class TileController { public getTiles: GetTilesHandler = async (req, res, next) => { try { - const { tile, sub_tile, disable_fuzziness, geo_context, geo_context_mode, limit, mgrs } = req.query; - const response = await this.manager.getTiles({ - tile, - subTile: sub_tile, - disableFuzziness: disable_fuzziness, - geoContext: geo_context, - geoContextMode: geo_context_mode, - limit, - mgrs, - }); + const { tile, sub_tile, reduce_fuzzy_match, size } = req.query; + const response = await this.manager.getTiles( + { tile, subTile: sub_tile ? parseInt(sub_tile) : undefined }, + reduce_fuzzy_match == 'true', + size ? parseInt(size) : undefined + ); return res.status(httpStatus.OK).json(response); } catch (error: unknown) { this.logger.warn('tileController.getTiles Error:', error); diff --git a/src/tile/models/tile.ts b/src/tile/models/tile.ts new file mode 100644 index 00000000..91cc6452 --- /dev/null +++ b/src/tile/models/tile.ts @@ -0,0 +1,3 @@ +import { Feature } from 'geojson'; + +export interface Tile extends Feature {} diff --git a/src/tile/models/tileManager.ts b/src/tile/models/tileManager.ts new file mode 100644 index 00000000..121f4aec --- /dev/null +++ b/src/tile/models/tileManager.ts @@ -0,0 +1,43 @@ +import { IConfig } from 'config'; +import { Logger } from '@map-colonies/js-logger'; +import { inject, injectable } from 'tsyringe'; +import { estypes } from '@elastic/elasticsearch'; +import { SERVICES } from '../../common/constants'; +import { TILE_REPOSITORY_SYMBOL, TileRepository } from '../DAL/tileRepository'; +import { formatResponse } from '../../common/utils'; +import { TileQueryParams } from '../DAL/queries'; +import { FeatureCollection } from '../../common/interfaces'; +import { getElasticClientQuerySize } from '../../common/elastic/utils'; +import { Tile } from './tile'; + +@injectable() +export class TileManager { + public constructor( + @inject(SERVICES.LOGGER) private readonly logger: Logger, + @inject(SERVICES.CONFIG) private readonly config: IConfig, + @inject(TILE_REPOSITORY_SYMBOL) private readonly tileRepository: TileRepository + ) {} + + public async getTiles(tileQueryParams: TileQueryParams, reduceFuzzyMatch = false, size?: number): Promise> { + let elasticResponse: estypes.SearchResponse | undefined = undefined; + const numberOfResults = size ?? getElasticClientQuerySize(this.config, 'control'); + if (tileQueryParams.subTile ?? 0) { + elasticResponse = await this.tileRepository.getSubTiles(tileQueryParams as Required, numberOfResults); + } else { + elasticResponse = await this.tileRepository.getTiles(tileQueryParams, numberOfResults); + } + + const formattedResponse = formatResponse(elasticResponse); + + if (reduceFuzzyMatch && formattedResponse.features.length > 0) { + const filterFunction = + tileQueryParams.subTile ?? 0 + ? (hit: Tile | undefined): hit is Tile => + hit?.properties?.SUB_TILE_ID === tileQueryParams.subTile && hit?.properties?.TILE_NAME === tileQueryParams.tile + : (hit: Tile | undefined): hit is Tile => hit?.properties?.TILE_NAME === tileQueryParams.tile; + formattedResponse.features = formattedResponse.features.filter(filterFunction); + } + + return formattedResponse; + } +} diff --git a/src/control/tile/routes/tileRouter.ts b/src/tile/routes/tileRouter.ts similarity index 100% rename from src/control/tile/routes/tileRouter.ts rename to src/tile/routes/tileRouter.ts diff --git a/tests/configurations/integration/jest.config.js b/tests/configurations/integration/jest.config.js index 673b558e..3ae834e9 100644 --- a/tests/configurations/integration/jest.config.js +++ b/tests/configurations/integration/jest.config.js @@ -11,8 +11,6 @@ module.exports = { testMatch: ['/tests/integration/**/*.spec.ts'], setupFiles: ['/tests/configurations/jest.setup.ts'], setupFilesAfterEnv: ['jest-openapi', '/tests/configurations/initJestOpenapi.setup.ts'], - globalSetup: '/tests/integration/globalSetup.ts', - globalTeardown: '/tests/integration/globalTeardown.ts', reporters: [ 'default', [ diff --git a/tests/configurations/unit/jest.config.js b/tests/configurations/unit/jest.config.js index bfd03fc4..07af12e5 100644 --- a/tests/configurations/unit/jest.config.js +++ b/tests/configurations/unit/jest.config.js @@ -13,7 +13,6 @@ module.exports = { '!*/common/**', '!**/controllers/**', '!**/routes/**', - '!**/redis/**', '!/src/*', ], coverageDirectory: '/coverage', diff --git a/tests/integration/control/item/item.spec.ts b/tests/integration/control/item/item.spec.ts deleted file mode 100644 index 0fad0366..00000000 --- a/tests/integration/control/item/item.spec.ts +++ /dev/null @@ -1,448 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import jsLogger from '@map-colonies/js-logger'; -import { trace } from '@opentelemetry/api'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import { Application } from 'express'; -import { DependencyContainer } from 'tsyringe'; -import httpStatusCodes from 'http-status-codes'; -import { getApp } from '../../../../src/app'; -import { SERVICES } from '../../../../src/common/constants'; -import { GetItemsQueryParams } from '../../../../src/control/item/controllers/itemController'; -import { Item } from '../../../../src/control/item/models/item'; -import { CommonRequestParameters, GenericGeocodingResponse, GeoContext, GeoContextMode } from '../../../../src/common/interfaces'; -import { cronLoadTileLatLonDataSymbol } from '../../../../src/latLon/DAL/latLonDAL'; -import { S3_REPOSITORY_SYMBOL } from '../../../../src/common/s3/s3Repository'; -import { expectedResponse } from '../utils'; -import { ItemRequestSender } from './helpers/requestSender'; -import { ITEM_1234, ITEM_1235, ITEM_1236 } from './mockObjects'; - -describe('/search/control/items', function () { - let requestSender: ItemRequestSender; - let app: { app: Application; container: DependencyContainer }; - - beforeEach(async function () { - app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: S3_REPOSITORY_SYMBOL, provider: { useValue: {} } }, - { token: SERVICES.S3_CLIENT, provider: { useValue: {} } }, - { token: cronLoadTileLatLonDataSymbol, provider: { useValue: {} } }, - ], - useChild: true, - }); - - requestSender = new ItemRequestSender(app.app); - }); - - afterAll(async function () { - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); - - jest.clearAllTimers(); - }); - - describe('Happy Path', function () { - it('should return 200 status code and items', async function () { - const requestParams: GetItemsQueryParams = { command_name: '123', limit: 5, disable_fuzziness: false }; - - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1234, ITEM_1235, ITEM_1236], expect) - ); - }); - - it('should return 200 status code and items in tile RIT', async function () { - const requestParams: GetItemsQueryParams = { command_name: '123', tile: 'RIT', limit: 5, disable_fuzziness: false }; - - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1234, ITEM_1235, ITEM_1236], expect) - ); - }); - - it('should return 200 status code and no items in tile RIC', async function () { - const requestParams: GetItemsQueryParams = { command_name: '123', tile: 'RIC', limit: 5, disable_fuzziness: false }; - - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [], expect) - ); - }); - - it('should return 200 status code and tiles biased by geo_context (bbox)', async function () { - const requestParams: GetItemsQueryParams = { - command_name: '123', - geo_context: { bbox: [12.507611205446722, 41.90406708449768, 12.517586703397825, 41.90966573813128] }, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1236, ITEM_1234, ITEM_1235], expect) - ); - }); - - it('should return 200 status code and tiles filtered by geo_context (bbox)', async function () { - const requestParams: GetItemsQueryParams = { - command_name: '123', - geo_context: { bbox: [12.507611205446722, 41.90406708449768, 12.517586703397825, 41.90966573813128] }, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1236], expect) - ); - }); - - it('should return 200 status code and tiles biased by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.512345456194254, lat: 41.90673389969385, radius: 10 }; - - const requestParams: GetItemsQueryParams = { - command_name: '123', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetItemsQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1236, ITEM_1234, ITEM_1235], expect) - ); - }); - - it('should return 200 status code and tiles filtered by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.512345456194254, lat: 41.90673389969385, radius: 10 }; - const requestParams: GetItemsQueryParams = { - command_name: '123', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetItemsQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1236], expect) - ); - }); - - it('should return 200 status code and tiles biased by geo_context (UTM)', async function () { - const geo_context = { x: 293671, y: 4642414, zone: 33, radius: 10 }; - const requestParams: GetItemsQueryParams = { - command_name: '123', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetItemsQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1236, ITEM_1234, ITEM_1235], expect) - ); - }); - - it('should return 200 status code and tiles filtered by geo_context (UTM)', async function () { - const geo_context = { x: 293671, y: 4642414, zone: 33, radius: 10 }; - const requestParams: GetItemsQueryParams = { - command_name: '123', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetItemsQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1236], expect) - ); - }); - - it('should return 200 status code and send 1234 item as disable_fuzziness is true', async function () { - const requestParams: GetItemsQueryParams = { - command_name: '1234', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1234], expect) - ); - }); - - it('should return 200 status code and no item as disable_fuzziness is true', async function () { - const requestParams: GetItemsQueryParams = { - command_name: '123', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [], expect) - ); - }); - - it('should return 200 status code and items in sub_tile', async function () { - const requestParams: GetItemsQueryParams = { - command_name: '123', - sub_tile: '37', - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getItems(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ITEM_1234, ITEM_1235], expect) - ); - }); - }); - describe('Bad Path', function () { - // All requests with status code of 400 - it("should return 400 status code and error message when item's command_name", async function () { - const response = await requestSender.getItems({} as unknown as GetItemsQueryParams); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "request/query must have required property 'command_name'", - }); - }); - - it('should return 400 status code and error message when command_name value is empty', async function () { - const response = await requestSender.getItems({ command_name: '', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "Empty value found for query parameter 'command_name'", - }); - }); - - it('should return 400 status code and error message when tile value is invalid', async function () { - let response = await requestSender.getItems({ command_name: '1234', tile: 'invalid', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: 'request/query/tile must NOT have more than 3 characters', - }); - - response = await requestSender.getItems({ command_name: '1234', tile: 'i', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: 'request/query/tile must NOT have fewer than 3 characters', - }); - }); - - test.each(['invalid', '6a6', '06', '-11', '6 ', ' 6', ' ', ' 6 ', ''])( - 'should return 400 status code and error message when sub_tile value is invalid', - async (sub_tile) => { - const response = await requestSender.getItems({ command_name: '1234', tile: 'RIT', sub_tile, limit: 5, disable_fuzziness: false }); - - const message = sub_tile ? 'request/query/sub_tile must match pattern "^[1-9][0-9]*$"' : "Empty value found for query parameter 'sub_tile'"; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - } - ); - - test.each>([ - { - geo_context: { x: 300850, y: 4642203, zone: 33, radius: 100 }, - }, - { - geo_context: { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }, - }, - { - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - }, - { - geo_context_mode: GeoContextMode.BIAS, - }, - { - geo_context_mode: GeoContextMode.FILTER, - }, - ])('should return 400 and message that geo_context and geo_context_mode must be both defined or both undefined', async function (requestParams) { - const response = await requestSender.getItems({ command_name: '1234', limit: 5, disable_fuzziness: false, ...requestParams }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: '/control/utils/geoContextQuery: geo_context and geo_context_mode must be both defined or both undefined', - }); - }); - - test.each>([ - { - geo_context: { x: 300850, y: 4642203, zone: 33, radius: 100 }, - }, - { - geo_context: { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }, - }, - { - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - }, - { - geo_context_mode: GeoContextMode.BIAS, - }, - { - geo_context_mode: GeoContextMode.FILTER, - }, - ])('should return 400 and message that geo_context and geo_context_mode must be both defined or both undefined', async function (requestParams) { - const response = await requestSender.getItems({ command_name: '1234', limit: 5, disable_fuzziness: false, ...requestParams }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: '/control/utils/geoContextQuery: geo_context and geo_context_mode must be both defined or both undefined', - }); - }); - - test.each>([ - { - disable_fuzziness: '' as unknown as boolean, - }, - { - disable_fuzziness: 'True' as unknown as boolean, - }, - { - disable_fuzziness: 'False' as unknown as boolean, - }, - ])('should return 400 status code and error message when disable_fuzziness value is invalid', async function (requestParams) { - const response = await requestSender.getItems({ command_name: '1234', limit: 5, ...requestParams }); - - const message = - (requestParams.disable_fuzziness as unknown as string) === '' - ? "Empty value found for query parameter 'disable_fuzziness'" - : 'request/query/disable_fuzziness must be boolean'; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - }); - - test.each>([ - { - limit: '' as unknown as number, - }, - { - limit: 0, - }, - { - limit: 101, - }, - ])('should return 400 status code and error message when limit value is invalid', async function (requestParams) { - const response = await requestSender.getItems({ command_name: '1234', disable_fuzziness: false, ...requestParams }); - - const message = - (requestParams.limit as unknown as string) === '' - ? "Empty value found for query parameter 'limit'" - : requestParams.limit < 1 - ? 'request/query/limit must be >= 1' - : 'request/query/limit must be <= 15'; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - }); - - test.each<(keyof GeoContext)[][]>([[['lat', 'lon', 'radius']], [['x', 'y', 'zone', 'radius']]])( - 'should return 400 for all invalid geo_context object for the keys %s', - async function (keys) { - function generateCombinations(keys: (keyof GeoContext)[]): GeoContext[] { - const combinations: object[] = []; - - function backtrack(current: object, remainingKeys: string[]): void { - if (remainingKeys.length === 0) { - combinations.push(current); - return; - } - - const key = remainingKeys[0]; - const remaining = remainingKeys.slice(1); - - backtrack({ ...current, [key]: 1 }, remaining); - backtrack(current, remaining); - } - - backtrack({}, keys); - - return combinations; - } - - const geoContexts = generateCombinations(keys); - - for (const geo_context of geoContexts) { - if (Object.keys(geo_context).length === keys.length) { - continue; - } - const response = await requestSender.getItems({ - command_name: '1234', - limit: 5, - disable_fuzziness: false, - geo_context: JSON.stringify(geo_context) as unknown as GetItemsQueryParams['geo_context'], - geo_context_mode: GeoContextMode.BIAS, - }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: - 'geo_context validation: geo_context must contain one of the following: {"bbox": [number,number,number,number] | [number,number,number,number,number,number]}, {"lat": number, "lon": number, "radius": number}, or {"x": number, "y": number, "zone": number, "radius": number}', - }); - } - } - ); - }); - describe('Sad Path', function () { - // All requests with status code 4XX-5XX - }); -}); diff --git a/tests/integration/control/item/mockObjects.ts b/tests/integration/control/item/mockObjects.ts deleted file mode 100644 index 96a62178..00000000 --- a/tests/integration/control/item/mockObjects.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-magic-numbers */ -import { Item } from '../../../../src/control/item/models/item'; - -export const ITEM_1234: Item = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [12.432792582620323, 41.9327692706986], - [12.432648028923637, 41.93209008126263], - [12.43295235249525, 41.93189198298137], - [12.435105441764364, 41.93235609798671], - [12.435516278586334, 41.93274663116725], - [12.43637599267521, 41.93308056343986], - [12.436026020567567, 41.93386161853829], - [12.432792582620323, 41.9327692706986], - ], - ], - type: 'Polygon', - }, - properties: { - LAYER_NAME: 'CONTROL.ITEMS', - OBJECT_COMMAND_NAME: '1234', - TILE_NAME: 'RIT', - SUB_TILE_ID: '37', - ENTITY_HEB: 'hotel', - TYPE: 'ITEM', - }, -}; - -export const ITEM_1235: Item = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [12.454224879037952, 41.93533128513971], - [12.4536294370983, 41.93493944442386], - [12.453354617740985, 41.934479454425286], - [12.453308814515196, 41.93386612926582], - [12.453789748389482, 41.933065390320394], - [12.454660009686364, 41.93255427509931], - [12.455667680662373, 41.93265649847106], - [12.456217319376208, 41.933355020461335], - [12.456217319376208, 41.934309086916585], - [12.45569058227531, 41.935092773686364], - [12.454957730656645, 41.93534832163783], - [12.454224879037952, 41.93533128513971], - ], - ], - type: 'Polygon', - }, - properties: { - LAYER_NAME: 'CONTROL.ITEMS', - OBJECT_COMMAND_NAME: '1235', - TILE_NAME: 'RIT', - SUB_TILE_ID: '37', - ENTITY_HEB: 'oplympic stadium', - TYPE: 'ITEM', - }, -}; - -export const ITEM_1236: Item = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [12.508591887289015, 41.908719199148294], - [12.5109416268715, 41.90414674778023], - [12.515619548794433, 41.905558627360875], - [12.514541686600694, 41.906697734719614], - [12.514929038978266, 41.9065233923005], - [12.515546733877699, 41.90711121610428], - [12.51436197480865, 41.90815873484965], - [12.513612640013008, 41.90876915151631], - [12.512974692823434, 41.90872393566693], - [12.512863305218872, 41.90807583830912], - [12.513561316395936, 41.90754911551221], - [12.513962675013943, 41.907221129768516], - [12.51419876831929, 41.906986853205694], - [12.513954805237745, 41.906893142339726], - [12.512516460769376, 41.90804005137895], - [12.511855399516293, 41.90857302052004], - [12.51176096219396, 41.908660872149284], - [12.508762577222939, 41.908924426310676], - [12.508591887289015, 41.908719199148294], - ], - ], - type: 'Polygon', - }, - properties: { - LAYER_NAME: 'CONTROL.ITEMS', - OBJECT_COMMAND_NAME: '1236', - TILE_NAME: 'RIT', - SUB_TILE_ID: '38', - ENTITY_HEB: 'hospital', - TYPE: 'ITEM', - }, -}; diff --git a/tests/integration/control/route/mockObjects.ts b/tests/integration/control/route/mockObjects.ts deleted file mode 100644 index eb60440b..00000000 --- a/tests/integration/control/route/mockObjects.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-magic-numbers */ -import { Route } from '../../../../src/control/route/models/route'; - -export const ROUTE_VIA_CAMILLUCCIA_A: Route = { - type: 'Feature', - geometry: { - coordinates: [ - [12.443243654365062, 41.93890891937724], - [12.442636325462843, 41.93804302794496], - [12.442828646282095, 41.93725242115204], - [12.443405608739027, 41.93556576056804], - [12.44388471483822, 41.93370245333628], - [12.445132826188399, 41.93084467089824], - [12.445812354584433, 41.93031849813403], - [12.445819951137906, 41.930296776658196], - ], - type: 'LineString', - }, - properties: { - OBJECT_COMMAND_NAME: 'via camillucciaA', - ENTITY_HEB: 'route', - TYPE: 'ROUTE', - LAYER_NAME: 'CONTROL.ROUTES', - }, -}; -export const ROUTE_VIA_CAMILLUCCIA_B: Route = { - type: 'Feature', - geometry: { - coordinates: [ - [12.445818466287847, 41.93029376141277], - [12.446047161911423, 41.930040913942264], - [12.446171038707206, 41.9297762500957], - [12.446167862379838, 41.92945014491775], - [12.446075748864388, 41.9286136066203], - ], - type: 'LineString', - }, - properties: { - OBJECT_COMMAND_NAME: 'via camillucciaB', - ENTITY_HEB: 'route', - TYPE: 'ROUTE', - LAYER_NAME: 'CONTROL.ROUTES', - }, -}; - -export const CONTROL_POINT_OLIMPIADE_111: Route = { - type: 'Feature', - geometry: { - coordinates: [12.475638293442415, 41.932360642739155], - type: 'Point', - }, - properties: { - OBJECT_COMMAND_NAME: '111', - ENTITY_HEB: 'control point', - TIED_TO: 'olimpiade', - TYPE: 'ITEM' as never, - LAYER_NAME: 'CONTROL_GIL_GDB.CTR_CONTROL_POINT_CROSS_N', - }, -}; - -export const CONTROL_POINT_OLIMPIADE_112: Route = { - type: 'Feature', - geometry: { - coordinates: [12.474175672012962, 41.932217551210556], - type: 'Point', - }, - properties: { - OBJECT_COMMAND_NAME: '112', - ENTITY_HEB: 'control point', - TIED_TO: 'olimpiade', - TYPE: 'ITEM' as never, - LAYER_NAME: 'CONTROL_GIL_GDB.CTR_CONTROL_POINT_CROSS_N', - }, -}; diff --git a/tests/integration/control/route/route.spec.ts b/tests/integration/control/route/route.spec.ts deleted file mode 100644 index 5b117c7a..00000000 --- a/tests/integration/control/route/route.spec.ts +++ /dev/null @@ -1,565 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import jsLogger from '@map-colonies/js-logger'; -import { trace } from '@opentelemetry/api'; -import httpStatusCodes from 'http-status-codes'; -import { Application } from 'express'; -import { DependencyContainer } from 'tsyringe'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import { getApp } from '../../../../src/app'; -import { SERVICES } from '../../../../src/common/constants'; -import { GetRoutesQueryParams } from '../../../../src/control/route/controllers/routeController'; -import { Route } from '../../../../src/control/route/models/route'; -import { CommonRequestParameters, GenericGeocodingResponse, GeoContext, GeoContextMode } from '../../../../src/common/interfaces'; -import { S3_REPOSITORY_SYMBOL } from '../../../../src/common/s3/s3Repository'; -import { cronLoadTileLatLonDataSymbol } from '../../../../src/latLon/DAL/latLonDAL'; -import { expectedResponse } from '../utils'; -import { RouteRequestSender } from './helpers/requestSender'; -import { ROUTE_VIA_CAMILLUCCIA_A, ROUTE_VIA_CAMILLUCCIA_B, CONTROL_POINT_OLIMPIADE_111, CONTROL_POINT_OLIMPIADE_112 } from './mockObjects'; - -describe('/search/control/route', function () { - let requestSender: RouteRequestSender; - let app: { app: Application; container: DependencyContainer }; - - beforeEach(async function () { - app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: S3_REPOSITORY_SYMBOL, provider: { useValue: {} } }, - { token: SERVICES.S3_CLIENT, provider: { useValue: {} } }, - { token: cronLoadTileLatLonDataSymbol, provider: { useValue: {} } }, - ], - useChild: true, - }); - - requestSender = new RouteRequestSender(app.app); - }); - - afterAll(async function () { - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); - - jest.clearAllTimers(); - }); - - describe('Happy Path', function () { - it('should return 200 status code and routes', async function () { - const requestParams: GetRoutesQueryParams = { command_name: 'via camilluccia', limit: 5, disable_fuzziness: false }; - - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_A, ROUTE_VIA_CAMILLUCCIA_B], expect) - ); - }); - - it('should return 200 status code and routes biased by geo_context (bbox)', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - geo_context: { bbox: [12.445945519411595, 41.92899524904075, 12.446385440853476, 41.9292587345808] }, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B, ROUTE_VIA_CAMILLUCCIA_A], expect) - ); - }); - - it('should return 200 status code and routes filtered by geo_context (bbox)', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - geo_context: { bbox: [12.445945519411595, 41.92899524904075, 12.446385440853476, 41.9292587345808] }, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B], expect) - ); - }); - - it('should return 200 status code and routes biased by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.446085277848027, lat: 41.928658505847835, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B, ROUTE_VIA_CAMILLUCCIA_A], expect) - ); - }); - - it('should return 200 status code and routes filtered by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.446085277848027, lat: 41.928658505847835, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B], expect) - ); - }); - - it('should return 200 status code and routes biased by geo_context (UTM)', async function () { - const geo_context = { x: 288247, y: 4645010, zone: 33, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B, ROUTE_VIA_CAMILLUCCIA_A], expect) - ); - }); - - it('should return 200 status code and routes filtered by geo_context (UTM)', async function () { - const geo_context = { x: 288247, y: 4645010, zone: 33, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B], expect) - ); - }); - - it('should return 200 status code and send via camillucciaB route as disable_fuzziness is true', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'via camillucciaB', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [ROUTE_VIA_CAMILLUCCIA_B], expect) - ); - }); - - it('should return 200 status code and no route as disable_fuzziness is true', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'via camilluccia', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [], expect) - ); - }); - - it('should return 200 status code and control points biased by geo_context (bbox)', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '11', - geo_context: { bbox: [12.473743746822265, 41.93195262135879, 12.474626229200709, 41.93249150688004] }, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_112, CONTROL_POINT_OLIMPIADE_111], expect) - ); - }); - - it('should return 200 status code and control points filtered by geo_context (bbox)', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '11', - geo_context: { bbox: [12.473743746822265, 41.93195262135879, 12.474626229200709, 41.93249150688004] }, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_112], expect) - ); - }); - - it('should return 200 status code and control points biased by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.474175672012962, lat: 41.932217551210556, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '11', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_112, CONTROL_POINT_OLIMPIADE_111], expect) - ); - }); - - it('should return 200 status code and control points filtered by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.474175672012962, lat: 41.932217551210556, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '11', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_112], expect) - ); - }); - - it('should return 200 status code and control points biased by geo_context (UTM)', async function () { - const geo_context = { x: 290588, y: 4645336, zone: 33, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '11', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_112, CONTROL_POINT_OLIMPIADE_111], expect) - ); - }); - - it('should return 200 status code and control points filtered by geo_context (UTM)', async function () { - const geo_context = { x: 290588, y: 4645336, zone: 33, radius: 10 }; - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '11', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_112], expect) - ); - }); - - it('should return 200 status code and control points', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '111', - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_111, CONTROL_POINT_OLIMPIADE_112], expect) - ); - }); - - it('should return 200 status code and 111 control_point with disable_fuzziness true', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'olimpiade', - control_point: '111', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [CONTROL_POINT_OLIMPIADE_111], expect) - ); - }); - - it('should return 200 status code and return no control points as no control points with disable_fuzziness true', async function () { - const requestParams: GetRoutesQueryParams = { - command_name: 'camilluccia', - control_point: '111', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getRoutes(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [], expect) - ); - }); - }); - describe('Bad Path', function () { - // All requests with status code of 400 - it('should return 400 status code and error message when empty object is passed', async function () { - const response = await requestSender.getRoutes({} as unknown as GetRoutesQueryParams); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "request/query must have required property 'command_name'", - }); - }); - - it('should return 400 status code and error message when command_name is empty', async function () { - const response = await requestSender.getRoutes({ command_name: '', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "Empty value found for query parameter 'command_name'", - }); - }); - - test.each(['invalid', '6a6', '06', '-11', '6 ', ' 6', ' ', ' 6 ', ''])( - 'should return 400 status code and error message when value: "%s" for control_point value is invalid', - async (control_point) => { - const response = await requestSender.getRoutes({ command_name: 'olimpiade', control_point, limit: 5, disable_fuzziness: false }); - - const message = control_point - ? 'request/query/control_point must match pattern "^[1-9][0-9]*$"' - : "Empty value found for query parameter 'control_point'"; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - } - ); - - test.each>([ - { - geo_context: { x: 300850, y: 4642203, zone: 33, radius: 100 }, - }, - { - geo_context: { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }, - }, - { - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - }, - { - geo_context_mode: GeoContextMode.BIAS, - }, - { - geo_context_mode: GeoContextMode.FILTER, - }, - ])('should return 400 and message that geo_context and geo_context_mode must be both defined or both undefined', async function (requestParams) { - const response = await requestSender.getRoutes({ command_name: '1234', limit: 5, disable_fuzziness: false, ...requestParams }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: '/control/utils/geoContextQuery: geo_context and geo_context_mode must be both defined or both undefined', - }); - }); - - test.each>([ - { - geo_context: { x: 300850, y: 4642203, zone: 33, radius: 100 }, - }, - { - geo_context: { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }, - }, - { - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - }, - { - geo_context_mode: GeoContextMode.BIAS, - }, - { - geo_context_mode: GeoContextMode.FILTER, - }, - ])('should return 400 and message that geo_context and geo_context_mode must be both defined or both undefined', async function (requestParams) { - const response = await requestSender.getRoutes({ command_name: '1234', limit: 5, disable_fuzziness: false, ...requestParams }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: '/control/utils/geoContextQuery: geo_context and geo_context_mode must be both defined or both undefined', - }); - }); - - test.each>([ - { - disable_fuzziness: '' as unknown as boolean, - }, - { - disable_fuzziness: 'True' as unknown as boolean, - }, - { - disable_fuzziness: 'False' as unknown as boolean, - }, - ])('should return 400 status code and error message when disable_fuzziness value is invalid', async function (requestParams) { - const response = await requestSender.getRoutes({ command_name: '1234', limit: 5, ...requestParams }); - - const message = - (requestParams.disable_fuzziness as unknown as string) === '' - ? "Empty value found for query parameter 'disable_fuzziness'" - : 'request/query/disable_fuzziness must be boolean'; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - }); - - test.each>([ - { - limit: '' as unknown as number, - }, - { - limit: 0, - }, - { - limit: 101, - }, - ])('should return 400 status code and error message when limit value is invalid', async function (requestParams) { - const response = await requestSender.getRoutes({ command_name: '1234', disable_fuzziness: false, ...requestParams }); - - const message = - (requestParams.limit as unknown as string) === '' - ? "Empty value found for query parameter 'limit'" - : requestParams.limit < 1 - ? 'request/query/limit must be >= 1' - : 'request/query/limit must be <= 15'; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - }); - - test.each<(keyof GeoContext)[][]>([[['lat', 'lon', 'radius']], [['x', 'y', 'zone', 'radius']]])( - 'should return 400 for all invalid geo_context object for the keys %s', - async function (keys) { - function generateCombinations(keys: (keyof GeoContext)[]): GeoContext[] { - const combinations: object[] = []; - - function backtrack(current: object, remainingKeys: string[]): void { - if (remainingKeys.length === 0) { - combinations.push(current); - return; - } - - const key = remainingKeys[0]; - const remaining = remainingKeys.slice(1); - - backtrack({ ...current, [key]: 1 }, remaining); - backtrack(current, remaining); - } - - backtrack({}, keys); - - return combinations; - } - - const geoContexts = generateCombinations(keys); - - for (const geo_context of geoContexts) { - if (Object.keys(geo_context).length === keys.length) { - continue; - } - const response = await requestSender.getRoutes({ - command_name: '1234', - limit: 5, - disable_fuzziness: false, - geo_context: JSON.stringify(geo_context) as unknown as GetRoutesQueryParams['geo_context'], - geo_context_mode: GeoContextMode.BIAS, - }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: - 'geo_context validation: geo_context must contain one of the following: {"bbox": [number,number,number,number] | [number,number,number,number,number,number]}, {"lat": number, "lon": number, "radius": number}, or {"x": number, "y": number, "zone": number, "radius": number}', - }); - } - } - ); - }); - describe('Sad Path', function () { - // All requests with status code 4XX-5XX - }); -}); diff --git a/tests/integration/control/tile/mockObjects.ts b/tests/integration/control/tile/mockObjects.ts deleted file mode 100644 index 2292a2cd..00000000 --- a/tests/integration/control/tile/mockObjects.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-magic-numbers */ -import { Tile } from '../../../../src/control/tile/models/tile'; - -export const RIT_TILE: Tile = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [12.539507865186607, 41.851751203650096], - [12.536787075186538, 41.94185043165008], - [12.42879133518656, 41.93952837265009], - [12.431625055186686, 41.84943698365008], - [12.539507865186607, 41.851751203650096], - ], - ], - type: 'Polygon', - }, - properties: { - TILE_NAME: 'RIT', - TYPE: 'TILE', - }, -}; - -export const RIC_TILE: Tile = { - type: 'Feature', - properties: { - TILE_NAME: 'RIC', - TYPE: 'TILE', - }, - geometry: { - coordinates: [ - [ - [12.64750356570994, 41.854073129598774], - [12.64478277570987, 41.94417235759876], - [12.536787035709892, 41.941850298598766], - [12.539620755710018, 41.85175890959876], - [12.64750356570994, 41.854073129598774], - ], - ], - type: 'Polygon', - }, -}; - -export const SUB_TILE_66: Tile = { - type: 'Feature', - properties: { - SUB_TILE_ID: '66', - TILE_NAME: 'RIT', - TYPE: 'SUB_TILE', - }, - geometry: { - coordinates: [ - [ - [12.44999804325252, 41.930226156898485], - [12.45011422425253, 41.939247112898514], - [12.439626097252557, 41.93934663789851], - [12.439510908252515, 41.93032564689851], - [12.44999804325252, 41.930226156898485], - ], - ], - type: 'Polygon', - }, -}; - -export const SUB_TILE_65: Tile = { - type: 'Feature', - properties: { - SUB_TILE_ID: '65', - TILE_NAME: 'RIT', - TYPE: 'SUB_TILE', - }, - geometry: { - coordinates: [ - [ - [12.439530324602458, 41.93031190061167], - [12.439646505602468, 41.9393328566117], - [12.429158378602494, 41.939432381611695], - [12.429043189602453, 41.930411390611695], - [12.439530324602458, 41.93031190061167], - ], - ], - type: 'Polygon', - }, -}; diff --git a/tests/integration/control/tile/tile.spec.ts b/tests/integration/control/tile/tile.spec.ts deleted file mode 100644 index c44d6967..00000000 --- a/tests/integration/control/tile/tile.spec.ts +++ /dev/null @@ -1,580 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import jsLogger from '@map-colonies/js-logger'; -import { trace } from '@opentelemetry/api'; -import { DependencyContainer } from 'tsyringe'; -import { Application } from 'express'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import httpStatusCodes from 'http-status-codes'; -import { BBox } from 'geojson'; -import { getApp } from '../../../../src/app'; -import { SERVICES } from '../../../../src/common/constants'; -import { GetTilesQueryParams } from '../../../../src/control/tile/controllers/tileController'; -import { Tile } from '../../../../src/control/tile/models/tile'; -import { CommonRequestParameters, GenericGeocodingResponse, GeoContext, GeoContextMode } from '../../../../src/common/interfaces'; -import { S3_REPOSITORY_SYMBOL } from '../../../../src/common/s3/s3Repository'; -import { cronLoadTileLatLonDataSymbol } from '../../../../src/latLon/DAL/latLonDAL'; -import { expectedResponse } from '../utils'; -import { TileRequestSender } from './helpers/requestSender'; -import { RIC_TILE, RIT_TILE, SUB_TILE_65, SUB_TILE_66 } from './mockObjects'; - -describe('/search/control/tiles', function () { - let requestSender: TileRequestSender; - let app: { app: Application; container: DependencyContainer }; - - beforeEach(async function () { - app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: S3_REPOSITORY_SYMBOL, provider: { useValue: {} } }, - { token: SERVICES.S3_CLIENT, provider: { useValue: {} } }, - { token: cronLoadTileLatLonDataSymbol, provider: { useValue: {} } }, - ], - useChild: true, - }); - - requestSender = new TileRequestSender(app.app); - }); - - afterAll(async function () { - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); - - jest.clearAllTimers(); - }); - - describe('Happy Path', function () { - it('should return 200 status code and tiles', async function () { - const requestParams: GetTilesQueryParams = { tile: 'RIT', limit: 5, disable_fuzziness: false }; - - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIT_TILE, RIC_TILE], expect) - ); - }); - - it('should return 200 status code and tiles biased by geo_context (bbox)', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RI', - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE, RIT_TILE], expect) - ); - }); - - it('should return 200 status code and tiles filtered by geo_context (bbox)', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RI', - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE], expect) - ); - }); - - it('should return 200 status code and tiles biased by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }; - const requestParams: GetTilesQueryParams = { - tile: 'RI', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE, RIT_TILE], expect) - ); - }); - - it('should return 200 status code and tiles filtered by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }; - const requestParams: GetTilesQueryParams = { - tile: 'RI', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE], expect) - ); - }); - - it('should return 200 status code and tiles biased by geo_context (UTM)', async function () { - const geo_context = { x: 300850, y: 4642203, zone: 33, radius: 100 }; - const requestParams: GetTilesQueryParams = { - tile: 'RI', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE, RIT_TILE], expect) - ); - }); - - it('should return 200 status code and tiles filtered by geo_context (UTM)', async function () { - const geo_context = { x: 300850, y: 4642203, zone: 33, radius: 100 }; - const requestParams: GetTilesQueryParams = { - tile: 'RI', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE], expect) - ); - }); - - it('should return 200 status code and send RIC tile as disable_fuzziness is true', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RIC', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE], expect) - ); - }); - - it('should return 200 status code and no tile as disable_fuzziness is true', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RI', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [], expect) - ); - }); - - it('should return 200 status code and sub tiles', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RIT', - sub_tile: '66', - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [SUB_TILE_66, SUB_TILE_65], expect) - ); - }); - - it('should return 200 status code and no sub tiles as disable_fuzziness: true', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RIT', - sub_tile: '11', - limit: 5, - disable_fuzziness: true, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [], expect) - ); - }); - - it('should return 200 status code and sub_tile "66" filtered by geo_context (UTM)', async function () { - const geo_context = { x: 288240, y: 4645787, zone: 33, radius: 100 }; - const requestParams: GetTilesQueryParams = { - tile: 'RIT', - sub_tile: '66', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - // expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [SUB_TILE_66], expect) - ); - }); - - it('should return 200 status code and sub_tile "66" filtered by geo_context (WGS84)', async function () { - const geo_context = { lon: 12.445715777842793, lat: 41.935651320498835, radius: 100 }; - const requestParams: GetTilesQueryParams = { - tile: 'RIT', - sub_tile: '66', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - // expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [SUB_TILE_66], expect) - ); - }); - it('should return 200 status code and sub_tile "66" filtered by geo_context (bbox)', async function () { - const requestParams: GetTilesQueryParams = { - tile: 'RIT', - sub_tile: '66', - geo_context: { bbox: [12.442900073406776, 41.92988103633658, 12.450060653825801, 41.939618057332865] }, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [SUB_TILE_66], expect) - ); - }); - - it('should return 200 status code and control tiles by MGRS', async function () { - const requestParams: GetTilesQueryParams = { - mgrs: '33TTG958462', - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIT_TILE, RIC_TILE], expect) - ); - }); - - it('should return 200 status code and control tiles by MGRS and geo_context filter (WGS84)', async function () { - const geo_context: { bbox: BBox } = { bbox: [12.593772987581218, 41.89988905812697, 12.593772987581218, 41.89988905812697] }; - const requestParams: GetTilesQueryParams = { - mgrs: '33TTG958462', - geo_context, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE], expect) - ); - }); - - it('should return 200 status code and control tiles by MGRS and geo_context bias (WGS84)', async function () { - const geo_context: { bbox: BBox } = { bbox: [12.593772987581218, 41.89988905812697, 12.593772987581218, 41.89988905812697] }; - const requestParams: GetTilesQueryParams = { - mgrs: '33TTG958462', - geo_context, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: false, - }; - const response = await requestSender.getTiles({ - ...requestParams, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - }); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject>>( - expectedResponse(requestParams, [RIC_TILE, RIT_TILE], expect) - ); - }); - }); - describe('Bad Path', function () { - // All requests with status code of 400 - it('should return 400 status code and error message when tile or mgrs is not defined', async function () { - const response = await requestSender.getTiles({} as unknown as GetTilesQueryParams); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "/control/tiles: only one of 'tile' or 'mgrs' query parameter must be defined", - }); - }); - - it('should return 400 status code and error message when tile value is empty', async function () { - const response = await requestSender.getTiles({ tile: '', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "Empty value found for query parameter 'tile'", - }); - }); - - it('should return 400 status code and error message when tile value is invalid', async function () { - let response = await requestSender.getTiles({ tile: 'invalid', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: 'request/query/tile must NOT have more than 3 characters', - }); - - response = await requestSender.getTiles({ tile: 'i', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: 'request/query/tile must NOT have fewer than 2 characters', - }); - }); - - it('should return 400 status code and error message when mgrs value is invalid', async function () { - let response = await requestSender.getTiles({ mgrs: '', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "Empty value found for query parameter 'mgrs'", - }); - - response = await requestSender.getTiles({ tile: 'i', limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: 'request/query/tile must NOT have fewer than 2 characters', - }); - }); - - test.each(['{dsa}', '0', 'z', '000', '!321'])( - 'should return 400 status code and error message when mgrs tile is invalid', - async (mgrs) => { - const response = await requestSender.getTiles({ mgrs, limit: 5, disable_fuzziness: false }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: `Invalid MGRS: ${mgrs}`, - }); - } - ); - - test.each([[[1]], [[1, 1]], [[1, 1, 1]], [[1, 1, 1, 1, 1]]])( - 'should return 400 status code and error message when bbox not containing 4 or 6 values', - async function (bbox) { - const response = await requestSender.getTiles({ - tile: 'RIT', - limit: 5, - disable_fuzziness: false, - geo_context: { bbox: bbox as BBox }, - geo_context_mode: GeoContextMode.FILTER, - }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: - 'geo_context validation: geo_context must contain one of the following: {"bbox": [number,number,number,number] | [number,number,number,number,number,number]}, {"lat": number, "lon": number, "radius": number}, or {"x": number, "y": number, "zone": number, "radius": number}', - }); - } - ); - - test.each(['invalid', '6a6', '06', '-11', '6 ', ' 6', ' ', ' 6 ', ''])( - 'should return 400 status code and error message when sub_tile value is invalid', - async (sub_tile) => { - const response = await requestSender.getTiles({ tile: 'RIT', sub_tile, limit: 5, disable_fuzziness: false }); - - const message = sub_tile ? 'request/query/sub_tile must match pattern "^[1-9][0-9]*$"' : "Empty value found for query parameter 'sub_tile'"; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - } - ); - - test.each>([ - { - geo_context: { x: 300850, y: 4642203, zone: 33, radius: 100 }, - }, - { - geo_context: { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }, - }, - { - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - }, - { - geo_context_mode: GeoContextMode.BIAS, - }, - { - geo_context_mode: GeoContextMode.FILTER, - }, - ])('should return 400 and message that geo_context and geo_context_mode must be both defined or both undefined', async function (requestParams) { - const response = await requestSender.getTiles({ tile: 'RIT', limit: 5, disable_fuzziness: false, ...requestParams }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: '/control/utils/geoContextQuery: geo_context and geo_context_mode must be both defined or both undefined', - }); - }); - - test.each>([ - { - disable_fuzziness: '' as unknown as boolean, - }, - { - disable_fuzziness: 'True' as unknown as boolean, - }, - { - disable_fuzziness: 'False' as unknown as boolean, - }, - ])('should return 400 status code and error message when disable_fuzziness value is invalid', async function (requestParams) { - const response = await requestSender.getTiles({ tile: 'RIT', limit: 5, ...requestParams }); - - const message = - (requestParams.disable_fuzziness as unknown as string) === '' - ? "Empty value found for query parameter 'disable_fuzziness'" - : 'request/query/disable_fuzziness must be boolean'; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - }); - - test.each>([ - { - limit: '' as unknown as number, - }, - { - limit: 0, - }, - { - limit: 101, - }, - ])('should return 400 status code and error message when limit value is invalid', async function (requestParams) { - const response = await requestSender.getTiles({ tile: 'RIT', disable_fuzziness: false, ...requestParams }); - - const message = - (requestParams.limit as unknown as string) === '' - ? "Empty value found for query parameter 'limit'" - : requestParams.limit < 1 - ? 'request/query/limit must be >= 1' - : 'request/query/limit must be <= 15'; - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message, - }); - }); - - test.each<(keyof GeoContext)[][]>([[['lat', 'lon', 'radius']], [['x', 'y', 'zone', 'radius']]])( - 'should return 400 for all invalid geo_context object for the keys %s', - async function (keys) { - function generateCombinations(keys: (keyof GeoContext)[]): GeoContext[] { - const combinations: object[] = []; - - function backtrack(current: object, remainingKeys: string[]): void { - if (remainingKeys.length === 0) { - combinations.push(current); - return; - } - - const key = remainingKeys[0]; - const remaining = remainingKeys.slice(1); - - backtrack({ ...current, [key]: 1 }, remaining); - backtrack(current, remaining); - } - - backtrack({}, keys); - - return combinations; - } - - const geoContexts = generateCombinations(keys); - - for (const geo_context of geoContexts) { - if (Object.keys(geo_context).length === keys.length) { - continue; - } - const response = await requestSender.getTiles({ - tile: 'RIT', - limit: 5, - disable_fuzziness: false, - geo_context: JSON.stringify(geo_context) as unknown as GetTilesQueryParams['geo_context'], - geo_context_mode: GeoContextMode.BIAS, - }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: - 'geo_context validation: geo_context must contain one of the following: {"bbox": [number,number,number,number] | [number,number,number,number,number,number]}, {"lat": number, "lon": number, "radius": number}, or {"x": number, "y": number, "zone": number, "radius": number}', - }); - } - } - ); - }); - describe('Sad Path', function () { - // All requests with status code 4XX-5XX - }); -}); diff --git a/tests/integration/control/utils.ts b/tests/integration/control/utils.ts deleted file mode 100644 index 889b2f9e..00000000 --- a/tests/integration/control/utils.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { CommonRequestParameters, GenericGeocodingResponse } from '../../../src/common/interfaces'; -import { GetItemsQueryParams } from '../../../src/control/item/controllers/itemController'; -import { Item } from '../../../src/control/item/models/item'; -import { GetRoutesQueryParams } from '../../../src/control/route/controllers/routeController'; -import { Route } from '../../../src/control/route/models/route'; -import { GetTilesQueryParams } from '../../../src/control/tile/controllers/tileController'; -import { Tile } from '../../../src/control/tile/models/tile'; - -const expectedObjectWithScore = (source: T, expect: jest.Expect): GenericGeocodingResponse['features'][number] => ({ - ...source, - properties: { - ...source.properties, - score: expect.any(Number) as number, - matches: [ - { - layer: expect.any(String) as string, - source: expect.any(String) as string, - source_id: [], - }, - ], - names: { - default: [expect.any(String) as string], - display: expect.any(String) as string, - }, - }, -}); - -const expectedGeocodingElasticResponseMetrics = ( - resultsCount: number, - expect: jest.Expect -): NonNullable['geocoding']>['response'] => ({ - results_count: resultsCount, - max_score: expect.any(Number) as number, - match_latency_ms: expect.any(Number) as number, -}); - -export const expectedResponse = ( - requestParams: U, - arr: T[], - expect: jest.Expect -): GenericGeocodingResponse> => ({ - type: 'FeatureCollection', - geocoding: { - version: process.env.npm_package_version, - query: requestParams, - response: expectedGeocodingElasticResponseMetrics(arr.length, expect), - }, - features: arr.map((item) => expectedObjectWithScore(item, expect)), -}); diff --git a/tests/integration/docs/docs.spec.ts b/tests/integration/docs/docs.spec.ts index e818ec69..88f06840 100644 --- a/tests/integration/docs/docs.spec.ts +++ b/tests/integration/docs/docs.spec.ts @@ -3,8 +3,6 @@ import { trace } from '@opentelemetry/api'; import httpStatusCodes from 'http-status-codes'; import { getApp } from '../../../src/app'; import { SERVICES } from '../../../src/common/constants'; -import { S3_REPOSITORY_SYMBOL } from '../../../src/common/s3/s3Repository'; -import { cronLoadTileLatLonDataSymbol } from '../../../src/latLon/DAL/latLonDAL'; import { DocsRequestSender } from './helpers/docsRequestSender'; describe('docs', function () { @@ -14,9 +12,6 @@ describe('docs', function () { override: [ { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: S3_REPOSITORY_SYMBOL, provider: { useValue: {} } }, - { token: SERVICES.S3_CLIENT, provider: { useValue: {} } }, - { token: cronLoadTileLatLonDataSymbol, provider: { useValue: {} } }, ], useChild: true, }); diff --git a/tests/integration/geotextSearch/geotext.spec.ts b/tests/integration/geotextSearch/geotext.spec.ts new file mode 100644 index 00000000..42f0c526 --- /dev/null +++ b/tests/integration/geotextSearch/geotext.spec.ts @@ -0,0 +1,172 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import config from 'config'; +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import httpStatusCodes from 'http-status-codes'; +import nock from 'nock'; +import { getApp } from '../../../src/app'; +import { SERVICES } from '../../../src/common/constants'; +import { IApplication } from '../../../src/common/interfaces'; +import { jfkAirport, losAngelesAirport, policeAirport } from './possibleObjects'; +import { GeotextSearchRequestSender } from './helpers/requestSender'; + +describe('/query', function () { + let requestSender: GeotextSearchRequestSender; + + beforeEach(async function () { + const app = await getApp({ + override: [ + { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, + { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, + ], + useChild: true, + }); + requestSender = new GeotextSearchRequestSender(app.app); + }); + + describe('Happy Path', function () { + it('should return 200 status code and the all available regions', async function () { + const response = await requestSender.getRegions(); + + expect(response.status).toBe(httpStatusCodes.OK); + // expect(response).toSatisfyApiSpec(); + + expect(response.body).toEqual(['USA']); + }); + + it('should return 200 status code and all airports', async function () { + const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) + .post('', { tokens: ['airport'] }) + .reply(httpStatusCodes.OK, [ + { + tokens: ['airport'], + prediction: ['essence'], + }, + ]); + + const response = await requestSender.getGeotextSearch({ query: 'airport', limit: 10 }); + + expect(response.status).toBe(httpStatusCodes.OK); + + // expect(response).toSatisfyApiSpec(); + + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + geocoding: { + version: process.env.npm_package_version, + query: { + query: 'airport', + limit: 10, + name: '', + placeTypes: ['transportation'], + subPlaceTypes: ['airport'], + hierarchies: [], + }, + name: '', + }, + features: [jfkAirport(1), policeAirport(2), losAngelesAirport(3)], + }); + + tokenTypesUrlScope.done(); + }); + }); + + it('should return 200 and los angeles airport while checking the hierarchy system', async () => { + const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) + .post('', { tokens: ['airport'] }) + .reply(httpStatusCodes.OK, [ + { + tokens: ['airport'], + prediction: ['essence'], + }, + ]); + + const response = await requestSender.getGeotextSearch({ query: 'airport, los angeles', limit: 1 }); + + expect(response.status).toBe(httpStatusCodes.OK); + // expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + geocoding: { + version: process.env.npm_package_version, + query: { + query: 'airport', + limit: 1, + name: '', + placeTypes: ['transportation'], + subPlaceTypes: ['airport'], + hierarchies: [ + { + geo_json: { + coordinates: [ + [ + [-118.54430957638033, 34.07939240620722], + [-118.5350828996408, 33.695192367610986], + [-118.04596133238863, 33.47690745532634], + [-117.66265886139905, 33.379872950239346], + [-117.57145361502153, 33.63336904289318], + [-117.67279277766329, 34.23871934668085], + [-118.2605599209839, 34.28059749364003], + [-118.54430957638033, 34.07939240620722], + ], + ], + type: 'Polygon', + }, + hierarchy: 'city', + placetype: 'city', + region: 'USA', + text: 'Los Angeles', + weight: 1.1, + }, + ], + }, + name: '', + }, + features: [losAngelesAirport(1)], + }); + + tokenTypesUrlScope.done(); + }); + + it('should retrun 200 and los angeles airport while checking the region from NLP analyser', async () => { + const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) + .post('', { tokens: ['airport', 'los', 'angeles'] }) + .reply(httpStatusCodes.OK, [ + { + tokens: ['airport', 'los', 'angeles'], + prediction: ['essence', 'essence', 'name'], + }, + ]); + + const response = await requestSender.getGeotextSearch({ query: 'airport los angeles', limit: 1 }); + + expect(response.status).toBe(httpStatusCodes.OK); + + // expect(response).toSatisfyApiSpec(); + + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + geocoding: { + version: process.env.npm_package_version, + query: { + query: 'airport los angeles', + limit: 1, + name: 'angeles', + placeTypes: ['transportation'], + subPlaceTypes: ['airport'], + hierarchies: [], + }, + name: 'angeles', + }, + features: [losAngelesAirport(1)], + }); + + tokenTypesUrlScope.done(); + }); + describe('Bad Path', function () { + // All requests with status code of 400 + }); + describe('Sad Path', function () { + // All requests with status code 4XX-5XX + }); +}); diff --git a/tests/integration/geotextSearch/helpers/requestSender.ts b/tests/integration/geotextSearch/helpers/requestSender.ts new file mode 100644 index 00000000..c924f538 --- /dev/null +++ b/tests/integration/geotextSearch/helpers/requestSender.ts @@ -0,0 +1,25 @@ +import * as supertest from 'supertest'; +import { GetGeotextSearchParams } from '../../../../src/geotextSearch/interfaces'; + +export class GeotextSearchRequestSender { + public constructor(private readonly app: Express.Application) {} + + public async getGeotextSearch(queryParams?: GetGeotextSearchParams): Promise { + return supertest + .agent(this.app) + .get('/v1/query') + .set('Content-Type', 'application/json') + .set('X-API-Key', 'abc123') + .set('x-user-id', 'abc123') + .query(queryParams ?? {}); + } + + public async getRegions(): Promise { + return supertest + .agent(this.app) + .get('/v1/query/regions') + .set('Content-Type', 'application/json') + .set('X-API-Key', 'abc123') + .set('x-user-id', 'abc123'); + } +} diff --git a/tests/integration/geotextSearch/possibleObjects.ts b/tests/integration/geotextSearch/possibleObjects.ts new file mode 100644 index 00000000..5ddd36e2 --- /dev/null +++ b/tests/integration/geotextSearch/possibleObjects.ts @@ -0,0 +1,167 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/no-magic-numbers */ +export const jfkAirport = (rank: number) => ({ + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-73.81278266814672, 40.66039916690434], + [-73.8177430790069, 40.66160065836647], + [-73.82178645734075, 40.66043068890016], + [-73.82341214553732, 40.658185554886586], + [-73.82407909454082, 40.65496001884071], + [-73.8229536180974, 40.650532558586235], + [-73.82211993184252, 40.647939195437345], + [-73.81290769732489, 40.643985699842915], + [-73.79214887014153, 40.63414837731818], + [-73.78339516446982, 40.62987771430167], + [-73.7898562329419, 40.62275933562921], + [-73.78443726476769, 40.620069953803636], + [-73.7791433570518, 40.627188619100366], + [-73.77639219241223, 40.62706207167477], + [-73.77159849644941, 40.62336045339214], + [-73.77209870820208, 40.619975033140975], + [-73.77047302000595, 40.61908910045176], + [-73.76547094984971, 40.628422477310664], + [-73.75338249916041, 40.63291467256053], + [-73.74733827381596, 40.63601474373485], + [-73.7467963777506, 40.64208793530722], + [-73.752548854642, 40.64749646458006], + [-73.76213624656812, 40.65309424493557], + [-73.78181122379466, 40.66270746643491], + [-73.79106514121902, 40.66438330508498], + [-73.7957754685564, 40.665205399216404], + [-73.79856831750864, 40.66283394629252], + [-73.80390390953731, 40.66175885985783], + [-73.8073637074931, 40.66039916690434], + [-73.8109068740743, 40.66074699797244], + [-73.81278266814672, 40.66039916690434], + ], + ], + }, + properties: { + rank, + source: 'OSM', + layer: 'osm_airports', + source_id: ['03ed6d97-fc81-4340-b68a-11993554eef1'], + name: { + en: ['JFK Airport'], + fr: ['Aeropuerto JFK'], + default: ['JFK'], + display: 'JFK', + }, + placetype: 'transportation', + sub_placetype: 'airport', + regions: [ + { + region: 'USA', + sub_regions: ['New York'], + }, + ], + region: ['USA'], + sub_region: ['New York'], + }, +}); + +export const policeAirport = (rank: number) => ({ + type: 'Feature', + geometry: { + coordinates: [ + [ + [-73.50019138870562, 40.76503398530525], + [-73.49991804403106, 40.75762191351754], + [-73.50172211928987, 40.75368779651572], + [-73.502924836068, 40.74958778448408], + [-73.50095675396722, 40.746067370663354], + [-73.49587254187276, 40.74117989901089], + [-73.48810955136324, 40.74010295019133], + [-73.4862508071566, 40.74097279482331], + [-73.48450140084462, 40.74209114977816], + [-73.48335335295225, 40.743250905424986], + [-73.49368578398266, 40.7500847690697], + [-73.49363111503533, 40.75112014169309], + [-73.49166303293454, 40.75087165373341], + [-73.49128035030373, 40.75435040065955], + [-73.48406404926651, 40.75385344795657], + [-73.4860321313673, 40.75840870869581], + [-73.50019138870562, 40.76503398530525], + ], + ], + type: 'Polygon', + }, + properties: { + rank, + source: 'OSM', + layer: 'osm_airports', + source_id: ['009c6b65-3dcb-4c4f-9f02-d766ebb5d808'], + name: { + en: ['Nassau County Police Airport'], + fr: ['Aeropuerto de la Policía del Condado de Nassau'], + default: ['Nassau County Police Airport'], + display: 'Nassau County Police Airport', + }, + placetype: 'transportation', + sub_placetype: 'airport', + regions: [ + { + region: 'USA', + sub_regions: ['New York'], + }, + ], + region: ['USA'], + sub_region: ['New York'], + }, +}); + +export const losAngelesAirport = (rank: number) => ({ + type: 'Feature', + geometry: { + coordinates: [ + [ + [-118.42713070992883, 33.9512236894319], + [-118.43440343023548, 33.94992163234602], + [-118.43571147345608, 33.94670980636225], + [-118.43560682999878, 33.943107208682775], + [-118.43325235220159, 33.938723119106925], + [-118.42875268352236, 33.9310829780122], + [-118.41394563426408, 33.931820976094315], + [-118.39756893251248, 33.932255089782316], + [-118.38516868278077, 33.935250412835686], + [-118.37904703905322, 33.936509285268926], + [-118.37899471732432, 33.943367642688045], + [-118.3788377519829, 33.94501703885841], + [-118.39181354073204, 33.94527746687625], + [-118.39510980964849, 33.945451085112225], + [-118.3980398264626, 33.94558129855595], + [-118.39746428744567, 33.949748023597365], + [-118.39720267894361, 33.953003135913036], + [-118.40813791735772, 33.95252573110099], + [-118.42713070992883, 33.9512236894319], + ], + ], + type: 'Polygon', + }, + properties: { + rank, + source: 'OSM', + layer: 'osm_airports', + source_id: ['a4f373ab-b824-41e2-b160-e7729c73bea6'], + name: { + en: ['Los Angeles International Airport'], + fr: ['Aeropuerto Internacional de Los Ángeles'], + default: ['Los Angeles International Airport'], + display: 'Los Angeles International Airport', + }, + placetype: 'transportation', + sub_placetype: 'airport', + regions: [ + { + region: 'USA', + sub_regions: ['Los Angeles'], + }, + ], + region: ['USA'], + sub_region: ['Los Angeles'], + }, +}); diff --git a/tests/integration/globalSetup.ts b/tests/integration/globalSetup.ts deleted file mode 100644 index ee34e0d7..00000000 --- a/tests/integration/globalSetup.ts +++ /dev/null @@ -1,39 +0,0 @@ -import 'reflect-metadata'; -import jsLogger from '@map-colonies/js-logger'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import { trace } from '@opentelemetry/api'; -import { getApp } from '../../src/app'; -import { SERVICES } from '../../src/common/constants'; -import { IConfig } from '../../src/common/interfaces'; -import importDataToS3 from '../../devScripts/importDataToS3'; -import importDataToElastic from '../../devScripts/importDataToElastic'; -import { cronLoadTileLatLonDataSymbol } from '../../src/latLon/DAL/latLonDAL'; - -export default async () => { - const app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { - token: cronLoadTileLatLonDataSymbol, - provider: { useValue: {} }, - }, - ], - useChild: true, - }); - - const config = app.container.resolve(SERVICES.CONFIG); - - await Promise.allSettled([await importDataToS3(config), await importDataToElastic(config)]).then((results) => { - results.forEach((result) => { - if (result.status === 'rejected') { - throw result.reason; - } - }); - }); - - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); - return; -}; diff --git a/tests/integration/globalTeardown.ts b/tests/integration/globalTeardown.ts deleted file mode 100644 index 96003dc9..00000000 --- a/tests/integration/globalTeardown.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import 'reflect-metadata'; -import jsLogger from '@map-colonies/js-logger'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import { trace } from '@opentelemetry/api'; -import { DeleteBucketCommand, DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3'; -import { getApp } from '../../src/app'; -import { elasticConfigPath, SERVICES } from '../../src/common/constants'; -import { IConfig } from '../../src/common/interfaces'; -import { S3Config, s3ConfigPath } from '../../src/common/s3'; -import { ElasticDbClientsConfig } from '../../src/common/elastic/interfaces'; -import { ElasticClients } from '../../src/common/elastic'; -import { cronLoadTileLatLonDataSymbol } from '../../src/latLon/DAL/latLonDAL'; - -export default async (): Promise => { - const app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { - token: cronLoadTileLatLonDataSymbol, - provider: { useValue: {} }, - }, - ], - useChild: true, - }); - - const config = app.container.resolve(SERVICES.CONFIG); - - const s3Client = app.container.resolve(SERVICES.S3_CLIENT); - const elasticClients = app.container.resolve(SERVICES.ELASTIC_CLIENTS); - - const s3Config = config.get(s3ConfigPath); - const elasticClientsConfig = config.get(elasticConfigPath); - - const clearS3Data = new Promise((resolve, reject) => { - void (async (): Promise => { - try { - if (s3Config.files.latLonConvertionTable !== undefined) { - await s3Client.send( - new DeleteObjectCommand({ Bucket: s3Config.files.latLonConvertionTable.bucket, Key: s3Config.files.latLonConvertionTable.fileName }) - ); - await s3Client.send(new DeleteBucketCommand({ Bucket: s3Config.files.latLonConvertionTable.bucket })); - } - resolve(); - } catch (error) { - console.error(error); - reject(error); - } - })(); - }); - - const clearElasticData = new Promise((resolve, reject) => { - void (async (): Promise => { - try { - for (const [key, value] of Object.entries(elasticClientsConfig)) { - await elasticClients[key as keyof ElasticDbClientsConfig].indices.delete({ - index: typeof value.properties.index === 'string' ? value.properties.index : Object.values(value.properties.index), - }); - } - resolve(); - } catch (error) { - console.error(error); - reject(error); - } - })(); - }); - - await Promise.allSettled([clearS3Data, clearElasticData]).then((results) => { - results.forEach((result) => { - if (result.status === 'rejected') { - throw result.reason; - } - }); - }); - - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); - await app.container.dispose(); - - console.log('Global Teardown completed'); - return; -}; diff --git a/tests/integration/control/item/helpers/requestSender.ts b/tests/integration/item/helpers/requestSender.ts similarity index 70% rename from tests/integration/control/item/helpers/requestSender.ts rename to tests/integration/item/helpers/requestSender.ts index 78a17c20..28f460fa 100644 --- a/tests/integration/control/item/helpers/requestSender.ts +++ b/tests/integration/item/helpers/requestSender.ts @@ -1,5 +1,5 @@ import * as supertest from 'supertest'; -import { GetItemsQueryParams } from '../../../../../src/control/item/controllers/itemController'; +import { GetItemsQueryParams } from '../../../../src/item/controllers/itemController'; export class ItemRequestSender { public constructor(private readonly app: Express.Application) {} @@ -7,9 +7,9 @@ export class ItemRequestSender { public async getItems(queryParams?: GetItemsQueryParams): Promise { return supertest .agent(this.app) - .get('/search/control/items') + .get('/v1/search/items/') .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') + .set('X-API-Key', 'abc123') .set('x-user-id', 'abc123') .query(queryParams ?? {}); } diff --git a/tests/integration/item/item.spec.ts b/tests/integration/item/item.spec.ts new file mode 100644 index 00000000..d1eb396b --- /dev/null +++ b/tests/integration/item/item.spec.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import httpStatusCodes from 'http-status-codes'; +import { getApp } from '../../../src/app'; +import { SERVICES } from '../../../src/common/constants'; +import { GetItemsQueryParams } from '../../../src/item/controllers/itemController'; +import { ItemRequestSender } from './helpers/requestSender'; + +describe('/items', function () { + let requestSender: ItemRequestSender; + + beforeEach(async function () { + const app = await getApp({ + override: [ + { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, + { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, + ], + useChild: true, + }); + requestSender = new ItemRequestSender(app.app); + }); + + describe('Happy Path', function () { + it('should return 200 status code and the item', async function () { + const response = await requestSender.getItems({ command_name: '4805' }); + + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + }); + it('should return 200 status code and empty response', async function () { + const response = await requestSender.getItems({ command_name: '48054805' }); + + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + features: [], + }); + }); + }); + describe('Bad Path', function () { + // All requests with status code of 400 + it('Should return 400 status code and meessage "request/query must have required property \'command_name\'"', async function () { + const message = "request/query must have required property 'command_name'"; + + const response = await requestSender.getItems(); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + }); + it('Should return 400 status code for unknown parameter', async function () { + const parameter = 'test1234'; + const message = `Unknown query parameter '${parameter}'`; + + const response = await requestSender.getItems({ [parameter]: parameter } as unknown as GetItemsQueryParams); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + }); + }); + describe('Sad Path', function () { + // All requests with status code 4XX-5XX + }); +}); diff --git a/tests/integration/latLon/helpers/requestSender.ts b/tests/integration/latLon/helpers/requestSender.ts index 2766e47c..8ace3344 100644 --- a/tests/integration/latLon/helpers/requestSender.ts +++ b/tests/integration/latLon/helpers/requestSender.ts @@ -1,15 +1,52 @@ import * as supertest from 'supertest'; -import { GetCoordinatesRequestParams } from '../../../../src/latLon/controllers/latLonController'; +import { + GetLatLonToTileQueryParams, + GetTileToLatLonQueryParams, + GetLatLonToMgrsQueryParams, + GetMgrsToLatLonQueryParams, +} from '../../../../src/latLon/controllers/latLonController'; + +const PREFIX = '/v1/lookup'; export class LatLonRequestSender { public constructor(private readonly app: Express.Application) {} - public async convertCoordinatesToGrid(queryParams?: GetCoordinatesRequestParams): Promise { + public async getLatlonToTile(queryParams?: GetLatLonToTileQueryParams): Promise { + return supertest + .agent(this.app) + .get(`${PREFIX}/latlonToTile`) + .set('Content-Type', 'application/json') + .set('X-API-Key', 'abc123') + .set('x-user-id', 'abc123') + .query(queryParams ?? {}); + } + + public async getTileToLatLon(queryParams?: GetTileToLatLonQueryParams): Promise { + return supertest + .agent(this.app) + .get(`${PREFIX}/tileToLatLon`) + .set('Content-Type', 'application/json') + .set('X-API-Key', 'abc123') + .set('x-user-id', 'abc123') + .query(queryParams ?? {}); + } + + public async getLatlonToMgrs(queryParams?: GetLatLonToMgrsQueryParams): Promise { + return supertest + .agent(this.app) + .get(`${PREFIX}/latlonToMgrs`) + .set('Content-Type', 'application/json') + .set('X-API-Key', 'abc123') + .set('x-user-id', 'abc123') + .query(queryParams ?? {}); + } + + public async getMgrsToLatlon(queryParams?: GetMgrsToLatLonQueryParams): Promise { return supertest .agent(this.app) - .get(`/lookup/coordinates`) + .get(`${PREFIX}/mgrsToLatLon`) .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') + .set('X-API-Key', 'abc123') .set('x-user-id', 'abc123') .query(queryParams ?? {}); } diff --git a/tests/integration/latLon/latLon.spec.ts b/tests/integration/latLon/latLon.spec.ts index 17e53bbe..c656effb 100644 --- a/tests/integration/latLon/latLon.spec.ts +++ b/tests/integration/latLon/latLon.spec.ts @@ -1,209 +1,169 @@ +/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/naming-convention */ import jsLogger from '@map-colonies/js-logger'; import { trace } from '@opentelemetry/api'; -import { Application } from 'express'; -import { DependencyContainer } from 'tsyringe'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; import httpStatusCodes from 'http-status-codes'; import { getApp } from '../../../src/app'; import { SERVICES } from '../../../src/common/constants'; -import { LatLonDAL } from '../../../src/latLon/DAL/latLonDAL'; import { LatLonRequestSender } from './helpers/requestSender'; -describe('/lookup', function () { +describe('/latLon', function () { let requestSender: LatLonRequestSender; - let app: { app: Application; container: DependencyContainer }; beforeEach(async function () { - app = await getApp({ + const app = await getApp({ override: [ { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, ], useChild: true, }); - requestSender = new LatLonRequestSender(app.app); - }); + }, 20000); - afterAll(async function () { - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); + describe('Happy Path', function () { + it('should return 200 status code and lat-lon from mgrs', async function () { + const response = await requestSender.getMgrsToLatlon({ mgrs: '18TWL8565011369' }); - jest.clearAllTimers(); - }); + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + lat: 40.74882151233783, + lon: -73.98543192220956, + }); + }); + + it('should return 200 status code and mgrs from lat-lon', async function () { + const reponse = await requestSender.getLatlonToMgrs({ + lat: 40.74882151233783, + lon: -73.98543192220956, + }); + + expect(reponse.status).toBe(httpStatusCodes.OK); + expect(reponse).toSatisfyApiSpec(); + expect(reponse.body).toMatchObject({ + mgrs: '18TWL8565011369', + }); + }); - describe('Happy Path', function () { it('should return 200 status code and tile from lat-lon', async function () { - const response = await requestSender.convertCoordinatesToGrid({ + const reponse = await requestSender.getLatlonToTile({ lat: 52.57326537485767, lon: 12.948781146422107, - target_grid: 'control', }); - expect(response.status).toBe(httpStatusCodes.OK); - expect(response).toSatisfyApiSpec(); - expect(response.body).toEqual({ - type: 'Feature', - geocoding: { - version: expect.any(String) as string, - query: { - lat: 52.57326537485767, - lon: 12.948781146422107, - target_grid: 'control', - }, - response: { - max_score: 1, - results_count: 1, - match_latency_ms: 0, - }, - }, - bbox: [12.93694771534361, 52.51211561266182, 13.080296161196031, 52.60444267653175], - geometry: { - type: 'Polygon', - coordinates: [ - [ - [12.93694771534361, 52.51211561266182], - [12.93694771534361, 52.60444267653175], - [13.080296161196031, 52.60444267653175], - [13.080296161196031, 52.51211561266182], - [12.93694771534361, 52.51211561266182], - ], - ], - }, - properties: { - name: 'BRN', - tileName: 'BRN', - subTileNumber: ['06', '97', '97'], - }, + expect(reponse.status).toBe(httpStatusCodes.OK); + expect(reponse).toSatisfyApiSpec(); + expect(reponse.body).toMatchObject({ + tileName: 'BRN', + subTileNumber: [0, 0, 0], }); }); - it('should return 200 status code and MGRS from lat-lon', async function () { - const response = await requestSender.convertCoordinatesToGrid({ - lat: 52.57326537485767, - lon: 12.948781146422107, - target_grid: 'MGRS', + it('should return 200 status code and lat-lon from tile', async function () { + const reponse = await requestSender.getTileToLatLon({ + tile: 'BRN', + sub_tile_number: [10, 10, 10], }); - expect(response.status).toBe(httpStatusCodes.OK); - expect(response).toSatisfyApiSpec(); - expect(response.body).toEqual({ - type: 'Feature', - geocoding: { - version: expect.any(String) as string, - query: { - lat: 52.57326537485767, - lon: 12.948781146422107, - target_grid: 'MGRS', - }, - response: { - max_score: 1, - results_count: 1, - match_latency_ms: 0, + expect(reponse.status).toBe(httpStatusCodes.OK); + expect(reponse).toSatisfyApiSpec(); + expect(reponse.body).toMatchObject({ + type: 'FeatureCollection', + features: [ + { + geometry: { + coordinates: [ + [ + [12.953293384350397, 52.512399536846765], + [12.953440643865289, 52.512402084451686], + [12.953436468347887, 52.51249192878939], + [12.95328920853307, 52.512489381176245], + [12.953293384350397, 52.512399536846765], + ], + ], + type: 'Polygon', + }, + properties: { + TYPE: 'TILE', + SUB_TILE_NUMBER: [10, 10, 10], + TILE_NAME: 'BRN', + }, }, - }, - bbox: [12.948777289238832, 52.57325754975297, 12.948791616108007, 52.57326678960368], - geometry: { - type: 'Point', - coordinates: [12.948781146422107, 52.57326537485767], - }, - properties: { - name: '33UUU6099626777', - accuracy: '1m', - mgrs: '33UUU6099626777', - }, + ], }); }); }); - describe('Bad Path', function () { - it('should retrun 400 status code when invalid lat lon', async function () { - const response = await requestSender.convertCoordinatesToGrid({ - lat: 67.9435100890131, - lon: -84.41903041752825, - target_grid: 'control', - }); + test.each<[keyof typeof requestSender, string[]]>([ + ['getLatlonToMgrs', ['lat', 'lon']], + ['getMgrsToLatlon', ['mgrs']], + ['getLatlonToTile', ['lat', 'lon']], + ['getTileToLatLon', ['tile', 'sub_tile_number']], + ])('should return 400 and message for missing required parameters', async (request, missingProperties) => { + const message = 'request/query must have required property'; + const response = await requestSender[request](); expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toEqual({ - message: "Invalid lat lon, check 'lat' and 'lon' keys exists and their values are legal", - }); + expect(response.body).toMatchObject({ message: missingProperties.map((txt) => `${message} '${txt}'`).join(', ') }); + expect(response).toSatisfyApiSpec(); }); - it('should return 400 status code when the coordinate is outside the grid extent', async function () { - const response = await requestSender.convertCoordinatesToGrid({ - lat: 32.57326537485767, - lon: 12.948781146422107, - target_grid: 'control', - }); + test.each<[keyof typeof requestSender]>([['getLatlonToMgrs'], ['getMgrsToLatlon'], ['getLatlonToTile'], ['getTileToLatLon']])( + 'should return 400 and message unknown query parameter', + async (request) => { + const parameter = 'test1234'; + const message = `Unknown query parameter '${parameter}'`; + + const response = await requestSender[request]({ [parameter]: parameter } as never); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + } + ); + + it('should return 400 and check that all numbers are positive', async function () { + const message = "Invalid tile, check that 'tileName' and 'subTileNumber' exists and subTileNumber is array of size 3 with positive integers"; + + for (let i = 0; i < 3; i++) { + const arr: number[] = [10, 10, 10]; + arr[i] = 0; + + const response = await requestSender.getTileToLatLon({ + tile: 'BRN', + sub_tile_number: arr, + }); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ + message, + }); + expect(response).toSatisfyApiSpec(); + } + }); + + it('should return 400 for lat-lon that outside the grid extent', async function () { + const response = await requestSender.getLatlonToTile({ lat: 1, lon: 1 }); expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toEqual({ + expect(response.body).toMatchObject({ message: 'The coordinate is outside the grid extent', }); + expect(response).toSatisfyApiSpec(); }); - }); - describe('Sad Path', function () { - it('should return 500 as isDataLoadError is true for control', async function () { - const dataLoadErrorSpy = jest.spyOn(LatLonDAL.prototype, 'getIsDataLoadError').mockReturnValue(true); + it('should return 400 for tile not found', async function () { + const response = await requestSender.getTileToLatLon({ tile: 'XXX', sub_tile_number: [10, 10, 10] }); - const response = await requestSender.convertCoordinatesToGrid({ - lat: 32.57326537485767, - lon: 12.948781146422107, - target_grid: 'control', - }); - - expect(response.status).toBe(httpStatusCodes.INTERNAL_SERVER_ERROR); - // expect(response).toSatisfyApiSpec(); - expect(response.body).toEqual({ - message: 'Lat-lon to tile data currently not available', - stacktrace: expect.any(String) as string, + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ + message: 'Tile not found', }); - - dataLoadErrorSpy.mockRestore(); + expect(response).toSatisfyApiSpec(); }); - - // it('should return 500 as init is errored', async function () { - // const dataLoadErrorSpy = jest.spyOn(LatLonDAL.prototype as any, 'loadLatLonData').mockRejectedValue(new Error('some error')); - - // const response = await requestSender.convertCoordinatesToGrid({ - // lat: 52.57326537485767, - // lon: 12.948781146422107, - // target_grid: 'control', - // }); - - // console.log(response.status, response.body); - // expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - // // expect(response).toSatisfyApiSpec(); - // expect(response.body).toEqual({ - // message: 'Lat-lon to tile data currently not available', - // stacktrace: expect.any(String) as string, - // }); - - // dataLoadErrorSpy.mockRestore(); - // }); }); - - it('should return error when cronPattern is not defined', async () => { - try { - await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: SERVICES.APPLICATION, provider: { useValue: {} } }, - ], - useChild: true, - }); - // If no error is thrown, fail the test - throw new Error('Expected error was not thrown'); - } catch (error) { - // eslint-disable-next-line jest/no-conditional-expect - expect((error as Error).message).toBe('cron pattern is not defined'); - } + describe('Sad Path', function () { + // All requests with status code 4XX-5XX }); }); diff --git a/tests/integration/location/helpers/requestSender.ts b/tests/integration/location/helpers/requestSender.ts deleted file mode 100644 index 56b867f9..00000000 --- a/tests/integration/location/helpers/requestSender.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as supertest from 'supertest'; -import { GetGeotextSearchParams } from '../../../../src/location/interfaces'; - -export class LocationRequestSender { - private readonly pathPrefix = '/search/location'; - - public constructor(private readonly app: Express.Application) {} - - public async getQuery(queryParams?: GetGeotextSearchParams): Promise { - return supertest - .agent(this.app) - .get(`${this.pathPrefix}/query`) - .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') - .set('x-user-id', 'abc123') - .query(queryParams ?? {}); - } - - public async getRegions(): Promise { - return supertest - .agent(this.app) - .get(`${this.pathPrefix}/regions`) - .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') - .set('x-user-id', 'abc123'); - } - - public async getSources(): Promise { - return supertest - .agent(this.app) - .get(`${this.pathPrefix}/sources`) - .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') - .set('x-user-id', 'abc123'); - } -} diff --git a/tests/integration/location/location.spec.ts b/tests/integration/location/location.spec.ts deleted file mode 100644 index 46eb28a2..00000000 --- a/tests/integration/location/location.spec.ts +++ /dev/null @@ -1,582 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import config from 'config'; -import { DependencyContainer } from 'tsyringe'; -import { Application } from 'express'; -import { Feature } from 'geojson'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import jsLogger from '@map-colonies/js-logger'; -import { trace } from '@opentelemetry/api'; -import httpStatusCodes from 'http-status-codes'; -import nock, { Body } from 'nock'; -import { getApp } from '../../../src/app'; -import { SERVICES } from '../../../src/common/constants'; -import { S3_REPOSITORY_SYMBOL } from '../../../src/common/s3/s3Repository'; -import { cronLoadTileLatLonDataSymbol } from '../../../src/latLon/DAL/latLonDAL'; -import { GetGeotextSearchParams } from '../../../src/location/interfaces'; -import { GenericGeocodingResponse, GeoContext, GeoContextMode, IApplication } from '../../../src/common/interfaces'; -import { LocationRequestSender } from './helpers/requestSender'; -import { - OSM_LA_PORT, - GOOGLE_LA_PORT, - LA_AIRPORT, - NY_JFK_AIRPORT, - NY_POLICE_AIRPORT, - NY_HIERRARCHY, - LA_HIERRARCHY, - MockLocationQueryFeature, - PARIS_WI_SCHOOL, -} from './mockObjects'; -import { expectedResponse, hierarchiesWithAnyWieght } from './utils'; - -describe('/search/location', function () { - let requestSender: LocationRequestSender; - let app: { app: Application; container: DependencyContainer }; - - beforeEach(async function () { - app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: S3_REPOSITORY_SYMBOL, provider: { useValue: {} } }, - { token: SERVICES.S3_CLIENT, provider: { useValue: {} } }, - { token: cronLoadTileLatLonDataSymbol, provider: { useValue: {} } }, - ], - useChild: true, - }); - - requestSender = new LocationRequestSender(app.app); - }); - - afterAll(async function () { - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - nock.cleanAll(); - app.container.reset(); - - jest.clearAllTimers(); - }); - - describe('Happy Path', function () { - it('should return 200 status code and airports', async function () { - const requestParams: GetGeotextSearchParams = { query: 'airport', limit: 5, disable_fuzziness: true }; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['airport'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - { - ...requestParams, - }, - { - place_types: ['transportation'], - sub_place_types: ['airport'], - hierarchies: [], - }, - [ - { - ...NY_JFK_AIRPORT, - properties: { - ...NY_JFK_AIRPORT.properties, - names: { - ...NY_JFK_AIRPORT.properties.names, - display: expect.stringContaining('JFK') as string, - }, - }, - }, - NY_POLICE_AIRPORT, - LA_AIRPORT, - ], - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - - it('should return 200 status code and airports filtered by geo_context (bbox)', async function () { - const requestParams: GetGeotextSearchParams = { - query: 'airport', - geo_context: { bbox: [-75.81665, 39.597223, -72.575684, 41.352072] }, - geo_context_mode: GeoContextMode.FILTER, - limit: 5, - disable_fuzziness: true, - }; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['airport'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - requestParams, - { - place_types: ['transportation'], - sub_place_types: ['airport'], - hierarchies: [], - }, - [ - { - ...NY_JFK_AIRPORT, - properties: { - ...NY_JFK_AIRPORT.properties, - names: { - ...NY_JFK_AIRPORT.properties.names, - display: expect.stringContaining('JFK') as string, - }, - }, - }, - NY_POLICE_AIRPORT, - ], - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - - it('should return 200 status code and airports biased by geo_context (bbox)', async function () { - const requestParams: GetGeotextSearchParams = { - query: 'airport', - geo_context: { bbox: [-75.81665, 39.597223, -72.575684, 41.352072] }, - geo_context_mode: GeoContextMode.BIAS, - limit: 5, - disable_fuzziness: true, - }; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['airport'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - { - ...requestParams, - }, - { - place_types: ['transportation'], - sub_place_types: ['airport'], - hierarchies: [], - }, - [ - { - ...NY_JFK_AIRPORT, - properties: { - ...NY_JFK_AIRPORT.properties, - names: { - ...NY_JFK_AIRPORT.properties.names, - display: expect.stringContaining('JFK') as string, - }, - }, - }, - NY_POLICE_AIRPORT, - LA_AIRPORT, - ], - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - - test.each< - Pick & { - hierarchies: GenericGeocodingResponse['geocoding']['response']['hierarchies']; - returnedFeatures: MockLocationQueryFeature[]; - } - >([ - { - query: 'new york', - hierarchies: hierarchiesWithAnyWieght([NY_HIERRARCHY], expect), - returnedFeatures: [ - { - ...NY_JFK_AIRPORT, - properties: { - ...NY_JFK_AIRPORT.properties, - names: { - ...NY_JFK_AIRPORT.properties.names, - display: expect.stringContaining('JFK') as string, - }, - }, - }, - NY_POLICE_AIRPORT, - LA_AIRPORT, - ], - }, - { - query: 'los angeles', - hierarchies: hierarchiesWithAnyWieght([LA_HIERRARCHY], expect), - returnedFeatures: [ - LA_AIRPORT, - { - ...NY_JFK_AIRPORT, - properties: { - ...NY_JFK_AIRPORT.properties, - names: { - ...NY_JFK_AIRPORT.properties.names, - display: expect.stringContaining('JFK') as string, - }, - }, - }, - NY_POLICE_AIRPORT, - ], - }, - ])('it should test airports response with hierrarchy in %s', async ({ query, hierarchies, returnedFeatures }) => { - const requestParams: GetGeotextSearchParams = { query: `airport, ${query}`, limit: 5, disable_fuzziness: true }; - - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: ['airport'] }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['airport'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - { - ...requestParams, - }, - { - place_types: ['transportation'], - sub_place_types: ['airport'], - hierarchies, - }, - returnedFeatures, - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - - test.each< - Pick & { - place_types: GenericGeocodingResponse['geocoding']['response']['place_types']; - sub_place_types: GenericGeocodingResponse['geocoding']['response']['sub_place_types']; - returnedFeatures: MockLocationQueryFeature[]; - } - >([ - { - query: 'new york', - returnedFeatures: [NY_JFK_AIRPORT, NY_POLICE_AIRPORT, LA_AIRPORT, OSM_LA_PORT, GOOGLE_LA_PORT], - place_types: ['transportation', 'transportation'], - sub_place_types: ['airport', 'port'], - }, - { - query: 'los angeles', - place_types: ['transportation'], - sub_place_types: ['airport'], - returnedFeatures: [LA_AIRPORT, OSM_LA_PORT, GOOGLE_LA_PORT, NY_JFK_AIRPORT, NY_POLICE_AIRPORT], - }, - ])('it should test airports response with NLP Analyzer in %s', async ({ query, place_types, sub_place_types, returnedFeatures }) => { - const requestParams: GetGeotextSearchParams = { query: `airport ${query}`, limit: 5, disable_fuzziness: false }; - - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: requestParams.query.split(' '), - prediction: ['essence', 'name', 'name'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - { - ...requestParams, - }, - { - name: query, - place_types, - sub_place_types, - hierarchies: [], - }, - returnedFeatures, - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - - it('should return 200 status code and all regions', async function () { - const response = await requestSender.getRegions(); - - expect(response.status).toBe(httpStatusCodes.OK); - expect(response.body).toEqual(expect.arrayContaining(['USA'])); - // expect(response).toSatisfyApiSpec(); - }); - - it('should return 200 status code and all sources', async function () { - const response = await requestSender.getSources(); - - expect(response.status).toBe(httpStatusCodes.OK); - expect(response.body).toEqual(expect.arrayContaining(['OSM', 'GOOGLE'])); - // expect(response).toSatisfyApiSpec(); - }); - - it('should return 200 status code and ports from the corresponding source', async function () { - const requestParams: GetGeotextSearchParams = { query: 'port', source: ['google'], limit: 5, disable_fuzziness: true }; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['port'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - requestParams, - { - place_types: ['transportation'], - sub_place_types: ['port'], - hierarchies: [], - }, - [GOOGLE_LA_PORT], - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - - it('should return 200 status code and schools in specified region', async function () { - const requestParams: GetGeotextSearchParams = { query: 'school', region: ['france'], limit: 5, disable_fuzziness: true }; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['school'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.OK); - // expect(response).toSatisfyApiSpec(); - - expect(response.body).toMatchObject>( - expectedResponse( - requestParams, - { - place_types: ['education'], - sub_place_types: ['school'], - hierarchies: [], - }, - [PARIS_WI_SCHOOL], - expect - ) - ); - - tokenTypesUrlScope.done(); - }); - }); - - describe('Bad Path', function () { - // All requests with status code 4XX-5XX - test.each>([ - { - geo_context: { x: 300850, y: 4642203, zone: 33, radius: 100 }, - }, - { - geo_context: { lon: 12.598899687444742, lat: 41.90667824634701, radius: 10 }, - }, - { - geo_context: { bbox: [12.554407132912445, 41.84962590648513, 12.652837919839953, 41.94545380230761] }, - }, - { - geo_context_mode: GeoContextMode.BIAS, - }, - { - geo_context_mode: GeoContextMode.FILTER, - }, - ])('should return 400 and message that geo_context and geo_context_mode must be both defined or both undefined', async function (requestParams) { - const badRequestParams: GetGeotextSearchParams = { query: 'airport', limit: 5, disable_fuzziness: true, ...requestParams }; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: badRequestParams.query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['airport'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery(badRequestParams); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: '/location/geotextQuery: geo_context and geo_context_mode must be both defined or both undefined', - }); - tokenTypesUrlScope.done(); - }); - }); - - describe('Sad Path', function () { - // All requests with status code 4XX-5XX - it('should return 500 status code when the NLP Analyzer service is down due to network error', async function () { - const errorMessage = 'NLP Analyzer service is down'; - const requestParams: GetGeotextSearchParams = { query: 'airport', limit: 5, disable_fuzziness: true }; - - // Intercept the request and simulate a network error - const nockScope = nock(config.get('application').services.tokenTypesUrl) - .post('') - .once() - .replyWithError({ message: errorMessage, code: 'ECONNREFUSED' }); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.INTERNAL_SERVER_ERROR); - expect(response.body).toHaveProperty('message', `NLP analyser is not available - ${errorMessage}`); - // expect(response).toSatisfyApiSpec(); - - nockScope.done(); - }); - - test.each<{ code: number; body: Body | undefined }>([ - { code: httpStatusCodes.OK, body: [] }, - { code: httpStatusCodes.OK, body: undefined }, - { code: httpStatusCodes.NO_CONTENT, body: { message: 'bad request' } }, - ])('should return 500 status code when the NLP Analyzer service not responding as expected', async function ({ code, body }) { - const requestParams: GetGeotextSearchParams = { query: 'airport', limit: 5, disable_fuzziness: false }; - - const nockScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .once() - .reply(code, body); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.INTERNAL_SERVER_ERROR); - - expect(response.body).toHaveProperty('message', expect.stringContaining('NLP analyser unexpected response:')); - - // expect(response).toSatisfyApiSpec(); - - nockScope.done(); - }); - - it('should return 400 status code when NLP Analyzer returns no tokens or prediction', async function () { - const requestParams: GetGeotextSearchParams = { query: 'airport', limit: 5, disable_fuzziness: false }; - - const nockScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: requestParams.query.split(' ') }) - .once() - .reply(httpStatusCodes.OK, [{ tokens: [], prediction: [] }]); - - const response = await requestSender.getQuery(requestParams); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toHaveProperty('message', 'No tokens or prediction'); - // expect(response).toSatisfyApiSpec(); - - nockScope.done(); - }); - - test.each<(keyof GeoContext)[][]>([[['lat', 'lon', 'radius']], [['x', 'y', 'zone', 'radius']]])( - 'should return 400 for all invalid geo_context object for the keys %s', - async function (keys) { - function generateCombinations(keys: (keyof GeoContext)[]): GeoContext[] { - const combinations: object[] = []; - - function backtrack(current: object, remainingKeys: string[]): void { - if (remainingKeys.length === 0) { - combinations.push(current); - return; - } - - const key = remainingKeys[0]; - const remaining = remainingKeys.slice(1); - - backtrack({ ...current, [key]: 1 }, remaining); - backtrack(current, remaining); - } - - backtrack({}, keys); - - return combinations; - } - - const geoContexts = generateCombinations(keys); - - for (const geo_context of geoContexts) { - if (Object.keys(geo_context).length === keys.length) { - continue; - } - const query = 'airport'; - const tokenTypesUrlScope = nock(config.get('application').services.tokenTypesUrl) - .post('', { tokens: query.split(' ') }) - .reply(httpStatusCodes.OK, [ - { - tokens: ['port'], - prediction: ['essence'], - }, - ]); - - const response = await requestSender.getQuery({ - query, - limit: 5, - disable_fuzziness: false, - geo_context: JSON.stringify(geo_context) as unknown as GetGeotextSearchParams['geo_context'], - geo_context_mode: GeoContextMode.BIAS, - }); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: - 'geo_context validation: geo_context must contain one of the following: {"bbox": [number,number,number,number] | [number,number,number,number,number,number]}, {"lat": number, "lon": number, "radius": number}, or {"x": number, "y": number, "zone": number, "radius": number}', - }); - - tokenTypesUrlScope.done(); - } - } - ); - }); -}); diff --git a/tests/integration/location/mockObjects.ts b/tests/integration/location/mockObjects.ts deleted file mode 100644 index 8fede417..00000000 --- a/tests/integration/location/mockObjects.ts +++ /dev/null @@ -1,403 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-magic-numbers */ -import { Feature } from 'geojson'; -import { GenericGeocodingResponse } from '../../../src/common/interfaces'; -import { HierarchySearchHit } from '../../../src/location/models/elasticsearchHits'; - -export type MockLocationQueryFeature = GenericGeocodingResponse['features'][number]; - -export const NY_JFK_AIRPORT: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-73.81278266814672, 40.66039916690434], - [-73.8177430790069, 40.66160065836647], - [-73.82178645734075, 40.66043068890016], - [-73.82341214553732, 40.658185554886586], - [-73.82407909454082, 40.65496001884071], - [-73.8229536180974, 40.650532558586235], - [-73.82211993184252, 40.647939195437345], - [-73.81290769732489, 40.643985699842915], - [-73.79214887014153, 40.63414837731818], - [-73.78339516446982, 40.62987771430167], - [-73.7898562329419, 40.62275933562921], - [-73.78443726476769, 40.620069953803636], - [-73.7791433570518, 40.627188619100366], - [-73.77639219241223, 40.62706207167477], - [-73.77159849644941, 40.62336045339214], - [-73.77209870820208, 40.619975033140975], - [-73.77047302000595, 40.61908910045176], - [-73.76547094984971, 40.628422477310664], - [-73.75338249916041, 40.63291467256053], - [-73.74733827381596, 40.63601474373485], - [-73.7467963777506, 40.64208793530722], - [-73.752548854642, 40.64749646458006], - [-73.76213624656812, 40.65309424493557], - [-73.78181122379466, 40.66270746643491], - [-73.79106514121902, 40.66438330508498], - [-73.7957754685564, 40.665205399216404], - [-73.79856831750864, 40.66283394629252], - [-73.80390390953731, 40.66175885985783], - [-73.8073637074931, 40.66039916690434], - [-73.8109068740743, 40.66074699797244], - [-73.81278266814672, 40.66039916690434], - ], - ], - }, - properties: { - matches: [{ source: 'OSM', layer: 'osm_airports', source_id: ['03ed6d97-fc81-4340-b68a-11993554eef1'] }], - names: { - en: ['JFK Airport'], - fr: ['Aeropuerto JFK'], - default: ['JFK'], - display: 'JFK Airport', - }, - placetype: 'transportation', - sub_placetype: 'airport', - regions: [ - { - region: 'USA', - sub_region_names: ['New York'], - }, - ], - }, -}; - -export const NY_POLICE_AIRPORT: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [-73.50019138870562, 40.76503398530525], - [-73.49991804403106, 40.75762191351754], - [-73.50172211928987, 40.75368779651572], - [-73.502924836068, 40.74958778448408], - [-73.50095675396722, 40.746067370663354], - [-73.49587254187276, 40.74117989901089], - [-73.48810955136324, 40.74010295019133], - [-73.4862508071566, 40.74097279482331], - [-73.48450140084462, 40.74209114977816], - [-73.48335335295225, 40.743250905424986], - [-73.49368578398266, 40.7500847690697], - [-73.49363111503533, 40.75112014169309], - [-73.49166303293454, 40.75087165373341], - [-73.49128035030373, 40.75435040065955], - [-73.48406404926651, 40.75385344795657], - [-73.4860321313673, 40.75840870869581], - [-73.50019138870562, 40.76503398530525], - ], - ], - type: 'Polygon', - }, - properties: { - matches: [{ source: 'OSM', layer: 'osm_airports', source_id: ['009c6b65-3dcb-4c4f-9f02-d766ebb5d808'] }], - names: { - en: ['Nassau County Police Airport'], - fr: ['Aeropuerto de la Policía del Condado de Nassau'], - default: ['Nassau County Police Airport'], - display: 'Nassau County Police Airport', - }, - placetype: 'transportation', - sub_placetype: 'airport', - regions: [ - { - region: 'USA', - sub_region_names: ['New York'], - }, - ], - }, -}; - -export const LA_AIRPORT: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [-118.42713070992883, 33.9512236894319], - [-118.43440343023548, 33.94992163234602], - [-118.43571147345608, 33.94670980636225], - [-118.43560682999878, 33.943107208682775], - [-118.43325235220159, 33.938723119106925], - [-118.42875268352236, 33.9310829780122], - [-118.41394563426408, 33.931820976094315], - [-118.39756893251248, 33.932255089782316], - [-118.38516868278077, 33.935250412835686], - [-118.37904703905322, 33.936509285268926], - [-118.37899471732432, 33.943367642688045], - [-118.3788377519829, 33.94501703885841], - [-118.39181354073204, 33.94527746687625], - [-118.39510980964849, 33.945451085112225], - [-118.3980398264626, 33.94558129855595], - [-118.39746428744567, 33.949748023597365], - [-118.39720267894361, 33.953003135913036], - [-118.40813791735772, 33.95252573110099], - [-118.42713070992883, 33.9512236894319], - ], - ], - type: 'Polygon', - }, - properties: { - matches: [{ source: 'OSM', layer: 'osm_airports', source_id: ['a4f373ab-b824-41e2-b160-e7729c73bea6'] }], - names: { - en: ['Los Angeles International Airport'], - fr: ['Aeropuerto Internacional de Los Ángeles'], - default: ['Los Angeles International Airport'], - display: 'Los Angeles International Airport', - }, - placetype: 'transportation', - sub_placetype: 'airport', - regions: [ - { - region: 'USA', - sub_region_names: ['Los Angeles'], - }, - ], - }, -}; - -export const OSM_LA_PORT: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-118.2505781304088, 33.7502674389752], - [-118.25604403409116, 33.76075151051916], - [-118.27057180697577, 33.748593059782564], - [-118.27503083555426, 33.741097783576635], - [-118.2747911028351, 33.734798055529765], - [-118.27215404292296, 33.73136889520775], - [-118.26807858669537, 33.720881194108856], - [-118.26424286318695, 33.721997816398385], - [-118.26640045650717, 33.72901625632974], - [-118.2431463824787, 33.735794882347946], - [-118.24492040460113, 33.739303607948656], - [-118.25072193640723, 33.73794798097781], - [-118.25220827926702, 33.74193505797223], - [-118.24937943317966, 33.74508471776615], - [-118.24798898340768, 33.74783559181691], - [-118.24909175391655, 33.74803492708783], - [-118.25096166912684, 33.74600168558719], - [-118.25326310323155, 33.745363795966625], - [-118.25278363779321, 33.74687877606813], - [-118.2505781304088, 33.7502674389752], - ], - ], - }, - properties: { - matches: [{ source: 'OSM', layer: 'osm_ports', source_id: ['0f36d985-cfbd-4aed-b0cb-ee56600c77f4'] }], - names: { - en: ['Port of Los Angeles'], - fr: ['Puerto de Los Ángeles'], - default: ['Port of Los Angeles'], - display: 'Port of Los Angeles', - }, - placetype: 'transportation', - sub_placetype: 'port', - regions: [ - { - region: 'USA', - sub_region_names: ['Los Angeles'], - }, - ], - }, -}; -export const GOOGLE_LA_PORT: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-118.2505781304088, 33.7502674389752], - [-118.25604403409116, 33.76075151051916], - [-118.27057180697577, 33.748593059782564], - [-118.27503083555426, 33.741097783576635], - [-118.2747911028351, 33.734798055529765], - [-118.27215404292296, 33.73136889520775], - [-118.26807858669537, 33.720881194108856], - [-118.26424286318695, 33.721997816398385], - [-118.26640045650717, 33.72901625632974], - [-118.2431463824787, 33.735794882347946], - [-118.24492040460113, 33.739303607948656], - [-118.25072193640723, 33.73794798097781], - [-118.25220827926702, 33.74193505797223], - [-118.24937943317966, 33.74508471776615], - [-118.24798898340768, 33.74783559181691], - [-118.24909175391655, 33.74803492708783], - [-118.25096166912684, 33.74600168558719], - [-118.25326310323155, 33.745363795966625], - [-118.25278363779321, 33.74687877606813], - [-118.2505781304088, 33.7502674389752], - ], - ], - }, - properties: { - matches: [{ source: 'GOOGLE', layer: 'google_ports', source_id: ['1bb11f54-939e-457b-bf68-a3920ccf629c'] }], - names: { - en: ['Port of Los Angeles'], - fr: ['Puerto de Los Ángeles'], - default: ['Port of Los Angeles'], - display: 'Port of Los Angeles', - }, - placetype: 'transportation', - sub_placetype: 'port', - regions: [ - { - region: 'USA', - sub_region_names: ['Los Angeles'], - }, - ], - }, -}; - -export const LA_WHITE_POINT_SCHOOL: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [-118.30812263653988, 33.71684417247593], - [-118.30861990876181, 33.71674433152869], - [-118.30879709771484, 33.71635922964194], - [-118.30619642115158, 33.71550819588987], - [-118.30586490633668, 33.715921827872904], - [-118.30587062210924, 33.716183318328746], - [-118.30812263653988, 33.71684417247593], - ], - ], - type: 'Polygon', - }, - properties: { - matches: [ - { - source: 'OSM', - layer: 'osm_schools', - source_id: ['1a5b981b-bb0e-44dd-b9e2-424b92f2de49'], - }, - ], - names: { - en: ['White Point Elementary School'], - fr: ['Escuela Primaria White Point'], - default: ['White Point Elementary School'], - display: 'White Point Elementary School', - }, - placetype: 'education', - sub_placetype: 'school', - regions: [ - { - region: 'USA', - sub_region_names: ['Los Angeles'], - }, - ], - }, -}; - -export const PARIS_WI_SCHOOL: MockLocationQueryFeature = { - type: 'Feature', - geometry: { - coordinates: [ - [ - [2.346441270696971, 48.88088750665477], - [2.3462780852304945, 48.88018258877358], - [2.347503576087604, 48.87999951892243], - [2.347737155284733, 48.88070864783427], - [2.346441270696971, 48.88088750665477], - ], - ], - type: 'Polygon', - }, - properties: { - matches: [ - { - source: 'OSM', - layer: 'osm_schools', - source_id: ['dc02a3f9-156a-4f61-85bd-fd040cd322a3'], - }, - ], - names: { - en: ['Wi School Paris 9'], - fr: ['Ecole Wi Paris 9'], - default: ['Wi School Paris 9'], - display: 'Wi School Paris 9', - }, - placetype: 'education', - sub_placetype: 'school', - regions: [ - { - region: 'FRANCE', - sub_region_names: ['Paris'], - }, - ], - }, -}; - -export const NY_HIERRARCHY: HierarchySearchHit = { - geo_json: { - coordinates: [ - [ - [-73.74286189030825, 40.566325396473786], - [-73.47084009765854, 40.56212896709357], - [-73.550927745189, 41.11163279131463], - [-73.74424271181776, 41.225972287315074], - [-73.99969469100891, 41.26438691280978], - [-74.24962338416366, 41.05959508414017], - [-74.13087273437748, 40.7506852054762], - [-74.00659879855483, 40.530651727069795], - [-73.74286189030825, 40.566325396473786], - ], - ], - type: 'Polygon', - }, - hierarchy: 'city', - placetype: 'city', - region: 'USA', - text: 'New York', - weight: 1.1, -}; - -export const LA_HIERRARCHY: HierarchySearchHit = { - geo_json: { - coordinates: [ - [ - [-118.54430957638033, 34.07939240620722], - [-118.5350828996408, 33.695192367610986], - [-118.04596133238863, 33.47690745532634], - [-117.66265886139905, 33.379872950239346], - [-117.57145361502153, 33.63336904289318], - [-117.67279277766329, 34.23871934668085], - [-118.2605599209839, 34.28059749364003], - [-118.54430957638033, 34.07939240620722], - ], - ], - type: 'Polygon', - }, - hierarchy: 'city', - placetype: 'city', - region: 'USA', - text: 'Los Angeles', - weight: 1.1, -}; - -export const PARIS_HIERRARCHY: HierarchySearchHit = { - geo_json: { - coordinates: [ - [ - [2.226678539753607, 49.06838747927134], - [1.9344166918067742, 48.906487548202136], - [2.014124468519668, 48.56855190252173], - [2.6536844864307625, 48.53463335095324], - [2.902296837606201, 48.82159183126478], - [2.6460932696008683, 49.0124047223114], - [2.384196288972504, 49.05097737411208], - [2.226678539753607, 49.06838747927134], - ], - ], - type: 'Polygon', - }, - hierarchy: 'city', - placetype: 'city', - region: 'FRANCE', - text: 'Paris', - weight: 1.1, -}; diff --git a/tests/integration/location/utils.ts b/tests/integration/location/utils.ts deleted file mode 100644 index ee83b5d0..00000000 --- a/tests/integration/location/utils.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Feature } from 'geojson'; -import { GetGeotextSearchParams } from '../../../src/location/interfaces'; -import { GenericGeocodingResponse } from '../../../src/common/interfaces'; -import { MockLocationQueryFeature } from './mockObjects'; - -const expectedObjectWithScore = (obj: MockLocationQueryFeature, expect: jest.Expect): GenericGeocodingResponse['features'][number] => - ({ - ...obj, - properties: { - ...obj.properties, - score: expect.any(Number) as number, - }, - } as GenericGeocodingResponse['features'][number]); - -const expectedGeocodingElasticResponseMetrics = ( - responseParams: Partial['geocoding']['response']>, - resultsCount: number, - expect: jest.Expect -): NonNullable['geocoding']>['response'] => ({ - results_count: resultsCount, - max_score: expect.any(Number) as number, - match_latency_ms: expect.any(Number) as number, - ...responseParams, -}); - -export const expectedResponse = ( - requestParams: GetGeotextSearchParams, - responseParams: Partial['geocoding']['response']>, - arr: MockLocationQueryFeature[], - expect: jest.Expect -): GenericGeocodingResponse => ({ - type: 'FeatureCollection', - geocoding: { - version: process.env.npm_package_version, - query: requestParams, - response: expectedGeocodingElasticResponseMetrics(responseParams, arr.length, expect), - }, - features: arr.map((item) => expectedObjectWithScore(item, expect)), -}); - -export const hierarchiesWithAnyWieght = ( - hierarchies: GenericGeocodingResponse['geocoding']['response']['hierarchies'], - expect: jest.Expect -): GenericGeocodingResponse['geocoding']['response']['hierarchies'] => - (hierarchies as { hierarchy: string }[] | undefined)?.map((hierarchy) => ({ ...hierarchy, weight: expect.any(Number) as number })); diff --git a/tests/integration/mgrs/helpers/requestSender.ts b/tests/integration/mgrs/helpers/requestSender.ts deleted file mode 100644 index f12c9483..00000000 --- a/tests/integration/mgrs/helpers/requestSender.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as supertest from 'supertest'; -import { GetTileQueryParams } from '../../../../src/mgrs/controllers/mgrsController'; - -export class MgrsRequestSender { - public constructor(private readonly app: Express.Application) {} - - public async getTile(queryParams?: GetTileQueryParams): Promise { - return supertest - .agent(this.app) - .get('/search/MGRS/tiles') - .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') - .set('x-user-id', 'abc123') - .query(queryParams ?? {}); - } -} diff --git a/tests/integration/mgrs/mgrs.spec.ts b/tests/integration/mgrs/mgrs.spec.ts deleted file mode 100644 index eb126aa8..00000000 --- a/tests/integration/mgrs/mgrs.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import config from 'config'; -import { DependencyContainer } from 'tsyringe'; -import { Application } from 'express'; -import { CleanupRegistry } from '@map-colonies/cleanup-registry'; -import jsLogger from '@map-colonies/js-logger'; -import { trace } from '@opentelemetry/api'; -import httpStatusCodes from 'http-status-codes'; -import { getApp } from '../../../src/app'; -import { SERVICES } from '../../../src/common/constants'; -import { S3_REPOSITORY_SYMBOL } from '../../../src/common/s3/s3Repository'; -import { cronLoadTileLatLonDataSymbol } from '../../../src/latLon/DAL/latLonDAL'; -import { MgrsRequestSender } from './helpers/requestSender'; - -describe('/search/MGRS', function () { - let requestSender: MgrsRequestSender; - let app: { app: Application; container: DependencyContainer }; - - beforeEach(async function () { - app = await getApp({ - override: [ - { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, - { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, - { token: S3_REPOSITORY_SYMBOL, provider: { useValue: {} } }, - { token: SERVICES.S3_CLIENT, provider: { useValue: {} } }, - { token: cronLoadTileLatLonDataSymbol, provider: { useValue: {} } }, - { token: SERVICES.ELASTIC_CLIENTS, provider: { useValue: {} } }, - ], - useChild: true, - }); - - requestSender = new MgrsRequestSender(app.app); - }); - - afterAll(async function () { - const cleanupRegistry = app.container.resolve(SERVICES.CLEANUP_REGISTRY); - await cleanupRegistry.trigger(); - app.container.reset(); - - jest.clearAllTimers(); - }); - - describe('Happy Path', function () { - it('should return 200 status code and MGRS in geojson', async function () { - const response = await requestSender.getTile({ tile: '18SUJ2339007393' }); - - expect(response.status).toBe(httpStatusCodes.OK); - expect(response).toSatisfyApiSpec(); - expect(response.body).toMatchObject({ - type: 'Feature', - geocoding: { - query: { - tile: '18SUJ2339007393', - }, - response: { - max_score: 1, - results_count: 1, - match_latency_ms: 0, - }, - }, - bbox: [-77.03654883669269, 38.89767541638445, -77.03653756947197, 38.897684623284015], - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-77.03654883669269, 38.89767541638445], - [-77.03654883669269, 38.897684623284015], - [-77.03653756947197, 38.897684623284015], - [-77.03653756947197, 38.89767541638445], - [-77.03654883669269, 38.89767541638445], - ], - ], - }, - properties: { - score: 1, - }, - }); - }); - }); - - describe('Bad Path', function () { - it('should return 400 status code when MGRS is invalid', async function () { - const response = await requestSender.getTile({ tile: 'ABC{}' }); - - expect(response.body).toMatchObject({ - message: 'Invalid MGRS tile', - }); - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - }); - - it('should return 400 status code when MGRS is missing', async function () { - const response = await requestSender.getTile(); - - expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); - expect(response.body).toMatchObject({ - message: "request/query must have required property 'tile'", - }); - }); - }); - - describe('Sad Path', function () { - // All requests with status code 4XX-5XX - it('should return 500 status code when MGRSPoint zone letter A not handled', async function () { - const response = await requestSender.getTile({ tile: '{ABC}' }); - - expect(response.status).toBe(httpStatusCodes.INTERNAL_SERVER_ERROR); - expect(response.body).toMatchObject({ - message: 'MGRSPoint zone letter A not handled: {ABC}', - }); - }); - }); -}); diff --git a/tests/integration/control/route/helpers/requestSender.ts b/tests/integration/route/helpers/requestSender.ts similarity index 69% rename from tests/integration/control/route/helpers/requestSender.ts rename to tests/integration/route/helpers/requestSender.ts index d5dd4b68..adac30a7 100644 --- a/tests/integration/control/route/helpers/requestSender.ts +++ b/tests/integration/route/helpers/requestSender.ts @@ -1,5 +1,5 @@ import * as supertest from 'supertest'; -import { GetRoutesQueryParams } from '../../../../../src/control/route/controllers/routeController'; +import { GetRoutesQueryParams } from '../../../../src/route/controllers/routeController'; export class RouteRequestSender { public constructor(private readonly app: Express.Application) {} @@ -7,9 +7,9 @@ export class RouteRequestSender { public async getRoutes(queryParams?: GetRoutesQueryParams): Promise { return supertest .agent(this.app) - .get('/search/control/routes') + .get('/v1/search/routes/') .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') + .set('X-API-Key', 'abc123') .set('x-user-id', 'abc123') .query(queryParams ?? {}); } diff --git a/tests/integration/route/route.spec.ts b/tests/integration/route/route.spec.ts new file mode 100644 index 00000000..6e195f95 --- /dev/null +++ b/tests/integration/route/route.spec.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import httpStatusCodes from 'http-status-codes'; +import { getApp } from '../../../src/app'; +import { SERVICES } from '../../../src/common/constants'; +import { GetRoutesQueryParams } from '../../../src/route/controllers/routeController'; +import { RouteRequestSender } from './helpers/requestSender'; + +describe('/routes', function () { + let requestSender: RouteRequestSender; + + beforeEach(async function () { + const app = await getApp({ + override: [ + { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, + { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, + ], + useChild: true, + }); + requestSender = new RouteRequestSender(app.app); + }); + + describe('Happy Path', function () { + it('should return 200 status code and the route', async function () { + const response = await requestSender.getRoutes({ command_name: 'route96' }); + + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + }); + it('should return 200 status code and empty response', async function () { + const response = await requestSender.getRoutes({ command_name: '48054805' }); + + expect(response.status).toBe(httpStatusCodes.OK); + + expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + features: [], + }); + }); + }); + describe('Bad Path', function () { + // All requests with status code of 400 + it('Should return 400 status code and meessage "request/query must have required property \'command_name\'"', async function () { + const message = "request/query must have required property 'command_name'"; + + const response = await requestSender.getRoutes(); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + }); + it('Should return 400 status code for unknown parameter', async function () { + const parameter = 'test1234'; + const message = `Unknown query parameter '${parameter}'`; + + const response = await requestSender.getRoutes({ [parameter]: parameter } as unknown as GetRoutesQueryParams); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + }); + }); + describe('Sad Path', function () { + // All requests with status code 4XX-5XX + }); +}); diff --git a/tests/integration/control/tile/helpers/requestSender.ts b/tests/integration/tile/helpers/requestSender.ts similarity index 70% rename from tests/integration/control/tile/helpers/requestSender.ts rename to tests/integration/tile/helpers/requestSender.ts index d15778c1..0eaed8de 100644 --- a/tests/integration/control/tile/helpers/requestSender.ts +++ b/tests/integration/tile/helpers/requestSender.ts @@ -1,5 +1,5 @@ import * as supertest from 'supertest'; -import { GetTilesQueryParams } from '../../../../../src/control/tile/controllers/tileController'; +import { GetTilesQueryParams } from '../../../../src/tile/controllers/tileController'; export class TileRequestSender { public constructor(private readonly app: Express.Application) {} @@ -7,9 +7,9 @@ export class TileRequestSender { public async getTiles(queryParams?: GetTilesQueryParams): Promise { return supertest .agent(this.app) - .get('/search/control/tiles') + .get('/v1/search/tiles/') .set('Content-Type', 'application/json') - .set('x-api-key', 'abc123') + .set('X-API-Key', 'abc123') .set('x-user-id', 'abc123') .query(queryParams ?? {}); } diff --git a/tests/integration/tile/tile.spec.ts b/tests/integration/tile/tile.spec.ts new file mode 100644 index 00000000..abd5bd6e --- /dev/null +++ b/tests/integration/tile/tile.spec.ts @@ -0,0 +1,124 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import httpStatusCodes from 'http-status-codes'; +import { getApp } from '../../../src/app'; +import { SERVICES } from '../../../src/common/constants'; +import { GetTilesQueryParams } from '../../../src/tile/controllers/tileController'; +import { TileRequestSender } from './helpers/requestSender'; + +describe('/tiles', function () { + let requestSender: TileRequestSender; + + beforeEach(async function () { + const app = await getApp({ + override: [ + { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, + { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, + ], + useChild: true, + }); + requestSender = new TileRequestSender(app.app); + }); + + describe('Happy Path', function () { + it('should return 200 status code and the tile', async function () { + const response = await requestSender.getTiles({ tile: 'RIT' }); + + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + coordinates: [ + [ + [12.539507865186607, 41.851751203650096], + [12.536787075186538, 41.94185043165008], + [12.42879133518656, 41.93952837265009], + [12.431625055186686, 41.84943698365008], + [12.539507865186607, 41.851751203650096], + ], + ], + type: 'Polygon', + }, + properties: { + TILE_NAME: 'RIT', + TYPE: 'TILE', + }, + }, + ], + }); + }); + + it('should return 200 status code and the subtile', async function () { + const response = await requestSender.getTiles({ tile: 'GRC', sub_tile: '65' }); + + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + coordinates: [ + [ + [27.149158174343427, 35.63159611670335], + [27.149274355343437, 35.64061707270338], + [27.138786228343463, 35.640716597703374], + [27.13867103934342, 35.631695606703374], + [27.149158174343427, 35.63159611670335], + ], + ], + type: 'Polygon', + }, + properties: { + SUB_TILE_ID: '65', + TILE_NAME: 'GRC', + TYPE: 'SUB_TILE', + }, + }, + ], + }); + }); + + it('should return 200 status code and response empty array', async function () { + const response = await requestSender.getTiles({ tile: 'xyz' }); + + expect(response.status).toBe(httpStatusCodes.OK); + expect(response).toSatisfyApiSpec(); + expect(response.body).toMatchObject({ + type: 'FeatureCollection', + features: [], + }); + }); + }); + describe('Bad Path', function () { + // All requests with status code of 400 + it('Should return 400 status code and meessage "request/query must have required property \'tile\'"', async function () { + const message = "request/query must have required property 'tile'"; + + const response = await requestSender.getTiles(); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + }); + it('Should return 400 status code for unknown parameter', async function () { + const parameter = 'test1234'; + const message = `Unknown query parameter '${parameter}'`; + + const response = await requestSender.getTiles({ [parameter]: parameter } as unknown as GetTilesQueryParams); + + expect(response.status).toBe(httpStatusCodes.BAD_REQUEST); + expect(response.body).toMatchObject({ message }); + expect(response).toSatisfyApiSpec(); + }); + }); + describe('Sad Path', function () { + // All requests with status code 4XX-5XX + }); +}); diff --git a/tests/unit/common/objects.ts b/tests/unit/common/objects.ts new file mode 100644 index 00000000..3ef7ec5b --- /dev/null +++ b/tests/unit/common/objects.ts @@ -0,0 +1,271 @@ +/* eslint-disable @typescript-eslint/no-magic-numbers */ +/* eslint-disable @typescript-eslint/naming-convention */ +import { estypes } from '@elastic/elasticsearch'; +import { FeatureCollection } from 'geojson'; + +/* ------------------- Objects for testing common/utils.ts: formatResponse() - ITEM type -------------------*/ +export const itemElasticResponse: Pick = { + hits: { + total: { value: 1, relation: 'eq' }, + max_score: 1.89712, + hits: [ + { + _index: 'control_gil_v5', + _id: 'CONTROL.ITEMS', + _score: 1.89712, + _source: { + type: 'Feature', + geometry: { + coordinates: [ + [ + [98.96871358832425, 18.77187541003238], + [98.96711613001014, 18.772012912146167], + [98.9668257091372, 18.77957500633211], + [98.96517988589727, 18.783516234000658], + [98.96305002071, 18.786174113473763], + [98.96222710028087, 18.786036643850125], + [98.9615009529004, 18.784478609414165], + [98.96096850521184, 18.78324114944766], + [98.96058105300438, 18.74932410944021], + [98.96029062202649, 18.747169677704846], + [98.9619364723165, 18.74666541646775], + [98.96479250629358, 18.7514784826093], + [98.96401797557468, 18.75193687161918], + [98.96614791571238, 18.754412116891245], + [98.96639000300826, 18.75940841151673], + [98.96779381973744, 18.759133392649602], + [98.96798745662056, 18.76018763174328], + [98.96789063809456, 18.761746062357858], + [98.96905242991687, 18.763487833908357], + [98.96871358832425, 18.77187541003238], + ], + ], + type: 'Polygon', + }, + properties: { + OBJECT_COMMAND_NAME: '4805', + TILE_NAME: 'DEF', + SUB_TILE_ID: '36', + ENTITY_HEB: 'airport', + TYPE: 'ITEM', + }, + }, + }, + ], + }, +}; + +export const itemExpectedFormattedResponse: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + coordinates: [ + [ + [98.96871358832425, 18.77187541003238], + [98.96711613001014, 18.772012912146167], + [98.9668257091372, 18.77957500633211], + [98.96517988589727, 18.783516234000658], + [98.96305002071, 18.786174113473763], + [98.96222710028087, 18.786036643850125], + [98.9615009529004, 18.784478609414165], + [98.96096850521184, 18.78324114944766], + [98.96058105300438, 18.74932410944021], + [98.96029062202649, 18.747169677704846], + [98.9619364723165, 18.74666541646775], + [98.96479250629358, 18.7514784826093], + [98.96401797557468, 18.75193687161918], + [98.96614791571238, 18.754412116891245], + [98.96639000300826, 18.75940841151673], + [98.96779381973744, 18.759133392649602], + [98.96798745662056, 18.76018763174328], + [98.96789063809456, 18.761746062357858], + [98.96905242991687, 18.763487833908357], + [98.96871358832425, 18.77187541003238], + ], + ], + type: 'Polygon', + }, + properties: { + OBJECT_COMMAND_NAME: '4805', + TILE_NAME: 'DEF', + SUB_TILE_ID: '36', + ENTITY_HEB: 'airport', + TYPE: 'ITEM', + }, + }, + ], +}; + +/* ------------------- Objects for testing common/utils.ts: formatResponse() - TILE type -------------------*/ + +export const tileElasticResponse: Pick = { + hits: { + total: { value: 1, relation: 'eq' }, + max_score: 2.184802, + hits: [ + { + _index: 'control_gil_v5', + _id: 'CONTROL.TILES', + _score: 2.184802, + _source: { + type: 'Feature', + geometry: { + coordinates: [ + [ + [12.539507865186607, 41.851751203650096], + [12.536787075186538, 41.94185043165008], + [12.42879133518656, 41.93952837265009], + [12.431625055186686, 41.84943698365008], + [12.539507865186607, 41.851751203650096], + ], + ], + type: 'Polygon', + }, + properties: { TILE_NAME: 'RIT', TYPE: 'TILE' }, + }, + }, + ], + }, +}; + +export const tileExpectedFormattedResponse: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + coordinates: [ + [ + [12.539507865186607, 41.851751203650096], + [12.536787075186538, 41.94185043165008], + [12.42879133518656, 41.93952837265009], + [12.431625055186686, 41.84943698365008], + [12.539507865186607, 41.851751203650096], + ], + ], + type: 'Polygon', + }, + properties: { TILE_NAME: 'RIT', TYPE: 'TILE' }, + }, + ], +}; + +/* ------------------- Objects for testing common/utils.ts: formatResponse() - SUB_TILE type -------------------*/ + +export const subTileElasticResponse: Pick = { + hits: { + total: { value: 1, relation: 'eq' }, + max_score: 2.877949, + hits: [ + { + _index: 'control_gil_v5', + _id: 'CONTROL.SUB_TILES', + _score: 2.877949, + _source: { + type: 'Feature', + geometry: { + coordinates: [ + [ + [27.149158174343427, 35.63159611670335], + [27.149274355343437, 35.64061707270338], + [27.138786228343463, 35.640716597703374], + [27.13867103934342, 35.631695606703374], + [27.149158174343427, 35.63159611670335], + ], + ], + type: 'Polygon', + }, + properties: { SUB_TILE_ID: '65', TILE_NAME: 'GRC', TYPE: 'SUB_TILE' }, + }, + }, + ], + }, +}; +export const subTileExpectedFormattedResponse: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + coordinates: [ + [ + [27.149158174343427, 35.63159611670335], + [27.149274355343437, 35.64061707270338], + [27.138786228343463, 35.640716597703374], + [27.13867103934342, 35.631695606703374], + [27.149158174343427, 35.63159611670335], + ], + ], + type: 'Polygon', + }, + properties: { SUB_TILE_ID: '65', TILE_NAME: 'GRC', TYPE: 'SUB_TILE' }, + }, + ], +}; + +/* ------------------- Objects for testing common/utils.ts: formatResponse() - ROUTE type -------------------*/ + +export const routeElasticResponse: Pick = { + hits: { + total: { value: 1, relation: 'eq' }, + max_score: 1.89712, + hits: [ + { + _index: 'control_gil_v5', + _id: 'CONTROL.ROUTES', + _score: 1.89712, + _source: { + type: 'Feature', + geometry: { + coordinates: [ + [13.448493352142947, 52.31016611400918], + [13.447219581381603, 52.313370282889224], + [13.448088381125075, 52.31631514453963], + [13.450458681234068, 52.31867376333767], + [13.451112278530388, 52.32227665244022], + [13.449728938644029, 52.32463678850752], + [13.445021899434977, 52.32863442881066], + [13.444723882330948, 52.340023400115086], + [13.446229682887974, 52.34532799609971], + ], + type: 'LineString', + }, + properties: { + OBJECT_COMMAND_NAME: 'route96', + ENTITY_HEB: 'route96', + TYPE: 'ROUTE', + }, + }, + }, + ], + }, +}; +export const routeExpectedFormattedResponse: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + coordinates: [ + [13.448493352142947, 52.31016611400918], + [13.447219581381603, 52.313370282889224], + [13.448088381125075, 52.31631514453963], + [13.450458681234068, 52.31867376333767], + [13.451112278530388, 52.32227665244022], + [13.449728938644029, 52.32463678850752], + [13.445021899434977, 52.32863442881066], + [13.444723882330948, 52.340023400115086], + [13.446229682887974, 52.34532799609971], + ], + type: 'LineString', + }, + properties: { + OBJECT_COMMAND_NAME: 'route96', + ENTITY_HEB: 'route96', + TYPE: 'ROUTE', + }, + }, + ], +}; diff --git a/tests/unit/common/utils.spec.ts b/tests/unit/common/utils.spec.ts new file mode 100644 index 00000000..1913381b --- /dev/null +++ b/tests/unit/common/utils.spec.ts @@ -0,0 +1,74 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { estypes } from '@elastic/elasticsearch'; +import { + formatResponse, + additionalSearchProperties, + convertUTMToWgs84, + convertWgs84ToUTM, + validateTile, + validateWGS84Coordinate, +} from '../../../src/common/utils'; +import config from '../../../config/test.json'; +import { FIELDS } from '../../../src/common/constants'; +import { WGS84Coordinate } from '../../../src/common/interfaces'; +import { + itemElasticResponse, + itemExpectedFormattedResponse, + tileElasticResponse, + tileExpectedFormattedResponse, + subTileElasticResponse, + subTileExpectedFormattedResponse, + routeElasticResponse, + routeExpectedFormattedResponse, +} from './objects'; + +describe('utils', () => { + test.each([ + [itemElasticResponse, itemExpectedFormattedResponse], + [tileElasticResponse, tileExpectedFormattedResponse], + [subTileElasticResponse, subTileExpectedFormattedResponse], + [routeElasticResponse, routeExpectedFormattedResponse], + ])('should convert ElasticSearch query response to FeatureCollection', (elasticResponse, formattedResponse) => { + const result = formatResponse(elasticResponse as estypes.SearchResponse); + expect(result).toMatchObject(formattedResponse); + }); + + it('should return additional search properties', () => { + const size = 10; + const result = additionalSearchProperties(size); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(result).toMatchObject({ size, index: config.db.elastic.searchy.properties.index as string, _source: FIELDS }); + }); + + it('should convert UTM to WGS84', () => { + const result = convertUTMToWgs84(630048, 4330433, 29); + expect(result).toMatchObject({ lat: 39.11335578352079, lon: -7.495780486809503 }); + }); + + it('should convert WGS84 to UTM', () => { + const result = convertWgs84ToUTM(39.11335712352982, -7.495784527093747); + expect(result).toMatchObject({ Easting: 630048, Northing: 4330433, ZoneNumber: 29 }); + }); + + test.each([ + [{ tileName: 'BRN', subTileNumber: [10, 10, 10] }, true], + [{ tileName: 'BRN', subTileNumber: [0, 10, 10] }, false], + [{ tileName: 'BRN', subTileNumber: [10, 0, 10] }, false], + [{ tileName: 'BRN', subTileNumber: [10, 10, 0] }, false], + [{ tileName: 'BRN', subTileNumber: [10, 10, 100] }, false], + [{ tileName: 'BRN', subTileNumber: [10, 10] }, false], + [{ tileName: '', subTileNumber: [10, 10, 10] }, false], + ])(`should validate tile`, (tile, expected) => { + expect(validateTile(tile as never)).toBe(expected); + }); + + test.each<[WGS84Coordinate, boolean]>([ + [{ lon: 50, lat: 50 }, true], + [{ lon: 190, lat: 50 }, false], + [{ lon: 50, lat: 190 }, false], + [{ lon: -10, lat: 50 }, false], + [{ lon: 50, lat: -10 }, false], + ])('should validate WGS84 coordinate', (coordinate, expected) => { + expect(validateWGS84Coordinate(coordinate as never)).toBe(expected); + }); +});