kintone-migrator 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/index.mjs +70 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,12 +178,17 @@ Applies seed data (records) to a kintone app using upsert (insert or update base
|
|
|
178
178
|
kintone-migrator seed
|
|
179
179
|
kintone-migrator seed -s my-seed.yaml
|
|
180
180
|
|
|
181
|
+
# Clean apply: delete all existing records, then apply seed data
|
|
182
|
+
kintone-migrator seed --clean
|
|
183
|
+
kintone-migrator seed --clean --yes
|
|
184
|
+
|
|
181
185
|
# Capture records from a kintone app
|
|
182
186
|
kintone-migrator seed --capture --key-field customer_code
|
|
183
187
|
kintone-migrator seed --capture --key-field customer_code -s seeds/customer.yaml
|
|
184
188
|
|
|
185
189
|
# Multi-app mode
|
|
186
190
|
kintone-migrator seed --all
|
|
191
|
+
kintone-migrator seed --clean --all --yes
|
|
187
192
|
kintone-migrator seed --capture --key-field code --all
|
|
188
193
|
kintone-migrator seed --app customer
|
|
189
194
|
```
|
|
@@ -193,8 +198,10 @@ kintone-migrator seed --app customer
|
|
|
193
198
|
| CLI Argument | Description |
|
|
194
199
|
|---------|------|
|
|
195
200
|
| `--capture` | Capture mode: fetch records from kintone and save to seed file |
|
|
201
|
+
| `--clean` | Delete all existing records before applying seed data. Cannot be used with `--capture`. Prompts for confirmation unless `--yes` is specified. |
|
|
196
202
|
| `--key-field`, `-k` | Key field code for upsert (required for `--capture`) |
|
|
197
203
|
| `--seed-file`, `-s` | Seed file path (default: `seed.yaml`) |
|
|
204
|
+
| `--yes`, `-y` | Skip confirmation prompts (for `--clean` mode) |
|
|
198
205
|
|
|
199
206
|
### `customize`
|
|
200
207
|
|
package/dist/index.mjs
CHANGED
|
@@ -845,6 +845,25 @@ var KintoneRecordManager = class {
|
|
|
845
845
|
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update records", error);
|
|
846
846
|
}
|
|
847
847
|
}
|
|
848
|
+
async deleteAllRecords() {
|
|
849
|
+
try {
|
|
850
|
+
const records = await this.client.record.getAllRecords({
|
|
851
|
+
app: this.appId,
|
|
852
|
+
fields: ["$id"]
|
|
853
|
+
});
|
|
854
|
+
if (records.length === 0) return { deletedCount: 0 };
|
|
855
|
+
const validated = records.map((r) => toKintoneRecordForResponse(r));
|
|
856
|
+
await this.client.record.deleteAllRecords({
|
|
857
|
+
app: this.appId,
|
|
858
|
+
records: validated.map((r) => ({ id: Number(r.$id.value) }))
|
|
859
|
+
});
|
|
860
|
+
return { deletedCount: records.length };
|
|
861
|
+
} catch (error) {
|
|
862
|
+
if (isBusinessRuleError(error)) throw error;
|
|
863
|
+
if (error instanceof SystemError) throw error;
|
|
864
|
+
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to delete all records", error);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
848
867
|
};
|
|
849
868
|
|
|
850
869
|
//#endregion
|
|
@@ -3643,10 +3662,22 @@ const UpsertPlanner = { plan: (key, seedRecords, existingRecords) => {
|
|
|
3643
3662
|
|
|
3644
3663
|
//#endregion
|
|
3645
3664
|
//#region src/core/application/seedData/upsertSeed.ts
|
|
3646
|
-
async function upsertSeed({ container }) {
|
|
3665
|
+
async function upsertSeed({ container, input }) {
|
|
3647
3666
|
const result = await container.seedStorage.get();
|
|
3648
3667
|
if (!result.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Seed file not found");
|
|
3649
3668
|
const seedData = SeedParser.parse(result.content);
|
|
3669
|
+
if (input.clean) {
|
|
3670
|
+
const { deletedCount } = await container.recordManager.deleteAllRecords();
|
|
3671
|
+
const kintoneRecords = seedData.records.map(RecordConverter.toKintoneRecord);
|
|
3672
|
+
if (kintoneRecords.length > 0) await container.recordManager.addRecords(kintoneRecords);
|
|
3673
|
+
return {
|
|
3674
|
+
added: seedData.records.length,
|
|
3675
|
+
updated: 0,
|
|
3676
|
+
unchanged: 0,
|
|
3677
|
+
deleted: deletedCount,
|
|
3678
|
+
total: seedData.records.length
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3650
3681
|
if (seedData.key === null) {
|
|
3651
3682
|
const kintoneRecords = seedData.records.map(RecordConverter.toKintoneRecord);
|
|
3652
3683
|
if (kintoneRecords.length > 0) await container.recordManager.addRecords(kintoneRecords);
|
|
@@ -3654,6 +3685,7 @@ async function upsertSeed({ container }) {
|
|
|
3654
3685
|
added: seedData.records.length,
|
|
3655
3686
|
updated: 0,
|
|
3656
3687
|
unchanged: 0,
|
|
3688
|
+
deleted: 0,
|
|
3657
3689
|
total: seedData.records.length
|
|
3658
3690
|
};
|
|
3659
3691
|
}
|
|
@@ -3674,6 +3706,7 @@ async function upsertSeed({ container }) {
|
|
|
3674
3706
|
added: plan.toAdd.length,
|
|
3675
3707
|
updated: plan.toUpdate.length,
|
|
3676
3708
|
unchanged: plan.unchanged,
|
|
3709
|
+
deleted: 0,
|
|
3677
3710
|
total: plan.toAdd.length + plan.toUpdate.length + plan.unchanged
|
|
3678
3711
|
};
|
|
3679
3712
|
}
|
|
@@ -3683,10 +3716,15 @@ async function upsertSeed({ container }) {
|
|
|
3683
3716
|
const seedArgs = {
|
|
3684
3717
|
...kintoneArgs,
|
|
3685
3718
|
...multiAppArgs,
|
|
3719
|
+
...confirmArgs,
|
|
3686
3720
|
capture: {
|
|
3687
3721
|
type: "boolean",
|
|
3688
3722
|
description: "Capture records from kintone app to seed file"
|
|
3689
3723
|
},
|
|
3724
|
+
clean: {
|
|
3725
|
+
type: "boolean",
|
|
3726
|
+
description: "Delete all existing records before applying seed data (clean apply)"
|
|
3727
|
+
},
|
|
3690
3728
|
"key-field": {
|
|
3691
3729
|
type: "string",
|
|
3692
3730
|
short: "k",
|
|
@@ -3699,7 +3737,11 @@ const seedArgs = {
|
|
|
3699
3737
|
}
|
|
3700
3738
|
};
|
|
3701
3739
|
function resolveSeedMode(values) {
|
|
3702
|
-
if (values.capture
|
|
3740
|
+
if (values.capture === true && values.clean === true) throw new ValidationError(ValidationErrorCode.InvalidInput, "--capture and --clean cannot be used together");
|
|
3741
|
+
if (values.capture !== true) return {
|
|
3742
|
+
type: "upsert",
|
|
3743
|
+
clean: values.clean === true
|
|
3744
|
+
};
|
|
3703
3745
|
const keyField = values["key-field"];
|
|
3704
3746
|
if (!keyField) throw new ValidationError(ValidationErrorCode.InvalidInput, "--key-field is required when using --capture mode");
|
|
3705
3747
|
return {
|
|
@@ -3732,18 +3774,33 @@ function resolveSeedAppConfig(app, projectConfig, cliValues) {
|
|
|
3732
3774
|
}
|
|
3733
3775
|
function printUpsertResult(result) {
|
|
3734
3776
|
const parts = [
|
|
3777
|
+
result.deleted > 0 ? pc.red(`-${result.deleted} deleted`) : null,
|
|
3735
3778
|
result.added > 0 ? pc.green(`+${result.added} added`) : null,
|
|
3736
3779
|
result.updated > 0 ? pc.yellow(`~${result.updated} updated`) : null,
|
|
3737
3780
|
result.unchanged > 0 ? `${result.unchanged} unchanged` : null
|
|
3738
3781
|
].filter(Boolean).join(pc.dim(" | "));
|
|
3739
3782
|
p.log.info(`Records: ${parts} (${result.total} total)`);
|
|
3740
3783
|
}
|
|
3741
|
-
async function
|
|
3784
|
+
async function confirmClean(skipConfirm) {
|
|
3785
|
+
p.log.warn(`${pc.bold(pc.red("WARNING:"))} This will delete ALL existing records before applying seed data.`);
|
|
3786
|
+
if (!skipConfirm) {
|
|
3787
|
+
const shouldContinue = await p.confirm({ message: "Are you sure you want to clean and re-apply seed data?" });
|
|
3788
|
+
if (p.isCancel(shouldContinue) || !shouldContinue) {
|
|
3789
|
+
p.cancel("Clean seed cancelled.");
|
|
3790
|
+
process.exit(0);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
async function runUpsert(config, clean, skipConfirm) {
|
|
3795
|
+
if (clean) await confirmClean(skipConfirm);
|
|
3742
3796
|
const container = createSeedCliContainer(config);
|
|
3743
3797
|
const s = p.spinner();
|
|
3744
|
-
s.start("Applying seed data...");
|
|
3745
|
-
const result = await upsertSeed({
|
|
3746
|
-
|
|
3798
|
+
s.start(clean ? "Cleaning and applying seed data..." : "Applying seed data...");
|
|
3799
|
+
const result = await upsertSeed({
|
|
3800
|
+
container,
|
|
3801
|
+
input: { clean }
|
|
3802
|
+
});
|
|
3803
|
+
s.stop(clean ? "Clean seed applied." : "Seed data applied.");
|
|
3747
3804
|
printUpsertResult(result);
|
|
3748
3805
|
}
|
|
3749
3806
|
async function runSeedCapture(config, keyField) {
|
|
@@ -3762,9 +3819,9 @@ async function runSeedCapture(config, keyField) {
|
|
|
3762
3819
|
p.log.success(`Seed saved to: ${pc.cyan(config.seedFilePath)} (${result.recordCount} records)`);
|
|
3763
3820
|
if (result.hasExistingSeed) p.log.warn("Existing seed file was overwritten.");
|
|
3764
3821
|
}
|
|
3765
|
-
async function runSeedOperation(config, mode) {
|
|
3822
|
+
async function runSeedOperation(config, mode, skipConfirm) {
|
|
3766
3823
|
if (mode.type === "capture") await runSeedCapture(config, mode.keyField);
|
|
3767
|
-
else await runUpsert(config);
|
|
3824
|
+
else await runUpsert(config, mode.clean, skipConfirm);
|
|
3768
3825
|
}
|
|
3769
3826
|
var seed_default = define({
|
|
3770
3827
|
name: "seed",
|
|
@@ -3774,18 +3831,20 @@ var seed_default = define({
|
|
|
3774
3831
|
try {
|
|
3775
3832
|
const values = ctx.values;
|
|
3776
3833
|
const mode = resolveSeedMode(values);
|
|
3834
|
+
const skipConfirm = values.yes === true;
|
|
3777
3835
|
await routeMultiApp(values, {
|
|
3778
3836
|
singleLegacy: async () => {
|
|
3779
|
-
await runSeedOperation(resolveSeedConfig(values), mode);
|
|
3837
|
+
await runSeedOperation(resolveSeedConfig(values), mode, skipConfirm);
|
|
3780
3838
|
},
|
|
3781
3839
|
singleApp: async (app, projectConfig) => {
|
|
3782
|
-
await runSeedOperation(resolveSeedAppConfig(app, projectConfig, values), mode);
|
|
3840
|
+
await runSeedOperation(resolveSeedAppConfig(app, projectConfig, values), mode, skipConfirm);
|
|
3783
3841
|
},
|
|
3784
3842
|
multiApp: async (plan, projectConfig) => {
|
|
3843
|
+
if (mode.type === "upsert" && mode.clean) await confirmClean(skipConfirm);
|
|
3785
3844
|
await runMultiAppWithFailCheck(plan, async (app) => {
|
|
3786
3845
|
const config = resolveSeedAppConfig(app, projectConfig, values);
|
|
3787
3846
|
printAppHeader(app.name, app.appId);
|
|
3788
|
-
await runSeedOperation(config, mode);
|
|
3847
|
+
await runSeedOperation(config, mode, true);
|
|
3789
3848
|
}, mode.type === "capture" ? "All captures completed successfully." : "All seed applications completed successfully.");
|
|
3790
3849
|
}
|
|
3791
3850
|
});
|