Sitecore XC – Everything you have to know to work with media items

This time I would like to share some insights on the topic, of how to use Stecore XP media library items within commerce engine entities. Maybe most of you, who already used Sitecore XC, have already seen the Images component of sellable items and variants.2020-01-18 20_49_56-Alnatura - Microsoft Visual Studio (Administrator)

Of course I already played around with that and used images of example habitat and adventure works. But recently I was faced the customer requirement, that customer wanted to map own images from Sitecore XP media library to previously imported products. And here the story begins…

As mentioned above I already around a bit with images on demo products. Below you can see the steps to map an image to a sellable item in demo environment

2020-01-18 20_53_33-Alnatura - Microsoft Visual Studio (Administrator)

2020-01-18 20_54_16-Studio X Over-the-Ear Wireless Headphones

As you can see, you can just use some kind of search to find the proper image, chose it and easily set it.

So my first step was to go into a custom product, within a custom environment and just try to add my images from media library there, as I did with habitat or adventure works products.

2020-01-18 21_09_31-Winterschokolade

One thing I realized very quick was, that I in deed find images, but not my ones.

2020-01-18 21_13_31-Winterschokolade

As you can see above, on my sellable item within my own environment, I just saw e.g. backpack stuff instead of all my food images I wanted to see.

2020-01-18 21_15_16-Winterschokolade

And once I entered the exact media item name I was searching for, I just got “No results” screen.  But I know my image was added correctly to the media item

2020-01-18 21_14_55-Desktop

What I did on XP side, was, that I just added a folder common, beside the folder adventure works and habitat and added all the product images I wanted to use later on there.

2020-01-18 20_51_19-Desktop

But obviously that was not enough. For some reasons my own new images were not found. So let’s start looking, what really happens on search call to understand, where the problem my be.

2020-01-18 21_17_03-Winterschokolade

As you can see, I started to have a look at the call, which was executed on search click. You can see, that a DoUIAction() call is triggered with some special parameters. One parameter is exactly, what we were looking for, the “Action: SearchMediaItems”. So we know now, that there must be some action called SearchMediaItems within commerce engine call.

So next step was a familiar one. I used dot peek to have a look at commerce engine dlls to find that function. To be sure I really find something, I just added all commerce engine dlls to dot peek and just searched for SearchMediaItems.

2020-01-18 21_22_09-JetBrains dotPeek

The results can be seen above. Very successful I would say. After looking into some of these classes, I finally found the interesting one, SearchMediaItemsBlock.cs. In the end this class is being used for searching media items in XP from XC side.2020-01-18 21_23_48-JetBrains dotPeek

The decompiled code of that class now tells us even more. Sitecore uses some basic SitecoreConnectionManager class to handle all kind of search and retrieving functionality for items from XP. And within that manager class we have also a function called SearchMediaItemsAsync, which is in the end used.2020-01-18 21_25_06-JetBrains dotPeek

Having a look now into that class into our function, we finally get the information we needed. Sitecore uses predefined item API call with some parameters to access XP. To do that, it also uses an API Key ODataItemServiceApiKey, which is stored within a policy. So let’s have a look into that policy. 2020-01-18 21_31_06-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

Of course now we can have a look into our environments to maybe find that property. And et voilà there really is a setting as you can see above. You can see some ItemID there. Now next step was to use that ItemID to find the corresponding item.

2020-01-18 21_33_15-Desktop

 And there was a Item. Within Sitecore XP in /system/settings/services/API keys there is an item which handles basic settings for searching. It tells e.g. which database should be used and defines some basic filters.

AND FINALLY the highlighted area shows us, why we did not retrieve any of our own images. There is a filter, that limits image retrieval to “images/habitat” and “images/adventure works”.2020-01-18 21_34_24-Desktop

So now we also know the steps how to fix it. And of course I don’t want to fix it with manipulating OOTB Items from Sitecore. So I just created a new API Key Item with some custom Search Filter setting. As highlighted above I just want to include the folder “images/common”. Everything else is more or less the same for now.2020-01-18 21_36_43-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

Next step after creating the new Api Key item, we also have to add it to our environment file. Above you can see, the comparisons of the OOTB and my custom environment. The nice thing about that approach of adding a new api key for my new environment is, that both environments now can search for media items completely decoupled from each others.2020-01-18 22_33_18-Winterschokolade.png

So, if we now execute the search again from the beginning, we finally also get the result, we wanted to have.

Of course it is nice, that we can now link our own images to our own products, but the initial requirement is not really fulfilled. The customer does not want to manually link all the images after an image import. So there has to be some kind of automatism.

I decided to integrate image linking to my standard product importer pipeline, which you already know from earlier blog posts. I hust added a new function called EditImageData.

2020-01-18 21_42_50-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

The code for that small function can be seen above. We just use the information we got from dot peek and use the SitecoreConnectionManager also within our code. One special thing on our images is, that the image name is exactly the so called ean (gtin).

Once we got the proper image, we just retrieve the standard ImagesComponent and add the Image Sitecore ID to the Images list. And that’s it. After doing that and re importing all our products, all our products finally now got an image fully automatically.

Sitecore XC – Finally price card snapshots also have an end date

Sitecore XC – Finally price card snapshots also have an end date

In this blog post I would like to present you an implementation for an issue we all might have suffered from. But let us start in the beginning and shortly discuss how pricing works in Sitecore XC in general.

How does pricing work in Sitecore XC?

The most basic way to set a price in Sitecore XC is, that we just set a price on the sellable item directly as List Price. This price can be of course set per currency like seen in the example below.

2019-11-13 07_54_17-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Because such a price setting is quiet inflexible Sitecore has build some piece of logic on top of that. The core principle of that mechanism is so define a price based on currency, date, and quantity, which is far more flexible, than just set one single price per sellable item.

This can be edited by an editor under the Pricing Application in the Business Tools of Sitecore XC.

2019-11-13 09_41_17-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung.png

Once you entered this application you are able to define a so called Pricebook.

2019-11-13 07_55_15-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Such a Pricebook can be linked to one or more catalogs and is itself just a container for more specific price settings, the so called Pricecards.

2019-11-13 09_05_38-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Such a Pricecard then be directly applied on a sellable item to a sellable item via the Pricing field.

2019-11-13 10_18_36-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Now the interesting part starts, where real price settings come into play. Pricecards themselves are again just containers for so called Snapshots.

2019-11-13 09_09_08-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

A snapshot itself then contains the information of a start date, when this specific snapshot should be applied, a tiered price per currency, which means you can define how much one single sellable item costs, in case a customer has X items in cart. And last but not least, you are able to tag the snapshot to define to which sellable items this specific snapshot should be applied.

2019-11-13 09_09_44-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Therefore you see, that this pricing mechanism is far more flexible and powerful, than just adding a simple list price to a sellable item directly.

2019-11-13 09_10_05-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Known limitations of snapshots

This overall pricing mechanism is really great and powerful. But when talking to clients and collecting all their wishes and requirements, one feature was wished very often, which is currently not possible to fulfill with the OOTB capabilities. The client always asks exactly the following question

How can I set an end date for the snapshots?

Right now the behavior of snapshots is very rough described like this:

  • Once a Pricecard is linked correctly to a product, Sitecore grabs all the snapshots and checks if the start dates of the snapshots are greater than the current date time.
  • Then Sitecore sorts all the snapshots descending
  • Finally Sitecore just grabs the first element from that list and evaluates its pricing information

This means, than if the start date of multiple snapshots have been reached and therefore multiple snapshots would be valid, Sitecore takes the latest one to ensure, that at a specific time just one single snapshot is valid.

