Skip to content

Commit

Permalink
Add xml doc for CosmosDbMigration package
Browse files Browse the repository at this point in the history
  • Loading branch information
kanilsz committed Jul 7, 2024
1 parent e4be46a commit 1383ab2
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,30 @@

namespace MSA.BuildingBlocks.CosmosDbMigration;

/// <summary>
/// This abstract class provides base operations for Cosmos Db container migrations.
/// </summary>
public abstract class BaseContainerMigration
{
protected CosmosClient _cosmosClient;
protected Container _container;
protected ContainerProperties _containerProperties;
protected ILogger<BaseContainerMigration> _logger;

protected BaseContainerMigration(CosmosClient cosmosClient, string databaseId, string containerId, ILogger<BaseContainerMigration> logger)
/// <summary>
/// Initializes a new instance of the <see cref="BaseContainerMigration"/> class.
/// </summary>
/// <param name="cosmosClient">The Cosmos client instance.</param>
/// <param name="databaseId">The ID of the existing database containing the target container.</param>
/// <param name="containerId">The ID of the existing target container.</param>
/// <param name="logger">Optional logger instance. If not provided, a default logger will be created.</param>
/// <exception cref="ArgumentNullException">Thrown if cosmosClient is null.</exception>
/// <exception cref="ArgumentException">Thrown if databaseId or containerId is null or empty.</exception>
protected BaseContainerMigration(
CosmosClient cosmosClient,
string databaseId,
string containerId,
ILogger<BaseContainerMigration>? logger = default)
{
ArgumentNullException.ThrowIfNull(cosmosClient);
ArgumentException.ThrowIfNullOrEmpty(databaseId);
Expand All @@ -23,22 +39,72 @@ protected BaseContainerMigration(CosmosClient cosmosClient, string databaseId, s
_cosmosClient = cosmosClient;
_container = cosmosClient.GetContainer(databaseId, containerId);
_containerProperties = _container.ReadContainerAsync().GetAwaiter().GetResult();
_logger = logger;
_logger = logger ?? new LoggerFactory().CreateLogger<BaseContainerMigration>();
}

/// <summary>
/// Retrieves a list of items from the container using a provided query.
/// </summary>
/// <param name="query">Optional SQL query to execute. Defaults to "SELECT * FROM c".</param>
/// <returns>An asynchronous task that returns a list of ExpandoObject instances representing the retrieved items.</returns>
public abstract Task<IList<ExpandoObject>> GetItems(string query = "SELECT * FROM c");

/// <summary>
/// Switches the target container to a different container within the Cosmos DB.
/// </summary>
/// <param name="containerId">The ID of the new target container.</param>
/// <param name="databaseId">Optional ID of the database containing the new target container. If not provided, uses the current database.</param>
/// <returns>An asynchronous task that completes the container switch.</returns>
public abstract Task SwitchToContainer(string containerId, string? databaseId = null);

/// <summary>
/// Upserts a list of items into the target container.
/// </summary>
/// <typeparam name="T">The type of the items to be upserted.</typeparam>
/// <param name="items">The list of items to upsert.</param>
/// <returns>An asynchronous task that completes the upsert operation.</returns>
public abstract Task UpsertItems<T>(IList<T> items);

/// <summary>
/// Removes items from the container based on a provided query.
/// </summary>
/// <param name="query">The SQL query to identify items for removal.</param>
/// <returns>An asynchronous task that completes the removal operation.</returns>
public abstract Task RemoveItemsByQuery(string query);

public abstract Task AddPropertyToItems(IList<ExpandoObject> items, string propertyPath, string propertyName, object value);

/// <summary>
/// Adds a new property to a list of ExpandoObject instances.
/// </summary>
/// <param name="items">The list of items to modify.</param>
/// <param name="propertyName">The name of the property to add.</param>
/// <param name="value">The value to assign to the new property.</param>
/// <returns>An asynchronous task that completes the property addition.</returns>
public abstract Task AddPropertyToItems(IList<ExpandoObject> items, string propertyName, object value);

/// <summary>
/// Adds a new property to a list of ExpandoObject instances.
/// </summary>
/// <param name="items">The list of items to modify.</param>
/// <param name="propertyPath">Optional path to a nested property within the object. If omitted, adds to the root level.</param>
/// <param name="propertyName">The name of the property to add.</param>
/// <param name="value">The value to assign to the new property.</param>
/// <returns>An asynchronous task that completes the property addition.</returns>
public abstract Task AddPropertyToItems(IList<ExpandoObject> items, string propertyPath, string propertyName, object value);

/// <summary>
/// Removes a property from a list of ExpandoObject instances.
/// </summary>
/// <param name="items">The list of items to modify.</param>
/// <param name="propertyName">The name of the property to remove.</param>
/// <returns>An asynchronous task that completes the property removal.</returns>
public abstract Task RemovePropertyFromItems(IList<ExpandoObject> items, string propertyName);

/// <summary>
/// Removes a property from a list of ExpandoObject instances, targeting a specific nested location.
/// </summary>
/// <param name="items">The list of items to modify.</param>
/// <param name="propertyPath">The path to the nested property to remove.</param>
/// <param name="propertyName">The name of the property to remove within the specified path.</param>
/// <returns>An asynchronous task that completes the property removal.</returns>
public abstract Task RemovePropertyFromItems(IList<ExpandoObject> items, string propertyPath, string propertyName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,30 @@

namespace MSA.BuildingBlocks.CosmosDbMigration;

/// <summary>
/// This abstract class provides base operations for Cosmos Db migrations.
/// </summary>
public abstract class BaseDatabaseMigration
{
protected CosmosClient _cosmosClient;
protected Container _container;
protected ContainerProperties _containerProperties;
protected ILogger<BaseDatabaseMigration> _logger;

protected BaseDatabaseMigration(CosmosClient cosmosClient, string databaseId, string containerId, ILogger<BaseDatabaseMigration> logger)
/// <summary>
/// Initializes a new instance of the <see cref="BaseDatabaseMigration"/> class.
/// </summary>
/// <param name="cosmosClient">The Cosmos client instance.</param>
/// <param name="databaseId">The ID of the existing database containing the target container.</param>
/// <param name="containerId">The ID of the existing target container.</param>
/// <param name="logger">Optional logger instance. If not provided, a default logger will be created.</param>
/// <exception cref="ArgumentNullException">Thrown if cosmosClient is null.</exception>
/// <exception cref="ArgumentException">Thrown if databaseId or containerId is null or empty.</exception>
protected BaseDatabaseMigration(
CosmosClient cosmosClient,
string databaseId,
string containerId,
ILogger<BaseDatabaseMigration>? logger = default)
{
ArgumentNullException.ThrowIfNull(cosmosClient);
ArgumentException.ThrowIfNullOrEmpty(databaseId);
Expand All @@ -24,27 +40,71 @@ protected BaseDatabaseMigration(CosmosClient cosmosClient, string databaseId, st
_cosmosClient = cosmosClient;
_container = cosmosClient.GetContainer(databaseId, containerId);
_containerProperties = _container.ReadContainerAsync().GetAwaiter().GetResult();
_logger = logger;
_logger = logger ?? new LoggerFactory().CreateLogger<BaseDatabaseMigration>();
}

public abstract Task CreateContainer(string containerId, string partitionKey);

/// <summary>
/// Creates a new container as a clone of the existing target container, including its data and indexing policy.
/// </summary>
/// <param name="containerId">The ID for the new container.</param>
/// <param name="partitionKey">The partition key path for the new container.</param>
/// <returns>An asynchronous task that completes the container cloning.</returns>
/// <exception cref="ArgumentException">Thrown if containerId or partitionKey is null or empty.</exception>
public abstract Task CloneContainer(string containerId, string partitionKey);

/// <summary>
/// Creates a new container within the current database with the specified ID and partition key path.
/// </summary>
/// <param name="containerId">The ID for the new container. Must not be null or empty.</param>
/// <param name="partitionKey">The partition key path for the new container.</param>
/// <returns>An asynchronous task that completes the container creation.</returns>
/// <exception cref="ArgumentException">Thrown if containerId or partitionKey is null or empty.</exception>
public abstract Task CreateContainer(string containerId, string partitionKey);

/// <summary>
/// Deletes the target container.
/// </summary>
/// <returns>An asynchronous task that completes the container deletion.</returns>
public abstract Task DeleteContainer();

/// <summary>
/// Recreates the target container with a new specified partition key path. Data and indexing policy are not preserved.
/// </summary>
/// <param name="partitionKey">The new partition key path for the target container.</param>
/// <returns>An asynchronous task that completes the container recreation.</returns>
public abstract Task RecreateContainerWithNewPartitionKey(string partitionKey);

/// <summary>
/// Updates the indexing policy for the target container.
/// </summary>
/// <param name="includedPaths">Optional collection of included paths for indexing.</param>
/// <param name="excludedPaths">Optional collection of excluded paths for indexing.</param>
/// <param name="compositePaths">Optional collection of composite paths for indexing.</param>
/// <returns>An asynchronous task that completes the indexing policy update.</returns>
public abstract Task AddIndexingPolicy(
Collection<IncludedPath>? includedPaths = null,
Collection<ExcludedPath>? excludedPaths = null,
Collection<Collection<CompositePath>>? compositePaths = null);

/// <summary>
/// Replaces the existing indexing policy for the target container.
/// </summary>
/// <param name="includedPaths">Optional collection of included paths for indexing.</param>
/// <param name="excludedPaths">Optional collection of excluded paths for indexing.</param>
/// <param name="compositePaths">Optional collection of composite paths for indexing.</param>
/// <returns>An asynchronous task that completes the indexing policy replacement.</returns>
public abstract Task ReplaceIndexingPolicy(
Collection<IncludedPath>? includedPaths = null,
Collection<ExcludedPath>? excludedPaths = null,
Collection<Collection<CompositePath>>? compositePaths = null);

/// <summary>
/// Switches the target container to a different container within the Cosmos DB.
/// </summary>
/// <param name="containerId">The ID of the new target container.</param>
/// <param name="databaseId">Optional ID of the database containing the new target container. If not provided, uses the current database.</param>
/// <returns>An asynchronous task that completes the container switch.</returns>
/// <exception cref="ArgumentNullException">Thrown if containerId is null or empty.</exception>
public abstract Task SwitchToContainer(string containerId, string? databaseId = null);

protected abstract Task<(IList<ExpandoObject>, double)> GetItems(string query = "SELECT * FROM c");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,24 @@

namespace MSA.BuildingBlocks.CosmosDbMigration;

public class ContainerMigration(CosmosClient cosmosClient, string databaseId, string containerId, ILogger<ContainerMigration> logger)
/// <summary>
/// This class inherits from <see cref="BaseContainerMigration"/> and provides an implementation for Cosmos DB container migration operations.
/// </summary>
/// <param name="cosmosClient">The Cosmos client instance.</param>
/// <param name="databaseId">The ID of the existing database containing the target container.</param>
/// <param name="containerId">The ID of the existing target container.</param>
/// <param name="logger">Optional logger instance. If not provided, a default logger will be created.</param>
/// <exception cref="ArgumentNullException">Thrown if cosmosClient is null.</exception>
/// <exception cref="ArgumentException">Thrown if databaseId or containerId is null or empty.</exception>
public class ContainerMigration(
CosmosClient cosmosClient,
string databaseId,
string containerId,
ILogger<ContainerMigration>? logger = default)
: BaseContainerMigration(cosmosClient, databaseId, containerId, logger)
{

/// <inheritdoc/>
public override async Task<IList<ExpandoObject>> GetItems(string query = "SELECT * FROM c")
{
double requestCharge = 0.0;
Expand All @@ -32,6 +47,7 @@ public override async Task<IList<ExpandoObject>> GetItems(string query = "SELECT
return items;
}

/// <inheritdoc/>
public override async Task SwitchToContainer(string containerId, string? databaseId = null)
{
ArgumentException.ThrowIfNullOrEmpty(containerId);
Expand All @@ -42,6 +58,7 @@ public override async Task SwitchToContainer(string containerId, string? databas
_logger.LogInformation("Switching to container {ContainerId} and database {DatabaseId} is successful", containerId, databaseId);
}

/// <inheritdoc/>
public override async Task UpsertItems<T>(IList<T> items)
{
ArgumentNullException.ThrowIfNull(items);
Expand All @@ -56,6 +73,7 @@ public override async Task UpsertItems<T>(IList<T> items)
_logger.LogInformation("{OperationName} with items count {Count} cost {Charge} RUs.", nameof(UpsertItems), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task RemoveItemsByQuery(string query)
{
ArgumentException.ThrowIfNullOrWhiteSpace(query);
Expand All @@ -77,6 +95,7 @@ public override async Task RemoveItemsByQuery(string query)
_logger.LogInformation("{OperationName} with items count {Count} cost {Charge} RUs.", nameof(RemoveItemsByQuery), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task AddPropertyToItems(IList<ExpandoObject> items, string propertyPath, string propertyName, object value)
{
ArgumentNullException.ThrowIfNull(items);
Expand All @@ -100,6 +119,7 @@ public override async Task AddPropertyToItems(IList<ExpandoObject> items, string
_logger.LogInformation("{OperationName} with items count {Count} cost {Charge} RUs.", nameof(AddPropertyToItems), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task AddPropertyToItems(IList<ExpandoObject> items, string propertyName, object value)
{
ArgumentNullException.ThrowIfNull(items);
Expand All @@ -121,6 +141,7 @@ public override async Task AddPropertyToItems(IList<ExpandoObject> items, string
_logger.LogInformation("{OperationName} with items count {Count} cost {Charge} RUs.", nameof(AddPropertyToItems), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task RemovePropertyFromItems(IList<ExpandoObject> items, string propertyName)
{
ArgumentNullException.ThrowIfNull(items);
Expand All @@ -141,6 +162,7 @@ public override async Task RemovePropertyFromItems(IList<ExpandoObject> items, s
_logger.LogInformation("{OperationName} with items count {Count} cost {Charge} RUs.", nameof(RemovePropertyFromItems), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task RemovePropertyFromItems(IList<ExpandoObject> items, string propertyPath, string propertyName)
{
ArgumentNullException.ThrowIfNull(items);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,23 @@

namespace MSA.BuildingBlocks.CosmosDbMigration;

public class DatabaseMigration(CosmosClient cosmosClient, string databaseId, string containerId, ILogger<DatabaseMigration> logger)
/// <summary>
/// This class inherits from <see cref="BaseDatabaseMigration"/> and provides an implementation for Cosmos DB migration operations.
/// </summary>
/// <param name="cosmosClient">The Cosmos client instance.</param>
/// <param name="databaseId">The ID of the existing database containing the target container.</param>
/// <param name="containerId">The ID of the existing target container.</param>
/// <param name="logger">Optional logger instance. If not provided, a default logger will be created.</param>
/// <exception cref="ArgumentNullException">Thrown if cosmosClient is null.</exception>
/// <exception cref="ArgumentException">Thrown if databaseId or containerId is null or empty.</exception>
public class DatabaseMigration(
CosmosClient cosmosClient,
string databaseId,
string containerId,
ILogger<DatabaseMigration>? logger = default)
: BaseDatabaseMigration(cosmosClient, databaseId, containerId, logger)
{
/// <inheritdoc/>
public override async Task CloneContainer(string containerId, string partitionKey)
{
ArgumentException.ThrowIfNullOrEmpty(containerId);
Expand All @@ -28,13 +42,15 @@ public override async Task CloneContainer(string containerId, string partitionKe
_logger.LogInformation("{OperationName} with items count {Count} operation cost {Charge} RUs.", nameof(CloneContainer), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task DeleteContainer()
{
ContainerResponse response = await _container.DeleteContainerAsync().ConfigureAwait(false);

_logger.LogInformation("{OperationName} operation cost {Charge} RUs.", nameof(DeleteContainer), response.RequestCharge);
}

/// <inheritdoc/>
public override async Task CreateContainer(string containerId, string partitionKey)
{
ArgumentException.ThrowIfNullOrEmpty(containerId);
Expand All @@ -45,6 +61,7 @@ public override async Task CreateContainer(string containerId, string partitionK
_logger.LogInformation("{OperationName} operation cost {Charge} RUs.", nameof(CreateContainer), response.RequestCharge);
}

/// <inheritdoc/>
public override async Task RecreateContainerWithNewPartitionKey(string partitionKey)
{
ArgumentException.ThrowIfNullOrEmpty(partitionKey);
Expand All @@ -60,6 +77,7 @@ public override async Task RecreateContainerWithNewPartitionKey(string partition
_logger.LogInformation("{OperationName} with items count {Count} operation cost {Charge} RUs.", nameof(RecreateContainerWithNewPartitionKey), items.Count, requestCharge);
}

/// <inheritdoc/>
public override async Task ReplaceIndexingPolicy(
Collection<IncludedPath>? includedPaths = null,
Collection<ExcludedPath>? excludedPaths = null,
Expand Down Expand Up @@ -102,6 +120,7 @@ public override async Task ReplaceIndexingPolicy(
_logger.LogInformation("{OperationName} operation cost {Charge} RUs.", nameof(ReplaceIndexingPolicy), response.RequestCharge);
}

/// <inheritdoc/>
public override async Task SwitchToContainer(string containerId, string? databaseId = null)
{
ArgumentException.ThrowIfNullOrEmpty(containerId);
Expand All @@ -112,6 +131,7 @@ public override async Task SwitchToContainer(string containerId, string? databas
_logger.LogInformation("Switching to container {ContainerId} and database {DatabaseId} is successful", containerId, databaseId);
}

/// <inheritdoc/>
public override async Task AddIndexingPolicy(
Collection<IncludedPath>? includedPaths = null,
Collection<ExcludedPath>? excludedPaths = null,
Expand Down

0 comments on commit 1383ab2

Please sign in to comment.