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:
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);
}
}
}
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);
}
}
}
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; } } }
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}");
}
}
}
}