Confirm company imports after preview
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
06f5632d1a
commit
c02dc73d3c
2 changed files with 93 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ import {
|
||||||
buildSelectedFilesFromImportSelection,
|
buildSelectedFilesFromImportSelection,
|
||||||
renderCompanyImportPreview,
|
renderCompanyImportPreview,
|
||||||
renderCompanyImportResult,
|
renderCompanyImportResult,
|
||||||
|
resolveCompanyImportApplyConfirmationMode,
|
||||||
resolveCompanyImportApiPath,
|
resolveCompanyImportApiPath,
|
||||||
} from "../commands/client/company.js";
|
} from "../commands/client/company.js";
|
||||||
|
|
||||||
|
|
@ -58,6 +59,48 @@ describe("resolveCompanyImportApiPath", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("resolveCompanyImportApplyConfirmationMode", () => {
|
||||||
|
it("skips confirmation when --yes is set", () => {
|
||||||
|
expect(
|
||||||
|
resolveCompanyImportApplyConfirmationMode({
|
||||||
|
yes: true,
|
||||||
|
interactive: false,
|
||||||
|
json: false,
|
||||||
|
}),
|
||||||
|
).toBe("skip");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prompts in interactive text mode when --yes is not set", () => {
|
||||||
|
expect(
|
||||||
|
resolveCompanyImportApplyConfirmationMode({
|
||||||
|
yes: false,
|
||||||
|
interactive: true,
|
||||||
|
json: false,
|
||||||
|
}),
|
||||||
|
).toBe("prompt");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires --yes for non-interactive apply", () => {
|
||||||
|
expect(() =>
|
||||||
|
resolveCompanyImportApplyConfirmationMode({
|
||||||
|
yes: false,
|
||||||
|
interactive: false,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
).toThrow(/non-interactive terminal requires --yes/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires --yes for json apply", () => {
|
||||||
|
expect(() =>
|
||||||
|
resolveCompanyImportApplyConfirmationMode({
|
||||||
|
yes: false,
|
||||||
|
interactive: false,
|
||||||
|
json: true,
|
||||||
|
})
|
||||||
|
).toThrow(/with --json requires --yes/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("renderCompanyImportPreview", () => {
|
describe("renderCompanyImportPreview", () => {
|
||||||
it("summarizes the preview with counts, selection info, and truncated examples", () => {
|
it("summarizes the preview with counts, selection info, and truncated examples", () => {
|
||||||
const preview: CompanyPortabilityPreviewResult = {
|
const preview: CompanyPortabilityPreviewResult = {
|
||||||
|
|
|
||||||
|
|
@ -704,6 +704,27 @@ export function resolveCompanyImportApiPath(input: {
|
||||||
return input.dryRun ? "/api/companies/import/preview" : "/api/companies/import";
|
return input.dryRun ? "/api/companies/import/preview" : "/api/companies/import";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveCompanyImportApplyConfirmationMode(input: {
|
||||||
|
yes?: boolean;
|
||||||
|
interactive: boolean;
|
||||||
|
json: boolean;
|
||||||
|
}): "skip" | "prompt" {
|
||||||
|
if (input.yes) {
|
||||||
|
return "skip";
|
||||||
|
}
|
||||||
|
if (input.json) {
|
||||||
|
throw new Error(
|
||||||
|
"Applying a company import with --json requires --yes. Use --dry-run first to inspect the preview.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!input.interactive) {
|
||||||
|
throw new Error(
|
||||||
|
"Applying a company import from a non-interactive terminal requires --yes. Use --dry-run first to inspect the preview.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return "prompt";
|
||||||
|
}
|
||||||
|
|
||||||
export function isHttpUrl(input: string): boolean {
|
export function isHttpUrl(input: string): boolean {
|
||||||
return /^https?:\/\//i.test(input.trim());
|
return /^https?:\/\//i.test(input.trim());
|
||||||
}
|
}
|
||||||
|
|
@ -1095,7 +1116,7 @@ export function registerCompanyCommands(program: Command): void {
|
||||||
.option("--collision <mode>", "Collision strategy: rename | skip | replace", "rename")
|
.option("--collision <mode>", "Collision strategy: rename | skip | replace", "rename")
|
||||||
.option("--ref <value>", "Git ref to use for GitHub imports (branch, tag, or commit)")
|
.option("--ref <value>", "Git ref to use for GitHub imports (branch, tag, or commit)")
|
||||||
.option("--paperclip-url <url>", "Alias for --api-base on this command")
|
.option("--paperclip-url <url>", "Alias for --api-base on this command")
|
||||||
.option("--yes", "Accept the default import selection without opening the TUI", false)
|
.option("--yes", "Accept default selection and skip the pre-import confirmation prompt", false)
|
||||||
.option("--dry-run", "Run preview only without applying", false)
|
.option("--dry-run", "Run preview only without applying", false)
|
||||||
.action(async (fromPathOrUrl: string, opts: CompanyImportOptions) => {
|
.action(async (fromPathOrUrl: string, opts: CompanyImportOptions) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1220,6 +1241,34 @@ export function registerCompanyCommands(program: Command): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ctx.json) {
|
||||||
|
printCompanyImportView(
|
||||||
|
"Import Preview",
|
||||||
|
renderCompanyImportPreview(preview, {
|
||||||
|
sourceLabel,
|
||||||
|
targetLabel: formatTargetLabel(targetPayload, preview),
|
||||||
|
infoMessages: adapterMessages,
|
||||||
|
}),
|
||||||
|
{ interactive: interactiveView },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmationMode = resolveCompanyImportApplyConfirmationMode({
|
||||||
|
yes: opts.yes,
|
||||||
|
interactive: interactiveView,
|
||||||
|
json: ctx.json,
|
||||||
|
});
|
||||||
|
if (confirmationMode === "prompt") {
|
||||||
|
const confirmed = await p.confirm({
|
||||||
|
message: "Apply this import? (y/N)",
|
||||||
|
initialValue: false,
|
||||||
|
});
|
||||||
|
if (p.isCancel(confirmed) || !confirmed) {
|
||||||
|
p.log.warn("Import cancelled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const importApiPath = resolveCompanyImportApiPath({
|
const importApiPath = resolveCompanyImportApiPath({
|
||||||
dryRun: false,
|
dryRun: false,
|
||||||
targetMode: targetPayload.mode,
|
targetMode: targetPayload.mode,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue