Search Results for

      Show / Hide Table of Contents

      Designer Scripting: Advanced Mappings

      Learn how to automate advanced mapping creation using scripts. This guide is for module builders and developers who want to programmatically create mappings between designer elements.

      What You'll Learn

      This guide covers:

      • Creating basic field-to-field mappings
      • Mapping collections and nested objects
      • Querying and validating existing mappings
      • Common patterns and troubleshooting

      What Are Advanced Mappings?

      Advanced Mappings are blueprints for code generation. When you map a CreateOrder command to an Order entity, you're telling templates:

      • Which constructor or method to call (Invocation Mapping)
      • Which fields map to which properties (Data Mapping)
      • How to filter or query data (Filter Mapping)

      Templates read these mappings and can generate code like this:

      // Your mapping creates this blueprint:
      // Invocation: new Order()
      // Data: RefNo → RefNo, CreatedDate → CreatedDate
      
      // Template generates:
      var order = new Order
      {
          RefNo = command.RefNo,
          CreatedDate = command.CreatedDate
      };
      

      Learn through examples

      Complete setup script

      Important

      Before running any examples, execute this comprehensive setup script. It creates all the model elements needed for every example in this guide.

      Step 1: Domain designer setup

      Open your Domain Designer and run this script:

      // Get the first package
      let packageId = getPackages()[0]?.id;
      if (!packageId) {
          await dialogService.error("Create a Domain package first");
          return;
      }
      
      const GuidTypeId = "6b649125-18ea-48fd-a6ba-0bfff0d8f488";
      
      // Create Order entity with attributes
      let order = createElement("Class", "Order", packageId);
      createElement("Attribute", "RefNo", order.id);
      createElement("Attribute", "CreatedDate", order.id);
      createElement("Attribute", "TotalAmount", order.id);
      
      // Create OrderLine entity for collection example
      let orderLine = createElement("Class", "OrderLine", packageId);
      createElement("Attribute", "Description", orderLine.id);
      
      // Order -> OrderLines (1:many)
      let orderLinesAssoc = createAssociation("Association", order.id, orderLine.id, "OrderLines");
      orderLinesAssoc.typeReference.setIsCollection(true);
      orderLinesAssoc.getOtherEnd().typeReference.setIsCollection(false);
      
      // Create ShippingInfo for nested object example
      let shippingInfo = createElement("Class", "ShippingInfo", packageId);
      createElement("Attribute", "Street", shippingInfo.id);
      createElement("Attribute", "City", shippingInfo.id);
      
      // Order -> ShippingInfo (1:1)
      let shippingAssoc = createAssociation("Association", order.id, shippingInfo.id, "ShippingInfo");
      shippingAssoc.typeReference.setIsCollection(false);
      shippingAssoc.getOtherEnd().typeReference.setIsCollection(false);
      
      const diagram = getCurrentDiagram();
      let space = diagram.findEmptySpace(diagram.getViewPort().getCenter(),  { width: 500, height: 550 });
      diagram.layoutVisuals(getPackages()[0], space, true);
      
      await dialogService.info("Domain entities created!");
      

      Step 2: Services designer setup

      Switch to your Services Designer and run this script:

      // Get the first package
      let packageId = getPackages()[0]?.id;
      if (!packageId) {
          await dialogService.error("Create a Services package first");
          return;
      }
      
      const GuidTypeId = "6b649125-18ea-48fd-a6ba-0bfff0d8f488";
      
      // Create CreateOrder command
      let createOrder = createElement("Command", "CreateOrder", packageId);
      createElement("DTO-Field", "RefNo", createOrder.id);
      createElement("DTO-Field", "CreatedDate", createOrder.id);
      createElement("DTO-Field", "TotalAmount", createOrder.id);
      
      // Create OrderLineDto for collection example
      let orderLineDto = createElement("DTO", "OrderLineDto", packageId);
      createElement("DTO-Field", "Description", orderLineDto.id);
      
      // Add OrderLines collection to CreateOrder
      let orderLinesField = createElement("DTO-Field", "OrderLines", createOrder.id);
      orderLinesField.typeReference.setType(orderLineDto.id);
      orderLinesField.typeReference.setIsCollection(true);
      
      // Create ShippingDetailsDto for nested example
      let shippingDetailsDto = createElement("DTO", "ShippingDetailsDto", packageId);
      createElement("DTO-Field", "Street", shippingDetailsDto.id);
      createElement("DTO-Field", "City", shippingDetailsDto.id);
      
      // Add ShippingDetails to CreateOrder
      let shippingField = createElement("DTO-Field", "ShippingDetails", createOrder.id);
      shippingField.typeReference.setType(shippingDetailsDto.id);
      
      // Create GetOrderById query
      let getOrderById = createElement("Query", "GetOrderById", packageId);
      let idField = createElement("DTO-Field", "Id", getOrderById.id);
      idField.typeReference.setType(GuidTypeId);
      
      // Create OrderDto for query return
      let orderDto = createElement("DTO", "OrderDto", packageId);
      idField = createElement("DTO-Field", "Id", orderDto.id);
      idField.typeReference.setType(GuidTypeId);
      createElement("DTO-Field", "RefNo", orderDto.id);
      createElement("DTO-Field", "CreatedDate", orderDto.id);
      createElement("DTO-Field", "TotalAmount", orderDto.id);
      
      getOrderById.typeReference.setType(orderDto.id);
      
      await dialogService.info("Services elements created!");
      

      Created Domain Elements

      Created Service Elements

      What was created:

      • Order, OrderLine, ShippingInfo entities with associations (Domain)
      • CreateOrder command with fields, collections, and nested objects (Services)
      • GetOrderById query and OrderDto for query example (Services)

      All examples run in the Services Designer. Make sure you've completed the setup scripts first.

      Example 1: Map command fields to Entity

      Map CreateOrder command fields to Order entity attributes using the three-step process.

      // Find the command and entity
      let command = lookupTypesOf("Command").find(x => x.getName() === "CreateOrder");
      let entity = lookupTypesOf("Class").find(x => x.getName() === "Order");
      
      // Step 1: Create association (declares intent)
      let action = createAssociation("Create Entity Action", command.id, entity.id);
      
      // Step 2: Create the mapping
      let mapping = action.createAdvancedMapping(command.id, entity.id);
      
      // Step 3a: Add invocation (tells template to call constructor)
      mapping.addMappedEnd("Invocation Mapping", [command.id], [entity.id]);
      
      // Step 3b: Map simple fields
      let fields = ["RefNo", "CreatedDate", "TotalAmount"];
      fields.forEach(name => {
          let field = command.getChildren("DTO-Field").find(x => x.getName() === name);
          let attr = entity.getChildren("Attribute").find(x => x.getName() === name);
          
          if (field && attr) {
              mapping.addMappedEnd(
                  "Data Mapping",
                  [command.id, field.id],  // Source: command.Field
                  [entity.id, attr.id]     // Target: entity.Attribute
              );
          }
      });
      
      // Step 3c: Map shipping info fields (nested object with multi-level paths)
      let shippingField = command.getChildren("DTO-Field").find(x => x.getName() === "ShippingDetails");
      let shippingAssoc = entity.getAssociations("Association").find(x => x.getName() === "ShippingInfo");
      
      if (shippingField && shippingAssoc) {
          let shippingDto = shippingField.typeReference.getType();
          let shippingEntity = shippingAssoc.typeReference.getType();
          
          // Map each nested field
          ["Street", "City"].forEach(fieldName => {
              let dtoField = shippingDto.getChildren("DTO-Field").find(x => x.getName() === fieldName);
              let entityAttr = shippingEntity.getChildren("Attribute").find(x => x.getName() === fieldName);
              
              if (dtoField && entityAttr) {
                  mapping.addMappedEnd(
                      "Data Mapping",
                      [command.id, shippingField.id, dtoField.id],      // command.ShippingDetails.Street
                      [entity.id, shippingAssoc.id, entityAttr.id]      // entity.ShippingInfo.Street
                  );
              }
          });
      }
      
      await dialogService.info("Mapping created!");
      

      Understanding the code:

      • lookupTypesOf("Command") - Finds all Commands in the Services Designer
      • createAssociation() - Creates "Create Entity Action" connecting command to entity
      • createAdvancedMapping() - Creates the mapping blueprint on the association
      • addMappedEnd() - Adds each transformation rule (Invocation + Data mappings)
      • Path arrays [command.id, field.id] - Represents "command.RefNo" traversal
      • Nested fields - Use 3-level paths like [command.id, shippingField.id, streetField.id] to map individual nested properties

      Advanced mapping created

      Possible generated code:

      var order = new Order  // From Invocation Mapping
      {
          RefNo = command.RefNo,             // From Data Mapping
          CreatedDate = command.CreatedDate, // From Data Mapping
          TotalAmount = command.TotalAmount, // From Data Mapping
          ShippingInfo = new ShippingInfo
          {
              Street = command.ShippingDetails.Street,  // From 3-level Data Mapping
              City = command.ShippingDetails.City       // From 3-level Data Mapping
          }
      };
      

      Example 2: Filter mapping for queries

      Create a query mapping that filters by ID.

      let query = lookupTypesOf("Query").find(x => x.getName() === "GetOrderById");
      let entity = lookupTypesOf("Class").find(x => x.getName() === "Order");
      
      // Create query action and mapping
      let action = createAssociation("Query Entity Action", query.id, entity.id);
      let mapping = action.createAdvancedMapping(query.id, entity.id);
      
      // Map Id field to Id attribute (filter condition)
      let idField = query.getChildren("DTO-Field").find(x => x.getName() === "Id");
      let idAttr = entity.getChildren("Attribute").find(x => x.getName() === "Id");
      
      if (idField && idAttr) {
          mapping.addMappedEnd(
              "Filter Mapping",  // Tells template this is a WHERE clause
              [query.id, idField.id],
              [entity.id, idAttr.id]
          );
      }
      
      await dialogService.info("Filter mapping created!");
      

      Understanding the code:

      • "Query Entity Action" - Association type for query operations
      • "Filter Mapping" - Tells templates to generate a WHERE clause
      • Same path array pattern: [parent.id, child.id]

      Advanced mapping query

      Possible generated code:

      var order = dbContext.Orders
          .Where(x => x.Id == query.Id)  // From Filter Mapping
          .FirstOrDefault();
      

      Example 3: Map collection fields

      Map a collection property from command to entity.

      let command = lookupTypesOf("Command").find(x => x.getName() === "CreateOrder");
      let entity = lookupTypesOf("Class").find(x => x.getName() === "Order");
      
      // Get or create mapping (reuse from Example 1 if it exists)
      let action = command.getAssociations("Create Entity Action")
          .find(x => x.typeReference?.typeId === entity.id);
      if (!action) {
          action = createAssociation("Create Entity Action", command.id, entity.id);
      }
      
      let mapping = action.getAdvancedMappings()[0];
      if (!mapping) {
          mapping = action.createAdvancedMapping(command.id, entity.id);
      }
      
      // Find collection field and association
      let orderLinesField = command.getChildren("DTO-Field").find(x => x.getName() === "OrderLines");
      let orderLinesAssoc = entity.getAssociations("Association").find(x => x.getName() === "OrderLines");
      
      if (orderLinesField && orderLinesAssoc) {
          // Map the collection
          mapping.addMappedEnd(
              "Data Mapping",
              [command.id, orderLinesField.id],
              [entity.id, orderLinesAssoc.id]
          );
          
          // Map fields within the collection
          let lineDto = orderLinesField.typeReference.getType();
          let lineEntity = orderLinesAssoc.typeReference.getType();
          
          let descField = lineDto.getChildren("DTO-Field").find(x => x.getName() === "Description");
          let descAttr = lineEntity.getChildren("Attribute").find(x => x.getName() === "Description");
          
          if (descField && descAttr) {
              mapping.addMappedEnd(
                  "Data Mapping",
                  [command.id, orderLinesField.id, descField.id],  // 3-level path
                  [entity.id, orderLinesAssoc.id, descAttr.id]     // command.OrderLines[].Description
              );
          }
      }
      
      await dialogService.info("Collection mapping created!");
      

      Understanding the code:

      • 3-level paths [command.id, orderLinesField.id, descField.id] - Represents "command.OrderLines[].Description"
      • First addMappedEnd() maps the collection itself
      • Second addMappedEnd() maps fields within each collection item

      Collection mapping with nested field mappings

      Possible generated code:

      OrderLines = command.OrderLines.Select(line => new OrderLine
      {
          Description = line.Description  // From 3-level Data Mapping
      }).ToList()
      

      Example 4: Query and Validate Mappings

      Check existing mappings and find unmapped fields.

      let command = lookupTypesOf("Command").find(x => x.getName() === "CreateOrder");
      let entity = lookupTypesOf("Class").find(x => x.getName() === "Order");
      
      // Find the association
      let action = command.getAssociations("Create Entity Action")
          .find(x => x.typeReference?.typeId === entity.id);
      
      if (!action) {
          console.log("No mapping found");
          return;
      }
      
      // Get the mapping
      let mapping = action.getAdvancedMappings()[0];
      if (!mapping) {
          console.log("No advanced mapping found");
          return;
      }
      
      // Show what's mapped
      console.log("\n=== Mapped Fields ===");
      
      mapping.getMappedEnds().forEach(end => {
          let sourcePath = end.sourcePath.map(p => p.name).join(".");
          let targetPath = end.targetPath.map(p => p.name).join(".");
          console.log(`${end.mappingType}: ${sourcePath} → ${targetPath}`);
      });
      
      // Find unmapped attributes
      let targetAttrs = entity.getChildren("Attribute");
      let mappedAttrIds = new Set();
      
      mapping.getMappedEnds().forEach(end => {
          if (end.targetPath.length > 0) {
              let lastElement = end.targetPath[end.targetPath.length - 1];
              mappedAttrIds.add(lastElement.id);
          }
      });
      
      let unmapped = targetAttrs.filter(attr => !mappedAttrIds.has(attr.id));
      
      let results = "=== Validation ===\n";
      
      if (unmapped.length > 0) {
          results += `⚠ Unmapped attributes: ${unmapped.map(a => a.getName()).join(", ")}`;
      } else {
          results += "✓ All attributes mapped";
      }
      
      await dialogService.info(results);
      

      Understanding the code:

      • getAdvancedMappings() - Returns all advanced mappings on the association
      • getMappedEnds() - Returns all mapped ends (Invocation + Data mappings)
      • sourcePath.map(p => p.name).join(".") - Converts path array to readable string
      • Uses Set to track which attributes are mapped
      • Finds unmapped attributes by comparing all attributes vs mapped ones

      Query results


      // Querying mappings action.getAdvancedMappings()                  // Get all mappings action.getAdvancedMapping(typeId)             // Get specific mapping type mapping.getMappedEnds()                       // Get all mapped ends

      Common Issues

      Duplicate mappings

      Cause: Running the same script multiple times.

      Fix: Check if mapping exists before creating:

      let action = element.getAssociations("Create Entity Action")
          .find(x => x.typeReference?.typeId === targetElement.id);
      
      if (!action) {
          action = createAssociation("Create Entity Action", element.id, targetElement.id);
      }
      

      Wrong generated code

      Cause: Incorrect path arrays. Must be in traversal order: [parent.id, child.id].

      Fix: Build paths from root to leaf:

      // ✅ Correct: command → ShippingDetails → Street
      [command.id, shippingDetailsField.id, streetField.id]
      
      // ❌ Wrong: Missing intermediate element
      [command.id, streetField.id]
      

      Quick Reference

      Common Mapping Type IDs

      // Use these constants at the top of your scripts
      const CreateEntityMappingId = "5f172141-fdba-426b-980e-163e782ff53e";
      const UpdateEntityMappingId = "01721b1a-a85d-4320-a5cd-8bd39247196a";
      const QueryEntityMappingId = "25f25af9-c38b-4053-9474-b0fabe9d7ea7";
      

      Key Methods

      // Finding elements
      lookupTypesOf("Command")                      // Get all commands
      element.getChildren("DTO-Field")              // Get child elements
      element.getAssociations("Create Entity Action") // Get associations by type
      
      // Creating mappings
      createAssociation(type, sourceId, targetId)   // Step 1: Create association
      action.createAdvancedMapping(srcId, tgtId)    // Step 2: Create mapping
      mapping.addMappedEnd(type, sourcePath, targetPath) // Step 3: Add mapped ends
      

      Next Steps

      • Designer Scripting Guide - Learn the full Designer Scripting API.
      • Advanced Mapping Tutorial - Configure mapping-enabled designers in the Module Builder.
      • API Documentation - Review Full API Documentation in the Designer Scripting reference for detailed method signatures.
      • Edit this page
      ☀
      ☾
      In this article
      Back to top Copyright © 2017-, Intent Architect Holdings Ltd