-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
144 lines (133 loc) · 4.55 KB
/
index.js
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
144
/* eslint-disable no-underscore-dangle */
const { faker } = require('@faker-js/faker/locale/en');
const { unflatten } = require('flat');
const { typeToMethodMap, commonFieldsToFakerMap } = require('./lib/mapper');
const { isMongooseSchema } = require('./lib/utils');
/**
* Extract min/max validator values for String & Number schema types
* Min/Max can be a Number or Array
* @param {Number | Array} validator
* @returns
*/
function _getMinMaxFromSchema(validator) {
if (Array.isArray(validator)) {
return validator[0];
}
return validator;
}
/**
* Get mock value from faker for given field type
* @param {string} fieldName
* @param {string} fieldType
* @param {object} fakerOptions
*/
function _getMockValue(fieldName, fieldType, fakerOptions = {}) {
let fakerMethod = typeToMethodMap[fieldType];
const commonFields = Object.keys(commonFieldsToFakerMap);
const matchingCommonField = commonFields.find((field) => fieldName.toLowerCase().includes(field));
const {
min, max, minLength, maxLength,
} = fakerOptions;
if (fakerOptions.enum && fakerOptions.enumValues) {
// TODO=> Validate if enum values follow min/max validations
return faker.helpers.arrayElement(fakerOptions.enumValues);
}
if (min || max) {
const options = {
...(min && { min }),
...(max && { max }),
};
return faker[fakerMethod.module][fakerMethod.type](options);
}
if (minLength || maxLength) {
const options = {
...(minLength && { min: minLength }),
...(maxLength && { max: maxLength }),
};
/**
* Using different faker module since word.sample will error/return incorrect value
* when it can't find word within given min/max range in its corpus
*/
return faker.string.sample(options);
}
if (fieldType === 'Buffer') {
return Buffer.from(faker.string.alphanumeric(), 'utf-8');
}
if (matchingCommonField) {
fakerMethod = commonFieldsToFakerMap[matchingCommonField];
return faker[fakerMethod.module][fakerMethod.type]();
}
if (fakerMethod) {
return faker[fakerMethod.module][fakerMethod.type]();
}
// Fallback/default faker value
return faker.string.sample();
}
/**
* Use mongoose field options like enum, min, max etc to form options to use while
* creating faker object
* @param {object} mongooseField
*/
function _constructFakerOptions(mongooseField) {
const fakerOptions = {};
const mongooseValidators = ['min', 'max', 'minLength', 'maxLength'];
/**
* Using options.enum instead of enumValues at root level of field definition
* since enumValues is not available in case of nested schemas.
*/
if (mongooseField?.options?.enum) {
fakerOptions.enum = true;
fakerOptions.enumValues = mongooseField.options.enum;
}
if (mongooseField?.options) {
for (const validator of mongooseValidators) {
fakerOptions[validator] = _getMinMaxFromSchema(mongooseField.options[validator]);
}
}
return fakerOptions;
}
/**
* Handle Array fields - There are two types here
* 1. Array of mongoose primitive data types
* 2. Array of subdocuments with its own schema
* @param {*} schemaField - Field in mongoose schema of type array
* @param {string} fieldName - Name of the array field
*/
function _mockArrayDataType(schemaField, fieldName, options) {
if (schemaField.schema) {
// eslint-disable-next-line no-use-before-define
return [generateMock(schemaField.schema, options)];
}
const fieldType = schemaField.$embeddedSchemaType.instance;
return [_getMockValue(fieldName, fieldType)];
}
/**
* Create a mock object based on mongoose schema
* @param {*} schema - mongoose schema
* @param {object} options - options from client for mock generation
*/
function generateMock(schema, options = {}) {
if (!schema || !isMongooseSchema(schema)) {
throw new Error('Valid mongoose schema is required to generate mock');
}
const mock = {};
for (const fieldName in schema.paths) {
const field = schema.paths[fieldName];
const fieldType = field.instance;
if (options.requiredOnly && !field?.options?.required) {
// eslint-disable-next-line no-continue
continue;
}
// Handle nested schemas
if (fieldType === 'Embedded') {
mock[fieldName] = generateMock(field.schema, options);
} else if (fieldType === 'Array') {
mock[fieldName] = _mockArrayDataType(field, fieldName, options);
} else {
const fakerOptions = _constructFakerOptions(field);
mock[fieldName] = _getMockValue(fieldName, fieldType, fakerOptions);
}
}
return unflatten(mock, { safe: true });
}
module.exports.generateMock = generateMock;