-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimport-data.ts
143 lines (120 loc) · 5.42 KB
/
import-data.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env node
import { destroy } from './strapi.js';
import fs from 'fs/promises';
import { ModelByName, Model, Component } from './example-models.js';
import { AttributesSetting, components, ComponentSetting, models, ModelSetting } from './strapi-settings.js';
const dryRun = process.argv.includes('--dry-run');
type ModelsByName = {
[Name in keyof ModelByName]: ModelByName[Name][];
};
const data: ModelsByName = JSON.parse((await fs.readFile('./data.json')).toString());
const modelNames: (keyof typeof data)[] = Object.keys(data) as any;
const idMaps: Record<string, Record<number, number>> = {};
for (const name of modelNames) {
console.log(name);
const model = models[name];
if (!model) throw new Error(`Model not found: ${name}`);
const idMap: Record<number, number> = {};
for (const item of data[name]) {
const itemWithoutRelation = omitRelationFields(model, item);
try {
let newId = item.id;
if (!dryRun) ({ id: newId } = await strapi.query(name, models[name]?.plugin!).create(itemWithoutRelation));
idMap[item.id] = newId;
} catch (e) {
if (e instanceof Error && e.message === 'Duplicate entry') {
let dupItem: any;
for (const [attrName, attr] of Object.entries(models[name]?.attributes ?? {})) {
if (!attr.unique) continue;
dupItem = await strapi.query(name, models[name]?.plugin!).findOne({ [attrName]: (itemWithoutRelation as Record<string, unknown>)[attrName] });
if (!dupItem) continue;
}
console.warn(`Couldn't create the item because of duplication error: ${JSON.stringify(itemWithoutRelation)}. Use the existing item for the relations of others: ${JSON.stringify(dupItem)}`);
idMap[item.id] = dupItem.id;
} else {
console.error('Error:', e);
console.error(`While creating: ${JSON.stringify(itemWithoutRelation)}`);
}
}
}
console.log('idMap:', idMap);
idMaps[name] = idMap;
}
// fill relation fields
const mm: Record<string, Record<number, unknown>> = {};
for (const name of modelNames) {
console.log(name);
const m: Record<number, unknown> = {};
for (const item of data[name]) {
const newId = idMaps[name]?.[item.id];
if (!newId) {
console.error(`Cannot find new id for ${name} ${item.id}`);
continue;
}
const model = models[name];
if (!model) throw new Error(`Model not found: ${name}`);
const newItem = fillRelationFields(model, item);
if (name === 'role' || name === 'permission') {
console.log('Writing:', newItem);
}
m[newId] = newItem;
try {
if (!dryRun) await strapi.query(name, models[name]?.plugin!).update({ id: newId }, newItem);
} catch (e) {
console.error('Error:', e);
console.error(`While updating #${newId}: ${JSON.stringify(newItem)}`);
}
}
mm[name] = m;
}
await fs.writeFile('./written-data.json', JSON.stringify(mm));
destroy();
function omitRelationFields(model: ModelSetting | ComponentSetting, item: Model | Component): Partial<Model | Component> {
return mapItemFields(model, item, (value, attr, attrName) => {
if (attrName === 'id') return undefined;
if ('model' in attr || 'collection' in attr) return undefined;
return value;
});
}
function fillRelationFields(model: ModelSetting | ComponentSetting, item: Model | Component): Partial<Model | Component> {
return mapItemFields(model, item, (attr, attrSetting) => {
if (attr == null) return attr;
if ('model' in attrSetting) {
const newId = idMaps[attrSetting.model]?.[attr];
if (!newId) console.error(`Cannot find new id for ${attrSetting.model} ${attr}`, idMaps[attrSetting.model]);
return newId;
}
if ('collection' in attrSetting) {
const newIds = attr.map((id: number) => {
const newId = idMaps[attrSetting.collection]?.[id];
if (!newId) console.error(`Cannot find new id for ${attrSetting.collection} ${id}`, idMaps[attrSetting.collection]);
return newId;
});
return newIds;
}
return attr;
});
}
function mapItemFields(model: ModelSetting | ComponentSetting, item: Model | Component, f: (attr: any, attrSetting: AttributesSetting[string], attrName: string) => unknown): Partial<Model | Component> {
return Object.fromEntries(Object.entries(model.attributes ?? {}).flatMap(([attrName, attrSetting]) => {
const attr = (item as Record<string, any>)[attrName];
if ('type' in attrSetting && attrSetting.type === 'component') {
console.log('component', attrName, attrSetting, attr);
if (attr == null) return [[attrName, attr]];
if (attrSetting.repeatable) {
if (!Array.isArray(attr)) throw new Error(`Expected array but got ${typeof attr}`);
const componentSetting = components[attrSetting.component];
if (!componentSetting) throw new Error(`component not found: ${attrSetting.component}`);
return [[attrName, attr.map((item: Record<string, unknown>) => mapItemFields(componentSetting, item, f))]];
} else {
if (typeof attr !== 'object' || !attr) throw new Error(`Expected object but got ${JSON.stringify(attr)}`);
const componentSetting = components[attrSetting.component];
if (!componentSetting) throw new Error(`component not found: ${attrSetting.component}`);
return [[attrName, mapItemFields(componentSetting, attr, f)]];
}
}
const newAttr = f(attr, attrSetting, attrName);
if (newAttr === undefined) return [];
return [[attrName, newAttr]];
}));
}