So for a content author this means, he has to create a snapshot to determine a specific pricing. Once he wants to change that pricing for a specific period of time, e.g. to offer a special price on a special day or a special time span, he would have to create a new snapshot with the new pricing information and and the proper start date. So now the issue starts. Because the author cannot set a specific end date, this price would be valid “forever”. And because we do not want to have that price forever, the author now has to create another snapshot with the old pricing again and the proper start date. This start date has then to be the date, when the special pricing should end. So instead of just creating two snapshots with proper start and end date, the author has to workaround that and create a third helper snapshot to restore old pricing again.

Of course this works, but to be honest is quiet unwieldy. We also asked Sitecore about the possiblity to set an end date on snapshots. The answer was, that they know about that missing feature, and that they are working on implementing that for a later version. But when exactly that feature is implemented by Sitecore themselves is not officially known.

The recommendation of Sitecore then was to implement such a feature on our own.

tenor

How to get rid of this limitation?

The solution is as simple as it might sound. We just implement the possibility to set and end date on snapshots, save that properly and then extend the snapshot evaluation while retrieving pricing information. Sounds easy, right? And yes it is, if you know where to hook in. Let’s have a rough overview of all todos:

  1. Extend the form to enter an end date in addition to the start date
  2. Extend the snapshot to also hold information about end date
  3. Extend the action, which saves the start date to the snapshot to also save the end date to the snapshot
  4. Extend the view to display also the end date in addition to the start date
  5. Extend sellable item and variation sell price calculation to respect the new end date field

Let us start with the task to extend to input form, where an editor can specify the Begin Date. Of course now we would like the user also to be able to specify an End Date the same way.

How do we achieve that?

Quiet simple! I knew this piece of code I need should be in the Pricing Plugin provided by Sitecore. Therefore I simply reflectored that dll to find something close to edit / show snapshot details. After searching a while, I also found the right class GetPriceSnapshotDetailsViewBlock, which you can see below.

2019-11-14 09_43_34-JetBrains dotPeek

So I just took that piece of code and added the new field End Date in the same way Start Date was added. You will see, that I slightly modified the OOTB code a bit also for the standard fields. The reasons for that were, that I wanted to change the date time format a bit when displaying the fields and that I wanted to include another field just in edit mode. But more details later on.

After adding all the stuff properly the new output of the dialog was the following.

2019-11-13 07_40_57-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

So next step is of course, that once an editor sets a proper End Date, we also want this date to be stored somehow on the Snapshot.

To do this, we have to implement a custom DoAction block, which is responsible for taking the properties entered in edit mode and saving them to the entities or components. Again I crawled in the dll and found the proper class DoActionAddPriceSnapshotBlock.

2019-11-14 09_56_12-JetBrains dotPeek.png

So again, I created my own version of that class and added piece of logic to also extract the End Date. The only problem, which now came up was, the issue, how the values were stored on the price card snapshot. Sitecore used a custom command for that. Unfortunately the parameter of that command did not really fit to the new situation of having now also an end date. As you can see, it just accepted a begin date. Of course there were also other overloads of that call, but more or less, they did not fit as well or would lead to a long rat tail I would have to handle. Therefore I decided to just create my own command, based on the given one, and extend also this piece of code to handle End Date properly. The basic implementation can be seen below.

2019-11-14 10_00_09-JetBrains dotPeek.png

My custom implementation is really nothing more, than a command with another endpoint also accepting the parameter End Date and a piece of logic to include the End Date into the snapshot.

2019-11-14 10_02_03-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung.png

Because we are in a an environment, where we prefer composition over inheritance, I had to create a new component, which in the end holds the End Date property.

2019-11-14 10_08_46-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

If Sitecore implements that feature on their own, I am pretty sure, they will just add another property directly in the Snapshot component, like it was done with Start Date. But in the end it does not matter if the End Date property is located directly in the Snapshot component or in another child component. You just have to know where it is for later usage. But that’s already all the magic to set the End Date Property from the point the editor enters it to the point it is part of the Snapshot. Below you can now the the result, after saving.

Note: Here you can already see, that I changed the output date format also a bit to also display time and not only date, which makes it much easier for editors to set Start and End Date minute-by-minute.

2019-11-13 07_43_46-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Now let’s come to the more interesting part of the implementation. After we have successfully set the End Date we of course also have to evaluate it properly. To do that, I had to find out, where the Snapshots are evaluated at all to also hook in there.

After some time crawling through the dll, I found exactly, what I needed. CalculateSellableItemSellPriceBlock for sellable items sell price calculation and CalculateVariationsSellPriceBlock for variants of sellable items sell price calculation. Within these classes you can see, that the calculation included functions like ResolveSnapshotByCard and ResolveSnapshotByTags, which in the end use functions like FilterPriceSnapshotsByDate and FilterPriceSnapshotsByTags.

2019-11-14 10_16_22-JetBrains dotPeek

2019-11-14 10_16_41-JetBrains dotPeek

In the screenshots above I highlighted exact the location in code, where the snapshot is evaluated. The only thing now we have to do is, that we have to also include the new End Date in that calculation. For demonstration reason I chose the approach to create new classes, one for sellable item sell price calculation and one for variations, the inherited from the existing once and just overwrite the virtual functions to include our new evaluation.

In the End the functions in the new class look like this.

2019-11-14 10_20_56-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

2019-11-14 10_22_41-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

Just a simple evaluation if the End Date has not been reached.

Note: You can see here already an extension I made for End Date evaluation, which I describe in the end in more Detail

Now let’s see all that in action. I created a Pricecard called Demo and attached it to my demo product.

2019-11-13 11_16_20-Rechner

Then I created two snapshots on it. One snapshot should be valid from now to some date far in the future (or has no End Date). And one snapshot should only be valid for just a small time span, in this case just a few minutes. After that, the price of the snapshot before, should automatically take over again, without the need to create a third snapshot. The two snapshots can be seen below.

2019-11-13 11_21_48-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

2019-11-13 11_25_07-Rechner

2019-11-13 11_25_13-Rechner

Now let’s put that sellable item into the cart and see what happens over the time.

We start with a time after the start date of the “normal” price and before the start date of the “special” price.

2019-11-13 11_24_26-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung.png

The result is exactly, what we wanted to have. Because the first snapshot is valid and the second not, Sitecore uses the price from the first snapshot to calculate sell price.

Now let’s call the cart a bit later. The time now falls into the start date of the first AND the second snapshot and also is located within the first and second snapshot end date. From Sitecore OOTB logic, it now should take the snapshot, which start date is closer to the current date time.

2019-11-13 11_41_34-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung.png

And this is also the case, like seen in the screen above. Sitecore uses now our special price instead of the normal price, when we call the cart again.

Last but not least we now want to test the really interesting case. What happens now if we call the cart at a time, where both snapshot start dates are matching, but the end date has expired. We would expect now, that because the end date of the special price snapshot has expired, Sitecore automatically chooses the normal price snapshot again according to the new implementation.

2019-11-13 12_05_58-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung.png

I know the sceeen above looks a bit wild, but displays exactly what happens. When we call the cart again at a time after the end time of the special price, Sitecore really takes the normal price again. As you can see the start and end date of the normal price match. And the start date of the special price matches. But because the end date, displayed in red, does not match, Sitecore does not use that snapshot, like it would do in standard implementation, but uses the first snapshot and uses its price as a valid sell price.

