diff --git a/src/PodmanClient/Internal/PodmanHttpResults.cs b/src/PodmanClient/Internal/PodmanHttpResults.cs index 3571070..7797672 100644 --- a/src/PodmanClient/Internal/PodmanHttpResults.cs +++ b/src/PodmanClient/Internal/PodmanHttpResults.cs @@ -1,8 +1,8 @@ using System.Net; +using System.Text.Json; using Microsoft.Extensions.Logging; -using MaksIT.Core.Extensions; using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.Results; @@ -13,7 +13,7 @@ internal static class PodmanHttpResults { if (string.IsNullOrWhiteSpace(content)) return "Podman API request failed."; - var error = content.ToObject(); + var error = JsonSerializer.Deserialize(content, PodmanJsonContext.Default.ErrorResponseDto); return string.IsNullOrWhiteSpace(error?.Message) ? content : error.Message; } diff --git a/src/PodmanClient/Internal/PodmanNdjsonStreams.cs b/src/PodmanClient/Internal/PodmanNdjsonStreams.cs index eed2592..a0aacf7 100644 --- a/src/PodmanClient/Internal/PodmanNdjsonStreams.cs +++ b/src/PodmanClient/Internal/PodmanNdjsonStreams.cs @@ -1,6 +1,7 @@ +using System.Text.Json; + using Microsoft.Extensions.Logging; -using MaksIT.Core.Extensions; using MaksIT.PodmanClientDotNet.Dtos.Build; using MaksIT.PodmanClientDotNet.Dtos.Image; using MaksIT.Results; @@ -24,7 +25,7 @@ internal static class PodmanNdjsonStreams { if (!line.Contains("\"error\"", StringComparison.Ordinal)) continue; - var errorDetails = line.ToObject(); + var errorDetails = JsonSerializer.Deserialize(line, PodmanJsonContext.Default.PullImageResponseDto); var message = errorDetails?.Error ?? $"{operation} failed."; logger.LogError("{Operation} failed: {Message}", operation, message); return Result.BadRequest(message); @@ -46,7 +47,7 @@ internal static class PodmanNdjsonStreams { if (string.IsNullOrWhiteSpace(line)) continue; - var progress = line.ToObject(); + var progress = JsonSerializer.Deserialize(line, PodmanJsonContext.Default.BuildProgressLineDto); if (progress is null) continue; diff --git a/src/PodmanClient/PodmanClient.Container.cs b/src/PodmanClient/PodmanClient.Container.cs index 6da7157..f564322 100644 --- a/src/PodmanClient/PodmanClient.Container.cs +++ b/src/PodmanClient/PodmanClient.Container.cs @@ -1,8 +1,9 @@ +using MaksIT.PodmanClientDotNet; using System.Net; +using System.Text.Json; using Microsoft.Extensions.Logging; -using MaksIT.Core.Extensions; using MaksIT.PodmanClientDotNet.Internal; using MaksIT.PodmanClientDotNet.Models; using MaksIT.PodmanClientDotNet.Dtos.Container; @@ -251,7 +252,9 @@ public partial class PodmanClient { return await PostJsonAsync( "/libpod/containers/create", "Create container", - createContainerParameters + createContainerParameters, + PodmanJsonContext.Default.CreateContainerRequest, + PodmanJsonContext.Default.CreateContainerResponseDto ).ConfigureAwait(false); } @@ -314,7 +317,7 @@ public partial class PodmanClient { if (response.IsSuccessStatusCode) { var value = !string.IsNullOrWhiteSpace(jsonResponse) - ? jsonResponse.ToObject() + ? JsonSerializer.Deserialize(jsonResponse, PodmanJsonContext.Default.DeleteContainerResponseDtoArray) : null; return PodmanHttpResults.Success(response.StatusCode, value); } @@ -336,7 +339,7 @@ public partial class PodmanClient { if (response.IsSuccessStatusCode) { var value = !string.IsNullOrWhiteSpace(jsonResponse) - ? jsonResponse.ToObject() + ? JsonSerializer.Deserialize(jsonResponse, PodmanJsonContext.Default.DeleteContainerResponseDtoArray) : null; return PodmanHttpResults.Success(response.StatusCode, value); } diff --git a/src/PodmanClient/PodmanClient.Containers.Api.cs b/src/PodmanClient/PodmanClient.Containers.Api.cs index e55a248..c275d84 100644 --- a/src/PodmanClient/PodmanClient.Containers.Api.cs +++ b/src/PodmanClient/PodmanClient.Containers.Api.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Container; using MaksIT.Results; @@ -16,6 +17,7 @@ public partial class PodmanClient { GetJsonAsync>( "/libpod/containers/json", "List containers", + PodmanJsonContext.Default.ListContainerListEntryDto, [ ("all", all.ToString().ToLowerInvariant()), ("limit", limit?.ToString()), @@ -27,7 +29,7 @@ public partial class PodmanClient { ); public Task> InspectContainerAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ContainerPath(name)}/json", "Inspect container", cancellationToken: cancellationToken); + GetJsonAsync($"{ContainerPath(name)}/json", "Inspect container", PodmanJsonContext.Default.ContainerInspectDto, cancellationToken: cancellationToken); public Task ContainerExistsAsync(string name, CancellationToken cancellationToken = default) => GetWithoutBodyAsync($"{ContainerPath(name)}/exists", "Container exists", cancellationToken: cancellationToken); @@ -58,6 +60,7 @@ public partial class PodmanClient { PostLibpodAsync( $"{ContainerPath(name)}/wait", "Wait container", + PodmanJsonContext.Default.ContainerWaitDto, query: condition is null ? null : [("condition", condition)], cancellationToken: cancellationToken ); @@ -92,6 +95,7 @@ public partial class PodmanClient { GetJsonAsync( $"{ContainerPath(name)}/stats", "Get container stats", + PodmanJsonContext.Default.ContainerStatsDto, [("stream", stream.ToString().ToLowerInvariant())], cancellationToken ); @@ -107,13 +111,14 @@ public partial class PodmanClient { query.Add(("containers", c)); } - return GetJsonAsync>("/libpod/containers/stats", "Get containers stats", query, cancellationToken); + return GetJsonAsync>("/libpod/containers/stats", "Get containers stats", PodmanJsonContext.Default.DictionaryStringContainerStatsDto, query, cancellationToken); } public Task> PruneContainersAsync(string? filters = null, CancellationToken cancellationToken = default) => PostLibpodAsync( "/libpod/containers/prune", "Prune containers", + PodmanJsonContext.Default.PruneReportDto, query: filters is null ? null : [("filters", filters)], cancellationToken: cancellationToken ); @@ -178,7 +183,7 @@ public partial class PodmanClient { ); public Task> MountContainerAsync(string name, CancellationToken cancellationToken = default) => - PostLibpodAsync($"{ContainerPath(name)}/mount", "Mount container", cancellationToken: cancellationToken); + PostLibpodAsync($"{ContainerPath(name)}/mount", "Mount container", PodmanJsonContext.Default.ContainerMountDto, cancellationToken: cancellationToken); public Task UnmountContainerAsync(string name, CancellationToken cancellationToken = default) => PostWithoutBodyAsync($"{ContainerPath(name)}/unmount", "Unmount container", cancellationToken: cancellationToken); @@ -228,7 +233,7 @@ public partial class PodmanClient { ); public Task> GetContainerChangesAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ContainerPath(name)}/changes", "Get container changes", cancellationToken: cancellationToken); + GetJsonAsync($"{ContainerPath(name)}/changes", "Get container changes", PodmanJsonContext.Default.ContainerChangesDto, cancellationToken: cancellationToken); public Task> CommitContainerAsync( string container, @@ -255,14 +260,14 @@ public partial class PodmanClient { query.Add(("changes", change)); } - return PostLibpodAsync("/libpod/commit", "Commit container", query: query, cancellationToken: cancellationToken); + return PostLibpodAsync("/libpod/commit", "Commit container", PodmanJsonContext.Default.ContainerCommitDto, query: query, cancellationToken: cancellationToken); } public Task> HealthCheckContainerAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ContainerPath(name)}/healthcheck", "Health check container", cancellationToken: cancellationToken); + GetJsonAsync($"{ContainerPath(name)}/healthcheck", "Health check container", PodmanJsonContext.Default.ContainerHealthCheckDto, cancellationToken: cancellationToken); public Task> GetMountedContainersAsync(CancellationToken cancellationToken = default) => - GetJsonAsync("/libpod/containers/showmounted", "Get mounted containers", cancellationToken: cancellationToken); + GetJsonAsync("/libpod/containers/showmounted", "Get mounted containers", PodmanJsonContext.Default.MountedContainersResponseDto, cancellationToken: cancellationToken); public Task> TopContainerAsync( string name, @@ -273,6 +278,7 @@ public partial class PodmanClient { GetJsonAsync( $"{ContainerPath(name)}/top", "Top container", + PodmanJsonContext.Default.ContainerTopDto, [ ("ps_args", psArgs), ("stream", stream.ToString().ToLowerInvariant()), diff --git a/src/PodmanClient/PodmanClient.Exec.cs b/src/PodmanClient/PodmanClient.Exec.cs index 051138f..88870ad 100644 --- a/src/PodmanClient/PodmanClient.Exec.cs +++ b/src/PodmanClient/PodmanClient.Exec.cs @@ -1,8 +1,8 @@ -using System.Text; +using MaksIT.PodmanClientDotNet; +using System.Text.Json; using Microsoft.Extensions.Logging; -using MaksIT.Core.Extensions; using MaksIT.PodmanClientDotNet.Internal; using MaksIT.PodmanClientDotNet.Models; using MaksIT.PodmanClientDotNet.Dtos.Exec; @@ -39,7 +39,9 @@ public partial class PodmanClient { return await PostJsonAsync( $"/libpod/containers/{Uri.EscapeDataString(containerName)}/exec", "Create exec", - execRequest + execRequest, + PodmanJsonContext.Default.CreateExecRequest, + PodmanJsonContext.Default.CreateExecResponseDto ).ConfigureAwait(false); } @@ -60,7 +62,8 @@ public partial class PodmanClient { var result = await PostJsonWithoutBodyAsync( $"/libpod/exec/{Uri.EscapeDataString(execId)}/start", "Start exec", - startExecRequest + startExecRequest, + PodmanJsonContext.Default.StartExecRequest ).ConfigureAwait(false); if (result.IsSuccess) @@ -72,7 +75,8 @@ public partial class PodmanClient { public Task> InspectExecAsync(string execId) => GetJsonAsync( $"/libpod/exec/{Uri.EscapeDataString(execId)}/json", - "Inspect exec" + "Inspect exec", + PodmanJsonContext.Default.InspectExecResponseDto ); public Task ResizeExecAsync(string execId, int height, int width, CancellationToken cancellationToken = default) => diff --git a/src/PodmanClient/PodmanClient.Generate.cs b/src/PodmanClient/PodmanClient.Generate.cs index 55cb0a9..4913ba2 100644 --- a/src/PodmanClient/PodmanClient.Generate.cs +++ b/src/PodmanClient/PodmanClient.Generate.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using System.Net.Http.Headers; using MaksIT.PodmanClientDotNet.Dtos.Generate; @@ -18,6 +19,7 @@ public partial class PodmanClient { GetJsonAsync( $"/libpod/generate/{Uri.EscapeDataString(name)}/systemd", "Generate systemd", + PodmanJsonContext.Default.GenerateSystemdDto, [ ("useName", useName.ToString().ToLowerInvariant()), ("new", createNew.ToString().ToLowerInvariant()), @@ -62,6 +64,7 @@ public partial class PodmanClient { return PostLibpodAsync( "/libpod/play/kube", "Play kube", + PodmanJsonContext.Default.PlayKubeReportDto, content, [ ("network", network), diff --git a/src/PodmanClient/PodmanClient.Http.cs b/src/PodmanClient/PodmanClient.Http.cs index 582069f..fe22d46 100644 --- a/src/PodmanClient/PodmanClient.Http.cs +++ b/src/PodmanClient/PodmanClient.Http.cs @@ -1,7 +1,8 @@ using System.Net; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; -using MaksIT.Core.Extensions; using MaksIT.PodmanClientDotNet.Internal; using MaksIT.Results; @@ -55,13 +56,14 @@ public partial class PodmanClient { internal Task> GetJsonAsync( string libpodPath, string operation, + JsonTypeInfo typeInfo, IEnumerable<(string Key, string? Value)>? query = null, CancellationToken cancellationToken = default ) => SendAsync( () => _httpClient.GetAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), cancellationToken), operation, - body => body.ToObject(), + body => JsonSerializer.Deserialize(body, typeInfo), cancellationToken ); @@ -81,17 +83,19 @@ public partial class PodmanClient { string libpodPath, string operation, TRequest? requestBody, + JsonTypeInfo requestTypeInfo, + JsonTypeInfo responseTypeInfo, IEnumerable<(string Key, string? Value)>? query = null, CancellationToken cancellationToken = default ) { var content = requestBody is null ? null - : new StringContent(requestBody.ToJson(), Encoding.UTF8, "application/json"); + : new StringContent(JsonSerializer.Serialize(requestBody, requestTypeInfo), Encoding.UTF8, "application/json"); return SendAsync( () => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken), operation, - body => string.IsNullOrWhiteSpace(body) ? default : body.ToObject(), + body => string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, responseTypeInfo), cancellationToken ); } @@ -100,12 +104,13 @@ public partial class PodmanClient { string libpodPath, string operation, TRequest? requestBody, + JsonTypeInfo requestTypeInfo, IEnumerable<(string Key, string? Value)>? query = null, CancellationToken cancellationToken = default ) { var content = requestBody is null ? null - : new StringContent(requestBody.ToJson(), Encoding.UTF8, "application/json"); + : new StringContent(JsonSerializer.Serialize(requestBody, requestTypeInfo), Encoding.UTF8, "application/json"); return SendWithoutBodyAsync( () => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken), @@ -155,6 +160,7 @@ public partial class PodmanClient { internal Task> PostLibpodAsync( string libpodPath, string operation, + JsonTypeInfo responseTypeInfo, HttpContent? content = null, IEnumerable<(string Key, string? Value)>? query = null, CancellationToken cancellationToken = default @@ -162,20 +168,21 @@ public partial class PodmanClient { SendAsync( () => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken), operation, - body => string.IsNullOrWhiteSpace(body) ? default : body.ToObject(), + body => string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, responseTypeInfo), cancellationToken ); internal Task> DeleteJsonAsync( string libpodPath, string operation, + JsonTypeInfo typeInfo, IEnumerable<(string Key, string? Value)>? query = null, CancellationToken cancellationToken = default ) => SendAsync( () => _httpClient.DeleteAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), cancellationToken), operation, - body => string.IsNullOrWhiteSpace(body) ? default : body.ToObject(), + body => string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, typeInfo), cancellationToken ); diff --git a/src/PodmanClient/PodmanClient.Images.Api.cs b/src/PodmanClient/PodmanClient.Images.Api.cs index 84ef97f..3f19a22 100644 --- a/src/PodmanClient/PodmanClient.Images.Api.cs +++ b/src/PodmanClient/PodmanClient.Images.Api.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using System.Net.Http.Headers; using MaksIT.PodmanClientDotNet.Dtos.Common; @@ -16,6 +17,7 @@ public partial class PodmanClient { GetJsonAsync>( "/libpod/images/json", "List images", + PodmanJsonContext.Default.ListImageListEntryDto, [ ("all", all.ToString().ToLowerInvariant()), ("filters", filters), @@ -24,7 +26,7 @@ public partial class PodmanClient { ); public Task> InspectImageAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ImagePath(name)}/json", "Inspect image", cancellationToken: cancellationToken); + GetJsonAsync($"{ImagePath(name)}/json", "Inspect image", PodmanJsonContext.Default.ImageInspectDto, cancellationToken: cancellationToken); public Task ImageExistsAsync(string name, CancellationToken cancellationToken = default) => GetWithoutBodyAsync($"{ImagePath(name)}/exists", "Image exists", cancellationToken: cancellationToken); @@ -33,6 +35,7 @@ public partial class PodmanClient { DeleteJsonAsync( ImagePath(name), "Delete image", + PodmanJsonContext.Default.ImageDeleteDtoArray, [("force", force.ToString().ToLowerInvariant())], cancellationToken ); @@ -50,11 +53,11 @@ public partial class PodmanClient { foreach (var image in images) query.Add(("images", image)); - return DeleteJsonAsync("/libpod/images/remove", "Remove images", query, cancellationToken); + return DeleteJsonAsync("/libpod/images/remove", "Remove images", PodmanJsonContext.Default.ImageDeleteDtoArray, query, cancellationToken); } public Task> PruneImagesAsync(CancellationToken cancellationToken = default) => - PostLibpodAsync("/libpod/images/prune", "Prune images", cancellationToken: cancellationToken); + PostLibpodAsync("/libpod/images/prune", "Prune images", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken); public Task?>> SearchImagesAsync( string term, @@ -64,6 +67,7 @@ public partial class PodmanClient { GetJsonAsync>( "/libpod/images/search", "Search images", + PodmanJsonContext.Default.ListImageSearchResultDto, [ ("term", term), ("limit", limit?.ToString()), @@ -115,13 +119,13 @@ public partial class PodmanClient { ); public Task?>> GetImageHistoryAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync>($"{ImagePath(name)}/history", "Get image history", cancellationToken: cancellationToken); + GetJsonAsync>($"{ImagePath(name)}/history", "Get image history", PodmanJsonContext.Default.ListImageHistoryEntryDto, cancellationToken: cancellationToken); public Task> GetImageTreeAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ImagePath(name)}/tree", "Get image tree", cancellationToken: cancellationToken); + GetJsonAsync($"{ImagePath(name)}/tree", "Get image tree", PodmanJsonContext.Default.ImageTreeDto, cancellationToken: cancellationToken); public Task> GetImageChangesAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ImagePath(name)}/changes", "Get image changes", cancellationToken: cancellationToken); + GetJsonAsync($"{ImagePath(name)}/changes", "Get image changes", PodmanJsonContext.Default.ImageChangesDto, cancellationToken: cancellationToken); public Task> ImportImageAsync( Stream? tarball = null, @@ -140,6 +144,7 @@ public partial class PodmanClient { return PostLibpodAsync( "/libpod/images/import", "Import image", + PodmanJsonContext.Default.ImageImportDto, content, [ ("changes", changes), @@ -154,7 +159,7 @@ public partial class PodmanClient { public Task> LoadImageAsync(Stream tarball, CancellationToken cancellationToken = default) { var content = new StreamContent(tarball); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-tar"); - return PostLibpodAsync("/libpod/images/load", "Load image", content, cancellationToken: cancellationToken); + return PostLibpodAsync("/libpod/images/load", "Load image", PodmanJsonContext.Default.ImageLoadDto, content, cancellationToken: cancellationToken); } public Task> ExportImagesAsync( diff --git a/src/PodmanClient/PodmanClient.Manifests.cs b/src/PodmanClient/PodmanClient.Manifests.cs index bb5d6e3..8dd6564 100644 --- a/src/PodmanClient/PodmanClient.Manifests.cs +++ b/src/PodmanClient/PodmanClient.Manifests.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using MaksIT.PodmanClientDotNet.Dtos.Manifest; using MaksIT.Results; @@ -13,6 +14,7 @@ public partial class PodmanClient { PostLibpodAsync( "/libpod/manifests/create", "Create manifest", + PodmanJsonContext.Default.ManifestCreateDto, query: [ ("name", name), ("image", image), @@ -30,10 +32,10 @@ public partial class PodmanClient { ); public Task> InspectManifestAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"{ManifestPath(name)}/json", "Inspect manifest", cancellationToken: cancellationToken); + GetJsonAsync($"{ManifestPath(name)}/json", "Inspect manifest", PodmanJsonContext.Default.ManifestInspectDto, cancellationToken: cancellationToken); public Task AddToManifestAsync(string name, ManifestAddRequestDto request, CancellationToken cancellationToken = default) => - PostJsonWithoutBodyAsync($"{ManifestPath(name)}/add", "Add to manifest", request, cancellationToken: cancellationToken); + PostJsonWithoutBodyAsync($"{ManifestPath(name)}/add", "Add to manifest", request, PodmanJsonContext.Default.ManifestAddRequestDto, cancellationToken: cancellationToken); public Task PushManifestAsync( string name, diff --git a/src/PodmanClient/PodmanClient.Networks.cs b/src/PodmanClient/PodmanClient.Networks.cs index e5557d0..65197fb 100644 --- a/src/PodmanClient/PodmanClient.Networks.cs +++ b/src/PodmanClient/PodmanClient.Networks.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using MaksIT.PodmanClientDotNet.Dtos.Network; using MaksIT.PodmanClientDotNet.Models.Network; using MaksIT.Results; @@ -11,14 +12,16 @@ public partial class PodmanClient { "/libpod/networks/create", "Create network", request, + PodmanJsonContext.Default.NetworkCreateRequest, + PodmanJsonContext.Default.NetworkListEntryDto, cancellationToken: cancellationToken ); public Task?>> ListNetworksAsync(CancellationToken cancellationToken = default) => - GetJsonAsync>("/libpod/networks/json", "List networks", cancellationToken: cancellationToken); + GetJsonAsync>("/libpod/networks/json", "List networks", PodmanJsonContext.Default.ListNetworkListEntryDto, cancellationToken: cancellationToken); public Task> InspectNetworkAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"/libpod/networks/{Uri.EscapeDataString(name)}/json", "Inspect network", cancellationToken: cancellationToken); + GetJsonAsync($"/libpod/networks/{Uri.EscapeDataString(name)}/json", "Inspect network", PodmanJsonContext.Default.NetworkInspectDto, cancellationToken: cancellationToken); public Task DeleteNetworkAsync(string name, CancellationToken cancellationToken = default) => DeleteWithoutBodyAsync($"/libpod/networks/{Uri.EscapeDataString(name)}", "Delete network", cancellationToken: cancellationToken); @@ -32,6 +35,7 @@ public partial class PodmanClient { $"/libpod/networks/{Uri.EscapeDataString(name)}/connect", "Connect network", request, + PodmanJsonContext.Default.NetworkConnectRequest, cancellationToken: cancellationToken ); @@ -44,6 +48,7 @@ public partial class PodmanClient { $"/libpod/networks/{Uri.EscapeDataString(name)}/disconnect", "Disconnect network", request, + PodmanJsonContext.Default.NetworkDisconnectRequest, cancellationToken: cancellationToken ); } diff --git a/src/PodmanClient/PodmanClient.Pods.cs b/src/PodmanClient/PodmanClient.Pods.cs index 744bcd2..372b19f 100644 --- a/src/PodmanClient/PodmanClient.Pods.cs +++ b/src/PodmanClient/PodmanClient.Pods.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Pod; using MaksIT.PodmanClientDotNet.Models.Pod; @@ -5,18 +6,19 @@ using MaksIT.Results; public partial class PodmanClient { public Task> CreatePodAsync(PodCreateRequest request, CancellationToken cancellationToken = default) => - PostJsonAsync("/libpod/pods/create", "Create pod", request, cancellationToken: cancellationToken); + PostJsonAsync("/libpod/pods/create", "Create pod", request, PodmanJsonContext.Default.PodCreateRequest, PodmanJsonContext.Default.PodListEntryDto, cancellationToken: cancellationToken); public Task?>> ListPodsAsync(bool all = false, CancellationToken cancellationToken = default) => GetJsonAsync>( "/libpod/pods/json", "List pods", + PodmanJsonContext.Default.ListPodListEntryDto, [("all", all.ToString().ToLowerInvariant())], cancellationToken ); public Task> InspectPodAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/json", "Inspect pod", cancellationToken: cancellationToken); + GetJsonAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/json", "Inspect pod", PodmanJsonContext.Default.PodInspectDto, cancellationToken: cancellationToken); public Task PodExistsAsync(string name, CancellationToken cancellationToken = default) => GetWithoutBodyAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/exists", "Pod exists", cancellationToken: cancellationToken); @@ -63,11 +65,11 @@ public partial class PodmanClient { PostWithoutBodyAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/unpause", "Unpause pod", cancellationToken: cancellationToken); public Task> PrunePodsAsync(CancellationToken cancellationToken = default) => - PostLibpodAsync("/libpod/pods/prune", "Prune pods", cancellationToken: cancellationToken); + PostLibpodAsync("/libpod/pods/prune", "Prune pods", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken); public Task> TopPodAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/top", "Top pod", cancellationToken: cancellationToken); + GetJsonAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/top", "Top pod", PodmanJsonContext.Default.PodTopDto, cancellationToken: cancellationToken); public Task> GetPodsStatsAsync(CancellationToken cancellationToken = default) => - GetJsonAsync("/libpod/pods/stats", "Get pods stats", cancellationToken: cancellationToken); + GetJsonAsync("/libpod/pods/stats", "Get pods stats", PodmanJsonContext.Default.PodStatsResponseDto, cancellationToken: cancellationToken); } diff --git a/src/PodmanClient/PodmanClient.Streaming.cs b/src/PodmanClient/PodmanClient.Streaming.cs index 5f435df..e9d51d8 100644 --- a/src/PodmanClient/PodmanClient.Streaming.cs +++ b/src/PodmanClient/PodmanClient.Streaming.cs @@ -1,8 +1,9 @@ +using MaksIT.PodmanClientDotNet; using System.Text; +using System.Text.Json; using Microsoft.Extensions.Logging; -using MaksIT.Core.Extensions; using MaksIT.PodmanClientDotNet.Dtos.Build; using MaksIT.PodmanClientDotNet.Dtos.Image; using MaksIT.PodmanClientDotNet.Internal; @@ -65,7 +66,7 @@ public partial class PodmanClient { Height = height, Width = width, }; - var body = Encoding.UTF8.GetBytes(startExecRequest.ToJson()); + var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(startExecRequest, PodmanJsonContext.Default.StartExecRequest)); var query = BuildQuery([]); var hijack = await PodmanHijackConnection.ConnectAsync( @@ -122,7 +123,7 @@ public partial class PodmanClient { return streamResult.ToResultOfType>(null!); return Result?>.Ok( - new PodmanProgressSession(streamResult.Value!) + new PodmanProgressSession(streamResult.Value!, PodmanJsonContext.Default.PullImageResponseDto) ); } @@ -169,7 +170,7 @@ public partial class PodmanClient { return streamResult.ToResultOfType>(null!); return Result?>.Ok( - new PodmanProgressSession(streamResult.Value!) + new PodmanProgressSession(streamResult.Value!, PodmanJsonContext.Default.BuildProgressLineDto) ); } diff --git a/src/PodmanClient/PodmanClient.System.cs b/src/PodmanClient/PodmanClient.System.cs index dddbd94..40ff192 100644 --- a/src/PodmanClient/PodmanClient.System.cs +++ b/src/PodmanClient/PodmanClient.System.cs @@ -1,22 +1,23 @@ +using MaksIT.PodmanClientDotNet; using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.System; using MaksIT.Results; public partial class PodmanClient { public Task> PingAsync(CancellationToken cancellationToken = default) => - GetJsonAsync("/libpod/_ping", "Ping", cancellationToken: cancellationToken); + GetJsonAsync("/libpod/_ping", "Ping", PodmanJsonContext.Default.LibpodPingDto, cancellationToken: cancellationToken); public Task> GetVersionAsync(CancellationToken cancellationToken = default) => - GetJsonAsync("/libpod/version", "Get version", cancellationToken: cancellationToken); + GetJsonAsync("/libpod/version", "Get version", PodmanJsonContext.Default.LibpodVersionDto, cancellationToken: cancellationToken); public Task> GetInfoAsync(CancellationToken cancellationToken = default) => - GetJsonAsync("/libpod/info", "Get info", cancellationToken: cancellationToken); + GetJsonAsync("/libpod/info", "Get info", PodmanJsonContext.Default.InfoDto, cancellationToken: cancellationToken); public Task> GetSystemDiskUsageAsync(CancellationToken cancellationToken = default) => - GetJsonAsync("/libpod/system/df", "Get system disk usage", cancellationToken: cancellationToken); + GetJsonAsync("/libpod/system/df", "Get system disk usage", PodmanJsonContext.Default.SystemDfDto, cancellationToken: cancellationToken); public Task> PruneSystemAsync(CancellationToken cancellationToken = default) => - PostLibpodAsync("/libpod/system/prune", "Prune system", cancellationToken: cancellationToken); + PostLibpodAsync("/libpod/system/prune", "Prune system", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken); public Task> GetEventsAsync(CancellationToken cancellationToken = default) => GetStreamAsync("/libpod/events", "Get events", cancellationToken: cancellationToken); diff --git a/src/PodmanClient/PodmanClient.Volumes.cs b/src/PodmanClient/PodmanClient.Volumes.cs index c040a74..ed6c12f 100644 --- a/src/PodmanClient/PodmanClient.Volumes.cs +++ b/src/PodmanClient/PodmanClient.Volumes.cs @@ -1,3 +1,4 @@ +using MaksIT.PodmanClientDotNet; using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Volume; using MaksIT.PodmanClientDotNet.Models.Volume; @@ -12,14 +13,16 @@ public partial class PodmanClient { "/libpod/volumes/create", "Create volume", request, + PodmanJsonContext.Default.CreateVolumeRequest, + PodmanJsonContext.Default.VolumeInspectResponseDto, cancellationToken: cancellationToken ); public Task?>> ListVolumesAsync(CancellationToken cancellationToken = default) => - GetJsonAsync>("/libpod/volumes/json", "List volumes", cancellationToken: cancellationToken); + GetJsonAsync>("/libpod/volumes/json", "List volumes", PodmanJsonContext.Default.ListVolumeListEntryDto, cancellationToken: cancellationToken); public Task> InspectVolumeAsync(string name, CancellationToken cancellationToken = default) => - GetJsonAsync($"/libpod/volumes/{Uri.EscapeDataString(name)}/json", "Inspect volume", cancellationToken: cancellationToken); + GetJsonAsync($"/libpod/volumes/{Uri.EscapeDataString(name)}/json", "Inspect volume", PodmanJsonContext.Default.VolumeInspectResponseDto, cancellationToken: cancellationToken); public Task DeleteVolumeAsync(string name, bool force = false, CancellationToken cancellationToken = default) => DeleteWithoutBodyAsync( @@ -30,5 +33,5 @@ public partial class PodmanClient { ); public Task> PruneVolumesAsync(CancellationToken cancellationToken = default) => - PostLibpodAsync("/libpod/volumes/prune", "Prune volumes", cancellationToken: cancellationToken); + PostLibpodAsync("/libpod/volumes/prune", "Prune volumes", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken); } diff --git a/src/PodmanClient/PodmanClientDotNet.csproj b/src/PodmanClient/PodmanClientDotNet.csproj index 7a10cdf..f6b544e 100644 --- a/src/PodmanClient/PodmanClientDotNet.csproj +++ b/src/PodmanClient/PodmanClientDotNet.csproj @@ -39,6 +39,9 @@ true true + + + true @@ -46,7 +49,6 @@ - diff --git a/src/PodmanClient/PodmanJsonContext.cs b/src/PodmanClient/PodmanJsonContext.cs new file mode 100644 index 0000000..984ea57 --- /dev/null +++ b/src/PodmanClient/PodmanJsonContext.cs @@ -0,0 +1,116 @@ +using System.Text.Json.Serialization; + +using MaksIT.PodmanClientDotNet.Dtos.Build; +using MaksIT.PodmanClientDotNet.Dtos.Common; +using MaksIT.PodmanClientDotNet.Dtos.Container; +using MaksIT.PodmanClientDotNet.Dtos.Exec; +using MaksIT.PodmanClientDotNet.Dtos.Generate; +using MaksIT.PodmanClientDotNet.Dtos.Image; +using MaksIT.PodmanClientDotNet.Dtos.Manifest; +using MaksIT.PodmanClientDotNet.Dtos.Network; +using MaksIT.PodmanClientDotNet.Dtos.Pod; +using MaksIT.PodmanClientDotNet.Dtos.System; +using MaksIT.PodmanClientDotNet.Dtos.Volume; +using MaksIT.PodmanClientDotNet.Models.Container; +using MaksIT.PodmanClientDotNet.Models.Exec; +using MaksIT.PodmanClientDotNet.Models.Network; +using MaksIT.PodmanClientDotNet.Models.Pod; +using MaksIT.PodmanClientDotNet.Models.Volume; + +namespace MaksIT.PodmanClientDotNet; + +// ----- Response / DTO types ----- + +// Common +[JsonSerializable(typeof(ErrorResponseDto))] +[JsonSerializable(typeof(IdResponseDto))] +[JsonSerializable(typeof(PruneReportDto))] +[JsonSerializable(typeof(ReportDto))] + +// Build +[JsonSerializable(typeof(BuildProgressLineDto))] +[JsonSerializable(typeof(BuildReportDto))] + +// Container +[JsonSerializable(typeof(ContainerChangesDto))] +[JsonSerializable(typeof(ContainerCommitDto))] +[JsonSerializable(typeof(ContainerHealthCheckDto))] +[JsonSerializable(typeof(ContainerInspectDto))] +[JsonSerializable(typeof(ContainerListEntryDto))] +[JsonSerializable(typeof(List))] +[JsonSerializable(typeof(ContainerMountDto))] +[JsonSerializable(typeof(ContainerStatsDto))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(ContainerTopDto))] +[JsonSerializable(typeof(ContainerWaitDto))] +[JsonSerializable(typeof(CreateContainerResponseDto))] +[JsonSerializable(typeof(DeleteContainerResponseDto))] +[JsonSerializable(typeof(DeleteContainerResponseDto[]))] +[JsonSerializable(typeof(MountedContainersResponseDto))] + +// Exec +[JsonSerializable(typeof(CreateExecResponseDto))] +[JsonSerializable(typeof(InspectExecResponseDto))] + +// Generate +[JsonSerializable(typeof(GenerateSystemdDto))] +[JsonSerializable(typeof(PlayKubeReportDto))] + +// Image +[JsonSerializable(typeof(ImageChangesDto))] +[JsonSerializable(typeof(ImageDeleteDto))] +[JsonSerializable(typeof(ImageDeleteDto[]))] +[JsonSerializable(typeof(ImageHistoryEntryDto))] +[JsonSerializable(typeof(List))] +[JsonSerializable(typeof(ImageImportDto))] +[JsonSerializable(typeof(ImageInspectDto))] +[JsonSerializable(typeof(ImageListEntryDto))] +[JsonSerializable(typeof(List))] +[JsonSerializable(typeof(ImageLoadDto))] +[JsonSerializable(typeof(ImageRemoveResponseDto))] +[JsonSerializable(typeof(ImageSearchResultDto))] +[JsonSerializable(typeof(List))] +[JsonSerializable(typeof(ImageTreeDto))] +[JsonSerializable(typeof(PullImageResponseDto))] + +// Manifest +[JsonSerializable(typeof(ManifestCreateDto))] +[JsonSerializable(typeof(ManifestInspectDto))] + +// Network +[JsonSerializable(typeof(NetworkInspectDto))] +[JsonSerializable(typeof(NetworkListEntryDto))] +[JsonSerializable(typeof(List))] + +// Pod +[JsonSerializable(typeof(PodInspectDto))] +[JsonSerializable(typeof(PodListEntryDto))] +[JsonSerializable(typeof(List))] +[JsonSerializable(typeof(PodTopDto))] +[JsonSerializable(typeof(PodStatsResponseDto))] + +// System +[JsonSerializable(typeof(InfoDto))] +[JsonSerializable(typeof(LibpodPingDto))] +[JsonSerializable(typeof(LibpodVersionDto))] +[JsonSerializable(typeof(SystemDfDto))] + +// Volume +[JsonSerializable(typeof(VolumeInspectResponseDto))] +[JsonSerializable(typeof(VolumeListEntryDto))] +[JsonSerializable(typeof(List))] + +// ----- Request / model types ----- +[JsonSerializable(typeof(CreateContainerRequest))] +[JsonSerializable(typeof(CreateExecRequest))] +[JsonSerializable(typeof(StartExecRequest))] +[JsonSerializable(typeof(NetworkCreateRequest))] +[JsonSerializable(typeof(NetworkConnectRequest))] +[JsonSerializable(typeof(NetworkDisconnectRequest))] +[JsonSerializable(typeof(PodCreateRequest))] +[JsonSerializable(typeof(CreateVolumeRequest))] +[JsonSerializable(typeof(ManifestAddRequestDto))] + +[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, PropertyNameCaseInsensitive = true)] +internal sealed partial class PodmanJsonContext : JsonSerializerContext { +} diff --git a/src/PodmanClient/Streaming/PodmanProgressSession.cs b/src/PodmanClient/Streaming/PodmanProgressSession.cs index 40c1e5c..5802302 100644 --- a/src/PodmanClient/Streaming/PodmanProgressSession.cs +++ b/src/PodmanClient/Streaming/PodmanProgressSession.cs @@ -1,15 +1,16 @@ using System.Text.Json; - -using MaksIT.Core.Extensions; +using System.Text.Json.Serialization.Metadata; namespace MaksIT.PodmanClientDotNet.Streaming; internal sealed class PodmanProgressSession : IPodmanProgressSession { private readonly Stream _stream; private readonly bool _ownsStream; + private readonly JsonTypeInfo _typeInfo; - internal PodmanProgressSession(Stream stream, bool ownsStream = true) { + internal PodmanProgressSession(Stream stream, JsonTypeInfo typeInfo, bool ownsStream = true) { _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + _typeInfo = typeInfo ?? throw new ArgumentNullException(nameof(typeInfo)); _ownsStream = ownsStream; } @@ -24,7 +25,7 @@ internal sealed class PodmanProgressSession : IPodmanProgressSession { T? item; try { - item = line.ToObject(); + item = JsonSerializer.Deserialize(line, _typeInfo); } catch (JsonException) { continue; diff --git a/src/PodmanClientDotNet.Tests/Streaming/PodmanProgressSessionTests.cs b/src/PodmanClientDotNet.Tests/Streaming/PodmanProgressSessionTests.cs index 3c0d88c..05736d5 100644 --- a/src/PodmanClientDotNet.Tests/Streaming/PodmanProgressSessionTests.cs +++ b/src/PodmanClientDotNet.Tests/Streaming/PodmanProgressSessionTests.cs @@ -10,7 +10,7 @@ public class PodmanProgressSessionTests { public async Task ReadProgressAsync_ParsesNdjsonLines() { var json = "{\"status\":\"Pulling fs layer\"}\n{\"id\":\"abc\"}\n"; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - await using var session = new PodmanProgressSession(stream, ownsStream: false); + await using var session = new PodmanProgressSession(stream, PodmanJsonContext.Default.PullImageResponseDto, ownsStream: false); var items = new List(); await foreach (var item in session.ReadProgressAsync(TestContext.Current.CancellationToken))