Summary

This document details Configured Commerce handlers, their roles in processing business logic, and how to add/override them. It explains how the Chain of Responsibility is used and how data is passed between handlers.

Full Transcript

Module services Describes the various handlers used when working with Configured Commerce. Suggest Edits The main purpose of services within a module is to operate the handler factory. This is where the Chain of Responsibility is executed. The context is required on all service calls....

Module services Describes the various handlers used when working with Configured Commerce. Suggest Edits The main purpose of services within a module is to operate the handler factory. This is where the Chain of Responsibility is executed. The context is required on all service calls. Module handlers At its core, Configured Commerce handlers are responsible for the heavy lifting of objects and processing the business logic. Handlers derive from HandlerBase which uses generics to leverage the Parameter Object and Result Object pattern. The platform dynamically loads the handlers using the DependencyName. This attribute is required when replacing existing handlers or adding new handlers to the chain of responsibility. The Order property on a handler represents the order in the each handler is executed in the chain. The lower the order number the higher precedence in which that handler is executed. The default order for all Configured Commerce handlers is 500. Handlers will always override the Execute method which will then define all of the core business logic of that handler. Add a handler The following considerations need to be made when adding handlers to the chain. By adding a handler to the chain of responsibly the base code will execute and then execute the next handler in the chain. Unless of course, if the order has been set lower than 500. Make sure to include the DependencyName attribute on all handlers. The name must be unique if the intent is to add a new handler to the chain however if should be the same as the base handler if the desired result is to override the base handler. Breaking the handler chain can be done one of two ways: By returning the result instead of calling this.NextHandler.Execute() By setting the next handler to be a NullHandler o For example, this.RegisterNext(new NullHandler(TParameter, TResult>()) Override a handler The following considerations need to be made when overriding handlers. The recommendation is to always add a handler to the chain unless the base functionality has to change for a given implementation. Set the DependencyName attribute on the handler class to the same name as the handler that you would like to override. The reason for naming it the same name as the base handler is that if you do not set the same name then the platform interprets two instances of the same handler. One with any changes that have been made and the other that is the base handler. When multiple handlers with the same DependencyName are registered, the one with the lower dependency order will be used by the platform. Those that are in the namespace that starts with "Insite" are registered with an order of 999. Handlers where the namespace does not start with "Insite" are registered with an order of 500. Dependency order can be explicitly set by adding the DependencyOrder attribute on the handler. Pass data to and from handlers Every REST call can accept a dictionary of data in the properties field. This is defined on BaseModel so any methods which takes objects derived from it will expose a properties field in typescript. Other rest calls which pass query string data also implicitly accept a properties dictionary. For example to pass extra data to the /products method, add a query string value in the form &properties={'extradata':'test','extradata2':'test2' } These property values will be automatically passed through to the handlers in the Properties dictionary on the ParameterBase derived service parameter object. I t is possible to send custom data on the query string as any arbitrary parameter and pick these up in a custom mapper. However, this requires custom code and overrides and should be avoided whenever possible in order to ease upgrades. In addition, many of the resulting objects that have child objects (usually in collections) will automatically have the Properties dictionary on each child object populated with the properties data from the corresponding service result child object. For example. WishListModel has a child collection of WishListLineModel objects with a Properties collection on each one and these will automatically get the properties dictionaries from the WishListLineResult objects created by the handler. To return arbitrary data from a rest call, put data in the Properties collection with Properties. Add and it will be automatically returned to the client. To pass back complex objects, use the AddObjectToResultProperties method in HandlerBase to get the correct serialization this.AddObjectToResultProperties(result, "extradata", new { Something = "test", SomethinglElse = "test2" }); On the client deserialize these complex objects with JSON.parse var extradata = JSON.parse(data.properties["extradata"]); For more handler details, see the Work with handlers article. Use APIs with promotions and discounts Describes how to apply overrides to accomplish two promotion-related scenarios. Retrieve the promotions that are applied to a given line item in the order. Provide information about which promotions were applied to the order AND also report which promotions were order level vs. order line level. The following table shows the default behavior of the APIs related to promotions: Return a collection of GET /api/v1/carts/{cartId}/promotions promotions applied to a specific shopping cart POS Apply promotions to a specific /api/v1/carts/{cartId}/promotions T shopping cart /api/v1/carts/{cartId}/promotions/{prom Return a single promotion for a GET otionId} specific shopping cart Retrieving the Cart Line Level Data Create a custom mapper class that inherits from the GetCartLineMapper base class. Override the MapResult method. Be sure to call the base method before doing any work. The GetCartLineResult object is passed into the MapResult method. This object contains the CartLine object as a property, which contains a PromotionResult property and a Promotion property. Add any additional information you need to the Properties collection of the GetCartLineModel object. The following mapper adds the Promotion "Name" to the Properties collection. C# public class CustomGetCartLineMapper : GetCartLineMapper { public CustomGetCartLineMapper(ICurrencyFormatProvider currencyFormatProvider, IObjectToObjectMapper objectToObjectMapper, IUrlHelper urlHelper, IRouteDataProvider routeDataProvider) : base(currencyFormatProvider, objectToObjectMapper, urlHelper, routeDataProvider) { } public override CartLineModel MapResult(GetCartLineResult serviceResult, HttpRequestMessage request) { var result = base.MapResult(serviceResult, request); result.Properties.Add("promotionName", serviceResult.CartLine.PromotionResult?.Promotion.Name); return result; } } The image below shows the new, updated response with the "promotionName" custom property. Using the Promotions API Create a custom mapper class, but this time inherit from the GetPromotionMapper base class. Again, override the MapResult method and be sure to call the base method before doing any work. In this method, you can access the cartid from the incoming request. C# var cartId = request.GetRouteData().Values["cartid"].ToString(); Using the cartid you can access the current cart. Then, just like before, you can add any additional information you need to the Properties collection of the PromotionModel. The following custom mapper adds the matching "orderLineId" to the promotion response. This indicates that the promotion applies to the cart line level. If this custom property does not exist in the response, this indicates that the promotion applies to the cart level. C# public class CustomGetPromotionMapper : GetPromotionMapper { protected readonly ICartService CartService; public CustomGetPromotionMapper(IObjectToObjectMapper objectToObjectMapper, IUrlHelper urlHelper, ICartService cartService) : base(objectToObjectMapper, urlHelper) { this.CartService = cartService; } public override PromotionModel MapResult(GetPromotionResult serviceResult, HttpRequestMessage request) { var result = base.MapResult(serviceResult, request); var cartId = request.GetRouteData().Values["cartid"].ToString(); var cart = this.CartService.GetCart(new GetCartParameter { CartId = cartId.ToCurrentOrGuid(), GetCartLineResults = true }); foreach (var cartLine in cart.CartLineResults.Select(o => o.CartLine)) { if (cartLine.PromotionResult?.Promotion.Id == serviceResult.Promotion.Id) { result.Properties.Add("orderLineId", cartLine.Id.ToString()); } } return result; } } This solution is very similar to the solution used in Configured Commerce 4.3 to indicate promotions at the order line or order level. The image below shows the new, updated response with the "promotionName" custom property. The endpoint /api/v1/carts/current returns order line data alongside the promotion data. This can help determine which promotions are applied at the order or order line level. The image below shows a response with the both a cart and order line level promotion applied. The first promotion object contains an "orderLineId" property that has a null value. This indicates that the promotion was applied at the order level. In the second promotion object, the "orderLineId" property contains the id of the matching order line to which the promotion was applied.

Use Quizgecko on...
Browser
Browser