Note: Of course we could come into the situation, where no snapshot is valid, because all the end dates would be reached. But that is also no problem. In such scenarios, Sitecore’s own fallback takes over and uses as fallback to the price card logic simply the list price directly set on sellable item. But of course, if this price would not exist you are screwed and the sellable item would not be sellable anymore and would lead to an error, if trying to add to cart.

Now let me finally show you one little extension I already made, while implementing End Date handling. I believe, that there are scenarios, where some basic price really should have just a start date and no end date. Because the OOTB Date Time field always needs a value to be set, I worked around that and created in addition a small checkbox to let the editor decide, whether the really wants to set an end date. In case he want, he just checks the checkbox and everything is like described above.

2019-11-13 12_16_15-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

In case he does not check the checkbox, the DoAction block I created just ignores, what was transmitted in End Date field and simply writes DatetimeOffset.MinValue into the End Date property. This then can later on be checked and treated e.g. like an non existing value in display, like seen below.

2019-11-13 12_16_59-ALNATURA-DEV - 51.141.30.11_3389 - Remotedesktopverbindung

And as seen before in the sell price calculation we can also react on that specific value to avoid End Date evaluation. This way, in case you do not set an End Date for a snapshot it acts exactly as it did OOTB before.

Conclusion

Within this blog post I showed you easy it can be in general to implemented some missing feature in Sitecore XC. I showed how to start, where to look any how to hook in correctly at the specific locations, which need to be changed.

Within this special change we created a feature, which allows the editor to set additionally to the start date also an end date for price card snapshots to be even more flexible in creating complex pricing scenarios.

As always the full demo code can be found in a repository on my github and as always this code is not production ready and should be used used as an entry point or for demonstration reason.

https://github.com/Chris1415/Plugin.Sample.Pricing.Pricecards

Sitecore XC – Usage of Composer Templates vs. Entity Components

Sitecore XC – Usage of Composer Templates vs. Entity Components

This time I would like to share some thoughts on the topic of how to extend your commerce entites.

If you are already working with Sitecore XC you will most probably know, that commerce entities in general are extended via components. So if e.g. your sellable item entity needs more properties, like nutrients or allergies for food products you would just create a new component and “attach” it to the sellable item.

From version 9 on Sitecore introduced a new application Composer, with which it should be very easy to extend all kind of OOTB commerce entities. If you would like to know more about Composer in general, I would recommend you to read one of my articles about Composer. In general with this Composer application you can create new custom views with all kind of custom properties. You can then link that new view to any of OOTB entities like sellable item, customer, catalog, category etc. And if you now e.g. use Merchandise tool to see the sellable item and edit it, you will directly also recognize all the new custom views and properties created via Composer and linked to that entity. So without writing a single line of code you are able to completely customize and individualize all you entities and maintain them directly.

So the question at this point would be:

Why should you not use that fancy feature?

The answer of course is not so easy. Let us have a closer look at Composer and how to really work with it in a real world scenario.

Composer in real world

In one of my Sitecore XC projects, we were faced the task to extend standard sellable item with quiet a lot new properties. The version we started was at this time was 9.1.  And because of that, the first approach which came into my mind was to use Composer. I already knew about one disadvantage of using Composer, which I directly wanted to work around. If you create a new composer template within e.g. your local instance, it might work perfectly fine. But the trouble already starts, when you try, to bring that newly created composer template to other DEV instances, testing or production. There is currently no OOTB way of deploying such templates to other environments. You can just create these templates and link them to other entities. This would later on lead to massively different instances, which is not track- or maintainable anymore. Because of that, the community already thought about some workarounds to overcome that issue, so that compser templates are deployable again.

For my solution I was building, I decided to use Code First Approach. Once you created a new template, you would, as usual, check in this file into your repository, where it is getting versioned and shared among other DEVs instances and later on in release management also on other environments like TEST or PROD fully automated.

And at first everything went well so far. I was able to programmatically create all of my new custom views and link them correctly to my sellable item entities. I was also able see and edit these new views and properties in entity view. OK, to be 100% honest, I was able to create and maintain all of my custom views for sellable item entity. But what about variants of sellable items? You might now, that variants of sellable items are no entities. Variants themselves are handled as components, which extend standard sellable items. If your now try to extend properties of sellable item variants, instead of sellable item, you will quickly find out, that this is right now not possible at all. At the moment, you just can link your composer templates to entities.

2019-09-08 14_20_33-Test.png

This means, that you cannot use composer to extend sellable item variants. To extend sellable item variants, you have to use plain, “old” components and do it on your own.

Next step was now to import product data from some web service to Sitecore. And here the odyssey started. I struggled a lot with adding data to composer generated custom views, but because if some reasons. For some of my issues I also created Sitecore support ticket. And one of the ticket also pointed out to be a bug in the system. But let me explain in detail, so you don’t have to go through all of that on your own.

It all started with the question of, how to access programmatically composer generated custom views. Of course Sitecore XC offers calls for creating, editing and retrieving sellable items in general. Now, based on the assumption, we correctly created or retrieved our sellable item entity, where do we find our custom views, so we can edit them? unfortunately the answer is not that easy, so I just provide you some code, which I wrote for my case, which does the job.

2019-09-07 19_36_14-_new 5 - Notepad++

  1. Grab the sellable item
  2. Use GetEntityViewCommand to grab sellable items entity view
  3. Find the view you would like to edit
  4. Grab the EditView of that specific view
  5. Iterate through all the properties you want to edit and edit them
  6. Use DoActionCommand to save all the changes

As you can already see, such an approach is more complicated than you would usually edit a sellable item with components. In addition with more and more custom views you edit, the performance of the overall import process is getting worse and worse. And not only that. I encountered the strange behavior, that from time to time the properties were not correctly added or edited for the custom views. Some times it worked and some times it just did nothing.  But OK. For now I accepted that, and created a support ticket to be sure, it is not a bug or something like that.

And after implementing the basic code for importing everything, I started testing various scenarios like, edit existing item, create item and add data, delete the item and recreate it, and also delete composer template and recreate that, to be sure, that all these scenarios work in other environments without any problems. But while testing around, I encountered another strange behavior. The situation was, that I created a sellable item with custom views. I added some data to that item and filled the custom view. Now I wanted to delete the composer template, recreate it and import the data again to the sellable item. The problem now was, even after deleting the composer template, the filled custom views were still present on the sellable item. And even worse, after recreating the composer template again, the custom view was present on the sellable item twice. One time filled from earlier test and one time new fresh empty one. I was not able to remove the custom view, until I completely deleted the current sellable item and recreate it. After doing that, it again just had all the custom views just one time. Because that really was a strange issue, I also created a support ticket for that behavior.

Support Tickets with topic Compser

  • Out of support scope – Properly fill composer properties programmatically
  • 278078 – Sitecore XC: Composer generated views are displayed multiple times on sellable items

2019-07-21 16_40_32-.png

 

To be completely honest, I created some more support tickets, than these two. But all the other tickets had some other issues while implementing an importer. And of course in future blog post I will keep you informed about newly found and registered bugs. For now we just have a look at these two tickets about composer. The first one, was unfortunately out of scope as part of Sitecore support. The problem was, that basically the code works, but just did not add values from time to time. To resolve that issue, I was asked to directly contact  regional Sitecore representative to get help from the Professional Services team.

Let’s have a look in the meantime at the other issue. After some discussions the issue was registered as bug under the reference number 278078. So right now, you might get trouble when you use composer templates on your entities, fill them, delete the templates and recreate them. And right now there is no easy way of overcoming that issue, other than rewriting some really basic composer code.

