Enhanced app update service with Redis caching for branch retrieval and current build information, improving performance and reducing API calls.

This commit is contained in:
Tom Butcher 2026-06-21 15:42:19 +01:00
parent 3d8e6325b2
commit c507f708eb

View File

@ -1,10 +1,16 @@
import axios from 'axios'; import axios from 'axios';
import config from '../../config.js'; import config from '../../config.js';
import log4js from 'log4js'; import log4js from 'log4js';
import { redisServer } from '../../database/redis.js';
const logger = log4js.getLogger('AppUpdate'); const logger = log4js.getLogger('AppUpdate');
logger.level = config.server.logLevel; logger.level = config.server.logLevel;
const APP_UPDATE_PREFIX = 'appupdate:';
const APP_UPDATE_BRANCHES_KEY = `${APP_UPDATE_PREFIX}branches`;
const APP_UPDATE_CURRENT_PREFIX = `${APP_UPDATE_PREFIX}current:`;
const APP_UPDATE_TTL_SECONDS = 120;
const normalizeProjectUrl = (projectUrl) => { const normalizeProjectUrl = (projectUrl) => {
if (typeof projectUrl !== 'string') return ''; if (typeof projectUrl !== 'string') return '';
return projectUrl.replace(/\/+$/, ''); return projectUrl.replace(/\/+$/, '');
@ -33,6 +39,11 @@ const mapArtifacts = (build, requestedBranch) => {
}; };
const getBranchesFromJenkins = async () => { const getBranchesFromJenkins = async () => {
const cached = await redisServer.getKey(APP_UPDATE_BRANCHES_KEY);
if (cached) {
return cached;
}
const projectUrl = getProjectUrl(); const projectUrl = getProjectUrl();
if (!projectUrl) { if (!projectUrl) {
throw new Error('Missing config.app.jenkinsProject'); throw new Error('Missing config.app.jenkinsProject');
@ -42,13 +53,16 @@ const getBranchesFromJenkins = async () => {
const response = await axios.get(jenkinsUrl, { timeout: 10000 }); const response = await axios.get(jenkinsUrl, { timeout: 10000 });
const jobs = Array.isArray(response.data?.jobs) ? response.data.jobs : []; const jobs = Array.isArray(response.data?.jobs) ? response.data.jobs : [];
return jobs const branches = jobs
.map((job) => ({ .map((job) => ({
name: job.name, name: job.name,
url: job.url, url: job.url,
color: job.color, color: job.color,
})) }))
.filter((job) => typeof job.name === 'string' && typeof job.url === 'string'); .filter((job) => typeof job.name === 'string' && typeof job.url === 'string');
await redisServer.setKey(APP_UPDATE_BRANCHES_KEY, branches, APP_UPDATE_TTL_SECONDS);
return branches;
}; };
const getLatestBuildForBranch = async (branchUrl) => { const getLatestBuildForBranch = async (branchUrl) => {
@ -91,7 +105,14 @@ export const appUpdateCurrentRouteHandler = async (req, res) => {
return res.status(400).send({ error: 'branch query parameter is required' }); return res.status(400).send({ error: 'branch query parameter is required' });
} }
const currentCacheKey = `${APP_UPDATE_CURRENT_PREFIX}${requestedBranch}`;
try { try {
const cachedCurrent = await redisServer.getKey(currentCacheKey);
if (cachedCurrent) {
return res.send(cachedCurrent);
}
const branches = await getBranchesFromJenkins(); const branches = await getBranchesFromJenkins();
const branch = branches.find((item) => item.name === requestedBranch); const branch = branches.find((item) => item.name === requestedBranch);
@ -111,7 +132,7 @@ export const appUpdateCurrentRouteHandler = async (req, res) => {
logger.error('Failed to parse version from build display name:', error); logger.error('Failed to parse version from build display name:', error);
} }
return res.send({ const payload = {
branch: requestedBranch, branch: requestedBranch,
buildNumber: build.number, buildNumber: build.number,
version: version, version: version,
@ -121,7 +142,10 @@ export const appUpdateCurrentRouteHandler = async (req, res) => {
builtAt: new Date(build.timestamp).toISOString(), builtAt: new Date(build.timestamp).toISOString(),
buildSource: source, buildSource: source,
artifacts: mapArtifacts(build, requestedBranch), artifacts: mapArtifacts(build, requestedBranch),
}); };
await redisServer.setKey(currentCacheKey, payload, APP_UPDATE_TTL_SECONDS);
return res.send(payload);
} catch (error) { } catch (error) {
logger.error('Failed to fetch Jenkins build info:', error); logger.error('Failed to fetch Jenkins build info:', error);
return res.status(500).send({ return res.status(500).send({