Skip to content

Commit c6d8140

Browse files
Fix: diff command (@W-19076728@) (#235)
* refactor diff * fix updateApis command * strip out minor and patch version from dir name * remove console logs * hide normalization under flag * add unit tests * update unit test * remove unecessary test file * update changelog * update example * invert logic to make normalization default * update unit tests
1 parent 3d3ce6d commit c6d8140

File tree

6 files changed

+124
-5
lines changed

6 files changed

+124
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 0.8.0
4+
5+
* Add `--disable-normalize-directory-names` flag to `diff` command. By default directory names will be normalized by major version, e.g: `shopper-stores-oas-1.0.16` --> `shopper-stores-oas-1` [#235](https://github.com/SalesforceCommerceCloud/raml-toolkit/pull/235/)
6+
37
## 0.7.0
48

59
* Add `ExchangeConfig` type [#230](https://github.com/SalesforceCommerceCloud/raml-toolkit/pull/230)

src/diff/diffCommand.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ Exit statuses:
7070
"Specifies the API spec of the files being compared. Defaults to RAML. Options are RAML or OAS",
7171
options: ["raml", "oas"],
7272
}),
73+
"disable-normalize-directory-names": flags.boolean({
74+
description:
75+
"Disable normalization of directory names. When enabled, directory names will keep their full version numbers (e.g., 'shopper-stores-oas-1.0.16' instead of 'shopper-stores-oas-1')",
76+
default: false,
77+
}),
7378
};
7479

7580
static args = [

src/diff/oasDiff.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { expect } from "chai";
1010
import proxyquire from "proxyquire";
1111
import sinon from "sinon";
12+
import { consoleStub } from "../../testResources/setup";
1213

1314
const pq = proxyquire.noCallThru();
1415

@@ -678,4 +679,84 @@ describe("oasDiffChangelog", () => {
678679
expect(execStub.called).to.be.true;
679680
expect(result).to.equal(1); // Changes should be reported
680681
});
682+
683+
it("should normalize directory names when disable-normalize-directory-names flag is false", async () => {
684+
const execStub = createMockExec();
685+
execStub.onSecondCall().callsArgWith(1, null, "changes in api-1", "");
686+
687+
const fsStub = createMockFs();
688+
// Setup directory structure with versioned directory names
689+
setupDirectoryStructure(fsStub, [
690+
{ path: "base", contents: ["api-1.0.16", "api-2.1.5"] },
691+
{ path: "base/api-1.0.16", contents: ["exchange.json", "spec.yaml"] },
692+
{ path: "base/api-2.1.5", contents: ["exchange.json", "spec.yaml"] },
693+
{ path: "new", contents: ["api-1.0.17", "api-2.1.6"] },
694+
{ path: "new/api-1.0.17", contents: ["exchange.json", "spec.yaml"] },
695+
{ path: "new/api-2.1.6", contents: ["exchange.json", "spec.yaml"] },
696+
]);
697+
698+
const oasDiff = createOasDiffProxy(execStub, fsStub);
699+
700+
const baseApi = "base";
701+
const newApi = "new";
702+
const flags = {
703+
"out-file": "output.txt",
704+
dir: true,
705+
"disable-normalize-directory-names": false,
706+
};
707+
708+
await oasDiff.oasDiffChangelog(baseApi, newApi, flags);
709+
710+
expect(fsStub.writeFile.called).to.be.true;
711+
const writtenContent = fsStub.writeFile.args[0][1];
712+
713+
// Verify that the function completes successfully with the normalize-directory-names flag enabled
714+
expect(writtenContent).to.be.a("string");
715+
expect(writtenContent.length).to.be.greaterThan(0);
716+
717+
// Verify that the console.log stub captured the "Processing directory pair" messages
718+
expect(consoleStub.called).to.be.true;
719+
// flattens the array of arrays into a single array of strings
720+
const allMessages = consoleStub.args.map((args) => args[0]);
721+
expect(allMessages).to.include("Processing directory pair: api-1");
722+
expect(allMessages).to.include("Processing directory pair: api-2");
723+
consoleStub.restore();
724+
});
725+
726+
it("should handle fs.readdir error in findYamlFiles function", async () => {
727+
const consoleWarnSpy = sinon.spy(console, "warn");
728+
const execStub = createMockExec();
729+
execStub.onSecondCall().callsArgWith(1, null, "changes in api-v1", "");
730+
731+
const fsStub = createMockFs();
732+
setupDirectoryStructure(fsStub, [
733+
{ path: "base", contents: ["api-v1"] },
734+
{ path: "base/api-v1", contents: ["exchange.json", "spec.yaml"] },
735+
{ path: "new", contents: ["api-v1"] },
736+
{ path: "new/api-v1", contents: ["exchange.json", "spec.yaml"] },
737+
]);
738+
739+
// Make readdir fail for the new/api-v1 directory to trigger the error handling branch
740+
fsStub.readdir
741+
.withArgs("new/api-v1")
742+
.rejects(new Error("Permission denied"));
743+
744+
const oasDiff = createOasDiffProxy(execStub, fsStub);
745+
746+
const baseApi = "base";
747+
const newApi = "new";
748+
const flags = {
749+
dir: true,
750+
};
751+
752+
await oasDiff.oasDiffChangelog(baseApi, newApi, flags);
753+
754+
// Verify that the function handles the error gracefully
755+
expect(consoleWarnSpy.called).to.be.true;
756+
expect(consoleWarnSpy.args[0][0]).to.include(
757+
"Warning: Could not read directory new/api-v1:"
758+
);
759+
expect(consoleWarnSpy.args[0][1]).to.include("Permission denied");
760+
consoleWarnSpy.restore();
761+
});
681762
});