So while struggling with all these kinds of issues, I was able to talk also with an architect from Sitecore. You remember, that this was the advice of Sitecore support I followed now. After a short discussion it pointed out, that composer might not be the best choice considering the current situation and how I use it.

And I have to admit, that I fully share that opinion after I really tried to integrate that new feature into the development workflow. It is still a quiet new feature with a few weaknesses, which have to be overcome first to unleash it’s full potential. But if we do not use Composer to extend all our sellable items, the question now is:

What can we use instead of using Composer?

Back to the roots – Why not using just components directly?

You really don’t have to use Composer to extend any kind of commerce entity. Before Composer was introduced, there was a pretty standard way, of how you can extend all kind of commerce entities on your own. You just created a new component, with all your properties you wanted the entity to have.

2019-09-08 14_27_39-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

Sitecore also has some helper functionality OOTB, with which you can easily get, create and set custom components on entities to maintain your custom components.

2019-09-08 14_28_38-Alnatura.Experience.Commerce - Microsoft Visual Studio  (Administrator).png

2019-09-08 14_29_40-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

In the end, after getting the component and setting your properties, you just have to persist the entity again to the database, and that’s it.

2019-09-08 14_30_20-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

So for an importer process, it’s also nothing more, than this. Instead of finding custom, edit views, adding properties there and calling a magic DoActionCommand in the background for each view you changed, you just grab the sellable item, grab the component with the OOTB helper, set the properties and in the end, as usual, persist entity. And I guess this might be the plainest, easiest and most performant way of editing custom properties of your entities.

I already tested this approach with 8 custom components with more than 80 custom properties, which extended the standard sellable item entity, and the importer was definitely not that slowly, than it was, when using the composer based approach.

2019-09-08 13_12_51-Support.pptx - PowerPoint.png

2019-09-08 14_39_30-Alnatura.Experience.Commerce - Microsoft Visual Studio (Administrator)

But I would say, we have one big disadvantage of using this approach. When using Composer to create templates, these templates and respective the custom views, which can be seen in the screenshot above, are directly visible and editable on the linked entities. This benefit, we do not have, when creating and adding components directly to our entities. To see and edit the new properties of our entities, we have to implement some further logic, which extends the standard entity view, in our case the sellable item entity view, with all our new components. And with exact this way we also can extend our sellable item variants. If you would like to know more, about how to extend Business Tools, and in this case entity views to see and edit custom properties, I would strongly recommend you, to read this article of me, where I describe in detail the process with the example of creating and extending the cart entity view.

So of course this produces a bit more work to get everything working, but I guess at the moment, for this scenario, I was faced to, it is still the better approach, because in the end, if something does not work as expected, you can completely customize or work around everything.

You might now remember the code from the beginning, where I showed, how to edit custom view of sellable item. I will now show you, how the code changed now in importer.

2019-09-08 14_33_34-Alnatura.Experience.Commerce - Microsoft Visual Studio  (Administrator).png

So you see, that this piece of code definitely became much easier, than before. Instead of duzend of lines of code for preparation and retrieval it is more or less just one line of code for retrieval and X lines for X properties to change.

Conclusion

Within this article you learned, that there are various ways of extending Sitecore XC entities with custom properties. You saw, that the newly introduced Composer application might look cool and in general has a lot of potential to make the life of authors and developers much easier. But right at the moment you clearly have to weigh well, if Composer is the right tool for your business needs and requirements. If not, don’t be afraid of using plain, old components directly. It might lead to more work in the beginning, but in the end it might safe you a lot more time.

Sitecore XC 9.1 – Working programmatically with SellableItems might lead to some exceptions on saving changes

This time I would like to share some nasty bug, which is currently present in the newest version of Sitecore XC with which I wasted a lot of time.

The issue appears, when you start working programmatically  with SellableItems. In my scenario I wrote some Pipeline block, which was response for taking some product information, and import these to Sitecore. For that I did in general the following things.

  1. Try to create sellable item with CreateSellableItemCommand
  2. If the result was null (Most likely the sellable item already exists)
    1. Try to grab the existing sellable item with GetSellableItemCommand
  3. If the result was NOT null
    1. Just use that newly created sellable item
  4. Make some changes e.g. edit list price via ListPricingPolicy
  5. In the end save the changes by using EditSellableItemCommand

So what happened now was the following:

  • On the first run, when the item was created, everything worked perfectly fine and was saved
  • BUT on the second and any further run, I got some strange error back, like seen below and nothing was saved 2019-07-15 08_26_15-Postman.png

Initially I had not idea, why this happens, because at this place I did not understand, why EditSellableItem would try to insert a new sellableitem to Database.

Because of that, I decided to as Sitecore Support about that. The result was, that this behavior was recognized as Bug with the reference number 280784.

The root cause of the issue is, that once you use GetSellableItemCommand (Or more directly usage of GetSellableItemPipeline) the result sellable item contains the property “isPersisted = false”, which means, once you try to use EditSellableItem with such an instance of a sellable item, Sitecore tries to persist / create that sellable item in Database.

The hotfix for now is quiet simple. To grab a sellable item correctly with the correct property set, we now just have to use basic GetEntityPipeline, which is much more generic and designed to grab any kind of entity. But of course after the usage of that Pipeline we can just cast the result CommerceEntity to SellableItem and everything works now as expected with EditSellableItem in the end.

Also here thanks to Sitecore Support, which helped me to find a quick solution.

Sitecore XC 9.1 – Edit decimal values in Business Tools might lead to inconsistent values in non-English environments

While working with the newest version of Sitecore XC, I now encountered some strange behavior, while using Business Tools, which I would like to share with you.

The behavior was the following.

  1. I navigated to my Inventory set
  2. I chose a random sellable item
  3. I entered the Edit View to change some values
  4. I entered 1.69 USD

But after the screen reloaded, I saw that Sitecore did not save 1.69 USD, but 169 USD.

2019-07-17 12_38_16-C__Users_Chris_Downloads_DecimalErrorBusinessTools.swf - Internet Explorer2019-07-17 12_38_26-C__Users_Chris_Downloads_DecimalErrorBusinessTools.swf - Internet Explorer

So it was obvious, that there is something wrong with parsing decimal values. Because I knew a similiar behavior already on older versions of Sitecore XC, I decided to ask Sitecore Support about the issue.

The response was, that the Issue was registered as Bug under the reference number 276950.

The reason, why this happened in my instance was, that the used IIS Apppool User, which is  a Windows user, just had all his regional Windows settings set to German instead of English.

20190716_095150

The solution for me now was the following

  1. Log in with the Windows user, used by Sitecore XC APP Pool
  2. Open Control Panel
  3. Navigate to Regional Settings “Control Panel -> Clock Language and Region -> Region”
  4. Set the Format to English (USA)2019-07-17 12_47_33-Zeit und Region
  5. Opened the Administration Tab at the top and and ensured that the Format setting is now English instead of German20190716_095315

After I did that, I resetted IIS and now everything wokred perfectly fine as expected

Note: This behavior is present in ALL decimal fields of Business Tools.

A big thanks to Sitecore Support for the quick fix.

Extend Sitecore XC Business Tools – The real meaning of data driven UI

Extend Sitecore XC Business Tools – The real meaning of data driven UI

This time I would like to demonstrate how easy it is to extend Sitecore XC Business Tools. When I am talking about Sitecore XC, I often tell the people, that one of the benefits of Business Tools is, that they are completely date driven. This weekend I really found out how powerful this benefit is in daily life work.

