UNPKG

shopware-admin-api-client

Version:
264 lines (226 loc) 7.87 kB
import types from '../utils/types.utils.js'; function castValueToNullIfNecessary(value) { if (value === '' || typeof value === 'undefined') { return null; } return value; } // eslint-disable-next-line sw-deprecation-rules/private-feature-declarations export default class ChangesetGenerator { constructor(definition) { this.definition = definition; } /** * returns the primary key data of an entity * @param entity */ getPrimaryKeyData(entity) { const definition = this.definition.get(entity.getEntityName()); const pkFields = definition.getPrimaryKeyFields(); const pkData = {}; Object.keys(pkFields).forEach((fieldName) => { pkData[fieldName] = entity[fieldName]; }); return pkData; } /** * Creates the change set for the provided entity. * @param entity * @returns {{changes: *, deletionQueue: Array}} */ generate(entity) { const deletionQueue = []; const changes = this.recursion(entity, deletionQueue); return { changes, deletionQueue }; } /** * @private * @param {Entity} entity * @param deletionQueue * @returns {null} */ recursion(entity, deletionQueue) { const definition = this.definition.get(entity.getEntityName()); const changes = {}; const origin = entity.getOrigin(); const draft = entity.getDraft(); definition.forEachField((field, fieldName) => { if (field.readOnly) { return; } if (field.flags.write_protected) { return; } let draftValue = castValueToNullIfNecessary(draft[fieldName]); let originValue = castValueToNullIfNecessary(origin[fieldName]); if (definition.isScalarField(field)) { if (draftValue !== originValue) { changes[fieldName] = draftValue; } return; } if (field.flags.extension) { // extensions cloud be undefined if (origin.extensions || draft.extensions) { draftValue = castValueToNullIfNecessary(draft.extensions[fieldName]); originValue = castValueToNullIfNecessary(origin.extensions[fieldName]); } } if (definition.isJsonField(field)) { if (!types.isEqual(originValue, draftValue)) { if (Array.isArray(draftValue) && draftValue.length <= 0) { changes[fieldName] = []; return; } changes[fieldName] = draftValue; } return; } if (field.type !== 'association') { // if we don't know what kind of field we write send complete draft if (draftValue !== originValue) { changes[fieldName] = draftValue; } return; } switch (field.relation) { case 'one_to_many': { const associationChanges = this.handleOneToMany(field, draftValue, originValue, deletionQueue); if (associationChanges.length > 0) { changes[fieldName] = associationChanges; } break; } case 'many_to_many': { const associationChanges = this.handleManyToMany(draftValue, originValue, deletionQueue, field, entity); if (associationChanges.length > 0) { changes[fieldName] = associationChanges; } break; } case 'one_to_one': { if (!draftValue) { return; } const change = this.recursion(draftValue, deletionQueue); if (change !== null) { // if a change is detected, add id as identifier for updates change.id = draftValue.id; changes[fieldName] = change; } break; } case 'many_to_one': default: { break; } } }); if (Object.keys(changes).length > 0) { return { ...this.getPrimaryKeyData(entity), ...changes }; } return null; } /** * @private * @param {EntityCollection} draft * @param {EntityCollection} origin * @param {Object} field * @param {Entity} entity * @param deletionQueue * @returns {Array} */ handleManyToMany(draft, origin, deletionQueue, field, entity) { const changes = []; // return early if origin is null if (!origin) { return changes; } const originIds = origin.getIds(); draft.forEach((nested) => { if (!originIds.includes(nested.id)) { changes.push({ id: nested.id }); } }); originIds.forEach((id) => { if (!draft.has(id)) { const primary = { [field.local]: entity.id, [field.reference]: id, }; deletionQueue.push({ route: draft.source, key: id, entity: field.mapping, primary: primary, }); } }); return changes; } /** * @private * @param {Object} field * @param {EntityCollection} draft * @param {EntityCollection} origin * @param {Array} deletionQueue * @returns {Array} */ handleOneToMany(field, draft, origin, deletionQueue) { const changes = []; // return early if origin is null if (!origin) { return changes; } const originIds = origin.getIds(); // check for new and updated items draft.forEach((entity) => { // new record? if (!originIds.includes(entity.id)) { let change = this.recursion(entity, []); if (change === null) { change = { id: entity.id }; } else { change.id = entity.id; } changes.push(change); return; } // check if some properties changed const change = this.recursion(entity, deletionQueue); if (change !== null) { // if a change is detected, add id as identifier for updates change.id = entity.id; changes.push(change); } }); if (field.flags?.cascade_delete) { originIds.forEach((id) => { if (!draft.has(id)) { const primary = { [field.primary]: id, }; // still existing? deletionQueue.push({ route: draft.source, key: id, entity: field.entity, primary, }); } }); return changes; } if (!field.referenceField) { return changes; } originIds.forEach((id) => { if (!draft.has(id)) { const data = { id }; data[field.referenceField] = null; changes.push(data); } }); return changes; } }