src/diff/oasDiff.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface OasDiffFlags {
1717
format?: "json" | "console";
1818
dir?: boolean;
1919
"out-file"?: string;
20+
"disable-normalize-directory-names"?: boolean;
2021
}
2122

2223
/**
@@ -242,6 +243,28 @@ async function executeOasDiff(
242243
});
243244
}
244245

246+
/**
247+
* Remove minor and patch versions from directory names and keep only major version
248+
* Example: 'shopper-stores-oas-1.0.16' -> 'shopper-stores-oas-1'
249+
* We want to keep the major version for multiple versions of the same API, ie: Shopper Baskets
250+
*
251+
* @param dirs - Array of directory objects with name and path properties
252+
* @returns Copy of passed array with updated name property
253+
*/
254+
function normalizeDirectoryNames(
255+
dirs: Array<{ name: string; path: string }>
256+
): Array<{ name: string; path: string }> {
257+
return dirs.map((dir) => ({
258+
...dir,
259+
// matches the pattern of the version number in the directory name, ie: -1.0.16
260+
// extracts the major version number if available, otherwise the original match if no major version is found
261+
name: dir.name.replace(/-\d+\.\d+\.\d+$/, (match) => {
262+
const majorVersion = match.match(/^-\d+/)?.[0];
263+
return majorVersion || match;
264+
}),
265+
}));
266+
}
267+
245268
/**
246269
* Handle directory mode comparison logic
247270
*
@@ -262,8 +285,13 @@ async function handleDirectoryMode(
262285
let hasErrors = false;
263286

264287
// Find all exchange.json files and their parent directories
265-
const baseExchangeDirs = await findExchangeDirectories(baseApi);
266-
const newExchangeDirs = await findExchangeDirectories(newApi);
288+
let baseExchangeDirs = await findExchangeDirectories(baseApi);
289+
let newExchangeDirs = await findExchangeDirectories(newApi);
290+
291+
if (!flags["disable-normalize-directory-names"]) {
292+
baseExchangeDirs = normalizeDirectoryNames(baseExchangeDirs);
293+
newExchangeDirs = normalizeDirectoryNames(newExchangeDirs);
294+
}
267295

268296
const allDirNames = new Set([
269297
...baseExchangeDirs.map((dir) => dir.name),

src/download/exchangeDownloader.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ function getLatestReleaseVersion(versionGroup: {
434434
const releaseAssetVersions = versionGroup.versions.filter((version) => {
435435
return releaseSemverRegex.test(version.version);
436436
});
437+
437438
// Sort versions and get the latest
438439
return releaseAssetVersions.sort((instanceA, instanceB) => {
439440
const [aMajor, aMinor, aPatch] = instanceA.version.split(".").map(Number);
@@ -442,5 +443,5 @@ function getLatestReleaseVersion(versionGroup: {
442443
if (aMajor !== bMajor) return bMajor - aMajor;
443444
if (aMinor !== bMinor) return bMinor - aMinor;
444445
return bPatch - aPatch;
445-
})[0].version;
446+
})[0]?.version;
446447
}

testResources/setup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import sinon from "sinon";
88
import { ramlToolLogger } from "../src/common/logger";
99

10-
let ramlToolLoggerStub: sinon.SinonStub;
11-
let consoleStub: sinon.SinonStub;
10+
export let ramlToolLoggerStub: sinon.SinonStub;
11+
export let consoleStub: sinon.SinonStub;
1212

1313
beforeEach(() => {
1414
ramlToolLoggerStub = sinon.stub(ramlToolLogger, "info");

0 commit comments

Comments
 (0)