Skip to content

Commit 3be05c7

Browse files
committed
Download multiple versions
1 parent fc05fd7 commit 3be05c7

File tree

3 files changed

+57
-141
lines changed

3 files changed

+57
-141
lines changed

src/download/exchangeDownloader.test.ts

Lines changed: 11 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
downloadRestApi,
99
downloadRestApis,
1010
searchExchange,
11-
getVersionByDeployment,
11+
getApiVersions,
1212
getSpecificApi,
1313
getAsset,
1414
runFetch,
@@ -33,9 +33,6 @@ const assetSearchResults = require("../../testResources/download/resources/asset
3333
// eslint-disable-next-line @typescript-eslint/no-var-requires
3434
const getAssetWithVersion = require("../../testResources/download/resources/getAssetWithVersion");
3535

36-
// eslint-disable-next-line @typescript-eslint/no-var-requires
37-
const getAssetWithVersionV2 = require("../../testResources/download/resources/getAssetWithVersionV2");
38-
3936
// eslint-disable-next-line @typescript-eslint/no-var-requires
4037
const getAssetWithoutVersion = require("../../testResources/download/resources/getAsset");
4138

@@ -234,39 +231,22 @@ describe("exchangeDownloader", () => {
234231
});
235232
});
236233

237-
describe("getVersionByDeployment", () => {
234+
describe("getApiVersions", () => {
238235
const scope = nock("https://anypoint.mulesoft.com/exchange/api/v1/assets");
239236

240-
it("should return the latest version if no deployment is specified", async () => {
241-
scope.get("/8888888/test-api").reply(200, getAssetWithoutVersion);
242-
243-
return expect(
244-
getVersionByDeployment("AUTH_TOKEN", REST_API)
245-
).to.eventually.equal("0.0.42");
246-
});
247-
248-
it("should return the latest version if a deployment exists", async () => {
237+
it("should return the latest version", async () => {
249238
scope.get("/8888888/test-api").reply(200, getAssetWithoutVersion);
250239

251240
return expect(
252-
getVersionByDeployment("AUTH_TOKEN", REST_API, /production/i)
253-
).to.eventually.equal("0.0.42");
254-
});
255-
256-
it("should return the latest version if the deployment does not exist", async () => {
257-
scope.get("/8888888/test-api").reply(200, getAssetWithoutVersion);
258-
259-
return expect(
260-
getVersionByDeployment("AUTH_TOKEN", REST_API, /NOT AVAILABLE/i)
261-
).to.eventually.equal("0.0.42");
241+
getApiVersions("AUTH_TOKEN", REST_API)
242+
).to.eventually.deep.equal(["0.1.1"]);
262243
});
263244

264245
it("should return undefined if the asset does not exist", async () => {
265246
scope.get("/8888888/test-api").reply(404, "Not Found");
266247

267-
return expect(
268-
getVersionByDeployment("AUTH_TOKEN", REST_API, /NOT AVAILABLE/i)
269-
).to.eventually.be.undefined;
248+
return expect(getApiVersions("AUTH_TOKEN", REST_API)).to.eventually.be
249+
.undefined;
270250
});
271251

272252
it("should return undefined if the asset does not have a version", async () => {
@@ -275,9 +255,8 @@ describe("exchangeDownloader", () => {
275255

276256
scope.get("/8888888/test-api").reply(200, assetWithoutVersion);
277257

278-
return expect(
279-
getVersionByDeployment("AUTH_TOKEN", REST_API, /NOT AVAILABLE/i)
280-
).to.eventually.be.undefined;
258+
return expect(getApiVersions("AUTH_TOKEN", REST_API)).to.eventually.be
259+
.undefined;
281260
});
282261
});
283262

@@ -343,7 +322,7 @@ describe("exchangeDownloader", () => {
343322
scope
344323
.get("/shop-products-categories-api-v1")
345324
.reply(200, getAssetWithVersion)
346-
.get("/shop-products-categories-api-v1/0.0.42")
325+
.get("/shop-products-categories-api-v1/0.1.1")
347326
.reply(200, getAssetWithVersion);
348327

349328
return expect(search("searchString")).to.eventually.deep.equal([
@@ -354,76 +333,7 @@ describe("exchangeDownloader", () => {
354333
it("works when an asset does not exist", () => {
355334
scope.get("/shop-products-categories-api-v1").reply(404, "Not Found");
356335

357-
return expect(search("searchString")).to.eventually.deep.equal([
358-
{
359-
id: null,
360-
name: "Shopper Products",
361-
description:
362-
"Enable developers to add functionality that shows product details in shopping apps.",
363-
updatedDate: null,
364-
groupId: "893f605e-10e2-423a-bdb4-f952f56eb6d8",
365-
assetId: "shop-products-categories-api-v1",
366-
version: null,
367-
categories: {
368-
"API layer": ["Process"],
369-
"CC API Family": ["Product"],
370-
"CC Version Status": ["Beta"],
371-
"CC API Visibility": ["External"],
372-
},
373-
fatRaml: {
374-
classifier: "fat-raml",
375-
packaging: "zip",
376-
createdDate: null,
377-
md5: null,
378-
sha1: null,
379-
mainFile: "shop-products-categories-api-v1.raml",
380-
},
381-
fatOas: null,
382-
},
383-
]);
384-
});
385-
386-
it("works when there are no matches for the specified deployment", () => {
387-
const asset = _.cloneDeep(shopperCustomersAsset);
388-
asset.id = "893f605e-10e2-423a-bdb4-f952f56eb6d8/shopper-customers/0.0.7";
389-
asset.version = "0.0.7";
390-
asset.fatRaml = {
391-
classifier: "fat-raml",
392-
packaging: "zip",
393-
externalLink: "https://short.url/test",
394-
createdDate: "2020-02-05T21:26:01.199Z",
395-
md5: "87b3ad2b2aa17639b52f0cc83c5a8d40",
396-
sha1: "f2b9b2de50b7250616e2eea8843735b57235c22b",
397-
mainFile: "shopper-customers.raml",
398-
};
399-
400-
scope
401-
.get("/shop-products-categories-api-v1")
402-
.reply(200, getAssetWithoutVersion)
403-
.get("/shop-products-categories-api-v1/0.0.42")
404-
.reply(200, getAssetWithoutVersion);
405-
406-
return expect(search("searchString")).to.eventually.deep.equal([asset]);
407-
});
408-
409-
/**
410-
* Returns root asset version when production tag is not found
411-
* on V2 API response. The actual deployed version is available
412-
* under otherVersions attributes, that has no external link to download
413-
* and no environmentName attribute to match
414-
*/
415-
it("returns the root asset version without production tag on V2 response", () => {
416-
const asset = _.cloneDeep(shopperCustomersAsset);
417-
asset.id = "893f605e-10e2-423a-bdb4-f952f56eb6d8/shopper-customers/0.5.0";
418-
asset.version = "0.5.0";
419-
asset.fatRaml.externalLink = "https://somewhere/fatraml.zip";
420-
scope
421-
.get("/shop-products-categories-api-v1")
422-
.reply(200, getAssetWithVersionV2)
423-
.get("/shop-products-categories-api-v1/0.5.0")
424-
.reply(200, getAssetWithVersionV2);
425-
426-
return expect(search("searchString")).to.eventually.deep.equal([asset]);
336+
return expect(search("searchString")).to.eventually.deep.equal([]);
427337
});
428338
});
429339

src/download/exchangeDownloader.ts

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import fs from "fs-extra";
1111
import path from "path";
1212

1313
import { getBearer } from "./bearerToken";
14-
import { removeVersionSpecificInformation } from "./exchangeTools";
1514
import {
1615
RawRestApi,
1716
RestApi,
@@ -27,8 +26,6 @@ export const DEFAULT_DOWNLOAD_FOLDER = "download";
2726
const ANYPOINT_BASE_URI = "https://anypoint.mulesoft.com/exchange";
2827
const ANYPOINT_API_URI_V1 = `${ANYPOINT_BASE_URI}/api/v1`;
2928
const ANYPOINT_API_URI_V2 = `${ANYPOINT_BASE_URI}/api/v2`;
30-
const DEPLOYMENT_DEPRECATION_WARNING =
31-
"The 'deployment' argument is deprecated. The latest RAML specification that is published to Anypoint Exchange will be downloaded always.";
3229
// Only allows MAJOR.MINOR.PATCH (no suffixes). see https://semver.org/
3330
const releaseSemverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
3431
/**
@@ -74,7 +71,10 @@ export async function downloadRestApi(
7471
}
7572
try {
7673
await fs.ensureDir(destinationFolder);
77-
const zipFilePath = path.join(destinationFolder, `${restApi.assetId}.zip`);
74+
const zipFilePath = path.join(
75+
destinationFolder,
76+
`${restApi.assetId}-${restApi.version}.zip`
77+
);
7878

7979
const fatRaml = restApi.fatRaml;
8080
const fatOas = restApi.fatOas;
@@ -105,6 +105,7 @@ export async function downloadRestApi(
105105
* Download the API specifications
106106
* @param restApi - Metadata of the API
107107
* @param destinationFolder - Destination directory for the download
108+
* @param isOas - True for Open Api Specification
108109
*/
109110
export async function downloadRestApis(
110111
restApi: RestApi[],
@@ -229,20 +230,15 @@ export async function searchExchange(
229230
* @export
230231
* @param {string} accessToken
231232
* @param {RestApi} restApi
232-
* @param {RegExp} [deployment]
233233
* @returns {Promise<string>} Returned the version string from the instance fetched asset.version value
234234
*/
235-
export async function getVersionByDeployment(
235+
export async function getApiVersions(
236236
accessToken: string,
237-
restApi: RestApi,
238-
deployment?: RegExp
239-
): Promise<void | string> {
240-
if (deployment) {
241-
ramlToolLogger.warn(DEPLOYMENT_DEPRECATION_WARNING);
242-
}
243-
const logPrefix = "[exchangeDownloader][getVersion]";
237+
restApi: RestApi
238+
): Promise<void | string[]> {
239+
const logPrefix = "[exchangeDownloader][getVersions]";
244240

245-
let asset;
241+
let asset: void | RawRestApi;
246242
try {
247243
asset = await getAsset(
248244
accessToken,
@@ -267,22 +263,31 @@ export async function getVersionByDeployment(
267263
return;
268264
}
269265

270-
if (!asset.instances) {
266+
if (!asset.versionGroups) {
271267
ramlToolLogger.error(
272-
`${logPrefix} The rest API ${restApi.assetId} is missing asset.instances`
268+
`${logPrefix} The rest API ${restApi.assetId} is missing asset.versionGroups`
273269
);
274270
return;
275271
}
276-
277-
const releaseAssetVersions = asset.instances.filter((instance) => {
278-
return instance.version && releaseSemverRegex.test(instance.version);
272+
const versions: string[] = [];
273+
asset.versionGroups.forEach((versionGroup) => {
274+
const version = getLatestReleaseVersion(versionGroup);
275+
if (version) {
276+
versions.push(version);
277+
}
279278
});
279+
return versions;
280+
}
280281

281-
if (releaseAssetVersions.length === 0) {
282-
// If there is no release version, just return the asset version
283-
// TBD: should we skip downloading the asset?
284-
return asset.version;
282+
function getLatestReleaseVersion(versionGroup: {
283+
versions: Array<{ version: string }>;
284+
}): void | string {
285+
if (!versionGroup.versions) {
286+
return;
285287
}
288+
const releaseAssetVersions = versionGroup.versions.filter((version) => {
289+
return releaseSemverRegex.test(version.version);
290+
});
286291
// Sort versions and get the latest
287292
return releaseAssetVersions.sort((instanceA, instanceB) => {
288293
const [aMajor, aMinor, aPatch] = instanceA.version.split(".").map(Number);
@@ -321,28 +326,28 @@ export async function getSpecificApi(
321326
* removes all the version specific information from the returned object.
322327
*
323328
* @param query - Exchange search query
324-
* @param [deployment] - RegExp matching the desired deployment targets
325329
*
326330
* @returns Information about the APIs found.
327331
*/
328-
export async function search(
329-
query: string,
330-
deployment?: RegExp
331-
): Promise<RestApi[]> {
332-
if (deployment) {
333-
ramlToolLogger.warn(DEPLOYMENT_DEPRECATION_WARNING);
334-
}
335-
332+
export async function search(query: string): Promise<RestApi[]> {
336333
const token = await getBearer(
337334
process.env.ANYPOINT_USERNAME,
338335
process.env.ANYPOINT_PASSWORD
339336
);
340337
const apis = await searchExchange(token, query);
341338
const promises = apis.map(async (api) => {
342-
const version = await getVersionByDeployment(token, api, deployment);
343-
return version
344-
? getSpecificApi(token, api.groupId, api.assetId, version)
345-
: removeVersionSpecificInformation(api);
339+
const versions = await getApiVersions(token, api);
340+
if (!versions || versions.length === 0) {
341+
return [];
342+
}
343+
const versionPromises = versions.map((version) => {
344+
console.log("api=", api.name, ",version=", version);
345+
return getSpecificApi(token, api.groupId, api.assetId, version);
346+
});
347+
348+
return Promise.all(versionPromises);
346349
});
347-
return Promise.all(promises);
350+
return Promise.all(promises).then((results) =>
351+
results.reduce((acc, val) => acc.concat(val), [])
352+
);
348353
}

src/download/exchangeTypes.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ export type RawCategories = {
4040
export type RawRestApi = Omit<RestApi, "categories" | "fatRaml"> & {
4141
categories: RawCategories[];
4242
files: FileInfo[];
43-
instances: {
44-
environmentName: string;
45-
version: string;
43+
versionGroups: {
44+
versions: {
45+
version: string;
46+
}[];
4647
}[];
4748
};
4849

0 commit comments

Comments
 (0)