Skip to content

Commit 0d8d290

Browse files
committed
FLS-1452 - Refactor AdapterCacheService to reduce duplicate code
Currently in AdapterCacheService we have a lot of duplicate code, specifically around (A) supporting both in-memory caching of forms configurations, as well as caching via Redis, and (B) Redis client configuration for both forms configurations and session management. In the case of (A), we handle this by using a new formStorage attribute. In the case of (B), we handle this by creating a new createRedisClient utility function.
1 parent f71a52e commit 0d8d290

File tree

2 files changed

+64
-187
lines changed

2 files changed

+64
-187
lines changed

runner/src/server/plugins/engine/MainPlugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const plugin = {
1818
let countError = 0;
1919
for (const config of configs) {
2020
try {
21-
await adapterCacheService.setFormConfiguration(config.id, config, server);
21+
await adapterCacheService.setFormConfiguration(config.id, config);
2222
countOk++;
2323
} catch (e) {
2424
countError++;

runner/src/server/services/AdapterCacheService.ts

Lines changed: 63 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import Crypto from 'crypto';
1515
import {HapiRequest, HapiServer} from "../types";
1616
import {AdapterFormModel} from "../plugins/engine/models";
1717
import Boom from "boom";
18-
import {FormConfiguration} from "@xgovformbuilder/model";
19-
import {AdapterSchema} from "@communitiesuk/model";
2018

2119
const partition = "cache";
2220
const LOGGER_DATA = {
@@ -42,23 +40,43 @@ enum ADDITIONAL_IDENTIFIER {
4240
Confirmation = ":confirmation",
4341
}
4442

43+
const createRedisClient = (): Redis | null => {
44+
if (redisHost || redisUri) {
45+
const redisOptions: {password?: string; tls?: {};} = {};
46+
if (redisPassword) redisOptions.password = redisPassword;
47+
if (redisTls) redisOptions.tls = {};
48+
49+
return isSingleRedis
50+
? new Redis(redisUri ?? {host: redisHost, port: redisPort, password: redisPassword})
51+
: new Redis.Cluster(
52+
[{host: redisHost, port: redisPort}],
53+
{dnsLookup: (address, callback) => callback(null, address, 4), redisOptions}
54+
);
55+
}
56+
return null;
57+
};
58+
4559
export class AdapterCacheService extends CacheService {
60+
private formStorage: Redis | any;
4661

4762
constructor(server: HapiServer) {
4863
//@ts-ignore
4964
super(server);
50-
//@ts-ignore
51-
server.app.redis = this.getRedisClient()
52-
//@ts-ignore
53-
if (!server.app.redis) {
54-
// starting up the in memory cache
65+
const redisClient = this.getRedisClient();
66+
if (redisClient) {
67+
this.formStorage = redisClient;
68+
} else {
69+
// Starting up the in memory cache
5570
this.cache.client.start();
56-
//@ts-ignore
57-
server.app.inMemoryFormKeys = []
71+
this.formStorage = {
72+
get: (key) => this.cache.get(key),
73+
set: (key, value) => this.cache.set(key, value, {expiresIn: 0}),
74+
setex: (key, ttl, value) => this.cache.set(key, value, {expiresIn: ttl}),
75+
}
5876
}
5977
}
6078

61-
async activateSession(jwt, request) {
79+
async activateSession(jwt, request): Promise<{ redirectPath: string }> {
6280
request.logger.info(`[ACTIVATE-SESSION] jwt ${jwt}`);
6381
const initialisedSession = await this.cache.get(this.JWTKey(jwt));
6482
request.logger.info(`[ACTIVATE-SESSION] session details ${initialisedSession}`);
@@ -67,14 +85,12 @@ export class AdapterCacheService extends CacheService {
6785
const userSessionKey = {segment: partition, id: `${request.yar.id}:${payload.group}`};
6886
request.logger.info(`[ACTIVATE-SESSION] session metadata ${userSessionKey}`);
6987
const {redirectPath} = await super.activateSession(jwt, request);
70-
7188
let redirectPathNew = redirectPath
7289
const form_session_identifier = initialisedSession.metadata?.form_session_identifier;
7390
if (form_session_identifier) {
7491
userSessionKey.id = `${userSessionKey.id}:${form_session_identifier}`;
7592
redirectPathNew = `${redirectPathNew}?form_session_identifier=${form_session_identifier}`;
7693
}
77-
7894
if (config.overwriteInitialisedSession) {
7995
request.logger.info("[ACTIVATE-SESSION] Replacing user session with initialisedSession");
8096
this.cache.set(userSessionKey, initialisedSession, sessionTimeout);
@@ -104,7 +120,7 @@ export class AdapterCacheService extends CacheService {
104120
* @param additionalIdentifier - appended to the id
105121
*/
106122
//@ts-ignore
107-
Key(request: HapiRequest, additionalIdentifier?: ADDITIONAL_IDENTIFIER) {
123+
Key(request: HapiRequest, additionalIdentifier?: ADDITIONAL_IDENTIFIER): { segment: string; id: string } {
108124
let id = `${request.yar.id}:${request.params.id}`;
109125

110126
if (request.query.form_session_identifier) {
@@ -125,94 +141,44 @@ export class AdapterCacheService extends CacheService {
125141
* @param configuration form definition configurations
126142
* @param server server object
127143
*/
128-
async setFormConfiguration(formId: string, configuration: any, server: HapiServer) {
129-
if (formId && configuration) {
130-
//@ts-ignore
131-
if (server.app.redis) {
132-
await this.addConfigurationsToRedisCache(server, configuration, formId);
133-
} else {
134-
await this.addConfigurationIntoInMemoryCache(configuration, formId, server);
135-
}
136-
}
137-
}
138-
139-
private async addConfigurationIntoInMemoryCache(configuration: any, formId: string, server: HapiServer) {
140-
const hashValue = Crypto.createHash('sha256').update(JSON.stringify(configuration)).digest('hex')
144+
async setFormConfiguration(formId: string, configuration: any): Promise<void> {
145+
if (!formId || !configuration) return;
146+
const hashValue = Crypto.createHash('sha256')
147+
.update(JSON.stringify(configuration))
148+
.digest('hex');
149+
const key = `${FORMS_KEY_PREFIX}${formId}`;
141150
try {
142-
const jsonDataString = await this.cache.get(`${FORMS_KEY_PREFIX}${formId}`);
143-
if (jsonDataString === null) {
144-
// Adding new config into redis cache service with the hash value
151+
const existingConfigString = await this.formStorage.get(key);
152+
if (existingConfigString === null) {
153+
// Adding new config with the hash value
145154
const stringConfig = JSON.stringify({
146155
...configuration,
147156
id: configuration.id,
148157
hash: hashValue
149158
});
150-
//@ts-ignore
151-
server.app.inMemoryFormKeys.push(`${FORMS_KEY_PREFIX}${formId}`)
152-
// Adding data into redis cache
153-
await this.cache.set(`${FORMS_KEY_PREFIX}${formId}`, stringConfig, {expiresIn: 0});
159+
await this.formStorage.set(key, stringConfig);
154160
} else {
155-
// Redis has the data and gets current data set to check hash
156-
const configObj = JSON.parse(jsonDataString);
157-
if (configObj && configObj.hash && hashValue !== configObj.hash) {
158-
// if hash function is change then updating the configuration
161+
// Check if hash has changed
162+
const existingConfig = JSON.parse(existingConfigString);
163+
if (existingConfig?.hash !== hashValue) {
164+
// Hash has changed, update the configuration
159165
const stringConfig = JSON.stringify({
160166
...configuration,
161167
id: configuration.id,
162168
hash: hashValue
163169
});
164-
await this.cache.set(`${FORMS_KEY_PREFIX}${formId}`, stringConfig, {expiresIn: 0});
170+
await this.formStorage.set(key, stringConfig);
165171
}
166172
}
167173
} catch (error) {
168174
console.log(error);
169175
}
170176
}
171177

172-
private async addConfigurationsToRedisCache(server: HapiServer, configuration: any, formId: string) {
173-
//@ts-ignore
174-
const redisClient: Redis = server.app.redis
175-
const hashValue = Crypto.createHash('sha256').update(JSON.stringify(configuration)).digest('hex')
176-
if (redisClient) {
177-
const jsonDataString = await redisClient.get(`${FORMS_KEY_PREFIX}${formId}`);
178-
if (jsonDataString === null) {
179-
// Adding new config into redis cache service with the hash value
180-
const stringConfig = JSON.stringify({
181-
...configuration,
182-
id: configuration.id,
183-
hash: hashValue
184-
});
185-
// Adding data into redis cache
186-
await redisClient.set(`${FORMS_KEY_PREFIX}${formId}`, stringConfig);
187-
} else {
188-
// Redis has the data and gets current data set to check hash
189-
const configObj = JSON.parse(jsonDataString);
190-
if (configObj && configObj.hash && hashValue !== configObj.hash) {
191-
// if hash function is change then updating the configuration
192-
const stringConfig = JSON.stringify({
193-
...configuration,
194-
id: configuration.id,
195-
hash: hashValue
196-
});
197-
await redisClient.set(`${FORMS_KEY_PREFIX}${formId}`, stringConfig);
198-
}
199-
}
200-
}
201-
}
202-
203-
async getFormAdapterModel(formId: string, request: HapiRequest) {
204-
//@ts-ignore
205-
if (request.server.app.redis) {
206-
return await this.getConfigurationFromRedisCache(request, formId);
207-
} else {
208-
return await this.getConfigurationFromInMemoryCache(request, formId);
209-
}
210-
}
211-
212-
private async getConfigurationFromInMemoryCache(request: HapiRequest, formId: string) {
178+
async getFormAdapterModel(formId: string, request: HapiRequest): Promise<AdapterFormModel> {
213179
const {translationLoaderService} = request.services([]);
214180
const translations = translationLoaderService.getTranslations();
215-
const jsonDataString = await this.cache.get(`${FORMS_KEY_PREFIX}${formId}`);
181+
const jsonDataString = await this.formStorage.get(`${FORMS_KEY_PREFIX}${formId}`);
216182
if (jsonDataString !== null) {
217183
const configObj = JSON.parse(jsonDataString);
218184
return new AdapterFormModel(configObj.configuration, {
@@ -221,7 +187,7 @@ export class AdapterCacheService extends CacheService {
221187
previewMode: true,
222188
translationEn: translations.en,
223189
translationCy: translations.cy
224-
})
190+
});
225191
}
226192
request.logger.error({
227193
...LOGGER_DATA,
@@ -230,72 +196,16 @@ export class AdapterCacheService extends CacheService {
230196
throw Boom.notFound("Cannot find the given form");
231197
}
232198

233-
private async getConfigurationFromRedisCache(request: HapiRequest, formId: string) {
234-
//@ts-ignore
235-
const redisClient: Redis = request.server.app.redis
236-
const {translationLoaderService} = request.services([]);
237-
const translations = translationLoaderService.getTranslations();
238-
const jsonDataString = await redisClient.get(`${FORMS_KEY_PREFIX}${formId}`);
239-
if (jsonDataString !== null) {
240-
const configObj = JSON.parse(jsonDataString);
241-
return new AdapterFormModel(configObj.configuration, {
242-
basePath: configObj.id ? configObj.id : formId,
243-
hash: configObj.hash,
244-
previewMode: true,
245-
translationEn: translations.en,
246-
translationCy: translations.cy
247-
})
248-
}
249-
request.logger.error({
250-
...LOGGER_DATA,
251-
message: `[FORM-CACHE] Cannot find the form ${formId}`
252-
});
253-
throw Boom.notFound("Cannot find the given form");
254-
}
255-
256-
private getRedisClient() {
257-
if (redisHost || redisUri) {
258-
const redisOptions: {
259-
password?: string;
260-
tls?: {};
261-
} = {};
262-
263-
if (redisPassword) {
264-
redisOptions.password = redisPassword;
265-
}
266-
267-
if (redisTls) {
268-
redisOptions.tls = {};
269-
}
270-
271-
const client = isSingleRedis
272-
? new Redis(
273-
redisUri ?? {
274-
host: redisHost,
275-
port: redisPort,
276-
password: redisPassword,
277-
}
278-
)
279-
: new Redis.Cluster(
280-
[
281-
{
282-
host: redisHost,
283-
port: redisPort,
284-
},
285-
],
286-
{
287-
dnsLookup: (address, callback) => callback(null, address, 4),
288-
redisOptions,
289-
}
290-
);
291-
return client;
292-
} else {
293-
console.log({
294-
...LOGGER_DATA,
295-
message: `[FORM-CACHE] using memory caching`,
296-
})
297-
}
298-
}
199+
private getRedisClient(): Redis | null {
200+
const client = createRedisClient();
201+
if (!client) {
202+
console.log({
203+
...LOGGER_DATA,
204+
message: `[FORM-CACHE] using memory caching`,
205+
});
206+
}
207+
return client;
208+
}
299209
}
300210

301211
export const catboxProvider = () => {
@@ -305,49 +215,16 @@ export const catboxProvider = () => {
305215
*/
306216
const provider = {
307217
constructor: redisHost || redisUri ? CatboxRedis.Engine : CatboxMemory.Engine,
308-
options: {},
218+
options: {partition},
309219
};
310220

311221
if (redisHost || redisUri) {
312-
console.log("Starting redis session management")
313-
const redisOptions: {
314-
password?: string;
315-
tls?: {};
316-
} = {};
317-
318-
if (redisPassword) {
319-
redisOptions.password = redisPassword;
320-
}
321-
322-
if (redisTls) {
323-
redisOptions.tls = {};
324-
}
325-
326-
const client = isSingleRedis
327-
? new Redis(
328-
redisUri ?? {
329-
host: redisHost,
330-
port: redisPort,
331-
password: redisPassword,
332-
}
333-
)
334-
: new Redis.Cluster(
335-
[
336-
{
337-
host: redisHost,
338-
port: redisPort,
339-
},
340-
],
341-
{
342-
dnsLookup: (address, callback) => callback(null, address, 4),
343-
redisOptions,
344-
}
345-
);
346-
provider.options = {client, partition};
347-
console.log(`Redis Url : ${redisUri} session management`);
222+
console.log("Starting redis session management");
223+
const client = createRedisClient();
224+
provider.options = {client, partition};
225+
console.log(`Redis Url : ${redisUri} session management`);
348226
} else {
349-
console.log("Starting in memory session management")
350-
provider.options = {partition};
227+
console.log("Starting in memory session management");
351228
}
352229

353230
return provider;

0 commit comments

Comments
 (0)