All Articles

Sitecore Powershell Extensions Example Scripts - 3

This article is the third one in my series of Sitecore Powershell Script Examples.

You can get all my posts on Sitecore Powershell Extensions Example Scripts here.

I had a great time working with JSON files and Web APIs with Sitecore Powershell and the below article lists down some helpers and snippets you can use.

Sitecore Powershell Extensions

Sitecore Powershell Extensions a.k.a SPE is one of my favorite modules for Sitecore. You can automate/build tools for most tasks you face as a Sitecore Developer. You can read more about SPEĀ here.

Directions to use

The scripts that I have posted below can be pasted and directly executed on the Powershell ISE provided by SPE.

Please modify the variables as per your requirement before executing them.

Read-ItemsFromJson

I had a requirement to build items from an API result. As the result of the API was JSON content, all I had to do was save the JSON file and then use the same to build my items.

I hit the API, saved the result as a TXT file, and then ran the below function.

Class MyClass {
	[string] $Title;
    [string] $Heading;
	
	MyClass([PSObject]$classEntry) {
        # You can read the values in JSON with the name
        <#
          In my example the structure of the JSON will be
          {
              "title": "John Doe",
              "heading": "John Doe"
          }
          Modify below code as needed
        #>
        $this.Title = $classEntry.title;
        $this.Heading = $classEntry.heading;
    }
    CreateMyItem() {
        # Logic to create your item
    }
}
function Read-ItemsFromJson {
    $File = Receive-File $SitecoreTempFolder
    if ($File -ne "cancel" -and $File.EndsWith(".txt")) {
        $json = Get-Content $File | Out-String | ConvertFrom-Json
        [MyClass[]]$AllItems = @()
        <#
          The JSON structure in my example will be as below
          {
            "success": "true",
            "objects":[
                {
                    "title": "John Doe",
                    "heading": "John Doe"
                },		
            ]
          }
          
          Modify the code as needed
        #>
        foreach ($entry in $json.objects) {
            if ($null -ne $entry) {
                $item = [MyClass]::new($entry)
                if ($null -ne $item) {
                    $AllItems += $item
                }
            }
        }
    }
    Remove-Item $File
    return $AllItems
}

Usage:

$MyItems = Read-ItemsFromJson
foreach ($item in $MyItems) {
   $item.CreateMyItem()
}

Hitting an API endpoint

I had a similar requirement as above, but this time it was more complicated.

I had to hit an API with pagination.

The paging was not numbered either. I was getting the API key to the next page in the current page API result.

The easiest approach was to save the JSON response for each page and use the previous approach. But, that would be too much manual work.

For some of the APIs, I could use the below command to read the response:

$Tags = (Invoke-RestMethod -Uri "https://api.myapi.com/path/to/api/tags").results
<#
 The JSON structure of the result was as below:
 {
	"success": "true",
	"results":[
		{
			
		},		
	]
}
#>

For the API with pagination, I had to use the below method:

I had to extend the Read-ItemsFromJson a bit:

<#
 The JSON structure of the result was as below:
 {
	"success": "true",
	"results":[
		{
			
		},		
	]
}
#>
Class MyClass {
	[string] $Title;
    [string] $Heading;
	
	MyClass([PSObject]$classEntry) {
        $this.Title = $classEntry.title;
        $this.Heading = $classEntry.heading;
    }
	CreateMyItem() {
        # Logic to create your item
    }
}
function Read-ItemsFromJson {
    Param([PSObject] $Json)
    Process {
        [MyClass[]]$AllItems = @()
        foreach ($entry in $Json.results) {
            if ($null -ne $entry) {
                $item = [MyClass]::new($entry)
                if ($null -ne $item) {
                    $AllItems += $item
                }
            }
        }
    }
    End {
        return $AllItems
    }
}

I had to take a do-while approach to hit the API again and again until I reached the end. So, I built a helper to re-generate the API endpoint for every call.

<#
 The JSON structure of the result was as below:
 {
	"success": "true",
	"results":[
		{
			
		},		
	],
	"paging":
	{
		"next": 
		{
			"after": "afterKeyToNextPage"
		}
	}
}
#>

function Generate-ApiEndpoint {
    Param([System.Uri] $Url, [System.String] $AfterKey = "")
    Process {
        $UrlBuilder = [System.UriBuilder]$Url
        $nvCollection = [System.Web.HttpUtility]::ParseQueryString($UrlBuilder.Query)
        if ($AfterKey -ne "") {
            $nvCollection.Add("after", $AfterKey)
        }
        $UrlBuilder.Query = $nvCollection.ToString()
    }
    End {
        return $UrlBuilder.Uri
    }
}

$HostUrl = [System.Uri]"https://api.myapi.com/path/to/api/items"
do {    
    $ApiEndpoint = Generate-ApiEndpoint -Url $HostUrl -AfterKey $AfterLink
    Write-Host "Hitting API $ApiEndpoint"
    $ApiResult = Invoke-RestMethod -Uri $ApiEndpoint
    If ($null -ne $ApiResult -and $ApiResult -ne "") {
        $MyItems = Read-ItemsFromJson -Json $ApiResult
        If ($null -ne $MyItems -And $MyItems.Count -gt 0) {
            Write-Host "$($MyItems.Count) items found"
            foreach ($item in $MyItems) {
                $item.CreateMyItem()
            }
        }
        Else {
            Write-Host "No valid items found"
        }
        $AfterLink = $ApiResult.paging.next.after
    }
    Else {
        break
    }
} while ($null -ne $AfterLink -And $AfterLink -ne "")

Please do let me know in the comments if you feel a particular use case is missing and you need it in the article.

Feedback and Suggestions are welcome!

Happy Sitecoring!!