Within this blog post I will guide you through the process, of how to implement such an application in Business Tools with the example of my Carts application. The idea for that application came up, when I had a requirements workshop with some clients. There the wish came up to be able to see all active carts in the backend system to be able as customer service employee to help customers, who have problems with there carts. Because Sitecore XC does not provide such an application OOTB, I started playing around with that to create one.

A great starting point for me was to have a look at some example plugin implementation provided here

This github repo contains a lot of great examples, of how thing can be done in Sitecore XC. As you can see in the screen below I migrated the code to Sitecore XC 9.1, integrated that into my demo solution and had a deeper look at the implementation

2019-05-26 20_20_32-logs

In addition to these demo applications, you can also see the mentioned new Carts application.  But now maybe the question comes up

How can you create new Business Tools applications?

This is much easier than you might think now. Let me first show you all the pipelines you should know to also know when to use which one

  • IBizFxNavigationPipeline
  • IGetEntityViewPipeline
  • IFormatEntityViewPipeline
  • IDoActionPipeline

IBizFxNavigationPipeline: This pipeline is the starting point of the whole Business Tools application. Once you open Business Tools, in the end the application grabs all applications to show, based on the results given by this pipeline. If you know want to add a new application to Business Tools, you just have to create a new block and hook in into this pipeline.

2019-05-27 21_21_27-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

As you can see in the screen below, the block has EntityView as in- and output parameter. The only condition, which has to be add is, that this block should only react if the current view is ToolsNavigation view. That’s our start screen.

2019-05-27 21_17_46-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

Once we enter that screen, we just create a new view and add that view to the given entity view as a child view. The result after compiling that code is in deed already the screen above with the new application Carts showing up in Business Tools start screen. We just provided the data to display and the Business Tools application did all the rendering job for us.

IGetEntityViewPipeline: Now things are getting more and more interesting. What we now would like to do, is to click on that new application to show some kind of overview or details screen. The IGetEntityViewPipeline is always called, once any kind of entity view should be shown. It does not matter for which application or if the view is a master view, details view or dialog view (forms). Because of that the content of this pipeline is also quiet big, because in the end every single view to display is added there as a block. The differentiation, which view should be applied when is done by view name and action (if it is an action or not). This you will also see later on in more detail.

Let us first add a block, which renders our overview of our new Carts application. To achieve that, we just create a new view called DevOpsCartsDashboard as you can see below in more detail.

2019-05-27 21_30_15-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

You can again see the same structure for entity views as before. The first statement is a check, if we are on the correct entity view. In our case the name of the entity view was outsourced into the policy to avoid redundancy. The name of the view is the view name of the carts application we added earlier as child view. Once we click on the icon, we open the entity view with that name. And because we want to render new information / child views in that application, we check for that specific entity view name. Once we are in that view, we add more child views by using a special command for that, the ChildViewActiveCarts command. The benefit of using a command to render child views is, that in this way code becomes cleaner and more reusable.

2019-05-27 21_40_05-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

As you can see in the screen above, we just use the given entity view, the Carts application view and add new child views to that. Each child view will be in the end displayed as new single accordion elements. In our case, we create a new Active Carts child view to display all active carts know by the commerce engine. Of course after adding that child view to the entity view we have to also add some properties to display.

We first grab all carts from the engine for the current commerce context. Then we iterate through all the carts. For each cart we create a dedicated cart view with some properties of the cart to show. In the end we just add every single cart view to our previously added active carts view. This way we generate a new accordion Active Carts with various single entries, each representing a single cart. The screen below shows, how this overview page in the end looks like after compiling that code.

2019-05-26 20_26_38-Custom Dev Ops

You can see, that in my example I had two active carts, one created via Postman and one created via Storefront. And both carts appeared in the overview page. What you also might see is some small but powerful detail. The name of the carts are in addition links and they are linked to the detail pages of the corresponding cart entities. So when you click on the click e.g. of Cart01 you will be redirected to the Entity View of Carts for the specific cart Cart01. You achieve that by simply doing 2 little things in your code.

  • On the name property of the cart you just have to specify the UiType and set it to EntityLink
  • In the Cart view set the property ItemId to the entity ID of the cart you would like to link to

Now let’s come to the entity view of a cart. If we have not done anything, the whole view would just consist of some really basic information like seen below in the Summary tab.

2019-05-26 20_26_52-Summary

Of course these information are basically nice, and of course it is really nice, that we are able to display our entity in that easy way. But I guess, we would like to see more details of our current cart in that view. To display more and more information, we just again have to create a new block hook in into the IGetEntityViewPipeline pipeline and implement the new child views. Below you can see the beginning of the new class EntityViewCart.

2019-05-27 22_09_19-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

You can see, that I just check if an entity is applied to the entity view, which is in deed the case, if we render an entity detail view like carts, orders, customers etc. In addition I try to grab a cart and check if the cart is a valid one. In my case, just checked if create date and update date are the same. In case we are on a valid cart entity view, these values will defer, because the cart already was created earlier and had interactions afterwards. In case we are on a other entity view e.g. customer, the GetCartCommand would return a newly created cart. (and yes not null ^^ ) But this cart would have date created and date updated the same date, so I would skip these views. I guess there are smarter ways of grabbing and checking the cart entity view. But for my experiment this was sufficient enough to cover all possible cases, also that this view should not be applied on other entities. As you can see from the code I do some pre-processing before really adding some new content.

Because such an entity view contains a lot of information I decided to split the adding into smaller pieces and show you each piece step by step. Each piece is structured always the same way.

  • Grabbing necessary information
  • Creating a child view
  • Adding that child view to the overall entity view
  • Adding properties to the child view

One of the easier child views was the Totals view, where I just wanted to show information about all the totals values on a cart.

2019-05-27 22_11_53-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

2019-05-26 20_27_02-Summary

A little bit more difficult was the display of all applied adjustments. In this view, we had to ensure, that we display not just a single view with some properties, but a list of views each with some properties. The procedure is the same, than we had in the Carts application overview page by showing a list of active carts in a single view

2019-05-27 22_13_39-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

2019-05-26 20_27_10-Summary

Now next information to show are the line items added on the cart. And not only the line items, but also some more detailed information about each added line.

2019-05-27 22_17_30-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)2019-05-27 22_18_04-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

Above you can see the whole code for Line Item and product component child views. I decided to split line items and product components to be able to to give customer service even more information about all added lines to cart. The line items with, holds just basic information like prices, quantities and totals.

2019-05-26 20_27_19-SummaryThe product component child view below holds even more information about the product behind the line item of the cart. As you can directly see in that component, this is again linked with the corresponding entity behind.

2019-05-26 20_28_00-SummarySo if you click on the name in the cart product component you will directly be redirect to the sellable item detail page of the product added to cart to see even more detail information.

2019-05-27 22_23_39-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

Next view is again some easier view, the voucher view. Within that view, we just grab the cart coupon component from cart and extract basic information about added coupon codes.

2019-05-27 22_27_44-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

2019-05-26 20_28_07-Summary

If a coupon code is added, you are also able to click on the promotion entity target to see the promotion behind the coupon code. Same procedure, like you saw earlier with the cart product component. You see, that you all the time, when displaying other entities, you are also able to directly link them to your view to allow easy navigation between them.

2019-05-27 22_29_44-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

Ne important child view is the physical fulfillment view, which just shows information about applied physical fulfillment method. Of course you can also add a view for digital fulfillment or even other ones. But for demonstration reason, that specific view is enough to demonstrate the possibilities.

2019-05-27 22_33_14-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

2019-05-26 20_28_24-Summary

