importPackage(Packages.de.elo.ix.client); //@include lib_Class.js //@include lib_sol.common.RepoUtils.js //@include lib_sol.common.SordUtils.js //@include lib_sol.common.JsonUtils.js //@include lib_sol.common.ix.RfUtils.js //@include lib_sol.common.WfUtils.js //@include lib_sol.common.ObjectFormatter.js //@include lib_sol.common.ix.ServiceBase.js //@include lib_sol.common.Template.js /** * Converts a {@link sol.common.ObjectFormatter.TemplateSord TemplateSord} to {@link sol.common.ix.functions.Set SET}-compatible * {@link sol.common.ix.functions.Set#entries instructions/entries} optionally using a {@link #mapping mapping} and returns them. * * Hint: target instructions in mapping can contains {@link sol.common.ix.functions.Set#entries} parameters. * Currently only `key`, `type`, `value`, `useKwl`, `useDynKwl`, `dynKwlCfg` and `onlyIfEmpty` will be mapped * to the output of the set instructions * * ### Mapping definitions * A mapping definition consists of source-fields and their respective target-fields. * * { source: { id: "MY_FIELD", type: "GRP" }, target: { id: "YOUR_FIELD", type: "GRP" } } * * Resulting instruction: * * { id: "YOUR_FIELD", type: "GRP", value: "{{sord.objKeys.MY_FIELD}}" } * * You must pass true as the `returnRendered` parameter and a valid objId (String) or templateSord (Object) as the `dataSource` parameter, if you want the value to be rendered in the result. * * It is also possible to define custom values which do not occur in the `dataSource` by including them in the mapping definition's target property: * * { target: { id: "Y_FIELD", type: "MAP", value: "{{sord.mapKeys.MY_CUSTOM_FIELD}} fixed text" } } * * If some values which are available in the dataSource should not be added to the generated instructions, the values can be defined as a `source` without a `target` * * { source: { id: NO_FIELD, type: "MAP" } } * * If you pass a templateSord as `dataSource`, the templateSord will be used as `sord` in any templating. * * If you pass an objid as `dataSource` and a flowId as `dataSourceFlowId`, the sord will be checked out and used in templating. * * ### Empty non rendered values * * As a default, if returnRendered is true and a value could not be rendered, the original template-String will be used as the `value` in the generated instruction. * If you define `renderOptions: { emptyNonRendered: true }`, templates which were not rendered, will be written to the instruction as an empty String. * NonRendered means, the templating result is an empty string. * * ### Only generate instructions for mapped values * * If only some values of the dataSource should be used for generating instructions, defining `returnMappingsOnly: true` will only add those values defined in the mapping * to the resulting instructions. * * ### Reverse Mappings * * Option `reverseMapping: true` can be used, to switch source and target mapping options. (this is e.g. useful for HR-requests.) * * ### Examples * * ### Using a TemplateSord and a mapping * * #### Arguments * { * dataSource: { * objKeys: { * MY_FIELD: "myvalue" * }, * mapKeys: { * NO_FIELD: "xyz", * MY_CUSTOM_FIELD: "this is" * } * }, * mapping: [ * { source: { id: "MY_FIELD", type: "GRP" }, target: { id: "YOUR_FIELD", type: "GRP" } }, * { target: { id: "Y_FIELD", type: "MAP", value: "{{sord.mapKeys.MY_CUSTOM_FIELD}} fixed text" } }, * { source: { id: "NO_FIELD", type: "MAP" } }, * { target: { id: "OLD_DATAHISTORY", type: "FORMBLOB", value: "{{sord.formBlobs.DATAHISTORY}}" } } * ], * returnRendered: true, * renderOptions: { * emptyNonRendered: true * } * } * * #### Return value * * { * id: "SetInstructions", * dataSource: { * objKeys: { * MY_FIELD: "myvalue" * }, * mapKeys: { * NO_FIELD: "xyz", * MY_CUSTOM_FIELD: "this is" * } * }, * instructions: [ * { key: "YOUR_FIELD", type: "GRP", value: "myvalue" }, * { key: "Y_FIELD", type: "MAP", value: "this is fixed text" }, * { key: "OLD_DATAHISTORY", type: "FORMBLOB", value: "" } * ], * rendered: true * } * * @eloix * @requires sol.common.RepoUtils * @requires sol.common.SordUtils * @requires sol.common.JsonUtils * @requires sol.common.ix.RfUtils * @requires sol.common.WfUtils * @requires sol.common.ObjectFormatter * @requires sol.common.ix.ServiceBase * @requires sol.common.Template */ sol.define("sol.common.ix.services.GenerateSetInstructions", { extend: "sol.common.ix.ServiceBase", /** * @cfg {String|Object} dataSource * objId or templateSord */ /** * @cfg {String} dataSourceFlowId (optional) * flowId. This will only be used, if `dataSource` is an objectId */ /** * @cfg {Object[]} mapping (optional) * @cfg {Object} mapping.Object * @cfg {Object} mapping.Object.source (optional) * @cfg {String} mapping.Object.source.id name of source field * @cfg {String|"GRP"|"MAP"|"WFMAP"|"FORMBLOB"} mapping.Object.source.type type of field * @cfg {Object} mapping.Object.target (optional) * @cfg {String} mapping.Object.target.id name of target field * @cfg {String|"GRP"|"MAP"|"WFMAP"|"FORMBLOB"} mapping.Object.target.type type of field * @cfg {String} mapping.Object.target.value (optional) fixed or template String. This is completely optional! */ /** * @cfg {Object[]} protectedFields (optional) * @cfg {Object} protectedFields.Object * @cfg {String|"GRP"|"MAP"|"WFMAP"|"FORMBLOB"} protectedFields.Object.type * @cfg {String} protectedFields.Object.id name of protected field */ /** * @cfg {Object[]} mandatoryFields (optional) (throws exception if a field is missing) * @cfg {Object} mandatoryFields.Object * @cfg {String|"GRP"|"MAP"|"WFMAP"|"FORMBLOB"} mandatoryFields.Object.type * @cfg {String} mandatoryFields.Object.id name of mandatory field */ /** * @cfg {Boolean} [reverseMapping = false] (optional) * switches `target` and `source` in every mapping */ /** * @cfg {Boolean} [returnMappingsOnly = false] (optional) * instructionset will only contain instructions resulting from a mapping */ /** * @cfg {Boolean} [returnRendered = false] (optional) * decides if resulting instructions will go through handlebars */ /** * @cfg {Boolean} [escapeHTML = true] (optional) * if set to false, template strings will be generated so html won't be escaped */ /** * @cfg {Object} renderOptions (optional) * additional options for rendering * * @cfg {Boolean} [renderOptions.emptyNonRendered = false] (optional) * empty value if handlebars could not render template */ optionalDontEscape: function (s) { var me = this; return (me.escapeHTML === false) ? "{" + s + "}" : s; }, getTemplate: function (name, type) { var t = type.trim().toUpperCase(), templateProp = ( (t === "GRP" && "objKeys") || (t === "MAP" && "mapKeys") || (t === "WFMAP" && "wfMapKeys") || (t === "FORMBLOB" && "formBlobs") || "" ); return "{{sord." + (templateProp ? templateProp + "." : "") + name.trim() + "}}"; }, getType: function (objName) { return ( (objName === "objKeys" && "GRP") || (objName === "mapKeys" && "MAP") || (objName === "wfMapKeys" && "WFMAP") || (objName === "formBlobs" && "FORMBLOB") || objName ); }, executeMapping: function (mappings) { var me = this; me.logger.debug("Generating instructions from mapping", mappings); return ( mappings .filter(function (mapping) { // if there is no target, remove from mappings return (mapping.target && mapping.target !== {}); }) .map(function (mapping) { return { key: mapping.target.id, type: mapping.target.type, useKwl: mapping.target.useKwl || false, useDynKwl: mapping.target.useDynKwl || false, dynKwlCfg: mapping.target.dynKwlCfg || undefined, onlyIfEmpty: mapping.target.onlyIfEmpty || false, value: ( mapping.target.value || ( mapping.source ? me.optionalDontEscape(me.getTemplate(mapping.source.id, mapping.source.type)) : "" ) ) }; }) ); }, buildInstructionsFromTemplateSord: function (tSord) { var me = this, result = []; me.logger.debug("Building instructions using TemplateSord", tSord); Object.keys(tSord).forEach(function (typeObjName) { var typeObj = tSord[typeObjName], t = me.getType(typeObjName); (typeof typeObj === "object") && Object.keys(typeObj).forEach(function (fieldName) { result.push({ key: fieldName, type: me.getType(typeObjName), value: me.optionalDontEscape(me.getTemplate(fieldName, t)) }); }); }); return result; }, /* * returns templateSord-instructions which were not mapped */ filterInstructions: function (instructions, mappings) { var me = this; me.logger.debug("Filtering mapped instructions"); return instructions.filter(function (i) { return !(mappings.some(function (m) { return m.source && (m.source.id === i.key && m.source.type === i.type); })); }); }, filterProtectedFields: function (instructions, protected) { var me = this; me.logger.debug("Filtering protected fields"); return instructions.filter(function (i) { return !(protected.some(function (p) { return (typeof p === "object") && (p.id === i.key && p.type === i.type); })); }); }, findMissingMandatories: function (instructions, mandatories) { var me = this; me.logger.debug("Checking if all mandatory fields have instructions"); return mandatories.filter(function (m) { return !(instructions.some(function (i) { return (i.key === m.id && i.type === m.type); })); }); }, getConvertedData: function (mapping, tplObj, flowId, render, renderOptions, asAdmin, formBlobs) { var me = this, result = { rendered: render }, templatingData = { sord: {} }, instructions = []; if (typeof tplObj === "object") { me.logger.debug("Using TemplateSord as dataSource"); templatingData.sord = tplObj; instructions = me.buildInstructionsFromTemplateSord(tplObj); } else if ((typeof tplObj === "string") && tplObj) { me.logger.debug(["Using objId as dataSource. Retrieving TemplateSord using objId `{0}` (and flowId `{1}`)"], tplObj, flowId); templatingData.sord = me.getSordData(tplObj, flowId, asAdmin, formBlobs); instructions = me.buildInstructionsFromTemplateSord(templatingData.sord); } result.instructions = ( me.returnMappingsOnly ? [] : me.filterInstructions(instructions, mapping) ).concat(me.executeMapping(mapping)); me.logger.debug("Generated instructions", result.instructions); render && tplObj && sol.common.TemplateUtils.render(result.instructions, templatingData, renderOptions) && me.logger.debug("Applied Templating"); me.logger.debug("Result", result); return result; }, reverseMappings: function (mappings) { var me = this; me.logger.debug("Reversing Mapping ...", mappings); return mappings.map(function (mapping) { return { source: mapping.target, target: mapping.source }; }); }, getSordData: function (objId, flowId, asAdmin, formBlobs) { try { return sol.common.WfUtils.getTemplateSord( ((asAdmin && typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect).ix().checkoutSord(objId, SordC.mbAllIndex, LockC.NO), flowId, { asAdmin: asAdmin, formBlobs: formBlobs } ).sord; } catch (e) { throw "GenerateSetInstructions: Could not retrieve TemplateSord using objId `" + objId + "` " + ((flowId && "and flowId + `" + flowId + "`") || "") + ". Insufficient rights?"; } }, mappingContainsFormBlobs: function (mapping) { return mapping.some(function (m) { return typeof m === "object" && typeof m.source === "object" && m.source.type === "FORMBLOB"; }); }, /** * @return {Object} * @return {Object[]} return.instructions instructions usable as SET-entries * @return {Object} return.instructions.Object * @return {String} return.instructions.Object.key target field name * @return {String|"GRP"|"MAP"|"WFMAP"|"FORMBLOB"} return.instructions.Object.type target field type * @return {String} return.instructions.Object.value target field value * @return {Boolean} return.rendered if the `value`s in `instructions` are rendered */ process: function () { var me = this, mapping = me.mapping || [], tplObj = me.dataSource, flowId = me.dataSourceFlowId, render = me.returnRendered, renderOptions = { emptyNonRendered: !!(me.renderOptions && me.renderOptions.emptyNonRendered), stringifyResults: !!(me.renderOptions && me.renderOptions.stringifyResults) }, asAdmin = me.asAdmin, missingMandatoryFields = [], result = {}, includeFormBlobs; mapping = me.reverseMapping ? me.reverseMappings(mapping) : mapping; includeFormBlobs = me.mappingContainsFormBlobs(mapping); result = me.getConvertedData(mapping, tplObj, flowId, render, renderOptions, asAdmin, includeFormBlobs); if (me.protectedFields) { if (!Array.isArray(me.protectedFields)) { throw "protectedFields parameter only accepts an Array as an argument"; } result.instructions = me.filterProtectedFields(result.instructions, me.protectedFields); } if (me.mandatoryFields) { missingMandatoryFields = me.findMissingMandatories(result.instructions, me.mandatoryFields); if (missingMandatoryFields.length > 0) { throw "Some mandatory fields were not found in templateSord or mapping and therefore the resulting instructions. Missing fields:" + JSON.stringify(me.mandatoryFields); } } return result; } }); /** * @member sol.common.ix.services.GenerateSetInstructions * @method RF_sol_common_service_GenerateSetInstructions * @static * @inheritdoc sol.common.ix.ServiceBase#RF_ServiceBaseName */ function RF_sol_common_service_GenerateSetInstructions(iXSEContext, args) { var me = this, rfUtils, rfArgs, serviceProc, result, logger = sol.create("sol.Logger", { scope: "sol.common.ix.services.GenerateSetInstructions" }); logger.enter("RF_sol_common_service_GenerateSetInstructions"); rfUtils = sol.common.ix.RfUtils; rfArgs = rfUtils.parseAndCheckParams(iXSEContext, arguments.callee.name, args); if (me.asAdmin) { me.logger("TemplateSord will be retrieved using ixConnectAdmin"); sol.common.ix.RfUtils.checkMainAdminRights(iXSEContext.user, rfArgs); } serviceProc = sol.create("sol.common.ix.services.GenerateSetInstructions", rfArgs); result = rfUtils.stringify(serviceProc.process()); logger.exit("RF_sol_common_service_GenerateSetInstructions"); return result; }