How to Migrate Cloud Assets in Sitecore Content Hub with WebClient SDK

Welcome to my blog, Sitecorians!👋


We all know in the digital era, managing digital assets efficiently is crucial for organizations to deliver engaging customer experiences. Sitecore Content Hub provides a unified platform to manage, collaborate, and distribute your digital assets effectively. Migrating cloud assets into Sitecore Content Hub caters to various use cases, crucial for organizations looking to enhance their digital asset management strategy.

However, many businesses today store their assets across various cloud storage services, such as Azure Blob Storage.

Whether you're a developer, or just a curious mind, this blog is your step-by-step guide to migrating cloud assets from Azure Cloud Storage to Sitecore Content Hub. Let's start with the implementation.


Use Case: Migrating Azure Cloud Assets to Sitecore Content Hub

Note: In my use case, while my specific approach involves a console application for manual execution, you can adapt this approach to automate it within your applications. I will be focusing on migrating assets from an Azure Storage account to Sitecore Content Hub. This process involves several key steps, from setting up our environment to creating and uploading assets into Content Hub.


Steps for Implementation:

Step 1: Setting Up Your Project

In my use case, i have created a C# console application in Visual Studio. 
Next, I installed all the the necessary NuGet packages: Sitecore Content Hub SDK for interacting with Content Hub and Azure.Storage.Blobs for Azure Blob Storage operations.

Step 2: Authenticate with Sitecore Content Hub

I created a class to authenticate with Sitecore Content Hub using OAuth credentials and tested the connection by making a simple API call. This method returns an instance of IWebMClient, which is then used to interact with the Content Hub.
 
using Stylelabs.M.Sdk.WebClient;
using Stylelabs.M.Sdk.WebClient.Authentication;
using System;

namespace ConsoleApp1
{
    internal static class MConnector
    {
        internal static IWebMClient Client()
        {
            Uri endpoint = new Uri("https://your-sitecore-content-hub-instance/");
            // Enter your credentials here
            OAuthPasswordGrant oauth = new OAuthPasswordGrant
            {
                ClientId = "your-client-id",
                ClientSecret = "your-client-secret",
                UserName = "your-username",
                Password = "your-password"
            };

            // Create the Web SDK client
            return MClientFactory.CreateMClient(endpoint, oauth);
        }
    }
}

Step 3: Access Azure Blob Storage

Next, I created a class to link to our Azure Blob Storage and get details about the blobs. It goes through each blob in our container, making SAS URIs. These URIs allow us to temporarily share our private media items safely, so they can be accessed by Sitecore Content Hub.
 
using System;
using System.IO;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Sas;

namespace ConsoleApp1
{
    internal static class AzureAssetImporter
{
    public static async Task GetAzureAsset()
    {
        string connectionString = "your-connection-string-storage-account";
        string containerName = "your-container-name";
        try
        {
            BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
            BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
            Log.Information("Successfully connected to Azure Blob Storage.");
            Console.WriteLine("Successfully connected to Azure Blob Storage.");
            // List all blobs in the container
            var pages = containerClient.GetBlobs().AsPages(default, pageSizeHint: 100);

            foreach (Page<BlobItem> page in pages)
            {
                foreach (BlobItem item in page.Values)
                {
                    await ProcessBlobs(item, containerClient, containerName);
                }
            }
            Log.Information("Completed processing all blobs in container: {ContainerName}", containerName);
            
            Console.WriteLine("Completed processing all blobs in container: {ContainerName}", containerName);
            Console.WriteLine($"Total upload time: {stopwatch.Elapsed.TotalSeconds} seconds");
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error occurred while processing Azure blobs.");
        }
        
        Console.WriteLine("Completed processing all blobs in container: {0}", containerName);
        Console.WriteLine("Total upload time: {0} seconds", stopwatch.Elapsed.TotalSeconds);

        // Wait for a key press before closing the console window
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }

    public static async Task ProcessBlobs(BlobItem blobItem, BlobContainerClient containerClient, string containerName)
    {
        var blobClient = containerClient.GetBlobClient(blobItem.Name);
        BlobSasBuilder sasBuilder = new BlobSasBuilder()
        {
            BlobContainerName = containerName,
            BlobName = blobItem.Name,
            Resource = "b", // "b" for blob
            StartsOn = DateTimeOffset.UtcNow,
            ExpiresOn = DateTimeOffset.UtcNow.AddHours(24)
        };
        sasBuilder.SetPermissions(BlobSasPermissions.Read);
        Uri sasUri = blobClient.GenerateSasUri(sasBuilder);
        Log.Information("Processing blob: {BlobName} with SAS URI: {SasUri}", blobItem.Name, sasUri);
        string filepath = sasUri.ToString();
        string filename = Path.GetFileNameWithoutExtension(blobClient.Name);
        await CreateEntity.CreateAssetEntity(filename, filepath);
    }
}
}

Step 4: Create Entities in Sitecore Content Hub

To create entities in Sitecore Content Hub, we need to fill out key fields such as Title, Asset Type, Final Life Cycle, Content Repository, and Identifier. If we don't provide an Identifier, one will be randomly generated. Additionally, we use a fetch job to attach the media item from Azure to our entity in Content Hub.

 
using Stylelabs.M.Framework.Essentials.LoadOptions;
using Stylelabs.M.Sdk;
using Stylelabs.M.Sdk.Contracts.Base;
using Stylelabs.M.Sdk.Models.Jobs;
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal static class CreateEntity
    {
        public static async Task<long> CreateAssetEntity(string AssetName, string FilePathUrl)
        {
            // Create the entity resource
            IEntity asset = await MConnector.Client().EntityFactory
                .CreateAsync(Constants.Asset.DefinitionName, CultureLoadOption.Default).ConfigureAwait(false);

            asset.SetPropertyValue("Title", AssetName);

            var standardContentRepository = ReadEntity.GetEntityIdByIdentifier("M.Content.Repository.Standard");
            var contentRepositoryRelation = asset.GetRelation<IChildToManyParentsRelation>("ContentRepositoryToAsset");
            contentRepositoryRelation.Parents.Add(standardContentRepository.Value);

            var finalLifeCycleCreated = ReadEntity.GetEntityIdByIdentifier("M.Final.LifeCycle.Status.Approved");
            var finalLifeCycleRelation = asset.GetRelation<IChildToOneParentRelation>("FinalLifeCycleStatusToAsset");
            finalLifeCycleRelation.Parent = finalLifeCycleCreated.Value;

            var assetType = ReadEntity.GetEntityIdByIdentifier("M.AssetType.AzureAsset");
            var assetTypeRelation = asset.GetRelation<IChildToOneParentRelation>("AssetTypeToAsset");
            assetTypeRelation.Parent = assetType.Value;

            // Create the asset
            var assetId = await MConnector.Client().Entities.SaveAsync(asset).ConfigureAwait(false);

            //Create a fetch job to associate a file
            Uri file = new Uri(FilePathUrl);
            if (file != null)
                await CreateFetchJob(assetId, file);
            return assetId;
        }

        private static async Task CreateFetchJob(long? assetId, Uri resource)
        {
            var fetchJobRequest = new WebFetchJobRequest("File", assetId.Value);
            fetchJobRequest.Urls.Add(resource);

            await MConnector.Client().Jobs.CreateFetchJobAsync(fetchJobRequest).ConfigureAwait(false);
        }
    }
}


Below is the code for the 'Read Entity' class, as referenced in the 'Create Entity' class

using Stylelabs.M.Base.Querying;
using System.Linq;
namespace ConsoleApp1
{
    internal static class ReadEntity
    {
        public static long? GetEntityIdByIdentifier(string identifier)
        {
            var query = Query.CreateQuery(entities => from e in entities
                                                      where e.Identifier == identifier
                                                      select e);

            return MConnector.Client().Querying.SingleIdAsync(query).Result;
        }
    }
}

Step 5: Call the Asset Importer from Code

using System.Threading.Tasks;
using System;

namespace ConsoleApp1
{
    public class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                await AzureAssetImporter.GetAzureAsset();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception Occurred: Unable to connect! {ex.Message}");
            }
        }
    }
}


Conclusion:




By following these steps, you will be able to migrate assets from your Azure storage account to Sitecore Content Hub successfully. Stay tuned for my next blog, where we'll dive into more insights and tips to help you on your Sitecore journey. Keep exploring and learning!😊

Post a Comment (0)
Previous Post Next Post