- Documentation generated by JSDoc 3.6.7 on Thu May 27 2021 00:27:28 GMT+0200 (Central European Summer Time)
+ Documentation generated by JSDoc 3.6.11 on Tue Jun 18 2024 22:22:36 GMT+0200 (czas środkowoeuropejski letni)
diff --git a/docs/ODataMockGenerator.js.html b/docs/ODataMockGenerator.js.html
index ee664e4..cd0d608 100644
--- a/docs/ODataMockGenerator.js.html
+++ b/docs/ODataMockGenerator.js.html
@@ -60,18 +60,18 @@ Source: ODataMockGenerator.js
/**
* OData Mock Data Generator
- *
+ *
*/
export class ODataMockGenerator {
/**
* @constructor
- * @param {string} metadata OData metadata XML
+ * @param {string} metadata OData metadata XML
* @param {Object} [options={}] Generation options and rules
* @param {number} [options.defaultLengthOfEntitySets=30] Number of entities to generate for each entity set
* @param {string} [options.mockDataRootURI=""] Root URI which prefixes __metadata.uri property in the generated entities
* @param {Object} [options.rules={}] Additional rules
* @param {string[]} [options.rules.skipMockGeneration=[]] Do not generate data for the given entity sets
- * @param {string[]} [options.rules.distinctValues=[]] Generate only distinct entries (based on the key properties) for the given entity sets
+ * @param {string[]} [options.rules.distinctValues=[]] Generate only distinct entries (based on the key properties) for the given entity sets
* @param {Object} [options.rules.predefined={}] Predefined values for the given entities, see README
* @param {Object} [options.rules.variables={}] Variables to use in "predefined" rules, see README
* @param {Object} [options.rules.faker={}] Faker.js methods used to generate data for given properties, see README
@@ -102,6 +102,12 @@ Source: ODataMockGenerator.js
this._predefinedChosenValues = {};
this._dataGenerator = new DataGenerator();
+ this._relationships = options.rules.relationships || {};
+ this._includeTypeAttribute =
+ options.includeTypeAttribute !== undefined
+ ? options.includeTypeAttribute
+ : true;
+
try {
this._metdataXMLDocument = parseXML(metadata);
} catch (error) {
@@ -110,8 +116,32 @@ Source: ODataMockGenerator.js
}
/**
- * Generates mock data based on the metadata and options passed to the constructor
- *
+ *
+ * Generates relationships between entities mentioned in the configuration
+ *
+ * @param {Object} oMockData incoming data
+ */
+ _applyRelationships(oMockData) {
+ console.log("_applyRelationships", this._relationships);
+ for (const [entity, relations] of Object.entries(this._relationships)) {
+ if (oMockData[entity]) {
+ oMockData[entity].forEach((entityData) => {
+ for (const [property, relation] of Object.entries(relations)) {
+ const relatedData = oMockData[relation.reference];
+ if (relatedData) {
+ const relatedEntity =
+ relatedData[Math.floor(Math.random() * relatedData.length)];
+ entityData[property] = relatedEntity[relation.key];
+ }
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Generates mock data based on the metadata and options passed to the constructor
+ *
* @returns {Object} Generated data in form { EntitySet1: [{ ..properties.. }], EntitySet2: [{ .. properties.. }] }
*/
createMockData() {
@@ -120,16 +150,21 @@ Source: ODataMockGenerator.js
//exclude adjustments
this._skipMockGeneration.forEach((element) => {
- if (entitySetNames.find((name) => {
+ if (
+ entitySetNames.find((name) => {
return name === element;
- })) {
-
+ })
+ ) {
delete entitySets[element];
}
});
- this._mEntityTypes = metadataExtract.findEntityTypes(this._metdataXMLDocument);
- this._mComplexTypes = metadataExtract.findComplexTypes(this._metdataXMLDocument);
+ this._mEntityTypes = metadataExtract.findEntityTypes(
+ this._metdataXMLDocument
+ );
+ this._mComplexTypes = metadataExtract.findComplexTypes(
+ this._metdataXMLDocument
+ );
this._generateMockdata(entitySets);
return this._oMockdata;
@@ -143,48 +178,89 @@ Source: ODataMockGenerator.js
const mEntitySet = {};
const oEntitySet = mEntitySets[sEntitySetName];
mEntitySet[oEntitySet.name] = oEntitySet;
- oMockData[sEntitySetName] = this._generateODataMockdataForEntitySet(mEntitySet)[sEntitySetName];
+ oMockData[sEntitySetName] =
+ this._generateODataMockdataForEntitySet(mEntitySet)[sEntitySetName];
}
+ this._applyRelationships(oMockData);
+
// changing the values if there is a referential constraint
for (const sEntitySetName in mEntitySets) {
const oEntitySet = mEntitySets[sEntitySetName];
for (const navprop in oEntitySet.navprops) {
const oNavProp = oEntitySet.navprops[navprop];
- const iPropRefLength = oNavProp.from.propRef.length;
+ let iPropRefLength;
+ try {
+ iPropRefLength = oNavProp.from.propRef.length;
+ } catch (error) {
+ console.log(error);
+ }
for (let j = 0; j < iPropRefLength; j++) {
for (let i = 0; i < oMockData[sEntitySetName].length; i++) {
// copy the value from the principle to the dependant;
const oEntity = oMockData[sEntitySetName][i];
- if (this._predefinedValuesConfig[oNavProp.name] &&
- this._predefinedValuesConfig[oNavProp.name][oNavProp.to.propRef[j]]) {
- const chosenValues = this._predefinedChosenValues[oNavProp.name][oNavProp.to.propRef[j]];
- oEntity[oNavProp.from.propRef[j]] = chosenValues[Math.floor(Math.random() * chosenValues.length)];
+ if (
+ this._predefinedValuesConfig[oNavProp.name] &&
+ this._predefinedValuesConfig[oNavProp.name][
+ oNavProp.to.propRef[j]
+ ]
+ ) {
+ const chosenValues =
+ this._predefinedChosenValues[oNavProp.name][
+ oNavProp.to.propRef[j]
+ ];
+ oEntity[oNavProp.from.propRef[j]] =
+ chosenValues[Math.floor(Math.random() * chosenValues.length)];
} else {
- oMockData[oNavProp.to.entitySet][i][oNavProp.to.propRef[j]] = oEntity[oNavProp.from.propRef[j]];
+ try {
+ oMockData[oNavProp.to.entitySet][i][oNavProp.to.propRef[j]] =
+ oEntity[oNavProp.from.propRef[j]];
+ } catch (error) {
+ throw new Error(
+ `Could not find a respective entry in ${oNavProp.to.entitySet} ` +
+ `to update its value from a navigation related property ${oNavProp.from.propRef} ` +
+ `in ${sEntitySetName}. Check it the target entity set generation is not limited or skipped`
+ );
+ }
}
}
}
}
}
- // set URIs
+ // set URIs
for (const sEntitySetName in mEntitySets) {
const oEntitySet = mEntitySets[sEntitySetName];
oMockData[sEntitySetName].forEach((oEntry) => {
// add the metadata for the entry
oEntry.__metadata = {
- uri: sRootUri + sEntitySetName + "(" + this._createKeysString(oEntitySet, oEntry) + ")",
- type: oEntitySet.schema + "." + oEntitySet.type
+ uri:
+ sRootUri +
+ sEntitySetName +
+ "(" +
+ this._createKeysString(oEntitySet, oEntry) +
+ ")",
+ type: oEntitySet.schema + "." + oEntitySet.type,
};
// add the navigation properties
- for (const sKey in oEntitySet.navprops) {
- oEntry[sKey] = {
- __deferred: {
- uri: sRootUri + sEntitySetName + "(" + this._createKeysString(oEntitySet, oEntry) + ")/" + sKey
- }
- };
+ if (this._includeTypeAttribute) {
+ for (const sKey in oEntitySet.navprops) {
+ oEntry[sKey] = {
+ __deferred: {
+ uri:
+ sRootUri +
+ sEntitySetName +
+ "(" +
+ this._createKeysString(oEntitySet, oEntry) +
+ ")/" +
+ sKey,
+ },
+ };
+ }
+ } else {
+ // remove the type attribute
+ delete oEntry.type;
}
});
}
@@ -266,7 +342,12 @@ Source: ODataMockGenerator.js
for (let i = 0; i < oEntityType.properties.length; i++) {
const oProperty = oEntityType.properties[i];
- oEntity[oProperty.name] = this._generatePropertyValue(oProperty, iIndex, oEntityType, oEntity);
+ oEntity[oProperty.name] = this._generatePropertyValue(
+ oProperty,
+ iIndex,
+ oEntityType,
+ oEntity
+ );
}
return oEntity;
@@ -279,9 +360,10 @@ Source: ODataMockGenerator.js
}
//predefined?
- if (this._predefinedValuesConfig[entityType.name] &&
- this._predefinedValuesConfig[entityType.name][property.name]) {
-
+ if (
+ this._predefinedValuesConfig[entityType.name] &&
+ this._predefinedValuesConfig[entityType.name][property.name]
+ ) {
if (!this._predefinedChosenValues[entityType.name]) {
this._predefinedChosenValues[entityType.name] = {};
}
@@ -290,15 +372,22 @@ Source: ODataMockGenerator.js
this._predefinedChosenValues[entityType.name][property.name] = [];
}
- const propertyConfig = this._predefinedValuesConfig[entityType.name][property.name];
+ const propertyConfig =
+ this._predefinedValuesConfig[entityType.name][property.name];
let chosenValue;
if (Array.isArray(propertyConfig)) {
//array of values
- chosenValue = propertyConfig[Math.floor(Math.random() * propertyConfig.length)];
- this._predefinedChosenValues[entityType.name][property.name].push(chosenValue);
+ chosenValue =
+ propertyConfig[Math.floor(Math.random() * propertyConfig.length)];
+ this._predefinedChosenValues[entityType.name][property.name].push(
+ chosenValue
+ );
return chosenValue;
- } else if (typeof propertyConfig === "string" && propertyConfig.indexOf("$ref") !== -1) {
+ } else if (
+ typeof propertyConfig === "string" &&
+ propertyConfig.indexOf("$ref") !== -1
+ ) {
const variableName = propertyConfig.split(":")[1];
if (this._variables && this._variables[variableName]) {
@@ -306,13 +395,16 @@ Source: ODataMockGenerator.js
if (Array.isArray(variable)) {
chosenValue = variable[Math.floor(Math.random() * variable.length)];
- this._predefinedChosenValues[entityType.name][property.name].push(chosenValue);
+ this._predefinedChosenValues[entityType.name][property.name].push(
+ chosenValue
+ );
return chosenValue;
} else {
return variable;
}
} else {
- throw new Error(`Variable ${propertyConfig} not found`);
+ throw new Error(`
+ Variable $ { propertyConfig } not found `);
}
} else {
//dependent?
@@ -334,9 +426,19 @@ Source: ODataMockGenerator.js
for (const i in entityType.properties) {
if (entityType.properties[i].name === propertyConfig.reference) {
const emptyProperty = entityType.properties[i];
- entity[emptyProperty.name] = this._generatePropertyValue(emptyProperty, iIndexParameter, entityType, entity);
+ entity[emptyProperty.name] = this._generatePropertyValue(
+ emptyProperty,
+ iIndexParameter,
+ entityType,
+ entity
+ );
//and run again for current
- return this._generatePropertyValue(property, iIndexParameter, entityType, entity);
+ return this._generatePropertyValue(
+ property,
+ iIndexParameter,
+ entityType,
+ entity
+ );
}
}
}
@@ -345,9 +447,10 @@ Source: ODataMockGenerator.js
}
// faker?
- if (this._fakerConfig[entityType.name] &&
- this._fakerConfig[entityType.name][property.name]) {
-
+ if (
+ this._fakerConfig[entityType.name] &&
+ this._fakerConfig[entityType.name][property.name]
+ ) {
const fakerCall = this._fakerConfig[entityType.name][property.name];
let generatedValue;
@@ -366,7 +469,9 @@ Source: ODataMockGenerator.js
return generatedValue;
} catch (error) {
- throw new Error(`faker.js call error, check the config for ${entityType.name}/${property.name}`);
+ throw new Error(
+ `faker.js call error, check the config for ${entityType.name}/${property.name}`
+ );
}
}
@@ -374,13 +479,22 @@ Source: ODataMockGenerator.js
let index = iIndexParameter;
if (!index) {
- index = Math.floor(this._dataGenerator.getPseudoRandomNumber("String") * 10000) + 101;
+ index =
+ Math.floor(
+ this._dataGenerator.getPseudoRandomNumber("String") * 10000
+ ) + 101;
}
- let value = this._dataGenerator.generateValueForODataProperty(property, index);
+ let value = this._dataGenerator.generateValueForODataProperty(
+ property,
+ index
+ );
if (value === null) {
- value = this._generateDataFromEntity(this._mComplexTypes[property.type], index);
+ value = this._generateDataFromEntity(
+ this._mComplexTypes[property.type],
+ index
+ );
}
return value;
@@ -418,7 +532,8 @@ Source: ODataMockGenerator.js
sUri = sUri && /([^?#]*)([?#].*)?/.exec(sUri)[1]; // remove URL parameters or anchors
return sUri;
}
-}
+}
+
@@ -434,7 +549,7 @@ Classes
- Documentation generated by JSDoc 3.6.7 on Thu May 27 2021 00:27:28 GMT+0200 (Central European Summer Time)
+ Documentation generated by JSDoc 3.6.11 on Tue Jun 18 2024 22:22:36 GMT+0200 (czas środkowoeuropejski letni)
diff --git a/docs/index.html b/docs/index.html
index 614fff6..36d39cd 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -43,20 +43,19 @@
-
-
-
-
+
OMG - OData Mock (data) Generator
Overview
Generates random mock data for entities described in the OData metadata document.
-Based on the code from OpenUI5 Mock Server , but has additional features for influencing the result:
+Based on the code from OpenUI5 Mock Server ,
+but has additional features:
use faker.js API methods for data generation
generate specific number of entities for given entity sets
skip generation of Entitiy Sets you don't need
provide sets of values, which should be used instead of pure random values
-more meanigful and related data - values from one property can have a specific value based on a value from another property, which helps with building navigations
+more meanigful and related data - values from one property can have a specific value based
+on a value from another property, which helps with building navigations
force to have only distinct entries within an Entity Set (based on key properties)
Installation
@@ -69,9 +68,9 @@ ES modules
See samples/node_usage_esm.js
CommonJS
-const { ODataMockGenerator } = require("omg-odata-mock-generator/dist/cjs")
+const { ODataMockGenerator } = require("omg-odata-mock-generator/dist/cjs");
// prepare metadata and options
-const generator = new ODataMockGenerator(metadata, options);
+const generator = new ODataMockGenerator(metadata, options);
See samples/node_usage_cjs.js
Browser
@@ -89,7 +88,7 @@ Usage
options - an optional parameter for fine-tuning the generation process. If not provided, defaults are used.
Options parameter structure is as follows:
-{
+{
defaultLengthOfEntitySets: <number>, // default 30
mockDataRootURI: <root uri for entity URIs>, // default ""
rules: {
@@ -104,19 +103,19 @@ Usage
Please also refer to docs
Default generation
-Note : assuming metadata is https://services.odata.org/V3/OData/OData.svc/$metadata
-const generator = new ODataMockGenerator(metadata)
+Note : assuming metadata is https://services.odata.org/V3/OData/OData.svc/$metadata
+const generator = new ODataMockGenerator(metadata);
const mockData = generator.createMockData();
will generate 30 entries for each entity set
{
- "Products": [
- {
+ "Products": [
+ {
"ID": 9,
"Name": "Name 1"
// rest of properties
}
- ],
+ ],
"ProductDetails": [
{
"ProductID": 6671,
@@ -126,14 +125,14 @@ Default generation
}
See samples/generatedDataSample.json
-Setting number of entities
+Setting number of generated entities
defaultLengthOfEntitySets sets the global, default number of generated entries; this can be overwritten
for specific entity sets using rules.lengthOf option
const options = {
- defaultLengthOfEntitySets: 3
+ defaultLengthOfEntitySets: 3,
};
-const generator = new ODataMockGenerator(metadata, options)
+const generator = new ODataMockGenerator(metadata, options);
const mockData = generator.createMockData();
Each entity set will have 3 entries. Setting generation of 2 entries for Products, 12 for Categories:
@@ -142,18 +141,18 @@ Setting number of entities
rules: {
lengthOf: {
Products: 2,
- Categories: 12
- }
- }
-}
+ Categories: 12,
+ },
+ },
+};
Setting the root URI
const options = {
defaultLengthOfEntitySets: 3,
- mockDataRootURI: "my/path"
+ mockDataRootURI: "my/path",
};
-const generator = new ODataMockGenerator(metadata, options)
+const generator = new ODataMockGenerator(metadata, options);
const mockData = generator.createMockData();
URI in __metadata will be prefixed:
@@ -166,26 +165,29 @@ Setting the root URI
Skipping mock data generation for entity sets
{
rules: {
- skipMockGeneration: ["EntitySet1", "EntitySet2"]
+ skipMockGeneration: ["EntitySet1", "EntitySet2"];
}
}
For example:
{
rules: {
- skipMockGeneration: ["Persons", "Suppliers"]
+ skipMockGeneration: ["Persons", "Suppliers"];
}
}
Using faker.js
Faker.js API methods can be provided and they will be used
-instead of default logic for data generation. Alternatively, Mustache-like string with several values can be also passed as described in the faker.js docs, for example {{name.lastName}}, {{name.firstName}} {{name.suffix}}
.
+instead of default logic for data generation. Alternatively, Mustache-like string with several values
+can be also passed as described in the faker.js docs, for
+example {{name.lastName}}, {{name.firstName}} {{name.suffix}}
.
If the string property has *MaxLength" attribute, generated value will be limited accordingly.
{
rules: {
- predefined: {
+ faker: {
Entity: {
- Property: [Value1, Value2, Value3]
+ Property1: "faker.method",
+ Property2: "{{faker.method}}, {{faker.method}}"
}
}
}
@@ -197,11 +199,11 @@ Using faker.js
faker: {
Product: {
Name: "commerce.productName",
- Description: "{{lorem.paragraph}}, {{commerce.productDescription}}"
- }
- }
- }
-}
+ Description: "{{lorem.paragraph}}, {{commerce.productDescription}}",
+ },
+ },
+ },
+};
Predefined values
If for some entities values should be randomly selected from a predefined set, it can be configured in the following way:
@@ -209,7 +211,7 @@ Predefined values
rules: {
predefined: {
Entity: {
- Property: [Value1, Value2, Value3]
+ Property: [Value1, Value2, Value3];
}
}
}
@@ -220,13 +222,13 @@ Predefined values
rules: {
predefined: {
Product: {
- Rating: [1, 2, 3]
- }
- }
- }
+ Rating: [1, 2, 3],
+ },
+ },
+ },
};
-const generator = new ODataMockGenerator(metadata, options)
+const generator = new ODataMockGenerator(metadata, options);
const mockData = generator.createMockData();
Rating will be a random value but from [1,2,3] set
@@ -270,23 +272,26 @@ Predefined values based on other values
Rating: [1, 2, 3],
Description: {
reference: "Rating",
- values: [{
+ values: [
+ {
key: 1,
- value: "Custom description for rating 1"
- }]
- }
- }
- }
- }
+ value: "Custom description for rating 1",
+ },
+ ],
+ },
+ },
+ },
+ },
};
-const generator = new ODataMockGenerator(metadata, options)
+const generator = new ODataMockGenerator(metadata, options);
const mockData = generator.createMockData();
Now if Rating = 1, Description will by "Custom...", not the generated one.
Not all dependent values has to be provided - if it is not found in values array, it will be generated as usual.
Reusing predefined values
-It easier to keep predefined values in one place, as they might be used in several places. It can be done with help of special variables property and special $ref:... handling:
+It easier to keep predefined values in one place, as they might be used in several places.
+It can be done with help of special variables property and special $ref:... handling:
{
rules: {
variables: {
@@ -342,9 +347,10 @@ Reusing predefined values
Name will be based on ID , which takes values from categoryIds variable.
Distinct values
-Having predefined values for entities and their key properties, duplicated entries will be present, as the generator always produces the number of entries specified by the mockDataEntitySize .
+
Having predefined values for entities and their key properties, duplicated entries will be present,
+as the generator always produces the number of entries specified by the mockDataEntitySize .
To have only distinct values (based on all key properties):
-{
+{
rules: {
"distinctValues": ["EnitytSet1", "EntitySet2"]
}
@@ -390,9 +396,9 @@ License
Author
Feel free to contact me:
@@ -410,7 +416,7 @@ Classes