Last but not least I also wanted to display one payment view. Because for testing I always use GiftCard payment, I decided to show you that example here. Again, you are totally free to display also other payment methods or types depending on your own custom business needs.

2019-05-27 22_34_50-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

2019-05-26 20_29_18-Summary

So by now you saw, how to step by step create your new entity view and fill it with custom data. But just displaying information is most likely not enough. We of course also want to edit some stuff. To be able to do that, the two missing pipelines are taking action.

IFormatEntityViewPipeline: This pipeline is now responsible for adding all kind of actions to our views. In our case we just have one single class for adding all kind of actions to all of our views. Of course depending on your preferences you can also split that up or arrange differently. What this class we have now does is in the end quiet simple. Depending on the fact if we are on our target view, like cart entity view, we just iterate through all the child views (all the once we created earlier) to grab the ones, we would like to add actions to. Then if we found the view we just have to access the action policy of that view and simply add a new action to that list. The new object is of type EntityActionView. An example of that can be seen below.

In this example you can see, that based on the entity view (Carts entity view) we just grab the child view of name Line Items. Once we found that, we add all our action we would like to add e.g. Add Line Item, Edit Line Item, Remove Line item. If we would like to add more actions like for gift cards, vouchers or other child views, we would just have to do the same for all other child views.

Once we have done that and open the cart entity view again, we can see all kind of new buttons beside our child views.

2019-05-26 20_27_19-SummaryIf we would click them now, just an empty or nearly empty dialog would open with an accept and reject button, nothing more.

2019-05-28 19_26_46-Summary

Note: The dialog you can see above will be displayed, once you set the property of EntityActionView RequiresConfirmation = true.

Now let’s do the next step. The next step is to fill the dialog with some new fields. This is much easier, than you might think. Let’s stay at the example of line items child view with add, edit and remove functionality and have a look how to add fields to the view to be able to add some custom information. IF we e.g. want to add a new line item to our cart we have to provide some basic information like, catalog, productId, variantId and quantity. Below you can see an example implementation of the add view

2019-05-26 20_27_27-Summary

2019-05-26 20_27_45-Summary

Now let’s see, what code is responsible for rendering that view. Because these dialogs are nothing more than entity views, the process of adding such a view is exactly the same, than before with views. We just have to create a new block and hook in into IGetEntityViewPipeline. The whole implementation of the dialog above can be seen below.

2019-05-28 19_35_33-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

In the beginning we just have to check, if we are in the right view, like in all other views. If it is this case, we in the end just add all our properties to the given entity view. For each property we can individually decide, if the property is hidden, if it is a mandatory field to add and it’s default value.

So what’s next now? If we would now use the dialog, like seen above, unfortunately nothing would happen. To make something happen, we finally have to implement a custom action, which should be triggered, once we trigger the action in the dialog. To do that, the last pipeline is now used.

IDoActionPipeline: In this pipeline we add all kind of blocks, which are responsible for doing actions. In our case we added a class DoActionAddLineItem, which should take over the responsibility of really adding now a line item to the given entity view cart. Once we click the “accept” button in the dialog the IDoActionPipeline is triggered with each block within. So what we first have to do, like always is, to determine, if the current action is the one we would like to react on. If that is the case, we simply grab all the previously added view properties and in the end just trigger the AddCartLineCommand to really add the cart line to the current cart. The code below shows the whole implementation of that action.

2019-05-28 19_45_17-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

If we now after doing all that, enter our cart entity view, click on Add Line in our Lines view, fill in some existing product with its data and click “accept” the line item is immediately added to the cart, and the cart entity view reloads and directly shows the new line item in the Cart Lines view.

In the same way, we can also implement functionality like edit or remove.

One special behavior of edit is, that we are able to choose a line item within our Line Items view and click on Edit Line Item. The application automatically knows which specific line item was selected, so we are able to use the given data to pre fill the input fields. An example of such an implementation can be seen below.

2019-05-28 19_50_56-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

The first special thing in the form view, is that based on the selected line item, we extract the current quantity as default value.2019-05-28 19_51_15-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

In the DoAction class of edit line item now, we just have to extract the quantity field and again grab the selected line item from the entity view. Based on these information we were now able to differentiate between RemoveCartLineCommand and UpdateCartLineCommand. Below is the rendering of the previously seen code.

2019-05-26 20_27_34-Summary

2019-05-26 20_27_52-Summary

The same way like seen above, I also implemented all the other actions and dialogs you can see in the following screens. These implementation contain

  • Remove Line Item
  • Add Voucher
  • Remove Voucher
  • Add physical fulfillment
  • Add GiftCard
  • Remove GiftCard

And of course like always. These implementations are just demonstrations of what you are able to do in commerce engine.

Voucher Actions

This slideshow requires JavaScript.

Physical Fulfillment Actions

This slideshow requires JavaScript.

Gift Card Actions

This slideshow requires JavaScript.

The code of all these implementations and the whole Carts application can be found in my github account under the following url

Conclusion

Within this blog post I hope you saw, how easy it is to add new applications to the Sitecore Commerce Business Tools and how easy it is to extend these views with new custom information. To do that, we as backend developer just have to hook in into some specific pipelines and provide the data, which should be displayed. The Business Tools application then reacts on these data and renders, whatever is given. There is no need to touch the frontend application or even know how it is implemented.

And that’s what I think, is the real meaning of Data Driven UI.

Bring your Sitecore XC deployments from the Stone Age to the future – How to deploy server specific environments fully automated

This time I would like to share some thoughts on a topic every developer will be faced, at least if the topic CI and CD comes up and you would like to completely automate that process for your Sitecore XC instance as hopefully most of you are already doing with your Sitecore XP implementations. For that I would like to describe first of all the general process of how to deploy an instance of commerce engine. After that, I will point out the limitations of that OOTB process and what problems now come up. In the end will will describe the newly introduced approach of solving that problem and show the results of that implementation.

How to deploy a commerce engine

Let’s first have a look at the topic of how the commerce engine is deployed in general, to understand later on, what we have to change in that process to realize server specific automated deployments.

From what we learned from the Sitecore documentation is, that in general there are 3 different ways to deploy commerce engine

  1. Visual Studio Publish
  2. Command Line Tool
  3. Inside a build environment

Visual Studio Publish: To deploy your commerce engine via VS publish, just open the commerce engine solution. Then right click on the Sitecore.Commerce.Engine and click Publish (Veröffentlichen). In the new screen you can now switch various kinds of publish methods.

2019-05-05 21_18_48-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

If you open that screen with the OOTB commerce engine SDK the filesystem publish method is already setup initially.

2019-05-05 21_19_16-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)2019-05-05 21_19_11-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

Just double check if the target path of the publishment is correctly and fits for your engine. After you finished that, just save the settings and execute the publishment.

Command Line Tool: The second way of how to publish a commerce engine is, via command line tool e.g. cmd tool. To do that, just navigate to the root of your commerce engine solution and execute dotnet publish command there with proper parameters.

2019-05-07 18_35_30-Administrator_ Eingabeaufforderung

dotnet publish {SOLUTION NAME} -f {FRAMEWORK} -c {BUILD CONFIGURATION} -o {PATH TO PUBLISH} -r {RUNTIME}

If you do not set the proper parameters for your environment the result of the publish might work, but if you try to use that engine and work with that, you will most likely get some IIS server error telling you the engine started but stopped again.

You can also either way choose the output path as any folder and copy the output manually to the proper location, or directly give the right path of your engine.

Note: To publish commerce engine to your IIS instance, make sure the current engine is not in use by either way reset or stop IIS for the time of the publish or copy process.

Inside a build environment: This way I will not describe in detail, cause it is highly dependent on your specific build environment. Depending on the environment you will most probably have different board tools of how to deploy a .NET Core application properly.

What are the problems now?

If we are working locally and just want to publish our instance for development reasons to our local IIS everything is totally fine so far and everything is working as expected. But of course we do not only want to deploy our code to the development engine locally, but also to some other servers like testing or productive environment.

And at that point we know already from our Sitecore XP solution, that there are various settings or configs, which are dependent to the specific environment. Examples for such files are connectionstrings.config, datafolder.config, transformations of the web.config and so on. Of course also Sitecore XC has configuration files. These files are called environments. If you want to learn more about environments, I recommend to first read that blog post of me, where I described environments in detail, what they are, how they are structured and how to adapt them.

Of course also in Sitecore XC we will also reach the point, where we need server specific settings, like in Sitecore XP. Scenarios of such settings could be endpoints of interfaces, which differ from local, to testing, to productive servers or just credentials or accounts to database.

In various projects I was able to see, how others deal with that kind problem. The overall result was, that some developer all the time deploys commerce engine manually by doing for example the steps described below

  • opening Visual Studio
  • building the solution
  • removing all the configs (for now)
  • zipping the output package
  • copying that package to the target server
  • unzipping that package
  • replacing all the files with the output package
  • manually go into each environment compare that environment with the one of the deployment package and edit that file piece by piece

You see, that such a deployment consists of many manual, time consuming and error-prone single steps. If you just do one little mistake in one of these steps, deployment will most probably fail. Results could lead from harsh server errors to hidden errors in implementations, which are found by chance. afterwards.

The aim of course should be to completely automate such a process to rule out human errors and minimize deployment time. In the next chapter I will introduce an approach of how to overcome all these issues by having easy maintainable server specific environment json files with .NET Core.

Approach of solving that problem

Of course I can imagine there are various ways, of how to overcome such problems within Sitecore Commerce Engine. In this article I just concentrqte on an a very generic and plugin like approach. The approach I will describe in detail now consists of various small changes and adaptions made in the solution at various places

  • Creation of new ASPNETCORE_ENVIRONMENT specific environment files
  • Creation of new plugin to handle the new environments on bootstrap command
  • Transformations of web.config to set ASPNETCORE_ENVIRONMENT

Together all these changes now make it able to use your own CI / CD systems to deploy the commerce engine fully automated.

Creation of new environment files

Let’s start with the creation of new environment files under “\wwwroot\data\Environments”. For demonstration reason I used PlugIn.AdventureWorks.CommerceAuthoring-1.0.0.json and created a new file called PlugIn.AdventureWorks.CommerceAuthoring-1.0.0.UAT-CM.json. You see the that the new file just has an extension shortly before the end. In VS the files look like this

2019-05-07 19_39_34-Customer.Sample.Solution - Microsoft Visual Studio  (Administrator).png

You can see, that VS directly and automatically recognizes the new file as “sub file” of the original one. This mechanism behind that relies on standard .NET Core environment handling. The extension “UAT-CM” is just a new .NET CORE environment I created for later usage. Maybe you already know such files with namings like

  • Development
  • Stating
  • Production

These mechanism are also already used by the commerce engine for example when dealing with config.json and the development variant of that file. This file is handled by OOTB .NET Core mechanisms like mentioned above.

From what I saw there, I got inspired to also use such an approach for our Sitecore XC environment json files. There this standard .NET Core behavior is not applied, cause these files are loaded and evaluated not by .NET Core, but by a dedicated plugin within commerce engine. This plugin just loads all the json files within /wwwroot/data/environments/ and processes them, with no differentiation of .NET Core environments. But that should not stop me ^^. Within the next sub chapters you will see the changes made to the plugin, so that all these pieces of functionalities work for our environment files.

Of course the naming of the .NET Core environments is completely up to you. I decided for now to use the following notations for historical and Sitecore reasons, where you have a local development machine a scaled Sitecore environment for testing called UAT (User Acceptance Testing) and a scaled Sitecore environment for production called PROD.

  • Development (Local deployment)
  • UAT-CM (Content Management commerce service of UAT)
  • UAT-CD (Content Delivery commerce service of UAT)
  • UAT-MINION (Minions commerce service of UAT)
  • PROD-CM (Content Management commerce service of PROD)
  • PROD-CD (Content Delivery commerce service of PROD)
  • PROD-MINION (Minions commerce service of PROD)

Now that we know, how we create and name our new environments, so that they are applied later on correctly, we will have a look at the content of these files.

I decided to offer two different ways of how the content can be added to these files

  • as full replacement
  • as merge / overwrite

Full replacement Mode: To create a new environment, which uses that mode, you just have to create the new environment file based on the original environment file, with all the given settings. Within the new file you now just have to change all the settings, which you would like to change for the specific environment. The rest stays as it is. The way Sitecore later on bootstraps the new environment is now, that it just, based on the given .NET CORE environment loads the new environment specific environment file, instead of the original one. Therefore the new environment specific files always need ALL the settings like the original one, nevertheless they are the same or not. For that file I guess you don’t need an example, cause it completely looks the same, except that the values are different at some places.

Overwrite / Merge Mode: To create a new environment, which uses that mode, you just have to create the new environment file with only the entries, which have been changed. This means, the file consists only the json structure of the entries, which should be overwritten or for arrays merged. Let’s have a look at an example of that file in comparison to the original one.

2019-05-01 11_22_10-C__Code_SXC910Playground_src_Sitecore.Commerce.Engine_wwwroot_data_Environments_.png

Above you can the the complete content of the new PlugIn.AdventureWorks.CommerceAuthoring-1.0.0.UAT-CM.json file. You can directly see, that it is much shorter than the original one. In this

Note: The only important thing you have to obey is, that the structure of the json file should be the same, from the element to change to the root element of the json.

For this demonstration I decided to just change the following settings

  • Sitecore.Commerce.Plugin.Tax.GlobalTaxPolicy,
    • DefaultCartTaxRate from 0.1 to 0.5
    • DefaultItemTaxRate from 0.1 to 0.5
  • Sitecore.Commerce.Plugin.Orders.GlobalCheckoutPolicy
    • EnableQuickCheckout from true to false
  • Sitecore.Commerce.Plugin.Catalog.VariationPropertyPolicy
    • Values from [Color, Size] to [Brand]
  • Sitecore.Commerce.Plugin.Orders.KnownOrderListsPolicy
    • Key Orders from list_style_bullets to list_style_bulletsTEST
    • ProblemOrders from ProblemOrders to TESTProblemOrders
  • Sitecore.Commerce.Plugin.Fulfillment.GlobalPhysicalFulfillmentPolicy
    • Fee for Ground from 15 USD to 150 USD

In the following screenshots you can see, the specific settings of the origin file

2019-05-01 11_23_10-C__Code_SXC910Playground_src_Sitecore.Commerce.Engine_wwwroot_data_Environments_

2019-05-01 11_23_53-C__Code_SXC910Playground_src_Sitecore.Commerce.Engine_wwwroot_data_Environments_

2019-05-01 11_24_14-C__Code_SXC910Playground_src_Sitecore.Commerce.Engine_wwwroot_data_Environments_

Creation of new plugin

Now let’s have a closer look into the newly created plugin to process the environments correctly. I started by investigating where the bootstrap commands ends up in commerce engine and found the following IBootstrapPipeline

2019-05-07 20_36_53-NodeConfiguration_Deployment01_965adef45dbb48458e2cc80329e6859f.log - Editor

Next I used dotPeek to investigate the BootStrapImportJsonBlock to see how Sitecore’s OOTB implementation imports all the environment files.

2019-05-07 20_41_34-JetBrains dotPeek.png

The most interesting part of that block can be seen above. You can see that it just does the following steps to import jsons

  • Grab all files under wwwroot\data\Environments
  • Iterate through all files
  • Make some validation checks if the current file is a valid environment / policy set
  • Based on the information if the current file is an environment or policy set
    • Call import environment command
    • Call import policy set command

My idea now, when I saw that code, was, that I should be able to use that code and modify it in a way, it would be able to also differentiate between .NET Core environment specific json environments. In the following screenshots.

2019-05-07 21_03_15-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

The first change I made in the standard code is highlighted above. I just implemented a piece of logic, which is responsible for loading the environment specific json files in parallel to the original one. The parameter environmentName is based on the current .NET Core variable ASPNETCORE_ENVIRONMENT and reflects exactly the naming used for our environment files. In addition you can see some boolean value ReplaceWithEnvironmentSpecificFile. This is right now some class level property which switches between the above mentioned modes replace and merge. In case of replacement it really just loads the environment specific file instead of the original one. In case it should merge the files you see the behavior below in the next screenshot.2019-05-07 21_04_26-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

That piece of logic just checks again the flag and if a server specific environment exists. If so, the new logic takes action. On high level the idea is very simple. The idea is just to take the both json files and use newtonsoft board tools to merge these two json files loaded as JObjects. Unfortunately this approach is not as easy as it might sound. If you try to merge the two jsons as whole, the result will not be, what we expect it to be. The board tools are not able to correctly merge the two environment jsons, so the result is a file where some of the policies were just added two times or other elements, which definitely do not belong together were merged together. So the solution was, not to use the whole file to merge the jsons, but to break down the jsons in multiple objects and merge these objects individually, some kind of divide & conquer approach. I decided to use all elements, which start with a type property, cause these elements are complex elements. In addition I only choose first level elements with a type property. In the screen below, you can see an example of how the environment file is spitted by the algorithm in smaller elements, which are then merged with corresponding element the original file.

2019-05-07 21_18_46-C__Code_SXC910Playground_src_Sitecore.Commerce.Engine_wwwroot_data_Environments_.png

To always iterate correctly through the environment specific file, find the elements to merge in the original file, I also created two helper functions, which achieve exactly this.2019-05-07 21_05_15-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)2019-05-07 21_05_27-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

These pieces of logic just iterate through the json. At first it skips the root level type property. After that it iterates till it reaches other type properties. Once it reached that point, it starts finding that type property in the original file. Once we reached one object with a type property, we stop iterating deeper into that object and just go on with the sibling objects to avoid merging sub objects with type property, which are already merged earlier. And cause we are just grabbing all the first level objects with type property we always grab objects ob type policy, which are unique in the environment specific and origin file, so it can easily be found.

Once we merged every object we found, we just come back and produce the json string of that merged file again, so it can be used afterwards in following processes.

Transformations of web.config

Now after we created the new environment files and the new plugin to process these files, we finally have to deal with the problem of how to set the ASPNETCORE_ENVIRONMENT correctly for the server. From what I already read in various posts, there are many ways of achieving that goal. You can

  • start the engine with the environment parameter
  • set it IIS wide
  • set it machine wide
  • set it on the website
  • set it in VS publish process
  • and so on

For now I decided to set the parameter on website level, furthermore within the web.config file. An example of the web.config can be seen below.

2019-05-07 21_36_15-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

In addition to that entry I created various web.config transformation files, which react on given build configuration parameter. Below you can see web.config transformations in the naming convention, we already know from earlier.

2019-05-07 21_36_43-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

Each of these files at the moment just overwrite the ASPNETCORE_ENVIRONMENT parameter with the correct value. So for example web.UAT-CM.config sets ASPNETCORE_ENVIRONMENT variable to UAT-CM and so on.2019-05-07 21_36_55-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

To trigger these build configurations I finally created them also in VS als build configurations, like seen below

2019-05-07 21_41_59-Customer.Sample.Solution - Microsoft Visual Studio (Administrator)

Results

So what is now the result of everything?

Now with all the changes we can use the dotnet publish command in the following way, very similar to the one you saw in the beginning, but slightly different in the configuration parameter

2019-05-07 21_45_34-Administrator_ Eingabeaufforderung

The outcome of that command is a fully working deployment package, where the web.config has the patched entry, we will use later on in bootstrap command

2019-05-07 21_48_24-Deploy.png

If we now deploy that engine to the right place the new ASPNETCORE_ENVIRONMENT parameter will take place on startup for that specific website

Note:  We could also deploy that directly to the right location or use some smarter powershell script to do that job like seen below. For local deployment I just created a small powershell script, which takes over all the manual steps I would have to do

  • takes some parameters
  • publishes the application,
  • stops IIS,
  • copies the deployment package to the destination,
  • starts IIS again,
  • grabs token from Identity server
  • and finally calls bootstrap

2019-05-07 21_59_43-_C__Code_CommerceEngineDeploymentScript.ps1 - Notepad++2019-05-07 22_01_05-_C__Code_CommerceEngineDeploymentScript.ps1 - Notepad++.png

Now it is getting more and more interesting. After we successfully deployed all the latest changes and called bootstrap we can now see the results. For better visualization I just saved the merged file together with the original one and used Notepad++ compare tool to compare these two files to see all changes.

2019-04-28 19_03_35-C__PlugIn.AdventureWorks.CommerceAuthoring-1.0.0_Orignial.json - Notepad++2019-04-28 19_04_29-C__PlugIn.AdventureWorks.CommerceAuthoring-1.0.0_Orignial.json - Notepad++2019-04-28 19_05_38-C__PlugIn.AdventureWorks.CommerceAuthoring-1.0.0_Orignial.json - Notepad++2019-04-28 19_06_08-C__PlugIn.AdventureWorks.CommerceAuthoring-1.0.0_Orignial.json - Notepad++

What are the results?

The results are exactly what we except them to be! All the settings we initially patched in the server specific environment are correctly taken over in the merged file. If we additionally have a look how commerce engine deals with the new environment json file, I just had a look at the outcome of the ImportEnvironmentCommand, which takes our json as string, saves that to DB and returns an environment objects. The screens below show the values of the properties we wanted to be changed cause of merging.

2019-04-28 19_20_33-Customer.Sample.Solution (Debugging) - Microsoft Visual Studio (Administrator)2019-04-28 19_20_52-Customer.Sample.Solution (Debugging) - Microsoft Visual Studio (Administrator)2019-04-28 19_20_11-Customer.Sample.Solution (Debugging) - Microsoft Visual Studio (Administrator)2019-04-28 19_21_17-Customer.Sample.Solution (Debugging) - Microsoft Visual Studio (Administrator)

Also here you can see, that Sitecore accepted the merged Json file add was able to create a usable environment file with the merged values of the parameters.

The whole plugin can be found under https://github.com/Chris1415/Custom.Sample.Plugin.CI

Conclusion

Within the today’s blog bost you learned about problems, which might occur when deploying your commerce engine to some other servers than local development. You saw the challenges and how deployments are done nowadays at some places. But in the end you also saw an approach of dealing with that problem to be able to fully automatically deploy your commerce engine based on the target environment.

And as always the code you saw here is just for demonstration reason and not deeply and fully tested. This should be just an entry point for you to get into that topic and get an idea of how to solve the given problem.