Sitecore Commerce Composer – Important tips for using this powerful business tool

Sitecore commerce composer is a really powerful tool to extend the Commerce entities. Recently working on a project I used them quite extensively. Of course, you can still write the code to do the same but the Composer would save you a lot of time.

Brief Overview: 

The composer is just another business tool of Sitecore Commerce which is used to create Templates using a user-friendly UI. It’s really intuitive. You can read more about it here if you haven’t started using it.

composer

A composer template is a collection of properties. Once you create a template using composer, you can link it to different entities or item definitions to extend them.

 

Few things you should consider before using Composer templates in your projects.

  1. Properties Are Required: All the properties that you create would be marked as Required by Sitecore Commerce. This means the content authors would need to fill in the values of all the properties in order to save the changes. I am on version 9.1 of Sitecore Commerce, Sitecore might have already provided this functionality to mark them optional or required.
  2. Importing to Different Environments: Composer templates’ synchronization to different environments would need additional effort. When you create a template, you would want it to be part of your code repository just like Sitecore templates. However, OOTB this functionality is not available. Hopefully, the future releases of Sitecore would have this facility. For now, you can create your own APIs to Export and Import them as Json to different environments. There is a great article on this which explains how to do, please have a look.
  3. Composer Property Value Might Not Reflect In Content Editor: If you are creating a template and linking it to your Category entity, you might come across this issue.  I came across this in version 9.1. When I was trying to open Content Editor, I saw the value for Custome Composer Property was empty.After digging in a little, I saw the following error being logged in Sitecore CM.
    19:17:31 ERROR There was an error in GetItemFields. ItemDefinition ID: {57806AD0-50FE-18B8-84F7-FA3DB6796BE8} 
    Template ID: {74C83509-A715-4C79-895F-2D5B937DA72C}.
    Error StackTrace: at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName)
     at Sitecore.Data.FieldList.Add(ID fieldId, String value)
     at Sitecore.Commerce.Engine.Connect.DataProvider.ReadOnlyCatalogDataProvider.GetItemFields(ItemDefinition itemDefinition, 
    VersionUri versionUri, CallContext context)

     

    If you look at the accepted answer on StackExchange, the solution would just avoid this error being logged to CM. But actual values would still not appear in Content Editor. So I decided to look into a proper fix as those values are important for our Content Author to see inside Content Editor.

    What we found at the end, GetCategoryBlock  in IGetCategoryConnectPipeline   is not using RawValue as Value in response to GetCategory command. So I had to replace this block with my custom block.
    Created a block named GetCategoryBlock in my Catalog feature.

        public class GetCategoryBlock : PipelineBlock<GetCategoryArgument, Category, CommercePipelineExecutionContext>
        {
            private readonly IFindEntityPipeline _getEntityPipeline;
    
            public GetCategoryBlock(IFindEntityPipeline getEntityPipeline) : base((string)null)
            {
                _getEntityPipeline = getEntityPipeline;
            }
    
            public override async Task<Category> Run(GetCategoryArgument arg, CommercePipelineExecutionContext context)
            {
                Condition.Requires(arg).IsNotNull(base.Name + ": The argument can not be null");
                FindEntityArgument findEntityArgument = new FindEntityArgument(typeof(Category), arg.CategoryId.EnsurePrefix(CommerceEntity.IdPrefix<Category>()));
    
                findEntityArgument.TrySetVersion(context.CommerceContext);
    
                var result = ((await _getEntityPipeline.Run(findEntityArgument, context).ConfigureAwait(continueOnCapturedContext: false)) as Category);
                if (result != null)
                {
                    SetViewPropertiesValue(result, context);
                    return result;
                }
                context.Abort(
                    await context.CommerceContext.AddMessage(context.GetPolicy<KnownResultCodes>().Error, "EntityNotFound", new object[1]
                    {
                      arg.CategoryId
                    },
                   $"{Name}: Entity {arg.CategoryId} was not found.").ConfigureAwait(continueOnCapturedContext: false), context);
                return null;
            }
    
            private void SetViewPropertiesValue(Category category, CommercePipelineExecutionContext context)
            {
                foreach (var composerTemplateViewsComponent in category.GetComponent<ComposerTemplateViewsComponent>().Views)
                {
                    var composerView = category.GetComposerView(composerTemplateViewsComponent.Key);
                    if (composerView != null)
                    {
                        foreach (var viewProperty in composerView.Properties)
                        {
                            var canbeProssed = (viewProperty != null && string.IsNullOrEmpty(viewProperty.Value));
                            if (canbeProssed)
                            {
                                context.Logger.LogInformation($"{Name}: Setting value of ViewProperty: {viewProperty.Name}");
                                viewProperty.SetValueFromRawValue();
                            }
                        }
                    }
                }
            }
        }

    All the code in this block is copied from Sitecore dll except the last line which is striked through. This makes the RawValue to become available as Value in the response and then the CatalogDataProvider stops throwing the exception we saw above.

    After creating this block I had to configure it in ConfigureSitecore

        public class ConfigureSitecore : IConfigureSitecore
        {
    
            public void ConfigureServices(IServiceCollection services)
            {
                var assembly = Assembly.GetExecutingAssembly();
                services.RegisterAllPipelineBlocks(assembly);
    
                services.Sitecore().Pipelines(config => config
                   .ConfigurePipeline<IGetCategoryConnectPipeline>(pipeline =>
                    {
                        pipeline.Replace<GetCategoryBlock, Pipelines.GetCategoryBlock>();
                    })
    
               );
    
                services.RegisterAllCommands(assembly);
            }
        }

    After deploying these changes, I can now see the actual values being reflected in Custom properties created using composer. Content editore is showing those values and content authors are happy.

Leave a Reply