Create Sitecore Commerce items with a script – Part 1 (Utils)

Importing and exporting single Sitecore Commerce items is not easy, there is no OOTB serialization of single items as we have for Sitecore CMS items. This is because Sitecore XC items are virtual items which are shown in Content Editor using Commerce Engine Connect Catalog Provider. The provider is defined in Sitecore.Commerce.Engine.DataProvider.config

There is a possibility to export an entire catalog with postman. It gives a zip file that you can import again. But this technique is very limited, as we can only export and import catalogs, not single categories or items. Quite frankly, my tests to import a catalog to a new environment has also failed for some reason.

This is why I have developped a set of Powershell scripts to create XC items easily and therefore to be able to deploy many XC items to different environments. Those scripts are based on what you do when you create XC items with Postman. The REST APIs delivered by Sitecore are a good beginning, but I found out that some of them have bugs and some of them are missing.

Because of the complexity of the problem this article will be separated in a few parts:

  • The basic idea behind these scripts
  • Creating a catalog
  • Creating some some categories
  • Creating a Sellable item with variants
  • Adding inventory information to a sellable item
  • Automatically approve the XC items in the workflow

The idea

It is well-known that we can use Postman and a set of Rest APIs to manage XC items. See this documentation to read about this technique. In short, you have to authenticate with a API call, get a token from the Identity server, and then you can call whatever API you want to create, update, list, delete and show XC items.

Our script will do exactly the same. We’ll pass a user and a password to the identity server, retrieve the authentication token and use it to our calls, all that in one script instead of doing that manually in Postman.

In order to visualise what we will do concretely, let’s browse to the Business Tools.

Let’s create a new Catalog, a new Category and a new Sellable Item. While we do that, let’s have Chrome’s debugging tool open on the network tab to see the interactions between my browser and the XC server.

Each time I created an item (Catalog, Category or Sellable Item), a call to DoAction() was made.

For example, the DoAction() call in order to create the catalog had the following request:

URL: https://{AUTORING_URL}/api/DoAction()

Method: POST

Headers:

  1. accept: application/json
  2. accept-encoding: gzip, deflate, br
  3. accept-language: en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7
  4. authorization: Bearer {TOKEN}
  5. content-length: 944
  6. content-type: application/json
  7. cookie: intercom-id-ye5u1085={xxx}; _ga={yyy}; _ga_YCPCYF2BS4={zzz}
  8. currency: SEK
  9. entityversion: 1
  10. environment: {ENVIROMENT_AUTHORING}
  11. language: en
  12. origin: {BUSINESS_TOOLS_URL}
  13. policykeys: IgnorePromotions
  14. referer: {BUSINESS_TOOLS_URL}
  15. sec-ch-ua: ” Not A;Brand”;v=”99″, “Chromium”;v=”90″, “Google Chrome”;v=”90″
  16. sec-ch-ua-mobile: ?0
  17. sec-fetch-dest: empty
  18. sec-fetch-mode: cors
  19. sec-fetch-site: same-site
  20. shopname: ShopSite
  21. user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36

Payload:

{
  "Name": "Details",
  "DisplayName": "Details",
  "EntityId": "",
  "EntityVersion": 1,
  "Action": "AddCatalog",
  "ItemId": "Entity-Catalog-TestCatalog",
  "VersionedItemId": "Entity-Catalog-TestCatalog-1",
  "DisplayRank": 500,
  "UiHint": "Flat",
  "Icon": "chart_column_stacked",
  "Policies": [],
  "SelectedChildView": {
    "Name": "",
    "Policies": []
  },
  "Properties": [
    {
      "Name": "Version",
      "DisplayName": "Version",
      "Value": "11",
      "IsHidden": true,
      "OriginalType": "System.Int32",
      "IsReadOnly": true,
      "UiType": "",
      "IsRequired": true,
      "Policies": []
    },
    {
      "Name": "Name",
      "DisplayName": "Name",
      "Value": "JeSuisSitecore",
      "IsHidden": false,
      "OriginalType": "System.String",
      "IsReadOnly": false,
      "UiType": "",
      "IsRequired": true,
      "Policies": []
    },
    {
      "Name": "DisplayName",
      "DisplayName": "Display Name",
      "Value": "Je Suis Sitecore",
      "IsHidden": false,
      "OriginalType": "System.String",
      "IsReadOnly": false,
      "UiType": "",
      "IsRequired": true,
      "Policies": []
    }
  ],
  "ChildViews": [],
  "@odata.type": "#Sitecore.Commerce.EntityViews.EntityView"
}

We’ll talk more about the payload JSON in the next article, but we can see that each DoAction() call have the same headers so let’s build a first structure for this script with all the necessary calls to create the headers.

The utility functions

We will need a function to get the authentication token and a function to create the request header that will follow every request we’ll make.

In order to send a request to a REST API, Powershell have a very nice module called Invoke-RestMethod. The parameters we’ll use are

  • Uri: Specifies the Uniform Resource Identifier (URI) of the internet resource to which the web request is sent. This parameter supports HTTP, HTTPS, FTP, and FILE values.
  • ContentType: Specifies the content type of the web request. It will always be “application/json” in our case
  • Method: Specifies the method used for the web request. We’ll always have a POST or GET method for our API calls
  • Headers: Specifies the headers of the web request. Enter a hash table or dictionary.
  • Body: Specifies the body of the request. The body is the content of the request that follows the headers. You can also pipe a body value to Invoke-RestMethod. We’ll use this parameter when we send a POST request.

To get the authorization token, we’ll have to POST a request API to $IdentityServiceUri/connect/token. We will mock what Postman is sending when using the OOTB API collection, so we will have the following parameters in the Body:

  • username
  • password
  • grant_type = “password”
  • client_id = “postman-api”
  • scope = “openid EngineAPI postman_api”

If everything is OK and the username/password is ok, then the identity server will return a simple JSON as a response, with the authentication token in the field “access_token”.

Our Get-Token function can then be easily developped:

Function Get-Token {
    [CmdletBinding()]
    PARAM(
        [string][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$UserName,
        [string][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$Password,
        [string][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$IdentityServiceUri
    )

    $body = @{
        username   = $UserName
        password   = $Password
        grant_type = 'password'
        client_id  = 'postman-api'
        scope      = 'openid EngineAPI postman_api'
    };

    $headers = @{
        "Cache-Control" = "no-cache"
        "Content-Type"  = "application/x-www-form-urlencoded"
        "Accept"        = "application/json"
    }

    try {
        $response = Invoke-RestMethod "$IdentityServiceUri/connect/token" -Method Post -Body $body -Headers $headers;
        $token = $response.access_token;
        return $token;
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}

To call this function and get the token in a Powershell variable, we’ll do:

$userName = 'sitecore\admin'
$idPassword = "YourPassword"
$identityAlias = "id.jss.localhost"
$token = Get-Token -UserName $userName -Password $idPassword -IdentityServiceUri "https://$identityAlias"

The token will be used in the request header called Authorization. Now we can also build our BuildHeaders function, with just the necessary headers:

Function BuildHeaders {
    [CmdletBinding()]
    PARAM
    (
        [string][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$Token
    )
    return @{"ShopName"="ShopSite"
        "ShopperId"="ShopperId"
        "Language"="en"
        "Currency"="SEK"
        "Environment"="JSSAuthoring"
        "GeoLocation"="IpAddress=1.0.0.0"
        "CustomerId"="CustomerId"
        "Authorization"="Bearer $token"}
}

In the next article I’ll show how to create a Catalog.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: