diff --git a/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs b/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs index ca96f1e..221115f 100644 --- a/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs +++ b/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs @@ -128,9 +128,10 @@ public virtual async Task PatchAsync(Patch patch, string name, string public virtual async Task PatchStatusAsync(Patch patch, string name, string? @namespace = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(patch); + var uri = string.IsNullOrWhiteSpace(@namespace) ? $"{this.Path}/{name}/status" : $"{this.Path}/namespace/{@namespace}/{name}/status"; var json = this.Serializer.SerializeToText(patch); using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, $"{this.Path}/status") { Content = content }, cancellationToken).ConfigureAwait(false); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, uri) { Content = content }, cancellationToken).ConfigureAwait(false); using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); return this.Serializer.Deserialize(json)!; diff --git a/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs b/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs index 74c878f..0353de4 100644 --- a/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs +++ b/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs @@ -57,6 +57,23 @@ public virtual async Task PatchResource(string name, [FromBody] P return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceCommand(name, null, patch, dryRun), cancellationToken).ConfigureAwait(false)); } + /// + /// Patches the specified resource status + /// + /// The patch to apply + /// The name of the resource to patch + /// A boolean indicating whether or not to persist changes + /// A + /// A new + [HttpPatch("{name}/status")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task PatchResourceStatus(string name, [FromBody] Patch patch, bool dryRun = false, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceStatusCommand(name, null, patch, dryRun), cancellationToken).ConfigureAwait(false)); + } + /// /// Deletes the specified resource /// diff --git a/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceCommand.cs b/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceCommand.cs index a0b4746..217ac8a 100644 --- a/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceCommand.cs +++ b/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceCommand.cs @@ -11,17 +11,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -using Json.Patch; -using Neuroglia.Data.Infrastructure.ResourceOriented; -using Neuroglia.Data.PatchModel.Services; -using Neuroglia.Data; -using System.Xml.Linq; -using Neuroglia.Serialization.Json; - namespace CloudStreams.Core.Application.Commands.Resources.Generic; /// -/// Represents the used to delete an existing +/// Represents the used to patch an existing /// /// The type of to patch public class PatchResourceCommand diff --git a/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceStatusCommand.cs b/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceStatusCommand.cs new file mode 100644 index 0000000..e606b48 --- /dev/null +++ b/src/core/CloudStreams.Core.Application/Commands/Resources/Generic/PatchResourceStatusCommand.cs @@ -0,0 +1,80 @@ +// Copyright © 2024-Present The Cloud Streams Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace CloudStreams.Core.Application.Commands.Resources.Generic; + +/// +/// Represents the used to patch the status of an existing +/// +/// The type of to patch +public class PatchResourceStatusCommand + : Command + where TResource : class, IResource, new() +{ + + /// + /// Initializes a new + /// + /// The name of the to patch + /// The namespace the to patch belongs to + /// The patch to apply + /// A boolean indicating whether or not to persist changes + public PatchResourceStatusCommand(string name, string? @namespace, Patch patch, bool dryRun) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + this.Patch = patch ?? throw new ArgumentNullException(nameof(patch)); + this.DryRun = dryRun; + } + + /// + /// Gets the name of the to patch + /// + public string Name { get; } + + /// + /// Gets the name of the to patch + /// + public string? Namespace { get; } + + /// + /// Gets the patch to apply + /// + public Patch Patch { get; } + + /// + /// Gets a boolean indicating whether or not to persist changes + /// + public bool DryRun { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to patch +/// The service used to manage s +public class PatchResourceStatusCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(PatchResourceStatusCommand command, CancellationToken cancellationToken) + { + var resource = await repository.PatchStatusAsync(command.Patch, command.Name, command.Namespace, null, command.DryRun, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} \ No newline at end of file diff --git a/src/core/CloudStreams.Core.Application/Commands/Resources/PatchResourceStatusCommand.cs b/src/core/CloudStreams.Core.Application/Commands/Resources/PatchResourceStatusCommand.cs new file mode 100644 index 0000000..f8eca7d --- /dev/null +++ b/src/core/CloudStreams.Core.Application/Commands/Resources/PatchResourceStatusCommand.cs @@ -0,0 +1,105 @@ +// Copyright © 2024-Present The Cloud Streams Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace CloudStreams.Core.Application.Commands.Resources; + +/// +/// Represents the used to patch the status of an existing +/// +public class PatchResourceStatusCommand + : Command +{ + + /// + /// Initializes a new + /// + protected PatchResourceStatusCommand() { } + + /// + /// Initializes a new + /// + /// The API group the resource to patch belongs to + /// The version of the resource to patch + /// The plural name of the type of resource to patch + /// The name of the to patch + /// The namespace the to patch belongs to + /// The patch to apply + /// A boolean indicating whether or not to persist changes + public PatchResourceStatusCommand(string group, string version, string plural, string name, string? @namespace, Patch patch, bool dryRun) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Name = name; + this.Namespace = @namespace; + this.Patch = patch ?? throw new ArgumentNullException(nameof(patch)); + this.DryRun = dryRun; + } + + /// + /// Gets the API group the resource to patch belongs to + /// + public string Group { get; } = null!; + + /// + /// Gets the version of the resource to patch + /// + public string Version { get; } = null!; + + /// + /// Gets the plural name of the type of resource to patch + /// + public string Plural { get; } = null!; + + /// + /// Gets the name of the to patch + /// + public string Name { get; } = null!; + + /// + /// Gets the name of the to patch + /// + public string? Namespace { get; } + + /// + /// Gets the patch to apply + /// + public Patch Patch { get; } = null!; + + /// + /// Gets a boolean indicating whether or not to persist changes + /// + public bool DryRun { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class PatchResourceStatusCommandHandler(IResourceRepository repository) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(PatchResourceStatusCommand command, CancellationToken cancellationToken) + { + var resource = await repository.PatchSubResourceAsync(command.Patch, command.Group, command.Version, command.Plural, command.Name, "status", command.Namespace, null, command.DryRun, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} \ No newline at end of file diff --git a/src/core/CloudStreams.Core.Application/Extensions/IServiceCollectionExtensions.cs b/src/core/CloudStreams.Core.Application/Extensions/IServiceCollectionExtensions.cs index 9f9f5dd..64dde3c 100644 --- a/src/core/CloudStreams.Core.Application/Extensions/IServiceCollectionExtensions.cs +++ b/src/core/CloudStreams.Core.Application/Extensions/IServiceCollectionExtensions.cs @@ -104,6 +104,12 @@ public static IServiceCollection AddCoreApiCommands(this IServiceCollection serv handlerImplementationType = typeof(PatchResourceCommandHandler<>).MakeGenericType(resourceType); services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + commandType = typeof(PatchResourceStatusCommand<>).MakeGenericType(resourceType); + resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); + handlerImplementationType = typeof(PatchResourceStatusCommandHandler<>).MakeGenericType(resourceType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + commandType = typeof(DeleteResourceCommand<>).MakeGenericType(resourceType); resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType);