Skip to content

Commit 4a3ceda

Browse files
committed
update: Faker data nested array level support
1 parent 908c2ac commit 4a3ceda

File tree

2 files changed

+85
-51
lines changed

2 files changed

+85
-51
lines changed

src/pages/faker/Faker.tsx

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -309,53 +309,69 @@ export default function FakerTool() {
309309
);
310310

311311
function fieldToSchema(f: FieldConfig): any {
312-
if (f.type === "object") {
312+
// Helper to build the non-array schema for a field
313+
function buildInner(field: FieldConfig): any {
314+
if (field.type === "object") {
315+
return {
316+
type: "object",
317+
properties: (field.children || []).reduce<Record<string, any>>((acc, c) => {
318+
acc[c.name] = fieldToSchema(c);
319+
return acc;
320+
}, {}),
321+
};
322+
}
323+
const map: Record<string, any> = {
324+
string: { type: "string" },
325+
number: { type: "number" },
326+
boolean: { type: "boolean" },
327+
uuid: { type: "string" },
328+
date: { type: "string", format: "date-time" },
329+
email: { type: "string" },
330+
firstName: { type: "string" },
331+
lastName: { type: "string" },
332+
fullName: { type: "string" },
333+
jobTitle: { type: "string" },
334+
phone: { type: "string" },
335+
streetAddress: { type: "string" },
336+
city: { type: "string" },
337+
country: { type: "string" },
338+
countryCode: { type: "string" },
339+
latitude: { type: "number" },
340+
longitude: { type: "number" },
341+
ip: { type: "string" },
342+
url: { type: "string" },
343+
domain: { type: "string" },
344+
username: { type: "string" },
345+
password: { type: "string" },
346+
company: { type: "string" },
347+
avatar: { type: "string" },
348+
color: { type: "string" },
349+
currencyCode: { type: "string" },
350+
iban: { type: "string" },
351+
bitcoinAddress: { type: "string" },
352+
sentence: { type: "string" },
353+
paragraph: { type: "string" },
354+
imageUrl: { type: "string" },
355+
};
356+
const base = map[field.type] || { type: "string" };
357+
const fakerPath = typeToFaker[field.type];
358+
if (fakerPath) (base as any).faker = fakerPath;
359+
return base;
360+
}
361+
362+
if (f.array) {
363+
const innerSchema = buildInner({ ...f, array: false });
364+
// Preserve fixed length with both standard keywords and custom extension
365+
const len = f.arrayLength || 3;
313366
return {
314-
type: "object",
315-
properties: (f.children || []).reduce<Record<string, any>>((acc, c) => {
316-
acc[c.name] = fieldToSchema(c);
317-
return acc;
318-
}, {}),
367+
type: "array",
368+
items: innerSchema,
369+
minItems: len,
370+
maxItems: len,
371+
"x-arrayLength": len,
319372
};
320373
}
321-
const map: Record<string, any> = {
322-
string: { type: "string" },
323-
number: { type: "number" },
324-
boolean: { type: "boolean" },
325-
uuid: { type: "string" },
326-
date: { type: "string", format: "date-time" },
327-
email: { type: "string" },
328-
firstName: { type: "string" },
329-
lastName: { type: "string" },
330-
fullName: { type: "string" },
331-
jobTitle: { type: "string" },
332-
phone: { type: "string" },
333-
streetAddress: { type: "string" },
334-
city: { type: "string" },
335-
country: { type: "string" },
336-
countryCode: { type: "string" },
337-
latitude: { type: "number" },
338-
longitude: { type: "number" },
339-
ip: { type: "string" },
340-
url: { type: "string" },
341-
domain: { type: "string" },
342-
username: { type: "string" },
343-
password: { type: "string" },
344-
company: { type: "string" },
345-
avatar: { type: "string" },
346-
color: { type: "string" },
347-
currencyCode: { type: "string" },
348-
iban: { type: "string" },
349-
bitcoinAddress: { type: "string" },
350-
sentence: { type: "string" },
351-
paragraph: { type: "string" },
352-
imageUrl: { type: "string" },
353-
};
354-
const base = map[f.type] || { type: "string" };
355-
const fakerPath = typeToFaker[f.type];
356-
if (fakerPath) (base as any).faker = fakerPath;
357-
if (f.array) return { type: "array", items: base };
358-
return base;
374+
return buildInner(f);
359375
}
360376
function fieldsToSchema(list: FieldConfig[]) {
361377
return {
@@ -379,7 +395,19 @@ export default function FakerTool() {
379395
}
380396
if (node.type === "array") {
381397
const inner = schemaNodeToField(name, node.items || { type: "string" });
382-
return { ...inner, array: true, arrayLength: 3 };
398+
const inferredLength = ((): number => {
399+
if (
400+
typeof node.minItems === "number" &&
401+
typeof node.maxItems === "number" &&
402+
node.minItems === node.maxItems
403+
)
404+
return node.minItems;
405+
if (typeof node["x-arrayLength"] === "number") return node["x-arrayLength"];
406+
if (typeof node.minItems === "number") return node.minItems;
407+
if (typeof node.maxItems === "number") return node.maxItems;
408+
return 3;
409+
})();
410+
return { ...inner, array: true, arrayLength: inferredLength };
383411
}
384412
return { id: faker.string.uuid(), name, type: guessFieldType(node) };
385413
}
@@ -913,10 +941,10 @@ export default function FakerTool() {
913941
}}
914942
title={
915943
<span>
916-
<b>Tip:</b> Drag top-level items to reorder only.
944+
<b>Tip:</b> Drag top-level items to reorder only. Use Add Object then + Child
945+
buttons to manage nesting;
917946
<br />
918-
Use Add Object then + Child buttons to manage nesting; dragging into nested
919-
levels is disabled.
947+
All the changes are saved automatically, you can come back later and edit them.
920948
</span>
921949
}
922950
/>

src/pages/faker/utils.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ export const primitiveTypes = [
5656
export function generateFromField(field: FieldConfig): any {
5757
if (field.array) {
5858
const len = field.arrayLength ?? 3;
59-
return Array.from({ length: len }, () => generateFromField({ ...field, array: false }));
59+
return Array.from({ length: len }, () => {
60+
const inner: FieldConfig = { ...field, array: false };
61+
if (inner.type === "object") {
62+
return buildObject(inner.children || []);
63+
}
64+
return generateFromField(inner);
65+
});
6066
}
6167
if (field.type === "object") {
6268
return buildObject(field.children || []);
@@ -136,8 +142,8 @@ export function generateFromField(field: FieldConfig): any {
136142
export function buildObject(fields: FieldConfig[]): any {
137143
const obj: Record<string, any> = {};
138144
for (const f of fields) {
139-
obj[f.name] =
140-
f.children && f.children.length > 0 ? buildObject(f.children) : generateFromField(f);
145+
// Always delegate to generateFromField so arrays (including arrays of objects) are respected.
146+
obj[f.name] = generateFromField(f);
141147
}
142148
return obj;
143149
}

0 commit comments

Comments
 (0)