Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions packages/cli/src/commands/reports/list/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const REPORTS_FILE_DIR = resolveCommandPath("reports", "12345");
const preconditions = {
hasReportFiles() {
vol.fromJSON({
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567890.jsonl")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567891.jsonl")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-components-push-1234567892.jsonl")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567890.json")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567891.json")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-components-push-1234567892.json")]: "foo",
});
},
hasNoReportFiles() {
Expand All @@ -26,6 +26,11 @@ const preconditions = {
"reports/12345/.gitkeep": "",
});
},
hasSpacelessReportFiles() {
vol.fromJSON({
[join(resolveCommandPath("reports"), "storyblok-schema-diff-1234567890.json")]: "foo",
});
},
};

describe("reports list command", () => {
Expand All @@ -43,13 +48,13 @@ describe("reports list command", () => {
expect.stringContaining('Found 3 report files for space "12345":'),
);
expect(console.error).toHaveBeenCalledWith(
expect.stringContaining("storyblok-components-push-1234567892.jsonl"),
expect.stringContaining("storyblok-components-push-1234567892.json"),
);
expect(console.error).toHaveBeenCalledWith(
expect.stringContaining("storyblok-migrations-run-1234567890.jsonl"),
expect.stringContaining("storyblok-migrations-run-1234567890.json"),
);
expect(console.error).toHaveBeenCalledWith(
expect.stringContaining("storyblok-migrations-run-1234567891.jsonl"),
expect.stringContaining("storyblok-migrations-run-1234567891.json"),
);
});

Expand All @@ -72,4 +77,25 @@ describe("reports list command", () => {
expect.stringContaining('No reports found for space "12345"'),
);
});

it('should list space-less reports without an "undefined" space label', async () => {
preconditions.hasSpacelessReportFiles();

await reportsCommand.parseAsync(["node", "test", "list"]);

expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Found 1 report file:"));
expect(console.error).not.toHaveBeenCalledWith(expect.stringContaining("undefined"));
expect(console.error).toHaveBeenCalledWith(
expect.stringContaining("storyblok-schema-diff-1234567890.json"),
);
});

it('should not say space "undefined" when no reports and no space given', async () => {
preconditions.hasNoReportFiles();

await reportsCommand.parseAsync(["node", "test", "list"]);

expect(console.error).toHaveBeenCalledWith(expect.stringContaining("No reports found."));
expect(console.error).not.toHaveBeenCalledWith(expect.stringContaining("undefined"));
});
});
11 changes: 6 additions & 5 deletions packages/cli/src/commands/reports/list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ listCmd.action(async (_options: unknown, command: Command) => {
const { space, path } = command.optsWithGlobals();
const ui = getUI();
const reportsPath = resolveCommandPath(directories.reports, space, path);
const reportFiles = Reporter.listReportFiles(reportsPath, ".jsonl");
const reportFiles = Reporter.listReportFiles(reportsPath);
// Reports from commands without a space (e.g. file-to-file `schema diff`)
// live in the base reports directory, so only mention a space when given.
const scope = space ? ` for space "${space}"` : "";

if (reportFiles.length === 0) {
ui.info(`No reports found for space "${space}".`);
ui.info(`No reports found${scope}.`);
return;
}

ui.info(
`Found ${reportFiles.length} report file${reportFiles.length === 1 ? "" : "s"} for space "${space}":`,
);
ui.info(`Found ${reportFiles.length} report file${reportFiles.length === 1 ? "" : "s"}${scope}:`);
ui.list(reportFiles);
});
14 changes: 7 additions & 7 deletions packages/cli/src/commands/reports/prune/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const REPORTS_FILE_DIR = resolveCommandPath("reports", "12345");
const preconditions = {
hasReportFiles() {
vol.fromJSON({
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567890.jsonl")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567891.jsonl")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-components-push-1234567892.jsonl")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567890.json")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-migrations-run-1234567891.json")]: "foo",
[join(REPORTS_FILE_DIR, "storyblok-components-push-1234567892.json")]: "foo",
});
},
};
Expand All @@ -35,7 +35,7 @@ describe("reports prune command", () => {
await reportsCommand.parseAsync(["node", "test", "prune", "--space", "12345"]);

expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Deleted 3 report files"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".jsonl"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".json"));
expect(remainingFiles).toHaveLength(0);
});

Expand All @@ -45,7 +45,7 @@ describe("reports prune command", () => {
await reportsCommand.parseAsync(["node", "test", "prune", "--space", "12345", "--keep", "2"]);

expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Deleted 1 report file"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".jsonl"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".json"));
expect(remainingFiles).toHaveLength(2);
});

Expand All @@ -55,7 +55,7 @@ describe("reports prune command", () => {
await reportsCommand.parseAsync(["node", "test", "prune", "--space", "12345", "--keep", "3"]);

expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Deleted 0 report files"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".jsonl"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".json"));
expect(remainingFiles).toHaveLength(3);
});

Expand All @@ -65,7 +65,7 @@ describe("reports prune command", () => {
await reportsCommand.parseAsync(["node", "test", "prune", "--space", "12345", "--keep", "10"]);

expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Deleted 0 report files"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".jsonl"));
const remainingFiles = Object.keys(vol.toJSON()).filter((path) => path.includes(".json"));
expect(remainingFiles).toHaveLength(3);
});
});
2 changes: 1 addition & 1 deletion packages/cli/src/commands/reports/prune/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pruneCmd.action(async (options: { keep: number }, command: Command) => {
const { space, path } = command.optsWithGlobals();
const ui = getUI();
const reportsPath = resolveCommandPath(directories.reports, space, path);
const deletedFilesCount = Reporter.pruneReportFiles(reportsPath, options.keep, ".jsonl");
const deletedFilesCount = Reporter.pruneReportFiles(reportsPath, options.keep);

ui.info(`Deleted ${deletedFilesCount} report file${deletedFilesCount === 1 ? "" : "s"}`);
});
35 changes: 34 additions & 1 deletion packages/cli/src/commands/schema/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
import type { RemoteSchemaData } from "./types";
import type { LocalFolder, NormalizedSchema, RemoteSchemaData, SchemaData } from "./types";
import { getMapiClient } from "../../api";
import { fetchAllPages } from "../../utils";
import { buildGroupPathByUuid } from "./folders";

/**
* Reduces remote state to the common {@link NormalizedSchema} shape. Remote
* component groups are resolved into slug-path identity space (via their uuid
* parent chain) so folders diff against local folders in the same terms.
*/
export function remoteToNormalized(remote: RemoteSchemaData): NormalizedSchema {
const groupPathByUuid = buildGroupPathByUuid([...remote.componentFolders.values()]);
const folders = new Map<string, LocalFolder>();
for (const folder of remote.componentFolders.values()) {
const segments = groupPathByUuid.get(folder.uuid);
if (!segments || segments.length === 0) {
continue;
}
const path = segments.join("/");
folders.set(path, {
name: folder.name,
path,
parentPath: segments.length > 1 ? segments.slice(0, -1).join("/") : null,
});
}
return { components: remote.components, datasources: remote.datasources, folders };
}

/** Reduces locally-loaded schema arrays to the common {@link NormalizedSchema} shape. */
export function localToNormalized(local: SchemaData): NormalizedSchema {
return {
components: new Map(local.components.map((c) => [c.name, c])),
datasources: new Map(local.datasources.map((d) => [d.name, d])),
folders: new Map(local.folders.map((f) => [f.path, f])),
};
}

/**
* Fetches remote components, component folders, and datasources from the MAPI.
Expand Down
Loading
Loading