3. commercetools Composable API

Exploration of the commercetool composable api that we’ll use to create products in commercetools.

Build Outcome

By the end of this section we will have:

  • Used the commercetools auth API to retrieve an API access token
  • Used the commercetools commerce API to add a Product Type
  • Used the commercetools commerce API to add a test product
  • Updated our Azure HTTP trigger function
  • Updated the Marketplacer webhook destination to our Azure Function
  • Created a product end to end using our webhook and Azure function

Steps

Create an API Access Token

All calls to the commercetools API require an access token, this token can be obtained by making a call to the commercetools auth api which we will perform in this section.

In order to make a call to the auth API endpoint you will need the following information (you should have obtained these when you set up a commercetools api client):

  • projectKey
  • clientId
  • clientSecret
  • scope
    • The scope in this example would be: manage_project:{projectKey}
  • region (this is part of the API endpoint route)

1: We then make a POST request to the auth endpoint with the URL structured in the following way:

https://{clientID}:{clientSecret}@auth.{region}.commercetools.com/oauth/token?grant_type=client_credentials&scope={scope}

So with (made up) values, a fully formed example using cURL would be:

curl -XPOST https://abc1234:xyz789@auth.australia-southeast1.gcp.commercetools.com/oauth/token
?grant_type=client_credentials&scope=manage_project:my-example-project

This should return with our API access token:

{
	"access_token": "VSfs9gBbUDWc-VA8-ae_-Te98b9rk7YO",
	"token_type": "Bearer",
	"expires_in": 172800,
	"scope": "manage_project:my-example-project"
}

Retain and secure the access_token as we will be using this in subsequent sections.

Create a Product Type

Our first step in exercising the commercetools commerce API will be to create a Product Type. Product Types are a set of attributes that act as a template for a group of similar products.

Your commercetools project must have a Product Type before products can be created.

The JSON payload we’ll POST to the product-types endpoint is shown below, (this is referred to as a ProductTypeDraft in commercetools.)

{
  "name": "Clothing",
  "description": "All clothing products"
}

2: We can make a request to create this Product Type using cURL in the following way:

curl -XPOST https://api.{region}.commercetools.com/{projectKey}/product-types 
-H "Content-Type: application/json"
-H "Authorization: Bearer {access_token}" 
-d '{  "name": "Clothing",  "description": "Al clothing products"}'

Note the use of the projectKey in the URL as well as the access_token as bearer token in the Authorization header


If successful this should respond with:

{
  "id": "9e1630ea-b9d7-467e-bc06-6f708a42ac9e",
  "version": 1,
  "versionModifiedAt": "2023-07-12T01:07:04.712Z",
  "createdAt": "2023-07-12T01:07:04.712Z",
  "lastModifiedAt": "2023-07-12T01:07:04.712Z",
  "lastModifiedBy": {
    "clientId": "abc123",
    "isPlatformClient": false
  },
  "createdBy": {
    "clientId": "abc123",
    "isPlatformClient": false
  },
  "name": "Clothing",
  "description": "All clothing products",
  "classifier": "Complex",
  "attributes": []
}

Retain the id as we will be using this in subsequent sections.

Create a Product

While we will be creating products using our Azure function, (via the CreateCommerceToolsProductAsync method), it’s worth testing this functionality with a direct call to the products endpoint.

The payload for our call is as follows:

{
  "name": {
    "en": "Mens standard T-Shirt"
  },
  "productType": {
    "id": "9e1630ea-b9d7-467e-bc06-6f708a42ac9e"
  },
  "slug": {
    "en": "mens-standard-tshirt"
  }
}

Briefly stepping back to our Azure Function and looking at our CTProductCreateDto, you can now see the parallels:

public partial class CTProductCreateDto
{
  [JsonProperty("name")]
  public Name Name { get; set; }

  [JsonProperty("productType")]
  public ProductType ProductType { get; set; }

  [JsonProperty("slug")]
  public Name Slug { get; set; }
}

public partial class Name
{
  [JsonProperty("en")]
  public string En { get; set; }
}

public partial class ProductType
{
  [JsonProperty("id")]
  public string Id { get; set; }
}

3: The call to the products endpoint is now straightforward, the only point of note is that we are using the Product Type we generated in the prior section.

An example call to the product create API using cURL would be:

curl -XPOST https://api.{region}.commercetools.com/{projectKey}/products 
-H "Content-Type: application/json"
-H "Authorization: Bearer {access_token}" 
-d '{ "name": { "en": "Mens standard T-Shirt" }, "productType": { "id": "9e1630ea-b9d7-467e-bc06-6f708a42ac9e" }, "slug": { "en": "mens-standard-tshirt" } }'

A success response would be similar to the following:

{
  "id": "6c27a757-54f9-4e20-8c16-494eb000f40e",
  "version": 1,
  "versionModifiedAt": "2023-07-04T04:03:20.628Z",
  "lastMessageSequenceNumber": 1,
  "createdAt": "2023-07-04T04:03:20.628Z",
  "lastModifiedAt": "2023-07-04T04:03:20.628Z",
  "lastModifiedBy": {
    "clientId": "abc123",
    "isPlatformClient": false
  },
  "createdBy": {
    "clientId": "abc123",
    "isPlatformClient": false
  },
  "productType": {
    "typeId": "product-type",
    "id": "a17c418a-26d6-4dd3-a7c3-1fad4c44b66d"
  },
  "masterData": {
    "current": {
      "name": {
        "en": "Mens standard T-Shirt"
      },
      "categories": [],
      "categoryOrderHints": {},
      "slug": {
        "en": "mens-standard-tshirt"
      },
      "masterVariant": {
        "id": 1,
        "prices": [],
        "images": [],
        "attributes": [],
        "assets": []
      },
      "variants": [],
      "searchKeywords": {}
    },
    "staged": {
      "name": {
        "en": "Mens standard T-Shirt"
      },
      "categories": [],
      "categoryOrderHints": {},
      "slug": {
        "en": "mens-standard-tshirt"
      },
      "masterVariant": {
        "id": 1,
        "prices": [],
        "images": [],
        "attributes": [],
        "assets": []
      },
      "variants": [],
      "searchKeywords": {}
    },
    "published": false,
    "hasStagedChanges": false
  },
  "lastVariantId": 1
}

Updating the Azure function

Updating the config

Having generated:

  • The API Access Token
  • The Product Type

4: We can now move back to our Azure project in VS Code and update the values in the the local.settings.json file, specifically:

  • ct_bearer_token - this should contain the value of the access token
  • ct_product_type_id - this should contain the Product Type we generated

Don’t forget to save the file

5: Likewise, go back to the Azure portal, and update those configuration elements for our function on Azure.

Again, don’t forget to save your changes.

Updating the code

Having updated the config of our function, we now need to uncomment the calls to CreateCommerceToolsProductAsync in the Run method.

6: Update the Run method as follows:

public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
  if (req.Headers["AuthKey"] != "marketplacerletmein")
    return new OkObjectResult("Good try...");
  log.LogInformation("C# HTTP trigger function processed a request.");

  var payload = JsonConvert.DeserializeObject<dynamic>(await new StreamReader(req.Body).ReadToEndAsync());

  Console.WriteLine($"---> Payload ID: {payload.id}");

  if (payload.event_name != "create")
  {
    return new OkObjectResult("Not a create event");
  }

  Console.WriteLine($"---> Product Title: {payload.payload.data.node.title}");

  var newProduct = new CTProductCreateDto
  {
    Name = new Name { En = payload.payload.data.node.title },
    ProductType = new ProductType { Id = Environment.GetEnvironmentVariable("ct_product_type_id") },
    Slug = new Name { En = payload.payload.data.node.legacyId }
  };

  if (await CreateCommerceToolsProductAsync(newProduct))
    return new OkObjectResult("Product Created");

  return new BadRequestObjectResult("Something went wrong...");
}

Don’t forget to save your changes.

Local Testing

7: Run up the function locally as per the steps found here.

8: And once again using the API client of your choice, make a call to the function. An example using cURL is shown below (note the new value for legacyId in our body payload):

curl -XPOST http://localhost:{your assigned port}/api/HttpTrigger1
-H 'AuthKey: <your key>' 
-H "Content-type: application/json" 
-d '{"id":"V2ViaG9va0V2ZW50LTI5","event_name":"create","payload":{"data":{"node":{"id":"QWR2ZXJ0LTEwMDAwMjU5NA==","title":"Platinum T-Shirt","legacyId":10034000,"variants":{"nodes":[{"id":"VmFyaWFudC0zNjQ4","sku":"cp4u-0001","countOnHand":1000}]},"__typename":"Advert","description":"This is a great phone"}}}}'

This should result in a successful product creation:

Successfully created product


To further “prove” that a product has in fact been created you could use the commercetools API to return all products. In the interests of time, we’ll leave that as a separate exercise.

Deploy Azure function changes

10: In order that we can test the whole process end to end, we of course need to deploy our Azure function changes. To do so follow the steps we have already performed here.

Update the webhook in Marketplacer

When we created our webhook originally, we used a placeholder endpoint using a tool called webhook.site in order that we could test our webhook, the time has come to update that endpoint to our Azure function.

11: Back in the Azure portal, ensure that your function resource is selected, and click Functions:

Select functions


12:: We should only have 1 function listed: HttpTrigger1, click this to continue:

Select http trigger function


13: Select Get Function Url then copy the URL, save this for now.

Get function url


14: Login back into the Marketplacer Operator Portal, select Configuration then Webhooks to bring up your list of your webhooks, then click the pencil icon next to the webhook you want to edit:

Edit Webhook


15: Update the URL field with our Azure endpoint, and don’t forget to save, (also make sure that the webhook is enabled).

Update URL

Testing end to end

Now comes the moment of truth where we want to perform an end to end test of the Product flow functionality!

Before we do that, please check that you have done the following:

  • Updated the config in Azure (and have saved it!)
    • Specifically, ensure that the commercetools access-token has not expired
  • Updated the code in our Azure function and re-deployed it
  • Checked the Azure function is running
  • You have updated the URL endpoint in the Marketplacer Webhook and that it is enabled

Having confirmed all of the above, create another advert in Marketplacer, using whatever method you used previously.

16: To check the status of the webhook, return to your list of webhooks and click the cross icon beside your advert create webhook:

Webhook status


17: This should reveal that a webhook has been sent (a HTTP 200 has been returned). To determine the body response from the Azure function, click on the cross icon next to the sent event:

Webhook response state


18: Check that the body of the response from the Azure webhook should be Product Created, remembering that we do also send HTTP 200 responses under other conditions.

Successful Creation


Congratulations! We have set up the first leg of our Connected Integration!