Skip to content

Commit 856a7a7

Browse files
committed
refactor(ui5Manifest): Add cache
1 parent 410fdb5 commit 856a7a7

File tree

2 files changed

+122
-21
lines changed

2 files changed

+122
-21
lines changed

src/utils/ui5Manifest.ts

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,61 @@
11
import {getLogger} from "@ui5/logger";
22
import {fetchCdn} from "./cdnHelper.js";
3+
import {Mutex} from "async-mutex";
34

45
const log = getLogger("utils:ui5Manifest");
5-
const MAPPING_URL = "https://raw.githubusercontent.com/SAP/ui5-manifest/main/mapping.json";
6+
67
const LATEST_SCHEMA_URL = "https://raw.githubusercontent.com/SAP/ui5-manifest/main/schema.json";
7-
const schemaCache = new Map<string, Promise<object>>();
8+
const schemaCache = new Map<string, object>();
9+
const fetchSchemaMutex = new Mutex();
10+
11+
let UI5ToManifestVersionMapping: Record<string, string> | null = null;
12+
const MAPPING_URL = "https://raw.githubusercontent.com/SAP/ui5-manifest/main/mapping.json";
13+
const ui5ToManifestVersionMappingMutex = new Mutex();
14+
15+
async function getUI5toManifestVersionMapping() {
16+
const release = await ui5ToManifestVersionMappingMutex.acquire();
17+
18+
try {
19+
if (UI5ToManifestVersionMapping) {
20+
log.info("Loading cached UI5 to manifest version mapping");
21+
return UI5ToManifestVersionMapping;
22+
}
823

9-
async function getUI5toManifestVersionMap() {
10-
const mapping = await fetchCdn(MAPPING_URL);
24+
log.info("Fetching UI5 to manifest version mapping");
25+
const mapping = await fetchCdn(MAPPING_URL);
26+
log.info(`Fetched UI5 to manifest version mapping from ${MAPPING_URL}`);
1127

12-
return mapping as Record<string, string>;
28+
UI5ToManifestVersionMapping = mapping as Record<string, string>;
29+
30+
return UI5ToManifestVersionMapping;
31+
} finally {
32+
release();
33+
}
1334
}
1435

1536
async function fetchSchema(manifestVersion: string) {
16-
if (schemaCache.has(manifestVersion)) {
17-
log.info(`Loading cached schema for manifest version: ${manifestVersion}`);
18-
19-
try {
20-
const schema = await schemaCache.get(manifestVersion)!;
21-
return schema;
22-
} catch {
23-
schemaCache.delete(manifestVersion);
37+
const release = await fetchSchemaMutex.acquire();
38+
39+
try {
40+
if (schemaCache.has(manifestVersion)) {
41+
log.info(`Loading cached schema for manifest version: ${manifestVersion}`);
42+
return schemaCache.get(manifestVersion)!;
2443
}
25-
}
2644

27-
log.info(`Fetching schema for manifest version: ${manifestVersion}`);
28-
schemaCache.set(manifestVersion, fetchCdn(LATEST_SCHEMA_URL));
29-
const schema = await schemaCache.get(manifestVersion)!;
30-
log.info(`Fetched UI5 manifest schema from ${LATEST_SCHEMA_URL}`);
45+
log.info(`Fetching schema for manifest version: ${manifestVersion}`);
46+
const schema = await fetchCdn(LATEST_SCHEMA_URL);
47+
log.info(`Fetched UI5 manifest schema from ${LATEST_SCHEMA_URL}`);
48+
49+
schemaCache.set(manifestVersion, schema);
3150

32-
return schema;
51+
return schema;
52+
} finally {
53+
release();
54+
}
3355
}
3456

3557
export async function getLatestManifestVersion() {
36-
const versionMap = await getUI5toManifestVersionMap();
58+
const versionMap = await getUI5toManifestVersionMapping();
3759

3860
if (!versionMap.latest) {
3961
throw new Error("Could not determine latest manifest version.");

test/lib/utils/ui5Manifest.ts

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const test = anyTest as TestFn<{
66
sinon: sinonGlobal.SinonSandbox;
77
fetchCdnStub: sinonGlobal.SinonStub;
88
getLatestManifestVersion: typeof import("../../../src/utils/ui5Manifest.js").getLatestManifestVersion;
9+
getManifestSchema: typeof import("../../../src/utils/ui5Manifest.js").getManifestSchema;
910
}>;
1011

1112
test.beforeEach(async (t) => {
@@ -15,13 +16,14 @@ test.beforeEach(async (t) => {
1516
t.context.fetchCdnStub = fetchCdnStub;
1617

1718
// Import the module with mocked dependencies
18-
const {getLatestManifestVersion} = await esmock("../../../src/utils/ui5Manifest.js", {
19+
const {getLatestManifestVersion, getManifestSchema} = await esmock("../../../src/utils/ui5Manifest.js", {
1920
"../../../src/utils/cdnHelper.js": {
2021
fetchCdn: fetchCdnStub,
2122
},
2223
});
2324

2425
t.context.getLatestManifestVersion = getLatestManifestVersion;
26+
t.context.getManifestSchema = getManifestSchema;
2527
});
2628

2729
test.afterEach.always((t) => {
@@ -43,6 +45,23 @@ test("getLatestManifestVersion returns correct version from CDN data", async (t)
4345
t.true(fetchCdnStub.calledOnce);
4446
});
4547

48+
test("getLatestManifestVersion uses cache on subsequent calls", async (t) => {
49+
const {fetchCdnStub, getLatestManifestVersion} = t.context;
50+
const mockData = {
51+
"latest": "1.79.0",
52+
"1.141": "1.79.0",
53+
"1.140": "1.78.0",
54+
};
55+
fetchCdnStub.resolves(mockData);
56+
57+
const latestVersion1 = await getLatestManifestVersion();
58+
const latestVersion2 = await getLatestManifestVersion();
59+
60+
t.is(latestVersion1, "1.79.0");
61+
t.is(latestVersion2, "1.79.0");
62+
t.true(fetchCdnStub.calledOnce);
63+
});
64+
4665
test("getLatestManifestVersion handles fetch errors", async (t) => {
4766
const {fetchCdnStub, getLatestManifestVersion} = t.context;
4867

@@ -78,3 +97,63 @@ test("getLatestManifestVersion handles missing latest version", async (t) => {
7897
);
7998
t.true(fetchCdnStub.calledOnce);
8099
});
100+
101+
test("getManifestSchema throws error for unsupported versions", async (t) => {
102+
const {getManifestSchema} = t.context;
103+
104+
await t.throwsAsync(
105+
async () => {
106+
await getManifestSchema("1.78.0");
107+
},
108+
{
109+
message: "Only 'latest' manifest version is supported, but got '1.78.0'.",
110+
}
111+
);
112+
});
113+
114+
test("getManifestSchema fetches schema for 'latest' version", async (t) => {
115+
const {fetchCdnStub, getManifestSchema} = t.context;
116+
const mockSchema = {
117+
$schema: "http://json-schema.org/draft-07/schema#",
118+
type: "object",
119+
};
120+
fetchCdnStub.resolves(mockSchema);
121+
122+
const schema = await getManifestSchema("latest");
123+
124+
t.deepEqual(schema, mockSchema);
125+
t.true(fetchCdnStub.calledOnce);
126+
});
127+
128+
test("getManifestSchema uses cache on subsequent calls", async (t) => {
129+
const {fetchCdnStub, getManifestSchema} = t.context;
130+
const mockSchema = {
131+
$schema: "http://json-schema.org/draft-07/schema#",
132+
type: "object",
133+
};
134+
fetchCdnStub.resolves(mockSchema);
135+
136+
const schema1 = await getManifestSchema("latest");
137+
const schema2 = await getManifestSchema("latest");
138+
139+
t.deepEqual(schema1, mockSchema);
140+
t.deepEqual(schema2, mockSchema);
141+
t.true(fetchCdnStub.calledOnce);
142+
});
143+
144+
test("getManifestSchema handles fetch errors", async (t) => {
145+
const {fetchCdnStub, getManifestSchema} = t.context;
146+
147+
// Mock fetch error
148+
fetchCdnStub.rejects(new Error("Network error"));
149+
150+
await t.throwsAsync(
151+
async () => {
152+
await getManifestSchema("latest");
153+
},
154+
{
155+
message: "Network error",
156+
}
157+
);
158+
t.true(fetchCdnStub.calledOnce);
159+
});

0 commit comments

Comments
 (0)