diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 385aca2..179efa5 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -77,6 +77,16 @@ jobs: --health-timeout 5s --health-retries 5 + redis: + image: redis + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: - name: Check out Git repository uses: actions/checkout@v4 diff --git a/README.md b/README.md index fe36e29..4da01df 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,9 @@ Almost all of our routes consists of the same common query parameters: `geo_cont | disable_fuzziness | Boolean | false | Fuzziness is on by default. If you want exact match, you may set `disable_fuzziness: true`. | > [!IMPORTANT] > We also have Feedback API. Each request is sent back with x-req-id which is the identifier of the request. We kindly ask our users to provide us with a request to Feedback API which contains x-api-key and clicked response. It enables us to research the request and response to be more accurate. -> Feedback API source code is built in a different repository at . +> Feedback API source code is built in a different repository at [feedback-api](https://github.com/MapColonies/feedback-api). > Geocoding API inserts the request and response to Redis before the response is sent. +Speaking of Redis, in the redis config you can add a `prefix` flag, if you'd like to add keys with prefixes to redis. ## Installation Setup Elasticsearch and S3 provider (For local environment, Minio as a personal recommendation). diff --git a/config/test.json b/config/test.json index 6eeb44d..8da55d3 100644 --- a/config/test.json +++ b/config/test.json @@ -35,7 +35,7 @@ "fileName": "table.json" }, "redis": { - "host": "localhost", + "host": "redis", "port": 6379, "username": "", "password": "", @@ -45,7 +45,8 @@ "key": "", "cert": "" }, - "dbIndex": 1 + "dbIndex": 1, + "ttl": 300 } }, "application": { diff --git a/package-lock.json b/package-lock.json index e8b2872..0549eef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@map-colonies/js-logger": "^2.0.0", "@map-colonies/openapi-express-viewer": "^4.0.0", "@map-colonies/read-pkg": "^1.0.0", - "@map-colonies/schemas": "^1.12.2", + "@map-colonies/schemas": "^1.14.0", "@map-colonies/telemetry": "^10.0.1", "@opentelemetry/api": "^1.9.0", "@smithy/node-http-handler": "^3.1.4", @@ -4258,9 +4258,9 @@ } }, "node_modules/@map-colonies/schemas": { - "version": "1.12.2", - "resolved": "https://ghatmpstorage.blob.core.windows.net/npm-packages/schemas-d7b33710c7a41d83f15469ca6c1d6800b0b6bd2d.tgz", - "integrity": "sha512-V5Wvo4yIfc+mkLUE3RYna8ME2Bn5sQfH+2KyyVB8kN+IgmvGqx47W0kPSiw3xtXhliuDFw9psT2/1m8mSSsI5g==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@map-colonies/schemas/-/schemas-1.14.0.tgz", + "integrity": "sha512-EBR36xOnIrRrXc+HG1MnGUKe/epXF5HAyEZc5jwV0RQzvn66Buo70+/jW9A/yb/mtrroSqHn2bdlLuH5kml6iQ==", "license": "MIT" }, "node_modules/@map-colonies/standard-version-update-helm-version": { @@ -27511,9 +27511,9 @@ } }, "@map-colonies/schemas": { - "version": "1.12.2", - "resolved": "https://ghatmpstorage.blob.core.windows.net/npm-packages/schemas-d7b33710c7a41d83f15469ca6c1d6800b0b6bd2d.tgz", - "integrity": "sha512-V5Wvo4yIfc+mkLUE3RYna8ME2Bn5sQfH+2KyyVB8kN+IgmvGqx47W0kPSiw3xtXhliuDFw9psT2/1m8mSSsI5g==" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@map-colonies/schemas/-/schemas-1.14.0.tgz", + "integrity": "sha512-EBR36xOnIrRrXc+HG1MnGUKe/epXF5HAyEZc5jwV0RQzvn66Buo70+/jW9A/yb/mtrroSqHn2bdlLuH5kml6iQ==" }, "@map-colonies/standard-version-update-helm-version": { "version": "2.0.1", diff --git a/package.json b/package.json index 13b48af..06295d9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@map-colonies/js-logger": "^2.0.0", "@map-colonies/openapi-express-viewer": "^4.0.0", "@map-colonies/read-pkg": "^1.0.0", - "@map-colonies/schemas": "^1.12.2", + "@map-colonies/schemas": "^1.14.0", "@map-colonies/telemetry": "^10.0.1", "@opentelemetry/api": "^1.9.0", "@smithy/node-http-handler": "^3.1.4", diff --git a/src/common/middlewares/feedbackApi.middleware.ts b/src/common/middlewares/feedbackApi.middleware.ts index 67507b5..a968a3c 100644 --- a/src/common/middlewares/feedbackApi.middleware.ts +++ b/src/common/middlewares/feedbackApi.middleware.ts @@ -21,6 +21,7 @@ export class FeedbackApiMiddlewareManager { const reqId = res.getHeader(XApi.REQUEST); const redisClient = this.redis; const logger = this.logger; + const prefix = this.config.get(redisConfigPath).prefix; const drSite = this.config.get(siteConfig); @@ -36,10 +37,11 @@ export class FeedbackApiMiddlewareManager { const { ttl: redisTtl } = this.config.get(redisConfigPath); const originalJson = res.json; + const fullRequestId = prefix !== undefined ? `${prefix}:${reqId as string}` : (reqId as string); const logJson = function (this: Response, body: JSON): Response { geocodingResponseDetails.response = body; redisClient - .setEx(reqId as string, redisTtl ?? defaultRedisTtl, JSON.stringify(geocodingResponseDetails)) + .setEx(fullRequestId, redisTtl ?? defaultRedisTtl, JSON.stringify(geocodingResponseDetails)) .then(() => { logger.info({ msg: `response ${reqId?.toString() ?? ''} saved to redis` }); }) diff --git a/tests/integration/control/item/helpers/index.ts b/tests/integration/control/item/helpers/index.ts new file mode 100644 index 0000000..84115c1 --- /dev/null +++ b/tests/integration/control/item/helpers/index.ts @@ -0,0 +1,20 @@ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import { SERVICES } from '@src/common/constants'; +import { S3_REPOSITORY_SYMBOL } from '@src/common/s3/s3Repository'; +import { cronLoadTileLatLonDataSymbol } from '@src/latLon/DAL/latLonDAL'; +import { GetBaseRegisterOptions } from '@tests/integration/helpers/types'; + +export const getBaseRegisterOptions: GetBaseRegisterOptions = (options = []) => { + return { + 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: {} } }, + ...options, + ], + useChild: true, + }; +}; diff --git a/tests/integration/control/item/item.spec.ts b/tests/integration/control/item/item.spec.ts index 2e35f4e..67d2e53 100644 --- a/tests/integration/control/item/item.spec.ts +++ b/tests/integration/control/item/item.spec.ts @@ -1,35 +1,26 @@ /* 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 { DependencyContainer } from 'tsyringe'; import httpStatusCodes from 'http-status-codes'; +import { IConfig } from 'config'; +import { RedisClient } from '@src/common/redis'; import { getApp } from '../../../../src/app'; -import { SERVICES } from '../../../../src/common/constants'; +import { redisConfigPath, 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 { ITEM_1234, ITEM_1235, ITEM_1236 } from '../../../mockObjects/items'; import { ItemRequestSender } from './helpers/requestSender'; +import { getBaseRegisterOptions } from './helpers'; describe('/search/control/items', function () { let requestSender: ItemRequestSender; let depContainer: DependencyContainer; beforeEach(async function () { - const [app, container] = 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, - }); + const [app, container] = await getApp(getBaseRegisterOptions()); + depContainer = container; requestSender = new ItemRequestSender(app); }); @@ -245,7 +236,47 @@ describe('/search/control/items', function () { expectedResponse(requestParams, [ITEM_1234, ITEM_1235], expect) ); }); + + describe('Redis uses prefix key', () => { + it('should return 200 status code and add key to Redis with prefix', async function () { + const realConfig = depContainer.resolve(SERVICES.CONFIG); + const prefix = 'test-prefix-item'; + + const configWithPrefix: IConfig = { + ...realConfig, + get(key: string): T { + if (key === redisConfigPath) { + const realRedisConfig = realConfig.get(redisConfigPath); + return { ...realRedisConfig, prefix } as T; + } + return realConfig.get(key); + }, + }; + + const mockRegisterOptions = getBaseRegisterOptions([ + { + token: SERVICES.CONFIG, + provider: { useValue: configWithPrefix }, + }, + ]); + + const [mockApp, localContainer] = await getApp(mockRegisterOptions); + const localRequestSender = new ItemRequestSender(mockApp); + + const redisConnection = localContainer.resolve(SERVICES.REDIS); + + const requestParams: GetItemsQueryParams = { command_name: '123', limit: 5, disable_fuzziness: false }; + const response = await localRequestSender.getItems(requestParams); + + const keys = await redisConnection.keys(prefix + '*'); + expect(keys.length).toBeGreaterThanOrEqual(1); + expect(response.status).toBe(httpStatusCodes.OK); + + await localContainer.dispose(); + }); + }); }); + 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 () { diff --git a/tests/integration/control/route/helpers/index.ts b/tests/integration/control/route/helpers/index.ts new file mode 100644 index 0000000..84115c1 --- /dev/null +++ b/tests/integration/control/route/helpers/index.ts @@ -0,0 +1,20 @@ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import { SERVICES } from '@src/common/constants'; +import { S3_REPOSITORY_SYMBOL } from '@src/common/s3/s3Repository'; +import { cronLoadTileLatLonDataSymbol } from '@src/latLon/DAL/latLonDAL'; +import { GetBaseRegisterOptions } from '@tests/integration/helpers/types'; + +export const getBaseRegisterOptions: GetBaseRegisterOptions = (options = []) => { + return { + 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: {} } }, + ...options, + ], + useChild: true, + }; +}; diff --git a/tests/integration/control/route/route.spec.ts b/tests/integration/control/route/route.spec.ts index dd15157..f47bc05 100644 --- a/tests/integration/control/route/route.spec.ts +++ b/tests/integration/control/route/route.spec.ts @@ -1,16 +1,14 @@ /* 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 { DependencyContainer } from 'tsyringe'; import { CleanupRegistry } from '@map-colonies/cleanup-registry'; +import { IConfig } from 'config'; +import { RedisClient } from '@src/common/redis'; import { getApp } from '../../../../src/app'; -import { SERVICES } from '../../../../src/common/constants'; +import { redisConfigPath, 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 { ROUTE_VIA_CAMILLUCCIA_A, @@ -19,22 +17,14 @@ import { CONTROL_POINT_OLIMPIADE_112, } from '../../../mockObjects/routes'; import { RouteRequestSender } from './helpers/requestSender'; +import { getBaseRegisterOptions } from './helpers'; describe('/search/control/route', function () { let requestSender: RouteRequestSender; let depContainer: DependencyContainer; beforeEach(async function () { - const [app, container] = 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, - }); + const [app, container] = await getApp(getBaseRegisterOptions()); depContainer = container; requestSender = new RouteRequestSender(app); @@ -382,7 +372,47 @@ describe('/search/control/route', function () { bbox: null, }); }); + + describe('Redis uses prefix key', () => { + it('should return 200 status code and add key to Redis with prefix', async function () { + const realConfig = depContainer.resolve(SERVICES.CONFIG); + const prefix = 'test-prefix-route'; + + const configWithPrefix: IConfig = { + ...realConfig, + get(key: string): T { + if (key === redisConfigPath) { + const realRedisConfig = realConfig.get(redisConfigPath); + return { ...realRedisConfig, prefix } as T; + } + return realConfig.get(key); + }, + }; + + const mockRegisterOptions = getBaseRegisterOptions([ + { + token: SERVICES.CONFIG, + provider: { useValue: configWithPrefix }, + }, + ]); + + const [mockApp, localContainer] = await getApp(mockRegisterOptions); + const localRequestSender = new RouteRequestSender(mockApp); + + const redisConnection = localContainer.resolve(SERVICES.REDIS); + + const requestParams: GetRoutesQueryParams = { command_name: 'via camilluccia', limit: 5, disable_fuzziness: false }; + const response = await localRequestSender.getRoutes(requestParams); + + const keys = await redisConnection.keys(prefix + '*'); + expect(keys.length).toBeGreaterThanOrEqual(1); + expect(response.status).toBe(httpStatusCodes.OK); + + await localContainer.dispose(); + }); + }); }); + 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 () { diff --git a/tests/integration/control/tile/helpers/index.ts b/tests/integration/control/tile/helpers/index.ts new file mode 100644 index 0000000..84115c1 --- /dev/null +++ b/tests/integration/control/tile/helpers/index.ts @@ -0,0 +1,20 @@ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import { SERVICES } from '@src/common/constants'; +import { S3_REPOSITORY_SYMBOL } from '@src/common/s3/s3Repository'; +import { cronLoadTileLatLonDataSymbol } from '@src/latLon/DAL/latLonDAL'; +import { GetBaseRegisterOptions } from '@tests/integration/helpers/types'; + +export const getBaseRegisterOptions: GetBaseRegisterOptions = (options = []) => { + return { + 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: {} } }, + ...options, + ], + useChild: true, + }; +}; diff --git a/tests/integration/control/tile/tile.spec.ts b/tests/integration/control/tile/tile.spec.ts index 5e8cd8a..7a67a02 100644 --- a/tests/integration/control/tile/tile.spec.ts +++ b/tests/integration/control/tile/tile.spec.ts @@ -1,36 +1,26 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import jsLogger from '@map-colonies/js-logger'; -import { trace } from '@opentelemetry/api'; import { DependencyContainer } from 'tsyringe'; import { CleanupRegistry } from '@map-colonies/cleanup-registry'; import httpStatusCodes from 'http-status-codes'; import type { BBox } from 'geojson'; +import { IConfig } from 'config'; +import { RedisClient } from '@src/common/redis'; import { getApp } from '../../../../src/app'; -import { SERVICES } from '../../../../src/common/constants'; +import { redisConfigPath, 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 { RIC_TILE, RIT_TILE, SUB_TILE_65, SUB_TILE_66 } from '../../../mockObjects/tiles'; import { TileRequestSender } from './helpers/requestSender'; +import { getBaseRegisterOptions } from './helpers'; describe('/search/control/tiles', function () { let requestSender: TileRequestSender; let depContainer: DependencyContainer; beforeEach(async function () { - const [app, container] = 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, - }); + const [app, container] = await getApp(getBaseRegisterOptions()); depContainer = container; requestSender = new TileRequestSender(app); @@ -356,7 +346,48 @@ describe('/search/control/tiles', function () { expectedResponse(requestParams, [RIC_TILE, RIT_TILE], expect) ); }); + + describe('Redis uses prefix key', () => { + it('should return 200 status code and add key to Redis with prefix', async function () { + const realConfig = depContainer.resolve(SERVICES.CONFIG); + const prefix = 'test-prefix-tile'; + + const configWithPrefix: IConfig = { + ...realConfig, + get(key: string): T { + if (key === redisConfigPath) { + const realRedisConfig = realConfig.get(redisConfigPath); + return { ...realRedisConfig, prefix } as T; + } + return realConfig.get(key); + }, + }; + + const mockRegisterOptions = getBaseRegisterOptions([ + { + token: SERVICES.CONFIG, + provider: { useValue: configWithPrefix }, + }, + ]); + + const [mockApp, localContainer] = await getApp(mockRegisterOptions); + const localRequestSender = new TileRequestSender(mockApp); + + const redisConnection = localContainer.resolve(SERVICES.REDIS); + + const requestParams: GetTilesQueryParams = { tile: 'RIT', limit: 5, disable_fuzziness: false }; + + const response = await localRequestSender.getTiles(requestParams); + + const keys = await redisConnection.keys(prefix + '*'); + expect(keys.length).toBeGreaterThanOrEqual(1); + expect(response.status).toBe(httpStatusCodes.OK); + + await localContainer.dispose(); + }); + }); }); + 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 () { diff --git a/tests/integration/helpers/types.ts b/tests/integration/helpers/types.ts new file mode 100644 index 0000000..3e80943 --- /dev/null +++ b/tests/integration/helpers/types.ts @@ -0,0 +1,4 @@ +import { RegisterOptions } from '@src/containerConfig'; +import { InjectionObject } from '@src/common/dependencyRegistration'; + +export type GetBaseRegisterOptions = (injectionObjects?: InjectionObject[]) => Required; diff --git a/tests/integration/latLon/latLon.spec.ts b/tests/integration/latLon/latLon.spec.ts index dfa305a..b8a7258 100644 --- a/tests/integration/latLon/latLon.spec.ts +++ b/tests/integration/latLon/latLon.spec.ts @@ -5,8 +5,10 @@ import { trace } from '@opentelemetry/api'; import { DependencyContainer } from 'tsyringe'; import { CleanupRegistry } from '@map-colonies/cleanup-registry'; import httpStatusCodes from 'http-status-codes'; +import { IConfig } from 'config'; +import { RedisClient } from '@src/common/redis'; import { getApp } from '../../../src/app'; -import { SERVICES } from '../../../src/common/constants'; +import { redisConfigPath, SERVICES } from '../../../src/common/constants'; import { LatLonDAL } from '../../../src/latLon/DAL/latLonDAL'; import { GenericGeocodingFeatureResponse } from '../../../src/common/interfaces'; import { LatLonRequestSender } from './helpers/requestSender'; @@ -139,6 +141,48 @@ describe('/lookup', function () { }, }); }); + + describe('Redis uses prefix key', () => { + it('should return 200 status code and add key to Redis with prefix', async function () { + const realConfig = depContainer.resolve(SERVICES.CONFIG); + const prefix = 'test-prefix-latlon'; + + const configWithPrefix: IConfig = { + ...realConfig, + get(key: string): T { + if (key === redisConfigPath) { + const realRedisConfig = realConfig.get(redisConfigPath); + return { ...realRedisConfig, prefix } as T; + } + return realConfig.get(key); + }, + }; + + const mockRegisterOptions = { + override: [ + { token: SERVICES.LOGGER, provider: { useValue: jsLogger({ enabled: false }) } }, + { token: SERVICES.TRACER, provider: { useValue: trace.getTracer('testTracer') } }, + { token: SERVICES.CONFIG, provider: { useValue: configWithPrefix } }, + ], + useChild: true, + }; + + const [mockApp, localContainer] = await getApp(mockRegisterOptions); + const localRequestSender = new LatLonRequestSender(mockApp); + + const redisConnection = localContainer.resolve(SERVICES.REDIS); + + const response = await localRequestSender.convertCoordinatesToGrid({ + lat: 52.57326537485767, + lon: 12.948781146422107, + target_grid: 'MGRS', + }); + + const keys = await redisConnection.keys(prefix + '*'); + expect(keys.length).toBeGreaterThanOrEqual(1); + expect(response.status).toBe(httpStatusCodes.OK); + }); + }); }); describe('Bad Path', function () { diff --git a/tests/integration/location/helpers/index.ts b/tests/integration/location/helpers/index.ts new file mode 100644 index 0000000..84115c1 --- /dev/null +++ b/tests/integration/location/helpers/index.ts @@ -0,0 +1,20 @@ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import { SERVICES } from '@src/common/constants'; +import { S3_REPOSITORY_SYMBOL } from '@src/common/s3/s3Repository'; +import { cronLoadTileLatLonDataSymbol } from '@src/latLon/DAL/latLonDAL'; +import { GetBaseRegisterOptions } from '@tests/integration/helpers/types'; + +export const getBaseRegisterOptions: GetBaseRegisterOptions = (options = []) => { + return { + 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: {} } }, + ...options, + ], + useChild: true, + }; +}; diff --git a/tests/integration/location/location.spec.ts b/tests/integration/location/location.spec.ts index 5f7fb80..bde71c0 100644 --- a/tests/integration/location/location.spec.ts +++ b/tests/integration/location/location.spec.ts @@ -2,15 +2,13 @@ import { DependencyContainer } from 'tsyringe'; import type { 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 { IConfig } from 'config'; +import { RedisClient } from '@src/common/redis'; import { getApp } from '../../../src/app'; import { ConfigType, getConfig } from '../../../src/common/config'; -import { SERVICES } from '../../../src/common/constants'; -import { S3_REPOSITORY_SYMBOL } from '../../../src/common/s3/s3Repository'; -import { cronLoadTileLatLonDataSymbol } from '../../../src/latLon/DAL/latLonDAL'; +import { redisConfigPath, SERVICES } from '../../../src/common/constants'; import { GetGeotextSearchParams } from '../../../src/location/interfaces'; import { GenericGeocodingResponse, GeoContext, GeoContextMode } from '../../../src/common/interfaces'; import { @@ -30,6 +28,7 @@ import { } from '../../mockObjects/locations'; import { LocationRequestSender } from './helpers/requestSender'; import { expectedResponse, hierarchiesWithAnyWieght } from './utils'; +import { getBaseRegisterOptions } from './helpers'; let config: ConfigType; @@ -42,16 +41,7 @@ describe('/search/location', function () { }); beforeEach(async function () { - const [app, container] = 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, - }); + const [app, container] = await getApp(getBaseRegisterOptions()); depContainer = container; requestSender = new LocationRequestSender(app); @@ -536,6 +526,44 @@ describe('/search/location', function () { tokenTypesUrlScope.done(); }); + + describe('Redis uses prefix key', () => { + it('should return 200 status code and add key to Redis with prefix', async function () { + const realConfig = depContainer.resolve(SERVICES.CONFIG); + const prefix = 'test-prefix-location'; + + const configWithPrefix: IConfig = { + ...realConfig, + get(key: string): T { + if (key === redisConfigPath) { + const realRedisConfig = realConfig.get(redisConfigPath); + return { ...realRedisConfig, prefix } as T; + } + return realConfig.get(key); + }, + }; + + const mockRegisterOptions = getBaseRegisterOptions([ + { + token: SERVICES.CONFIG, + provider: { useValue: configWithPrefix }, + }, + ]); + + const [mockApp, localContainer] = await getApp(mockRegisterOptions); + const localRequestSender = new LocationRequestSender(mockApp); + + const redisConnection = localContainer.resolve(SERVICES.REDIS); + + const response = await localRequestSender.getRegions(); + + const keys = await redisConnection.keys(prefix + '*'); + expect(keys.length).toBeGreaterThanOrEqual(1); + expect(response.status).toBe(httpStatusCodes.OK); + + await localContainer.dispose(); + }); + }); }); describe('Bad Path', function () { diff --git a/tests/integration/mgrs/helpers/index.ts b/tests/integration/mgrs/helpers/index.ts new file mode 100644 index 0000000..3791fc1 --- /dev/null +++ b/tests/integration/mgrs/helpers/index.ts @@ -0,0 +1,21 @@ +import jsLogger from '@map-colonies/js-logger'; +import { trace } from '@opentelemetry/api'; +import { SERVICES } from '@src/common/constants'; +import { S3_REPOSITORY_SYMBOL } from '@src/common/s3/s3Repository'; +import { cronLoadTileLatLonDataSymbol } from '@src/latLon/DAL/latLonDAL'; +import { GetBaseRegisterOptions } from '@tests/integration/helpers/types'; + +export const getBaseRegisterOptions: GetBaseRegisterOptions = (options = []) => { + return { + 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: {} } }, + ...options, + ], + useChild: true, + }; +}; diff --git a/tests/integration/mgrs/mgrs.spec.ts b/tests/integration/mgrs/mgrs.spec.ts index 6371aff..52697f7 100644 --- a/tests/integration/mgrs/mgrs.spec.ts +++ b/tests/integration/mgrs/mgrs.spec.ts @@ -2,32 +2,21 @@ import 'jest-openapi'; import { DependencyContainer } from 'tsyringe'; 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 { IConfig } from 'config'; +import { RedisClient } from '@src/common/redis'; 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 { redisConfigPath, SERVICES } from '../../../src/common/constants'; import { GenericGeocodingFeatureResponse } from '../../../src/common/interfaces'; import { MgrsRequestSender } from './helpers/requestSender'; +import { getBaseRegisterOptions } from './helpers'; describe('/search/MGRS', function () { let requestSender: MgrsRequestSender; let depContainer: DependencyContainer; beforeEach(async function () { - const [app, container] = 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, - }); + const [app, container] = await getApp(getBaseRegisterOptions()); depContainer = container; requestSender = new MgrsRequestSender(app); @@ -89,6 +78,44 @@ describe('/search/MGRS', function () { }, }); }); + + describe('Redis uses prefix key', () => { + it('should return 200 status code and add key to Redis with prefix', async function () { + const realConfig = depContainer.resolve(SERVICES.CONFIG); + const prefix = 'test-prefix-mgrs'; + + const configWithPrefix: IConfig = { + ...realConfig, + get(key: string): T { + if (key === redisConfigPath) { + const realRedisConfig = realConfig.get(redisConfigPath); + return { ...realRedisConfig, prefix } as T; + } + return realConfig.get(key); + }, + }; + + const mockRegisterOptions = getBaseRegisterOptions([ + { + token: SERVICES.CONFIG, + provider: { useValue: configWithPrefix }, + }, + ]); + + const [mockApp, localContainer] = await getApp(mockRegisterOptions); + const localRequestSender = new MgrsRequestSender(mockApp); + + const redisConnection = localContainer.resolve(SERVICES.REDIS); + + const response = await localRequestSender.getTile({ tile: '18SUJ2339007393' }); + + const keys = await redisConnection.keys(prefix + '*'); + expect(keys.length).toBeGreaterThanOrEqual(1); + expect(response.status).toBe(httpStatusCodes.OK); + + await localContainer.dispose(); + }); + }); }); describe('Bad Path', function () {