diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 2be63e7b..48e05ca7 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -78,6 +78,28 @@ "__name": "S3_FILES_DATA", "__format": "json" } + }, + "redis": { + "host": "REDIS_HOST", + "port": { + "__name": "REDIS_PORT", + "__format": "number" + }, + "username": "REDIS_USERNAME", + "password": "REDIS_PASSWORD", + "enableSslAuth": { + "__name": "REDIS_ENABLE_SSL_AUTH", + "__format": "boolean" + }, + "sslPaths": { + "ca": "REDIS_CA_PATH", + "key": "REDIS_KEY_PATH", + "cert": "REDIS_CERT_PATH" + }, + "database": { + "__name": "REDIS_DATABASE", + "__format": "number" + } } }, "application": { diff --git a/config/default.json b/config/default.json index 4c6af3f2..9c165b10 100644 --- a/config/default.json +++ b/config/default.json @@ -69,6 +69,19 @@ "fileName": "table.json" } } + }, + "redis": { + "host": "REDIS_HOST", + "port": 6379, + "username": "", + "password": "", + "enableSslAuth": false, + "sslPaths": { + "ca": "", + "key": "", + "cert": "" + }, + "database": 0 } }, "application": { diff --git a/config/test.json b/config/test.json index 8014b339..8305cd32 100644 --- a/config/test.json +++ b/config/test.json @@ -43,6 +43,19 @@ "fileName": "table.json" } } + }, + "redis": { + "host": "localhost", + "port": 6379, + "username": "", + "password": "", + "enableSslAuth": false, + "sslPaths": { + "ca": "", + "key": "", + "cert": "" + }, + "database": 1 } }, "application": { diff --git a/dataSource.ts b/dataSource.ts deleted file mode 100644 index ad31f0f6..00000000 --- a/dataSource.ts +++ /dev/null @@ -1,13 +0,0 @@ -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/helm/templates/configmap.yaml b/helm/templates/configmap.yaml index 69202f7f..b83572d1 100644 --- a/helm/templates/configmap.yaml +++ b/helm/templates/configmap.yaml @@ -18,5 +18,18 @@ 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 007efa2a..5484cfd7 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -53,6 +53,11 @@ 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 }} @@ -109,6 +114,11 @@ 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 25fd7688..3adb3d5b 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -71,6 +71,19 @@ 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 fe86d34e..1d92fd88 100644 --- a/openapi3.yaml +++ b/openapi3.yaml @@ -1,7 +1,7 @@ -openapi: "3.0.1" +openapi: '3.0.1' info: - version: "0.1.0" - title: "Geocoding" + 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. license: @@ -15,7 +15,7 @@ paths: /search/query: get: operationId: GetSmartQuery - summary: "Search anything" + summary: 'Search anything' description: |- This is for general queries. the services will make a sophisticated guess. parameters: @@ -26,16 +26,16 @@ paths: type: string minLength: 3 maxLength: 100 - title: "Query" + 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" + - $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" + description: 'OK
Will return valid GeoJSON FeatureCollection' headers: x-request-id: schema: @@ -45,15 +45,15 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/genericGeocodingResponse" + $ref: '#/components/schemas/genericGeocodingResponse' 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: [] @@ -61,8 +61,8 @@ paths: /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." + 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: @@ -73,7 +73,7 @@ paths: type: string minLength: 3 maxLength: 100 - title: "Query" + title: 'Query' description: Text to search allowReserved: true - name: source @@ -82,7 +82,7 @@ paths: schema: type: array items: - $ref: "#/components/schemas/Source" + $ref: '#/components/schemas/Source' title: Source description: | Sources to include (if not specified, all sources will be queried) @@ -92,17 +92,17 @@ paths: 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" + - $ref: '#/components/parameters/geo_context' + - $ref: '#/components/parameters/geo_context_mode' + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/disable_fuzziness' responses: 200: - description: "OK" + description: 'OK' x-request-id: schema: type: string @@ -139,13 +139,13 @@ paths: sub_placetype: 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: [] @@ -155,10 +155,10 @@ paths: operationId: locationGetRegions tags: - Location Name Based Search - summary: "Get all available regions to filter on using location query" + summary: 'Get all available regions to filter on using location query' responses: 200: - description: "All regions" + description: 'All regions' content: application/json: schema: @@ -166,13 +166,13 @@ paths: items: type: string # return geojson with name property 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: [] @@ -182,10 +182,10 @@ paths: operationId: locationGetSources tags: - Location Name Based Search - summary: "Get all available sources to filter on using location query" + summary: 'Get all available sources to filter on using location query' responses: 200: - description: "All sources" + description: 'All sources' content: application/json: schema: @@ -193,13 +193,13 @@ paths: items: 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: [] @@ -209,50 +209,50 @@ paths: operationId: controlGetTilesByQueryParams tags: - Control - summary: "Search tiles and sub tiles" + 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. parameters: - - name: "tile" - in: "query" - description: "Tile name" + - name: 'tile' + in: 'query' + description: 'Tile name' schema: - type: "string" + type: 'string' minLength: 2 maxLength: 3 - - name: "sub_tile" - in: "query" - description: "Sub tile number" + - name: 'sub_tile' + in: 'query' + description: 'Sub tile number' schema: - type: "string" - pattern: "^[1-9][0-9]*$" + type: 'string' + pattern: '^[1-9][0-9]*$' - name: mgrs - description: "1 meters MGRS Tile" - example: "18SUJ2338907395" + description: '1 meters MGRS Tile' + example: '18SUJ2338907395' in: query schema: type: string - - $ref: "#/components/parameters/geo_context" - - $ref: "#/components/parameters/geo_context_mode" - - $ref: "#/components/parameters/limit" - - $ref: "#/components/parameters/disable_fuzziness" + - $ref: '#/components/parameters/geo_context' + - $ref: '#/components/parameters/geo_context_mode' + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/disable_fuzziness' responses: 200: - description: "OK" + description: 'OK' content: application/json: schema: - $ref: "#/components/schemas/tilesSchema" + $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: [] @@ -261,51 +261,51 @@ paths: operationId: controlGetItemsByQueryParams tags: - Control - summary: "Search control items" - description: "" + summary: 'Search control items' + description: '' 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: 'tile' + in: 'query' + description: 'The tile the item in it (full name of it)' schema: - type: "string" + 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)" + - 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: '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' responses: 200: - description: "OK" + description: 'OK' content: application/json: schema: - $ref: "#/components/schemas/itemsSchema" + $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: [] @@ -314,42 +314,42 @@ paths: operationId: controlGetRoutesByQueryParams 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. " + 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: "control_point" - in: "query" - description: "The associated report control point of the route" + - name: 'control_point' + in: 'query' + description: 'The associated report control point of the route' 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: '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' responses: 200: - description: "OK (can be routesSchema or itemsSchema)" + description: 'OK (can be routesSchema or itemsSchema)' content: application/json: schema: anyOf: - - $ref: "#/components/schemas/routesSchema" - - $ref: "#/components/schemas/itemsSchema" + - $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: [] @@ -358,29 +358,29 @@ paths: operationId: getMGRSToGeom tags: - MGRS - summary: "Convert a MGRS string to Geometry in GeoJSON" + summary: 'Convert a MGRS string to Geometry in GeoJSON' parameters: - - name: "tile" - in: "query" - description: "MGRS tile string" + - name: 'tile' + in: 'query' + description: 'MGRS tile string' schema: - type: "string" + type: 'string' required: true responses: 200: - description: "OK" + description: 'OK' content: application/json: schema: - $ref: "#/components/schemas/mgrsTileSchema" + $ref: '#/components/schemas/mgrsTileSchema' 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: [] @@ -389,34 +389,34 @@ paths: operationId: convertionGetLatLonToMgrs tags: - Convertions - summary: "Convert a WGS84 coordinate to US Army MGRS / Control Grid" + summary: 'Convert a WGS84 coordinate to US Army MGRS / Control Grid' 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" + - name: 'target_grid' in: query description: Choose target grid schema: type: string - enum: ["control", "MGRS"] + enum: ['control', 'MGRS'] required: true responses: 200: - description: "OK" + description: 'OK' content: application/json: schema: @@ -459,13 +459,13 @@ paths: additionalProperty1: type: object 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: [] @@ -475,11 +475,11 @@ paths: operationId: postSearchFeedback tags: - Feedback - summary: "Retrieve feedback from services about quality of results" + summary: 'Retrieve feedback from services about quality of results' parameters: - name: choosen_result_id - in: "query" - description: "The result ID of chosen search" + in: 'query' + description: 'The result ID of chosen search' schema: type: string minimum: 1 @@ -495,17 +495,17 @@ paths: required: true responses: 204: - description: "OK - No Content" + description: 'OK - No Content' 400: - "$ref": "#/components/responses/BadRequest" + '$ref': '#/components/responses/BadRequest' 404: - $ref: "#/components/responses/NotFound" + $ref: '#/components/responses/NotFound' 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: [] @@ -513,55 +513,55 @@ paths: 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" + description: 'Please provide a valid token' content: application/json: schema: - $ref: "#/components/schemas/errorSchema" + $ref: '#/components/schemas/errorSchema' NotFound: - description: "Resource Not Found" + description: 'Resource Not Found' 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" + name: 'disable_fuzziness' + in: 'query' description: |- If an accurate result is obtained, only it will be returned schema: - $ref: "#/components/schemas/disable_fuzziness" + $ref: '#/components/schemas/disable_fuzziness' limit: - name: "limit" - in: "query" - description: "Maximum number of results" + name: 'limit' + in: 'query' + description: 'Maximum number of results' schema: default: 5 minimum: 1 maximum: 15 - type: "number" + type: 'number' geo_context: - in: "query" - name: "geo_context" + in: 'query' + name: 'geo_context' description: |- Geo context of search. @@ -572,72 +572,72 @@ components: {"lon":value,"lat":value,"radius":value}
{"x":value,"y":value,"zone": number, "radius":value} schema: - $ref: "#/components/schemas/geo_context" + $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" + name: 'geo_context_mode' + in: 'query' + description: 'Choose whether geo_context query parameter will be a filter or a bias value' schema: type: string - enum: ["filter", "bias"] + enum: ['filter', 'bias'] 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" + type: 'object' properties: type: - type: "string" - enum: ["Feature"] + type: 'string' + enum: ['Feature'] geometry: - $ref: "#/components/schemas/Polygon" + $ref: '#/components/schemas/Polygon' properties: - type: "object" + type: 'object' required: - - "TYPE" - - "tile_name" + - 'TYPE' + - 'tile_name' properties: TYPE: - type: "string" + type: 'string' tile_name: - type: "string" + type: 'string' sub_tile_id: - type: "string" + type: 'string' SUB_TILE_NUMBER: - type: "array" + type: 'array' items: - type: "number" + type: 'number' minimum: 0 maximum: 100 minItems: 3 maxItems: 3 mgrsTileSchema: - type: "object" + type: 'object' properties: type: type: "string" @@ -768,22 +768,22 @@ components: 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 an item' required: - - "type" - - "features" - - "geocoding" + - 'type' + - 'features' + - 'geocoding' properties: type: - type: "string" + type: 'string' enum: - - "FeatureCollection" + - 'FeatureCollection' geocoding: type: object required: - - "query" - - "response" + - 'query' + - 'response' properties: version: type: string @@ -792,10 +792,10 @@ components: query: type: object required: - - "text" - - "limit" - - "geo_context" - - "disable_fuzziness" + - 'text' + - 'limit' + - 'geo_context' + - 'disable_fuzziness' properties: text: type: string @@ -806,15 +806,15 @@ components: minimum: 1 maximum: 10 geo_context: - $ref: "#/components/schemas/geo_context" + $ref: '#/components/schemas/geo_context' disable_fuzziness: - $ref: "#/components/schemas/disable_fuzziness" + $ref: '#/components/schemas/disable_fuzziness' response: type: object required: - - "max_score" - - "results_count" - - "match_latency" + - 'max_score' + - 'results_count' + - 'match_latency' properties: max_score: type: number @@ -829,14 +829,14 @@ components: minimum: 0 maximum: 5000 features: - type: "array" + type: 'array' items: - type: "object" + type: 'object' properties: geometry: - $ref: "#/components/schemas/Geometry" + $ref: '#/components/schemas/Geometry' properties: - type: "object" + type: 'object' additionalProperties: true required: - id @@ -944,69 +944,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,7 +1015,7 @@ components: items: type: array items: - $ref: "#/components/schemas/Point3D" + $ref: '#/components/schemas/Point3D' Source: type: string @@ -1025,8 +1025,8 @@ components: title: Region BoundingBox: type: array - description: "Bounding box array that contains [minX,minY,maxX,maxY]" - example: "[-74.382527,40.477003,-73.322346,40.916383]" + description: 'Bounding box array that contains [minX,minY,maxX,maxY]' + example: '[-74.382527,40.477003,-73.322346,40.916383]' items: type: number minLength: 4 @@ -1058,18 +1058,18 @@ components: - type: object properties: bbox: - $ref: "#/components/schemas/BoundingBox" - - $ref: "#/components/schemas/WGS84Circle" - - $ref: "#/components/schemas/UTMCircle" + $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" + type: 'apiKey' + name: 'x-api-key' + in: 'header' x-user-id: - type: "apiKey" - name: "x-user-id" - in: "header" + type: 'apiKey' + name: 'x-user-id' + in: 'header' diff --git a/package-lock.json b/package-lock.json index 168166a4..5eb0f3f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,11 +34,12 @@ "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", @@ -4026,6 +4027,11 @@ "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", @@ -7372,6 +7378,59 @@ "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", @@ -10412,6 +10471,14 @@ "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", @@ -11503,6 +11570,14 @@ "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", @@ -14237,6 +14312,29 @@ "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", @@ -16814,11 +16912,21 @@ "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", @@ -18046,43 +18154,6 @@ "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", @@ -18091,14 +18162,6 @@ "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", @@ -18119,14 +18182,6 @@ "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", @@ -19011,6 +19066,38 @@ "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", @@ -19696,6 +19783,11 @@ "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", @@ -24114,6 +24206,11 @@ "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", @@ -26550,6 +26647,46 @@ "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", @@ -29116,6 +29253,11 @@ "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", @@ -29994,6 +30136,11 @@ "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", @@ -32041,6 +32188,22 @@ "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", @@ -33954,11 +34117,21 @@ "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", @@ -34896,41 +35069,11 @@ "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", @@ -34948,14 +35091,6 @@ "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", @@ -35620,6 +35755,32 @@ "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", @@ -36142,6 +36303,11 @@ } } }, + "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", diff --git a/package.json b/package.json index 890dbf5f..81a0a0bd 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,7 @@ "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", - "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" + "dev:scripts": "npx ts-node ./devScripts/index.ts" }, "directories": { "test": "tests" @@ -70,11 +66,12 @@ "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", diff --git a/src/common/constants.ts b/src/common/constants.ts index b570de47..c1044083 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -14,6 +14,7 @@ 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'), }; @@ -23,3 +24,5 @@ export const ON_SIGNAL = Symbol('onSignal'); export const HEALTHCHECK = Symbol('healthcheck'); export const elasticConfigPath = 'db.elastic'; +export const s3EndpointConfig = 'db.s3.endpoint'; + diff --git a/src/common/errors.ts b/src/common/errors.ts index 244c0576..f23ed0cd 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -49,3 +49,11 @@ export class NotImplementedError extends Error implements HttpError { 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 f569c40a..14cd497b 100644 --- a/src/common/interfaces.ts +++ b/src/common/interfaces.ts @@ -61,6 +61,14 @@ export enum GeoContextMode { 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; diff --git a/src/common/middlewares/feedbackApi.middleware.ts b/src/common/middlewares/feedbackApi.middleware.ts new file mode 100644 index 00000000..33b1b1d8 --- /dev/null +++ b/src/common/middlewares/feedbackApi.middleware.ts @@ -0,0 +1,61 @@ +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, s3EndpointConfig } from '../constants'; +import { RedisClient } from '../redis'; +import { FeebackApiGeocodingResponse, IConfig } from '../interfaces'; +import { XApi } from './utils'; + +const SITE_INDEX = 1; +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 s3Endpoint = this.config.get(s3EndpointConfig); + const drSite = s3Endpoint.split('.'); + + 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[SITE_INDEX], + 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 new file mode 100644 index 00000000..31b6288c --- /dev/null +++ b/src/common/middlewares/utils.ts @@ -0,0 +1,5 @@ +export enum XApi { + REQUEST = 'x-request-id', + USER = 'x-user-id', + KEY = 'x-api-key', +} diff --git a/src/common/redis/index.ts b/src/common/redis/index.ts new file mode 100644 index 00000000..514c3ed5 --- /dev/null +++ b/src/common/redis/index.ts @@ -0,0 +1,48 @@ +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 => { + const logger = container.resolve(SERVICES.LOGGER); + const config = container.resolve(SERVICES.CONFIG); + const dbConfig = config.get('db.redis'); + const connectionOptions = createConnectionOptions(dbConfig); + + 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; +}; diff --git a/src/common/redis/interfaces.ts b/src/common/redis/interfaces.ts new file mode 100644 index 00000000..145a202b --- /dev/null +++ b/src/common/redis/interfaces.ts @@ -0,0 +1,8 @@ +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/utils.ts b/src/common/utils.ts index b1640bd2..94f9873d 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -6,6 +6,8 @@ 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 { ElasticClients } from './elastic'; @@ -108,6 +110,7 @@ export const healthCheckFactory: FactoryFunction = (container: DependencyC 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 @@ -128,6 +131,28 @@ export const healthCheckFactory: FactoryFunction = (container: DependencyC .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]); }; export const parseGeo = (input: GeoContext): Geometry | undefined => { diff --git a/src/containerConfig.ts b/src/containerConfig.ts index 7f34d4e9..be3ba2a4 100644 --- a/src/containerConfig.ts +++ b/src/containerConfig.ts @@ -3,10 +3,10 @@ import { getOtelMixin } from '@map-colonies/telemetry'; import { ListBucketsCommand, S3Client } from '@aws-sdk/client-s3'; 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 { CleanupRegistry } from '@map-colonies/cleanup-registry'; import { ScheduledTask } from 'node-cron'; import { HEALTHCHECK, ON_SIGNAL, SERVICES, SERVICE_NAME } from './common/constants'; import { tracing } from './common/tracing'; @@ -23,6 +23,7 @@ import { GEOTEXT_REPOSITORY_SYMBOL, geotextRepositoryFactory } from './location/ import { GEOTEXT_SEARCH_ROUTER_SYMBOL, geotextSearchRouterFactory } from './location/routes/locationRouter'; import { cronLoadTileLatLonDataFactory, cronLoadTileLatLonDataSymbol } 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'; @@ -148,6 +149,15 @@ export const registerExternalValues = async (options?: RegisterOptions): Promise }); }, }, + { + token: SERVICES.REDIS, + provider: { useFactory: instancePerContainerCachingFactory(redisClientFactory) }, + postInjectionHook: async (deps: DependencyContainer): Promise => { + const redis = deps.resolve(SERVICES.REDIS); + cleanupRegistry.register({ func: redis.disconnect.bind(redis), id: SERVICES.REDIS }); + await redis.connect(); + }, + }, ]; const container = await registerDependencies(dependencies, options?.override, options?.useChild); return container; diff --git a/src/control/route/DAL/routeRepository.ts b/src/control/route/DAL/routeRepository.ts index d1bfd737..34da5b2e 100644 --- a/src/control/route/DAL/routeRepository.ts +++ b/src/control/route/DAL/routeRepository.ts @@ -1,4 +1,3 @@ -import { Logger } from '@map-colonies/js-logger'; import { estypes } from '@elastic/elasticsearch'; import { FactoryFunction } from 'tsyringe'; import { ElasticClient } from '../../../common/elastic'; @@ -11,7 +10,7 @@ import { additionalControlSearchProperties } from '../../utils'; import { RouteQueryParams, queryForControlPointInRoute, queryForRoute } from './queries'; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createRouteRepository = (client: ElasticClient, config: IConfig, logger: Logger) => { +const createRouteRepository = (client: ElasticClient, config: IConfig) => { return { async getRoutes(routeQueryParams: RouteQueryParams, size: number): Promise> { const response = await queryElastic(client, { ...additionalControlSearchProperties(config, size), ...queryForRoute(routeQueryParams) }); @@ -38,8 +37,7 @@ export type RouteRepository = ReturnType; export const routeRepositoryFactory: FactoryFunction = (depContainer) => { return createRouteRepository( depContainer.resolve(SERVICES.ELASTIC_CLIENTS).control, - depContainer.resolve(SERVICES.CONFIG), - depContainer.resolve(SERVICES.LOGGER) + depContainer.resolve(SERVICES.CONFIG) ); }; diff --git a/src/control/tile/DAL/tileRepository.ts b/src/control/tile/DAL/tileRepository.ts index dea6cac8..336818e4 100644 --- a/src/control/tile/DAL/tileRepository.ts +++ b/src/control/tile/DAL/tileRepository.ts @@ -1,4 +1,3 @@ -import { Logger } from '@map-colonies/js-logger'; import { estypes } from '@elastic/elasticsearch'; import { FactoryFunction } from 'tsyringe'; import { BBox } from 'geojson'; @@ -11,7 +10,7 @@ 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, logger: Logger) => { +const createTileRepository = (client: ElasticClient, config: IConfig) => { return { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async getTiles(tileQueryParams: TileQueryParams & Required>): Promise> { @@ -44,11 +43,7 @@ const createTileRepository = (client: ElasticClient, config: IConfig, logger: Lo 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) - ); + 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/utils.ts b/src/control/utils.ts index 7079f423..a1080f88 100644 --- a/src/control/utils.ts +++ b/src/control/utils.ts @@ -101,4 +101,4 @@ export const additionalControlSearchProperties = (config: IConfig, size: number) index: config.get(elasticConfigPath).control.properties.index as string, // eslint-disable-next-line @typescript-eslint/naming-convention _source: CONTROL_FIELDS, -}); +}); \ No newline at end of file diff --git a/src/location/controllers/locationController.ts b/src/location/controllers/locationController.ts index d1efeecd..a8ec476e 100644 --- a/src/location/controllers/locationController.ts +++ b/src/location/controllers/locationController.ts @@ -50,11 +50,11 @@ export class GeotextSearchController { } }; - public getRegions: GetRegionshHandler = (req, res, next) => { + public getRegions: GetRegionshHandler = (req, res) => { return res.status(httpStatus.OK).json(this.manager.regions()); }; - public getSources: GetSourcesHandler = (req, res, next) => { + public getSources: GetSourcesHandler = (req, res) => { return res.status(httpStatus.OK).json(this.manager.sources()); }; } diff --git a/src/serverBuilder.ts b/src/serverBuilder.ts index 729fd433..1c267728 100644 --- a/src/serverBuilder.ts +++ b/src/serverBuilder.ts @@ -16,6 +16,7 @@ import { ROUTE_ROUTER_SYMBOL } from './control/route/routes/routeRouter'; import { LAT_LON_ROUTER_SYMBOL } from './latLon/routes/latLonRouter'; import { GEOTEXT_SEARCH_ROUTER_SYMBOL } from './location/routes/locationRouter'; import { cronLoadTileLatLonDataSymbol } from './latLon/DAL/latLonDAL'; +import { FeedbackApiMiddlewareManager } from './common/middlewares/feedbackApi.middleware'; import { MGRS_ROUTER_SYMBOL } from './mgrs/routers/mgrsRouter'; @injectable() @@ -31,6 +32,7 @@ export class ServerBuilder { @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 ) { this.serverInstance = express(); @@ -57,12 +59,15 @@ export class ServerBuilder { } private buildRoutes(): void { + this.serverInstance.use(this.feedbackApiMiddleware.saveResponses); const router = Router(); + + 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('/search', router); this.serverInstance.use('/lookup', this.latLonRouter); } @@ -92,6 +97,8 @@ 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/tests/configurations/unit/jest.config.js b/tests/configurations/unit/jest.config.js index 07af12e5..bfd03fc4 100644 --- a/tests/configurations/unit/jest.config.js +++ b/tests/configurations/unit/jest.config.js @@ -13,6 +13,7 @@ module.exports = { '!*/common/**', '!**/controllers/**', '!**/routes/**', + '!**/redis/**', '!/src/*', ], coverageDirectory: '/coverage', diff --git a/tests/integration/location/location.spec.ts b/tests/integration/location/location.spec.ts index 03776cf8..faa8c15e 100644 --- a/tests/integration/location/location.spec.ts +++ b/tests/integration/location/location.spec.ts @@ -18,14 +18,12 @@ import { OSM_LA_PORT, GOOGLE_LA_PORT, LA_AIRPORT, - LA_WHITE_POINT_SCHOOL, NY_JFK_AIRPORT, NY_POLICE_AIRPORT, NY_HIERRARCHY, LA_HIERRARCHY, MockLocationQueryFeature, PARIS_WI_SCHOOL, - PARIS_HIERRARCHY, } from './mockObjects'; import { expectedResponse, hierarchiesWithAnyWieght } from './utils';