Avoid additional serialization/deserialization of response bodies (#1372)

* Avoid additional serialization/deserialization of response bodies when using GenericClient.

* Changed code generation to add generic methods when API return type is object.

* Removed code duplication.
This commit is contained in:
Christian Prochnow
2023-08-30 11:12:48 +02:00
committed by GitHub
parent 476ee69c7e
commit ca3068a1f0
5 changed files with 179 additions and 36 deletions

View File

@@ -92,7 +92,6 @@ namespace k8s
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
}
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
{
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);

View File

@@ -36,112 +36,112 @@ namespace k8s
public async Task<T> CreateAsync<T>(T obj, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.CreateClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.CreateClusterCustomObjectWithHttpMessagesAsync<T>(obj, group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> CreateNamespacedAsync<T>(T obj, string ns, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync<T>(obj, group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> ListAsync<T>(CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> ListNamespacedAsync<T>(string ns, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> ReadNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.GetNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.GetNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> ReadAsync<T>(string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.GetClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.GetClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> DeleteAsync<T>(string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.DeleteClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.DeleteClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> DeleteNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.DeleteNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.DeleteNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> PatchAsync<T>(V1Patch patch, string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.PatchClusterCustomObjectWithHttpMessagesAsync(patch, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.PatchClusterCustomObjectWithHttpMessagesAsync<T>(patch, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> PatchNamespacedAsync<T>(V1Patch patch, string ns, string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.PatchNamespacedCustomObjectWithHttpMessagesAsync(patch, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.PatchNamespacedCustomObjectWithHttpMessagesAsync<T>(patch, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> ReplaceAsync<T>(T obj, string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.ReplaceClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.ReplaceClusterCustomObjectWithHttpMessagesAsync<T>(obj, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public async Task<T> ReplaceNamespacedAsync<T>(T obj, string ns, string name, CancellationToken cancel = default)
where T : IKubernetesObject
{
var resp = await kubernetes.CustomObjects.ReplaceNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
var resp = await kubernetes.CustomObjects.ReplaceNamespacedCustomObjectWithHttpMessagesAsync<T>(obj, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
return resp.Body;
}
public IAsyncEnumerable<(WatchEventType, T)> WatchAsync<T>(Action<Exception> onError = null, CancellationToken cancel = default)
where T : IKubernetesObject
{
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, watch: true, cancellationToken: cancel);
return respTask.WatchAsync<T, object>();
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, watch: true, cancellationToken: cancel);
return respTask.WatchAsync<T, T>();
}
public IAsyncEnumerable<(WatchEventType, T)> WatchNamespacedAsync<T>(string ns, Action<Exception> onError = null, CancellationToken cancel = default)
where T : IKubernetesObject
{
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, watch: true, cancellationToken: cancel);
return respTask.WatchAsync<T, object>();
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, watch: true, cancellationToken: cancel);
return respTask.WatchAsync<T, T>();
}
public Watcher<T> Watch<T>(Action<WatchEventType, T> onEvent, Action<Exception> onError = null, Action onClosed = null)
where T : IKubernetesObject
{
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, watch: true);
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, watch: true);
return respTask.Watch(onEvent, onError, onClosed);
}
public Watcher<T> WatchNamespaced<T>(string ns, Action<WatchEventType, T> onEvent, Action<Exception> onError = null, Action onClosed = null)
where T : IKubernetesObject
{
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, watch: true);
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, watch: true);
return respTask.Watch(onEvent, onError, onClosed);
}

View File

@@ -32,5 +32,28 @@ public partial interface I{{name}}Operations
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders = null,
CancellationToken cancellationToken = default);
{{#IfReturnType operation "object"}}
/// <summary>
/// {{ToXmlDoc operation.description}}
/// </summary>
{{#operation.parameters}}
/// <param name="{{GetDotNetName .}}">
/// {{ToXmlDoc description}}
/// </param>
{{/operation.parameters}}
/// <param name="customHeaders">
/// The headers that will be added to request.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
/// </param>
Task<HttpOperationResponse<T>> {{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
{{#operation.parameters}}
{{GetDotNetType .}} {{GetDotNetName . "true"}},
{{/operation.parameters}}
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders = null,
CancellationToken cancellationToken = default);
{{/IfReturnType operation "object"}}
{{/apis}}
}

View File

@@ -9,8 +9,15 @@ namespace k8s;
public partial class AbstractKubernetes : I{{name}}Operations
{
{{#apis}}
/// <inheritdoc/>
async Task<HttpOperationResponse{{GetReturnType operation "<>"}}> I{{name}}Operations.{{GetMethodName operation "WithHttpMessagesAsync"}}(
{{#IfReturnType operation "void"}}
private async Task<HttpOperationResponse> I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
{{/IfReturnType operation "void"}}
{{#IfReturnType operation "obj"}}
private async Task<HttpOperationResponse<T>> I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
{{/IfReturnType operation "obj"}}
{{#IfReturnType operation "stream"}}
private async Task<HttpOperationResponse<Stream>> I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
{{/IfReturnType operation "stream"}}
{{#operation.parameters}}
{{GetDotNetType .}} {{GetDotNetName .}},
{{/operation.parameters}}
@@ -64,7 +71,7 @@ public partial class AbstractKubernetes : I{{name}}Operations
HttpOperationResponse result = new HttpOperationResponse() { Request = httpRequest, Response = httpResponse };
{{/IfReturnType operation "void"}}
{{#IfReturnType operation "obj"}}
var result = await CreateResultAsync{{GetReturnType operation "<>"}}(
var result = await CreateResultAsync<T>(
httpRequest,
httpResponse,
{{#IfParamContains operation "watch"}}
@@ -76,12 +83,64 @@ public partial class AbstractKubernetes : I{{name}}Operations
cancellationToken);
{{/IfReturnType operation "obj"}}
{{#IfReturnType operation "stream"}}
var result = new HttpOperationResponse{{GetReturnType operation "<>"}}() {
var result = new HttpOperationResponse<Stream>() {
Request = httpRequest,
Response = httpResponse,
Body = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false) };
{{/IfReturnType operation "stream"}}
return result;
}
/// <inheritdoc/>
async Task<HttpOperationResponse{{GetReturnType operation "<>"}}> I{{name}}Operations.{{GetMethodName operation "WithHttpMessagesAsync"}}(
{{#operation.parameters}}
{{GetDotNetType .}} {{GetDotNetName .}},
{{/operation.parameters}}
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders,
CancellationToken cancellationToken)
{
{{#IfReturnType operation "void"}}
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
{{#operation.parameters}}
{{GetDotNetName .}},
{{/operation.parameters}}
customHeaders,
cancellationToken).ConfigureAwait(false);
{{/IfReturnType operation "void"}}
{{#IfReturnType operation "obj"}}
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}{{GetReturnType operation "<>"}}(
{{#operation.parameters}}
{{GetDotNetName .}},
{{/operation.parameters}}
customHeaders,
cancellationToken).ConfigureAwait(false);
{{/IfReturnType operation "obj"}}
{{#IfReturnType operation "stream"}}
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
{{#operation.parameters}}
{{GetDotNetName .}},
{{/operation.parameters}}
customHeaders,
cancellationToken).ConfigureAwait(false);
{{/IfReturnType operation "stream"}}
}
{{#IfReturnType operation "object"}}
/// <inheritdoc/>
async Task<HttpOperationResponse<T>> I{{name}}Operations.{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
{{#operation.parameters}}
{{GetDotNetType .}} {{GetDotNetName .}},
{{/operation.parameters}}
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders,
CancellationToken cancellationToken)
{
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
{{#operation.parameters}}
{{GetDotNetName .}},
{{/operation.parameters}}
customHeaders,
cancellationToken).ConfigureAwait(false);
}
{{/IfReturnType operation "object"}}
{{/apis}}
}

View File

@@ -38,6 +38,34 @@ public static partial class {{name}}OperationsExtensions
).GetAwaiter().GetResult();
}
{{#IfReturnType operation "object"}}
/// <summary>
/// {{ToXmlDoc operation.description}}
/// </summary>
/// <param name='operations'>
/// The operations group for this extension method.
/// </param>
{{#operation.parameters}}
/// <param name="{{GetDotNetName .}}">
/// {{ToXmlDoc description}}
/// </param>
{{/operation.parameters}}
public static T {{GetMethodName operation ""}}<T>(
this I{{name}}Operations operations
{{#operation.parameters}}
,{{GetDotNetType .}} {{GetDotNetName . "true"}}
{{/operation.parameters}}
)
{
return operations.{{GetMethodName operation "Async"}}<T>(
{{#operation.parameters}}
{{GetDotNetName .}},
{{/operation.parameters}}
CancellationToken.None
).GetAwaiter().GetResult();
}
{{/IfReturnType operation "object"}}
/// <summary>
/// {{ToXmlDoc operation.description}}
/// </summary>
@@ -92,5 +120,39 @@ public static partial class {{name}}OperationsExtensions
{{/IfReturnType operation "void"}}
}
{{#IfReturnType operation "object"}}
/// <summary>
/// {{ToXmlDoc operation.description}}
/// </summary>
/// <param name='operations'>
/// The operations group for this extension method.
/// </param>
{{#operation.parameters}}
/// <param name="{{GetDotNetName .}}">
/// {{ToXmlDoc description}}
/// </param>
{{/operation.parameters}}
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
/// </param>
public static async Task<T> {{GetMethodName operation "Async"}}<T>(
this I{{name}}Operations operations,
{{#operation.parameters}}
{{GetDotNetType .}} {{GetDotNetName . "true"}},
{{/operation.parameters}}
CancellationToken cancellationToken = default(CancellationToken))
{
using (var _result = await operations.{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
{{#operation.parameters}}
{{GetDotNetName .}},
{{/operation.parameters}}
null,
cancellationToken).ConfigureAwait(false))
{
return _result.Body;
}
}
{{/IfReturnType}}
{{/apis}}
}