diff --git a/src/main/extension-updater/__tests__/extension-parser.test.ts b/src/main/extension-updater/__tests__/extension-parser.test.ts index b8c4e8eeec..2690661e2a 100644 --- a/src/main/extension-updater/__tests__/extension-parser.test.ts +++ b/src/main/extension-updater/__tests__/extension-parser.test.ts @@ -13,66 +13,65 @@ describe("BundledExtensionParser", () => { fetchMock.reset(); }); - it("Should return empty arrays if no url passed", async () => { + it("Should return empty objects if no url passed", async () => { const lists = await new BundledExtensionParser("5.4.0-latest12345", "").getExtensionLists(); expect(lists).toEqual({ - release: [], - available: [], + release: {}, + available: {}, }); }); - it("Should return empty releases array if no release json found", async () => { + it("Should return empty releases object if no release json found", async () => { fetchMock - .get("http://my-example-url.com/versions.json", [ { "node-menu": "0.0.1" } ] ) + .get("http://my-example-url.com/versions.json", { "node-menu": "0.0.1" } ) .get("http://my-example-url.com/5.4.0-latest12345.json", 408 ); const lists = await new BundledExtensionParser("5.4.0-latest12345", "http://my-example-url.com").getExtensionLists(); expect(lists).toEqual({ - release: [], - available: [{ "node-menu": "0.0.1" }], + release: {}, + available: { "node-menu": "0.0.1" }, }); }); - it("Should return empty available array if no versions json found", async () => { + it("Should return empty available object if no versions json found", async () => { fetchMock .get("http://my-example-url.com/versions.json", 408 ) - .get("http://my-example-url.com/5.4.0-latest12345.json", [ { "node-menu": "0.0.1" } ]); + .get("http://my-example-url.com/5.4.0-latest12345.json", { "node-menu": "0.0.1" }); const lists = await new BundledExtensionParser("5.4.0-latest12345", "http://my-example-url.com").getExtensionLists(); expect(lists).toEqual({ - release: [{ "node-menu": "0.0.1" }], - available: [], + release: { "node-menu": "0.0.1" }, + available: {}, }); }); it("Should return proper lists for both release.json and version.json files", async () => { fetchMock - .get("http://my-example-url.com/versions.json", [ - { "node-menu": "0.0.1" }, - { "survey": "0.1.1" }, - ]) - .get("http://my-example-url.com/5.4.0-latest12345.json", [ - { "node-menu": "0.0.1" }, - { "survey": "0.0.1" }, - ]); - + .get("http://my-example-url.com/versions.json", { + "node-menu": "0.0.1", + "survey": "0.1.1", + }) + .get("http://my-example-url.com/5.4.0-latest12345.json", { + "node-menu": "0.0.1", + "survey": "0.0.1", + }); const lists = await new BundledExtensionParser("5.4.0-latest12345", "http://my-example-url.com").getExtensionLists(); expect(lists).toEqual({ - release: [ - { "node-menu": "0.0.1" }, - { "survey": "0.0.1" }, - ], - available: [ - { "node-menu": "0.0.1" }, - { "survey": "0.1.1" }, - ], + release: { + "node-menu": "0.0.1", + "survey": "0.0.1", + }, + available: { + "node-menu": "0.0.1", + "survey": "0.1.1", + }, }); }); }); diff --git a/src/main/extension-updater/__tests__/extensions-comparer.test.ts b/src/main/extension-updater/__tests__/extensions-comparer.test.ts new file mode 100644 index 0000000000..156aefe34b --- /dev/null +++ b/src/main/extension-updater/__tests__/extensions-comparer.test.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { BundledExtensionComparer } from "../bundled-extension-comparer"; + +describe("BundledExtensionComparer", () => { + it("Should return empty array if all extensions have equal versions", () => { + const releaseExtensions = { + "node-menu": "0.1.1", + "survey": "0.1.1", + }; + const availableExtensions = { + "node-menu": "0.1.1", + "survey": "0.1.1", + }; + + const downloads = new BundledExtensionComparer(releaseExtensions, availableExtensions, "https://myurl.com").getExtensionsToDownload(); + + expect(downloads).toHaveLength(0); + }); + + it("Should return empty array if no extensions found in release list", () => { + const availableExtensions = { + "node-menu": "0.1.1", + "survey": "0.1.1", + }; + + const downloads = new BundledExtensionComparer({}, availableExtensions, "https://myurl.com").getExtensionsToDownload(); + + expect(downloads).toHaveLength(0); + }); + + it("Should return empty array if no extensions found in available list", () => { + const releaseExtensions = { + "node-menu": "0.1.1", + "survey": "0.1.1", + }; + + const downloads = new BundledExtensionComparer(releaseExtensions, {}, "https://myurl.com").getExtensionsToDownload(); + + expect(downloads).toHaveLength(0); + }); + + it("Should return extensions with never versions", () => { + const releaseExtensions = { + "node-menu": "0.1.1", + "survey": "0.1.1", + }; + const availableExtensions = { + "node-menu": "0.1.1", + "survey": "1.0.1", + }; + + const downloads = new BundledExtensionComparer(releaseExtensions, availableExtensions, "https://myurl.com").getExtensionsToDownload(); + + expect(downloads).toEqual([{ + name: "survey", + version: "1.0.1", + downloadUrl: "https://myurl.com/survey-1.0.1.tgz", + }]); + }); + + it("Should skip extensions not found in releases list", () => { + const releaseExtensions = { + "node-menu": "0.1.1", + }; + const availableExtensions = { + "node-menu": "0.1.1", + "survey": "1.0.1", + }; + + const downloads = new BundledExtensionComparer(releaseExtensions, availableExtensions, "https://myurl.com").getExtensionsToDownload(); + + expect(downloads).toHaveLength(0); + }); +}); diff --git a/src/main/extension-updater/bundled-extension-comparer.ts b/src/main/extension-updater/bundled-extension-comparer.ts new file mode 100644 index 0000000000..283f7aa43c --- /dev/null +++ b/src/main/extension-updater/bundled-extension-comparer.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import semverValid from "semver/functions/valid"; +import semverGt from "semver/functions/gt"; + +type Extensions = Record; + +type ExtensionToDownload = { + name: string; + version: string; + downloadUrl: string; +}; + +export class BundledExtensionComparer { + private releaseExtensions: Map; + private availableExtensions: Map; + + constructor(releaseExtensions: Extensions, availableExtensions: Extensions, private downloadUrl: string) { + this.releaseExtensions = new Map(Object.entries(releaseExtensions)); + this.availableExtensions = new Map(Object.entries(availableExtensions)); + } + + private getDownloadUrl(name: string, version: string) { + return `${this.downloadUrl}/${name}-${version}.tgz`; + } + + public getExtensionsToDownload(): ExtensionToDownload[] { + const extensions: ExtensionToDownload[] = []; + + for (const [name, version] of this.availableExtensions) { + if (!semverValid(this.releaseExtensions.get(name))) { + continue; + } + + if (semverGt(version, this.releaseExtensions.get(name))) { + extensions.push({ + name, + version, + downloadUrl: this.getDownloadUrl(name, version), + }); + } + } + + return extensions; + } +} diff --git a/src/main/extension-updater/bundled-extension-parser.ts b/src/main/extension-updater/bundled-extension-parser.ts index 84d1caf625..63681b1c17 100644 --- a/src/main/extension-updater/bundled-extension-parser.ts +++ b/src/main/extension-updater/bundled-extension-parser.ts @@ -5,7 +5,7 @@ import logger from "../logger"; -type Extensions = Record[]; +type Extensions = Record; interface ExtensionList { release: Extensions, @@ -30,19 +30,19 @@ export class BundledExtensionParser { return response.json(); } - return []; + return {}; }).catch(error => { logger.error(`[EXTENSION-PARSER]: Failed to download and parse extension list: ${error}`); - return []; + return {}; }); } public async getExtensionLists(): Promise { if (!this.url) { return { - release: [], - available: [], + release: {}, + available: {}, }; }