Skip to content

Commit ca3068a

Browse files
authored
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.
1 parent 476ee69 commit ca3068a

File tree

5 files changed

+179
-36
lines changed

5 files changed

+179
-36
lines changed

src/KubernetesClient.Models/KubernetesJson.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ public static TValue Deserialize<TValue>(Stream json, JsonSerializerOptions json
9292
return JsonSerializer.Deserialize<TValue>(json, jsonSerializerOptions ?? JsonSerializerOptions);
9393
}
9494

95-
9695
public static string Serialize(object value, JsonSerializerOptions jsonSerializerOptions = null)
9796
{
9897
return JsonSerializer.Serialize(value, jsonSerializerOptions ?? JsonSerializerOptions);

src/KubernetesClient/GenericClient.cs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -36,112 +36,112 @@ public GenericClient(IKubernetes kubernetes, string group, string version, strin
3636
public async Task<T> CreateAsync<T>(T obj, CancellationToken cancel = default)
3737
where T : IKubernetesObject
3838
{
39-
var resp = await kubernetes.CustomObjects.CreateClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
40-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
39+
var resp = await kubernetes.CustomObjects.CreateClusterCustomObjectWithHttpMessagesAsync<T>(obj, group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
40+
return resp.Body;
4141
}
4242

4343
public async Task<T> CreateNamespacedAsync<T>(T obj, string ns, CancellationToken cancel = default)
4444
where T : IKubernetesObject
4545
{
46-
var resp = await kubernetes.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
47-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
46+
var resp = await kubernetes.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync<T>(obj, group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
47+
return resp.Body;
4848
}
4949

5050
public async Task<T> ListAsync<T>(CancellationToken cancel = default)
5151
where T : IKubernetesObject
5252
{
53-
var resp = await kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
54-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
53+
var resp = await kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, cancellationToken: cancel).ConfigureAwait(false);
54+
return resp.Body;
5555
}
5656

5757
public async Task<T> ListNamespacedAsync<T>(string ns, CancellationToken cancel = default)
5858
where T : IKubernetesObject
5959
{
60-
var resp = await kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
61-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
60+
var resp = await kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, cancellationToken: cancel).ConfigureAwait(false);
61+
return resp.Body;
6262
}
6363

6464
public async Task<T> ReadNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default)
6565
where T : IKubernetesObject
6666
{
67-
var resp = await kubernetes.CustomObjects.GetNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
68-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
67+
var resp = await kubernetes.CustomObjects.GetNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
68+
return resp.Body;
6969
}
7070

7171
public async Task<T> ReadAsync<T>(string name, CancellationToken cancel = default)
7272
where T : IKubernetesObject
7373
{
74-
var resp = await kubernetes.CustomObjects.GetClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
75-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
74+
var resp = await kubernetes.CustomObjects.GetClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
75+
return resp.Body;
7676
}
7777

7878
public async Task<T> DeleteAsync<T>(string name, CancellationToken cancel = default)
7979
where T : IKubernetesObject
8080
{
81-
var resp = await kubernetes.CustomObjects.DeleteClusterCustomObjectWithHttpMessagesAsync(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
82-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
81+
var resp = await kubernetes.CustomObjects.DeleteClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
82+
return resp.Body;
8383
}
8484

8585
public async Task<T> DeleteNamespacedAsync<T>(string ns, string name, CancellationToken cancel = default)
8686
where T : IKubernetesObject
8787
{
88-
var resp = await kubernetes.CustomObjects.DeleteNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
89-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
88+
var resp = await kubernetes.CustomObjects.DeleteNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
89+
return resp.Body;
9090
}
9191

9292
public async Task<T> PatchAsync<T>(V1Patch patch, string name, CancellationToken cancel = default)
9393
where T : IKubernetesObject
9494
{
95-
var resp = await kubernetes.CustomObjects.PatchClusterCustomObjectWithHttpMessagesAsync(patch, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
96-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
95+
var resp = await kubernetes.CustomObjects.PatchClusterCustomObjectWithHttpMessagesAsync<T>(patch, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
96+
return resp.Body;
9797
}
9898

9999
public async Task<T> PatchNamespacedAsync<T>(V1Patch patch, string ns, string name, CancellationToken cancel = default)
100100
where T : IKubernetesObject
101101
{
102-
var resp = await kubernetes.CustomObjects.PatchNamespacedCustomObjectWithHttpMessagesAsync(patch, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
103-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
102+
var resp = await kubernetes.CustomObjects.PatchNamespacedCustomObjectWithHttpMessagesAsync<T>(patch, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
103+
return resp.Body;
104104
}
105105

106106
public async Task<T> ReplaceAsync<T>(T obj, string name, CancellationToken cancel = default)
107107
where T : IKubernetesObject
108108
{
109-
var resp = await kubernetes.CustomObjects.ReplaceClusterCustomObjectWithHttpMessagesAsync(obj, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
110-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
109+
var resp = await kubernetes.CustomObjects.ReplaceClusterCustomObjectWithHttpMessagesAsync<T>(obj, group, version, plural, name, cancellationToken: cancel).ConfigureAwait(false);
110+
return resp.Body;
111111
}
112112

113113
public async Task<T> ReplaceNamespacedAsync<T>(T obj, string ns, string name, CancellationToken cancel = default)
114114
where T : IKubernetesObject
115115
{
116-
var resp = await kubernetes.CustomObjects.ReplaceNamespacedCustomObjectWithHttpMessagesAsync(obj, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
117-
return KubernetesJson.Deserialize<T>(resp.Body.ToString());
116+
var resp = await kubernetes.CustomObjects.ReplaceNamespacedCustomObjectWithHttpMessagesAsync<T>(obj, group, version, ns, plural, name, cancellationToken: cancel).ConfigureAwait(false);
117+
return resp.Body;
118118
}
119119

120120
public IAsyncEnumerable<(WatchEventType, T)> WatchAsync<T>(Action<Exception> onError = null, CancellationToken cancel = default)
121121
where T : IKubernetesObject
122122
{
123-
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, watch: true, cancellationToken: cancel);
124-
return respTask.WatchAsync<T, object>();
123+
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, watch: true, cancellationToken: cancel);
124+
return respTask.WatchAsync<T, T>();
125125
}
126126

127127
public IAsyncEnumerable<(WatchEventType, T)> WatchNamespacedAsync<T>(string ns, Action<Exception> onError = null, CancellationToken cancel = default)
128128
where T : IKubernetesObject
129129
{
130-
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, watch: true, cancellationToken: cancel);
131-
return respTask.WatchAsync<T, object>();
130+
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, watch: true, cancellationToken: cancel);
131+
return respTask.WatchAsync<T, T>();
132132
}
133133

134134
public Watcher<T> Watch<T>(Action<WatchEventType, T> onEvent, Action<Exception> onError = null, Action onClosed = null)
135135
where T : IKubernetesObject
136136
{
137-
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, watch: true);
137+
var respTask = kubernetes.CustomObjects.ListClusterCustomObjectWithHttpMessagesAsync<T>(group, version, plural, watch: true);
138138
return respTask.Watch(onEvent, onError, onClosed);
139139
}
140140

141141
public Watcher<T> WatchNamespaced<T>(string ns, Action<WatchEventType, T> onEvent, Action<Exception> onError = null, Action onClosed = null)
142142
where T : IKubernetesObject
143143
{
144-
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, watch: true);
144+
var respTask = kubernetes.CustomObjects.ListNamespacedCustomObjectWithHttpMessagesAsync<T>(group, version, ns, plural, watch: true);
145145
return respTask.Watch(onEvent, onError, onClosed);
146146
}
147147

src/LibKubernetesGenerator/templates/IOperations.cs.template

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,28 @@ public partial interface I{{name}}Operations
3232
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders = null,
3333
CancellationToken cancellationToken = default);
3434

35+
{{#IfReturnType operation "object"}}
36+
/// <summary>
37+
/// {{ToXmlDoc operation.description}}
38+
/// </summary>
39+
{{#operation.parameters}}
40+
/// <param name="{{GetDotNetName .}}">
41+
/// {{ToXmlDoc description}}
42+
/// </param>
43+
{{/operation.parameters}}
44+
/// <param name="customHeaders">
45+
/// The headers that will be added to request.
46+
/// </param>
47+
/// <param name="cancellationToken">
48+
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
49+
/// </param>
50+
Task<HttpOperationResponse<T>> {{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
51+
{{#operation.parameters}}
52+
{{GetDotNetType .}} {{GetDotNetName . "true"}},
53+
{{/operation.parameters}}
54+
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders = null,
55+
CancellationToken cancellationToken = default);
56+
{{/IfReturnType operation "object"}}
57+
3558
{{/apis}}
3659
}

src/LibKubernetesGenerator/templates/Operations.cs.template

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ namespace k8s;
99
public partial class AbstractKubernetes : I{{name}}Operations
1010
{
1111
{{#apis}}
12-
/// <inheritdoc/>
13-
async Task<HttpOperationResponse{{GetReturnType operation "<>"}}> I{{name}}Operations.{{GetMethodName operation "WithHttpMessagesAsync"}}(
12+
{{#IfReturnType operation "void"}}
13+
private async Task<HttpOperationResponse> I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
14+
{{/IfReturnType operation "void"}}
15+
{{#IfReturnType operation "obj"}}
16+
private async Task<HttpOperationResponse<T>> I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
17+
{{/IfReturnType operation "obj"}}
18+
{{#IfReturnType operation "stream"}}
19+
private async Task<HttpOperationResponse<Stream>> I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
20+
{{/IfReturnType operation "stream"}}
1421
{{#operation.parameters}}
1522
{{GetDotNetType .}} {{GetDotNetName .}},
1623
{{/operation.parameters}}
@@ -64,7 +71,7 @@ public partial class AbstractKubernetes : I{{name}}Operations
6471
HttpOperationResponse result = new HttpOperationResponse() { Request = httpRequest, Response = httpResponse };
6572
{{/IfReturnType operation "void"}}
6673
{{#IfReturnType operation "obj"}}
67-
var result = await CreateResultAsync{{GetReturnType operation "<>"}}(
74+
var result = await CreateResultAsync<T>(
6875
httpRequest,
6976
httpResponse,
7077
{{#IfParamContains operation "watch"}}
@@ -76,12 +83,64 @@ public partial class AbstractKubernetes : I{{name}}Operations
7683
cancellationToken);
7784
{{/IfReturnType operation "obj"}}
7885
{{#IfReturnType operation "stream"}}
79-
var result = new HttpOperationResponse{{GetReturnType operation "<>"}}() {
86+
var result = new HttpOperationResponse<Stream>() {
8087
Request = httpRequest,
8188
Response = httpResponse,
8289
Body = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false) };
8390
{{/IfReturnType operation "stream"}}
8491
return result;
8592
}
93+
94+
/// <inheritdoc/>
95+
async Task<HttpOperationResponse{{GetReturnType operation "<>"}}> I{{name}}Operations.{{GetMethodName operation "WithHttpMessagesAsync"}}(
96+
{{#operation.parameters}}
97+
{{GetDotNetType .}} {{GetDotNetName .}},
98+
{{/operation.parameters}}
99+
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders,
100+
CancellationToken cancellationToken)
101+
{
102+
{{#IfReturnType operation "void"}}
103+
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
104+
{{#operation.parameters}}
105+
{{GetDotNetName .}},
106+
{{/operation.parameters}}
107+
customHeaders,
108+
cancellationToken).ConfigureAwait(false);
109+
{{/IfReturnType operation "void"}}
110+
{{#IfReturnType operation "obj"}}
111+
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}{{GetReturnType operation "<>"}}(
112+
{{#operation.parameters}}
113+
{{GetDotNetName .}},
114+
{{/operation.parameters}}
115+
customHeaders,
116+
cancellationToken).ConfigureAwait(false);
117+
{{/IfReturnType operation "obj"}}
118+
{{#IfReturnType operation "stream"}}
119+
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}(
120+
{{#operation.parameters}}
121+
{{GetDotNetName .}},
122+
{{/operation.parameters}}
123+
customHeaders,
124+
cancellationToken).ConfigureAwait(false);
125+
{{/IfReturnType operation "stream"}}
126+
}
127+
128+
{{#IfReturnType operation "object"}}
129+
/// <inheritdoc/>
130+
async Task<HttpOperationResponse<T>> I{{name}}Operations.{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
131+
{{#operation.parameters}}
132+
{{GetDotNetType .}} {{GetDotNetName .}},
133+
{{/operation.parameters}}
134+
IReadOnlyDictionary<string, IReadOnlyList<string>> customHeaders,
135+
CancellationToken cancellationToken)
136+
{
137+
return await I{{name}}Operations_{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
138+
{{#operation.parameters}}
139+
{{GetDotNetName .}},
140+
{{/operation.parameters}}
141+
customHeaders,
142+
cancellationToken).ConfigureAwait(false);
143+
}
144+
{{/IfReturnType operation "object"}}
86145
{{/apis}}
87146
}

src/LibKubernetesGenerator/templates/OperationsExtensions.cs.template

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,35 @@ public static partial class {{name}}OperationsExtensions
3636
{{/operation.parameters}}
3737
CancellationToken.None
3838
).GetAwaiter().GetResult();
39-
}
39+
}
40+
41+
{{#IfReturnType operation "object"}}
42+
/// <summary>
43+
/// {{ToXmlDoc operation.description}}
44+
/// </summary>
45+
/// <param name='operations'>
46+
/// The operations group for this extension method.
47+
/// </param>
48+
{{#operation.parameters}}
49+
/// <param name="{{GetDotNetName .}}">
50+
/// {{ToXmlDoc description}}
51+
/// </param>
52+
{{/operation.parameters}}
53+
public static T {{GetMethodName operation ""}}<T>(
54+
this I{{name}}Operations operations
55+
{{#operation.parameters}}
56+
,{{GetDotNetType .}} {{GetDotNetName . "true"}}
57+
{{/operation.parameters}}
58+
)
59+
{
60+
return operations.{{GetMethodName operation "Async"}}<T>(
61+
{{#operation.parameters}}
62+
{{GetDotNetName .}},
63+
{{/operation.parameters}}
64+
CancellationToken.None
65+
).GetAwaiter().GetResult();
66+
}
67+
{{/IfReturnType operation "object"}}
4068

4169
/// <summary>
4270
/// {{ToXmlDoc operation.description}}
@@ -92,5 +120,39 @@ public static partial class {{name}}OperationsExtensions
92120
{{/IfReturnType operation "void"}}
93121
}
94122

123+
{{#IfReturnType operation "object"}}
124+
/// <summary>
125+
/// {{ToXmlDoc operation.description}}
126+
/// </summary>
127+
/// <param name='operations'>
128+
/// The operations group for this extension method.
129+
/// </param>
130+
{{#operation.parameters}}
131+
/// <param name="{{GetDotNetName .}}">
132+
/// {{ToXmlDoc description}}
133+
/// </param>
134+
{{/operation.parameters}}
135+
/// <param name="cancellationToken">
136+
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
137+
/// </param>
138+
public static async Task<T> {{GetMethodName operation "Async"}}<T>(
139+
this I{{name}}Operations operations,
140+
{{#operation.parameters}}
141+
{{GetDotNetType .}} {{GetDotNetName . "true"}},
142+
{{/operation.parameters}}
143+
CancellationToken cancellationToken = default(CancellationToken))
144+
{
145+
using (var _result = await operations.{{GetMethodName operation "WithHttpMessagesAsync"}}<T>(
146+
{{#operation.parameters}}
147+
{{GetDotNetName .}},
148+
{{/operation.parameters}}
149+
null,
150+
cancellationToken).ConfigureAwait(false))
151+
{
152+
return _result.Body;
153+
}
154+
}
155+
{{/IfReturnType}}
156+
95157
{{/apis}}
96158
}

0 commit comments

Comments
 (0)