From 5850351244e7370d2f9bdaa706e866aaa0f45a20 Mon Sep 17 00:00:00 2001 From: DJ Venkataraman Date: Wed, 14 Jul 2021 13:08:18 +0100 Subject: [PATCH 1/4] Implement changes to Cursor API to include the stream transaction id to the request headers. fix #322 --- .../CollectionApi/CollectionApiClientTest.cs | 15 +++---- .../CursorApi/CursorApiClientTest.cs | 9 ++-- .../DocumentApi/DocumentApiClientTest.cs | 24 +++++------ .../CursorApi/CursorApiClient.cs | 42 ++++++++++++++++--- .../CursorApi/ICursorApiClient.cs | 10 +++-- .../CursorApi/Models/PostCursorBody.cs | 5 +++ .../Transport/Http/HttpApiTransport.cs | 34 +++++++++++++-- .../Transport/IApiClientTransport.cs | 11 +++-- 8 files changed, 111 insertions(+), 39 deletions(-) diff --git a/arangodb-net-standard.Test/CollectionApi/CollectionApiClientTest.cs b/arangodb-net-standard.Test/CollectionApi/CollectionApiClientTest.cs index c54770da..29ebc6bc 100644 --- a/arangodb-net-standard.Test/CollectionApi/CollectionApiClientTest.cs +++ b/arangodb-net-standard.Test/CollectionApi/CollectionApiClientTest.cs @@ -1,13 +1,13 @@ -using ArangoDBNetStandard; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using ArangoDBNetStandard; using ArangoDBNetStandard.CollectionApi; using ArangoDBNetStandard.CollectionApi.Models; using ArangoDBNetStandard.DocumentApi.Models; using ArangoDBNetStandard.Transport; using Moq; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; using Xunit; namespace ArangoDBNetStandardTest.CollectionApi @@ -160,8 +160,9 @@ public async Task PostCollectionAsync_ShouldUseQueryParameter() mockTransport.Setup(x => x.PostAsync( It.IsAny(), - It.IsAny())) - .Returns((string uri, byte[] content) => + It.IsAny(), + It.IsAny())) + .Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) => { requestUri = uri; return Task.FromResult(mockResponse.Object); diff --git a/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs b/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs index 10cf75c3..750bb6ef 100644 --- a/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs +++ b/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs @@ -1,12 +1,12 @@ -using ArangoDBNetStandard; -using ArangoDBNetStandard.CursorApi; -using ArangoDBNetStandard.CursorApi.Models; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; +using ArangoDBNetStandard; +using ArangoDBNetStandard.CursorApi; +using ArangoDBNetStandard.CursorApi.Models; using ArangoDBNetStandard.Serialization; using ArangoDBNetStandard.Transport; using Moq; @@ -187,7 +187,8 @@ public async Task PostCursorAsync_ShouldThrow_WhenErrorDeserializationFailed() mockTransport.Setup(x => x.PostAsync( It.IsAny(), - It.IsAny())) + It.IsAny(), + It.IsAny())) .Returns(Task.FromResult(mockResponse.Object)); var cursorApi = new CursorApiClient(mockTransport.Object); diff --git a/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs b/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs index 1e59a1a4..4531eae3 100644 --- a/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs +++ b/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs @@ -1,15 +1,15 @@ -using ArangoDBNetStandard; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using ArangoDBNetStandard; using ArangoDBNetStandard.DocumentApi; using ArangoDBNetStandard.DocumentApi.Models; using ArangoDBNetStandard.Transport; using ArangoDBNetStandardTest.DocumentApi.Models; using Moq; using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; using Xunit; namespace ArangoDBNetStandardTest.DocumentApi @@ -108,8 +108,8 @@ public async Task DeleteDocument_ShouldUseQueryParameters_WhenProvided() string requestUri = null; - mockTransport.Setup(x => x.DeleteAsync(It.IsAny())) - .Returns((string uri) => + mockTransport.Setup(x => x.DeleteAsync(It.IsAny(), It.IsAny())) + .Returns((string uri, WebHeaderCollection webHeaderCollection) => { requestUri = uri; return Task.FromResult(mockResponse.Object); @@ -619,8 +619,8 @@ public async Task PutDocument_ShouldUseQueryParameters_WhenProvided() string requestUri = null; - mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny())) - .Returns((string uri, byte[] content) => + mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) => { requestUri = uri; return Task.FromResult(mockResponse.Object); @@ -725,8 +725,8 @@ public async Task PutDocumentsAsync_ShouldUseQueryParameters_WhenProvided() string requestUri = null; - mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny())) - .Returns((string uri, byte[] content) => + mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) => { requestUri = uri; return Task.FromResult(mockResponse.Object); diff --git a/arangodb-net-standard/CursorApi/CursorApiClient.cs b/arangodb-net-standard/CursorApi/CursorApiClient.cs index 8ac7f5e4..cbbca1e3 100644 --- a/arangodb-net-standard/CursorApi/CursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/CursorApiClient.cs @@ -45,6 +45,25 @@ public CursorApiClient(IApiClientTransport client, IApiClientSerialization seria _client = client; } + /// + /// Method to get the header collection. + /// + /// The values. + /// values. + private static WebHeaderCollection GetHeaderCollection(PostCursorBody body) + { + var headerCollection = new WebHeaderCollection(); + if (body != null) + { + if (!string.IsNullOrWhiteSpace(body.TransactionId)) + { + headerCollection.Add("x-arango-trx-id", body.TransactionId); + } + } + + return headerCollection; + } + /// /// Execute an AQL query, creating a cursor which can be used to page query results. /// @@ -57,6 +76,7 @@ public CursorApiClient(IApiClientTransport client, IApiClientSerialization seria /// /// /// + /// Optional. The stream transaction Id. /// public virtual async Task> PostCursorAsync( string query, @@ -66,7 +86,8 @@ public virtual async Task> PostCursorAsync( long? batchSize = null, bool? cache = null, long? memoryLimit = null, - int? ttl = null) + int? ttl = null, + string transactionId = null) { return await PostCursorAsync(new PostCursorBody { @@ -77,6 +98,7 @@ public virtual async Task> PostCursorAsync( BatchSize = batchSize, Cache = cache, MemoryLimit = memoryLimit, + TransactionId = transactionId, Ttl = ttl }); } @@ -90,7 +112,8 @@ public virtual async Task> PostCursorAsync( PostCursorBody postCursorBody) { var content = GetContent(postCursorBody, new ApiClientSerializationOptions(true, true)); - using (var response = await _client.PostAsync(_cursorApiPath, content)) + var headerCollection = GetHeaderCollection(postCursorBody); + using (var response = await _client.PostAsync(_cursorApiPath, content, headerCollection)) { if (response.IsSuccessStatusCode) { @@ -106,16 +129,20 @@ public virtual async Task> PostCursorAsync( /// DELETE /_api/cursor/{cursor-identifier} /// /// The id of the cursor to delete. + /// Optional. The stream transaction Id. /// - public virtual async Task DeleteCursorAsync(string cursorId) + public virtual async Task DeleteCursorAsync(string cursorId, string transactionId = null) { - using (var response = await _client.DeleteAsync(_cursorApiPath + "/" + WebUtility.UrlEncode(cursorId))) + var headerCollection = GetHeaderCollection(new PostCursorBody { TransactionId = transactionId }); + using (var response = await _client.DeleteAsync( + _cursorApiPath + "/" + WebUtility.UrlEncode(cursorId), headerCollection)) { if (response.IsSuccessStatusCode) { var stream = await response.Content.ReadAsStreamAsync(); return DeserializeJsonFromStream(stream); } + throw await GetApiErrorException(response); } } @@ -125,17 +152,20 @@ public virtual async Task DeleteCursorAsync(string cursorI /// /// Result type to deserialize to /// ID of the existing query cursor. + /// Optional. The stream transaction Id. /// - public virtual async Task> PutCursorAsync(string cursorId) + public virtual async Task> PutCursorAsync(string cursorId, string transactionId = null) { string uri = _cursorApiPath + "/" + WebUtility.UrlEncode(cursorId); - using (var response = await _client.PutAsync(uri, new byte[0])) + var headerCollection = GetHeaderCollection(new PostCursorBody { TransactionId = transactionId }); + using (var response = await _client.PutAsync(uri, new byte[0], headerCollection)) { if (response.IsSuccessStatusCode) { var stream = await response.Content.ReadAsStreamAsync(); return DeserializeJsonFromStream>(stream); } + throw await GetApiErrorException(response); } } diff --git a/arangodb-net-standard/CursorApi/ICursorApiClient.cs b/arangodb-net-standard/CursorApi/ICursorApiClient.cs index 0df0e628..03056742 100644 --- a/arangodb-net-standard/CursorApi/ICursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/ICursorApiClient.cs @@ -22,6 +22,7 @@ public interface ICursorApiClient /// /// /// + /// Optional. The stream transaction Id. /// Task> PostCursorAsync( string query, @@ -31,7 +32,8 @@ Task> PostCursorAsync( long? batchSize = null, bool? cache = null, long? memoryLimit = null, - int? ttl = null); + int? ttl = null, + string transactionId = null); /// /// Execute an AQL query, creating a cursor which can be used to page query results. @@ -46,15 +48,17 @@ Task> PostCursorAsync( /// DELETE /_api/cursor/{cursor-identifier} /// /// The id of the cursor to delete. + /// Optional. The stream transaction Id. /// - Task DeleteCursorAsync(string cursorId); + Task DeleteCursorAsync(string cursorId, string transactionId = null); /// /// Advances an existing query cursor and gets the next set of results. /// /// Result type to deserialize to /// ID of the existing query cursor. + /// Optional. The stream transaction Id. /// - Task> PutCursorAsync(string cursorId); + Task> PutCursorAsync(string cursorId, string transactionId = null); } } diff --git a/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs b/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs index 923e2c40..771f4d18 100644 --- a/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs +++ b/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs @@ -54,6 +54,11 @@ public class PostCursorBody /// public long? MemoryLimit { get; set; } + /// + /// Gets or set the stream transaction Id. + /// + public string TransactionId { get; set; } + /// /// The time-to-live for the cursor (in seconds). /// The cursor will be removed on the server automatically after the specified amount of time. diff --git a/arangodb-net-standard/Transport/Http/HttpApiTransport.cs b/arangodb-net-standard/Transport/Http/HttpApiTransport.cs index 6fc46a73..bac64687 100644 --- a/arangodb-net-standard/Transport/Http/HttpApiTransport.cs +++ b/arangodb-net-standard/Transport/Http/HttpApiTransport.cs @@ -40,6 +40,22 @@ public HttpApiTransport(HttpClient client, HttpContentType contentType) _contentType = contentType; } + /// + /// Method to apply the additional headers. + /// + /// Object containing a dictionary of Header keys and values. + /// The header to update. + private static void ApplyHeaders(WebHeaderCollection webHeaderCollection, HttpHeaders headers) + { + if (webHeaderCollection != null) + { + foreach (var key in webHeaderCollection.AllKeys) + { + headers.Add(key, webHeaderCollection[key]); + } + } + } + /// /// Get an instance of that uses no authentication. /// @@ -168,10 +184,14 @@ public void SetJwtToken(string jwt) /// Sends a DELETE request using . /// /// + /// Object containing a dictionary of Header keys and values. /// - public async Task DeleteAsync(string requestUri) + public async Task DeleteAsync( + string requestUri, WebHeaderCollection webHeaderCollection = null) { - var response = await _client.DeleteAsync(requestUri); + var request = new HttpRequestMessage(HttpMethod.Delete, requestUri); + ApplyHeaders(webHeaderCollection, request.Headers); + var response = await _client.SendAsync(request); return new HttpApiClientResponse(response); } @@ -197,11 +217,14 @@ public async Task DeleteAsync(string requestUri, byte[] cont /// /// /// The content of the request, must not be null. + /// Object containing a dictionary of Header keys and values. /// - public async Task PostAsync(string requestUri, byte[] content) + public async Task PostAsync( + string requestUri, byte[] content, WebHeaderCollection webHeaderCollection = null) { var httpContent = new ByteArrayContent(content); httpContent.Headers.ContentType = new MediaTypeHeaderValue(_contentTypeMap[_contentType]); + ApplyHeaders(webHeaderCollection, httpContent.Headers); var response = await _client.PostAsync(requestUri, httpContent); return new HttpApiClientResponse(response); } @@ -211,11 +234,14 @@ public async Task PostAsync(string requestUri, byte[] conten /// /// /// The content of the request, must not be null. + /// Object containing a dictionary of Header keys and values. /// - public async Task PutAsync(string requestUri, byte[] content) + public async Task PutAsync( + string requestUri, byte[] content, WebHeaderCollection webHeaderCollection = null) { var httpContent = new ByteArrayContent(content); httpContent.Headers.ContentType = new MediaTypeHeaderValue(_contentTypeMap[_contentType]); + ApplyHeaders(webHeaderCollection, httpContent.Headers); var response = await _client.PutAsync(requestUri, httpContent); return new HttpApiClientResponse(response); } diff --git a/arangodb-net-standard/Transport/IApiClientTransport.cs b/arangodb-net-standard/Transport/IApiClientTransport.cs index b7f900ac..e09e1be7 100644 --- a/arangodb-net-standard/Transport/IApiClientTransport.cs +++ b/arangodb-net-standard/Transport/IApiClientTransport.cs @@ -14,15 +14,18 @@ public interface IApiClientTransport : IDisposable /// /// /// + /// Object containing a dictionary of Header keys and values. /// - Task PostAsync(string requestUri, byte[] content); + Task PostAsync( + string requestUri, byte[] content, WebHeaderCollection webHeaderCollection = null); /// /// Send a DELETE request. /// /// + /// Object containing a dictionary of Header keys and values. /// - Task DeleteAsync(string requestUri); + Task DeleteAsync(string requestUri, WebHeaderCollection webHeaderCollection = null); /// /// Send a DELETE request with body content. @@ -37,8 +40,10 @@ public interface IApiClientTransport : IDisposable /// /// /// + /// Object containing a dictionary of Header keys and values. /// - Task PutAsync(string requestUri, byte[] content); + Task PutAsync( + string requestUri, byte[] content, WebHeaderCollection webHeaderCollection = null); /// /// Send a GET request. From 104a4ab5dcd19bc646342b546fd78237f1f9d62c Mon Sep 17 00:00:00 2001 From: DJ Venkataraman Date: Tue, 3 Aug 2021 16:52:16 +0100 Subject: [PATCH 2/4] Removed stream transaction Ids for DeleteCursor and PutCursor APIs. Added a new class HeaderProperties with TransactionId as the property. fix #322 --- .../DocumentApi/DocumentApiClientTest.cs | 24 +++---- .../CursorApi/CursorApiClient.cs | 67 ++++++++++--------- .../CursorApi/ICursorApiClient.cs | 14 ++-- .../CursorApi/Models/HeaderProperties.cs | 13 ++++ .../CursorApi/Models/PostCursorBody.cs | 5 -- .../Transport/Http/HttpApiTransport.cs | 12 +--- .../Transport/IApiClientTransport.cs | 6 +- 7 files changed, 72 insertions(+), 69 deletions(-) create mode 100644 arangodb-net-standard/CursorApi/Models/HeaderProperties.cs diff --git a/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs b/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs index 4531eae3..1e59a1a4 100644 --- a/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs +++ b/arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs @@ -1,15 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using ArangoDBNetStandard; +using ArangoDBNetStandard; using ArangoDBNetStandard.DocumentApi; using ArangoDBNetStandard.DocumentApi.Models; using ArangoDBNetStandard.Transport; using ArangoDBNetStandardTest.DocumentApi.Models; using Moq; using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; using Xunit; namespace ArangoDBNetStandardTest.DocumentApi @@ -108,8 +108,8 @@ public async Task DeleteDocument_ShouldUseQueryParameters_WhenProvided() string requestUri = null; - mockTransport.Setup(x => x.DeleteAsync(It.IsAny(), It.IsAny())) - .Returns((string uri, WebHeaderCollection webHeaderCollection) => + mockTransport.Setup(x => x.DeleteAsync(It.IsAny())) + .Returns((string uri) => { requestUri = uri; return Task.FromResult(mockResponse.Object); @@ -619,8 +619,8 @@ public async Task PutDocument_ShouldUseQueryParameters_WhenProvided() string requestUri = null; - mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) => + mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny())) + .Returns((string uri, byte[] content) => { requestUri = uri; return Task.FromResult(mockResponse.Object); @@ -725,8 +725,8 @@ public async Task PutDocumentsAsync_ShouldUseQueryParameters_WhenProvided() string requestUri = null; - mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) => + mockTransport.Setup(x => x.PutAsync(It.IsAny(), It.IsAny())) + .Returns((string uri, byte[] content) => { requestUri = uri; return Task.FromResult(mockResponse.Object); diff --git a/arangodb-net-standard/CursorApi/CursorApiClient.cs b/arangodb-net-standard/CursorApi/CursorApiClient.cs index b907f96d..2430fb44 100644 --- a/arangodb-net-standard/CursorApi/CursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/CursorApiClient.cs @@ -1,9 +1,9 @@ -using ArangoDBNetStandard.CursorApi.Models; -using ArangoDBNetStandard.Serialization; -using ArangoDBNetStandard.Transport; -using System.Collections.Generic; +using System.Collections.Generic; using System.Net; using System.Threading.Tasks; +using ArangoDBNetStandard.CursorApi.Models; +using ArangoDBNetStandard.Serialization; +using ArangoDBNetStandard.Transport; namespace ArangoDBNetStandard.CursorApi { @@ -48,16 +48,16 @@ public CursorApiClient(IApiClientTransport client, IApiClientSerialization seria /// /// Method to get the header collection. /// - /// The values. + /// The values. /// values. - private static WebHeaderCollection GetHeaderCollection(PostCursorBody body) + private static WebHeaderCollection GetHeaderCollection(HeaderProperties headerProperties) { var headerCollection = new WebHeaderCollection(); - if (body != null) + if (headerProperties != null) { - if (!string.IsNullOrWhiteSpace(body.TransactionId)) + if (!string.IsNullOrWhiteSpace(headerProperties.TransactionId)) { - headerCollection.Add("x-arango-trx-id", body.TransactionId); + headerCollection.Add("x-arango-trx-id", headerProperties.TransactionId); } } @@ -89,30 +89,37 @@ public virtual async Task> PostCursorAsync( int? ttl = null, string transactionId = null) { - return await PostCursorAsync(new PostCursorBody + var headerProperties = new HeaderProperties(); + if (!string.IsNullOrWhiteSpace(transactionId)) { - Query = query, - BindVars = bindVars, - Options = options, - Count = count, - BatchSize = batchSize, - Cache = cache, - MemoryLimit = memoryLimit, - TransactionId = transactionId, - Ttl = ttl - }).ConfigureAwait(false); + headerProperties.TransactionId = transactionId; + } + + return await PostCursorAsync( + new PostCursorBody + { + Query = query, + BindVars = bindVars, + Options = options, + Count = count, + BatchSize = batchSize, + Cache = cache, + MemoryLimit = memoryLimit, + Ttl = ttl + }, headerProperties).ConfigureAwait(false); } /// /// Execute an AQL query, creating a cursor which can be used to page query results. /// /// Object encapsulating options and parameters of the query. + /// Optional. Additional Header properties. /// public virtual async Task> PostCursorAsync( - PostCursorBody postCursorBody) + PostCursorBody postCursorBody, HeaderProperties headerProperties = null) { var content = GetContent(postCursorBody, new ApiClientSerializationOptions(true, true)); - var headerCollection = GetHeaderCollection(postCursorBody); + var headerCollection = GetHeaderCollection(headerProperties); using (var response = await _client.PostAsync(_cursorApiPath, content, headerCollection)) { if (response.IsSuccessStatusCode) @@ -120,6 +127,7 @@ public virtual async Task> PostCursorAsync( var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return DeserializeJsonFromStream>(stream); } + throw await GetApiErrorException(response).ConfigureAwait(false); } } @@ -129,19 +137,17 @@ public virtual async Task> PostCursorAsync( /// DELETE /_api/cursor/{cursor-identifier} /// /// The id of the cursor to delete. - /// Optional. The stream transaction Id. /// - public virtual async Task DeleteCursorAsync(string cursorId, string transactionId = null) + public virtual async Task DeleteCursorAsync(string cursorId) { - var headerCollection = GetHeaderCollection(new PostCursorBody { TransactionId = transactionId }); - using (var response = await _client.DeleteAsync( - _cursorApiPath + "/" + WebUtility.UrlEncode(cursorId), headerCollection)) + using (var response = await _client.DeleteAsync(_cursorApiPath + "/" + WebUtility.UrlEncode(cursorId))) { if (response.IsSuccessStatusCode) { var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return DeserializeJsonFromStream(stream); } + throw await GetApiErrorException(response).ConfigureAwait(false); } } @@ -151,19 +157,18 @@ public virtual async Task DeleteCursorAsync(string cursorI /// /// Result type to deserialize to /// ID of the existing query cursor. - /// Optional. The stream transaction Id. /// - public virtual async Task> PutCursorAsync(string cursorId, string transactionId = null) + public virtual async Task> PutCursorAsync(string cursorId) { string uri = _cursorApiPath + "/" + WebUtility.UrlEncode(cursorId); - var headerCollection = GetHeaderCollection(new PostCursorBody { TransactionId = transactionId }); - using (var response = await _client.PutAsync(uri, new byte[0], headerCollection)) + using (var response = await _client.PutAsync(uri, new byte[0])) { if (response.IsSuccessStatusCode) { var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return DeserializeJsonFromStream>(stream); } + throw await GetApiErrorException(response).ConfigureAwait(false); } } diff --git a/arangodb-net-standard/CursorApi/ICursorApiClient.cs b/arangodb-net-standard/CursorApi/ICursorApiClient.cs index 03056742..2e3dc094 100644 --- a/arangodb-net-standard/CursorApi/ICursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/ICursorApiClient.cs @@ -1,7 +1,6 @@ -using ArangoDBNetStandard.CursorApi.Models; -using ArangoDBNetStandard.Serialization; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using ArangoDBNetStandard.CursorApi.Models; namespace ArangoDBNetStandard.CursorApi { @@ -39,26 +38,25 @@ Task> PostCursorAsync( /// Execute an AQL query, creating a cursor which can be used to page query results. /// /// Object encapsulating options and parameters of the query. + /// Optional. Additional Header properties. /// Task> PostCursorAsync( - PostCursorBody postCursorBody); + PostCursorBody postCursorBody, HeaderProperties headerProperties); /// /// Deletes an existing cursor and frees the resources associated with it. /// DELETE /_api/cursor/{cursor-identifier} /// /// The id of the cursor to delete. - /// Optional. The stream transaction Id. /// - Task DeleteCursorAsync(string cursorId, string transactionId = null); + Task DeleteCursorAsync(string cursorId); /// /// Advances an existing query cursor and gets the next set of results. /// /// Result type to deserialize to /// ID of the existing query cursor. - /// Optional. The stream transaction Id. /// - Task> PutCursorAsync(string cursorId, string transactionId = null); + Task> PutCursorAsync(string cursorId); } } diff --git a/arangodb-net-standard/CursorApi/Models/HeaderProperties.cs b/arangodb-net-standard/CursorApi/Models/HeaderProperties.cs new file mode 100644 index 00000000..8853321c --- /dev/null +++ b/arangodb-net-standard/CursorApi/Models/HeaderProperties.cs @@ -0,0 +1,13 @@ +namespace ArangoDBNetStandard.CursorApi.Models +{ + /// + /// Class representing the additional header properties for Cursor Api. + /// + public class HeaderProperties + { + /// + /// Gets or sets the stream transaction Id. + /// + public string TransactionId { get; set; } + } +} diff --git a/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs b/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs index 771f4d18..923e2c40 100644 --- a/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs +++ b/arangodb-net-standard/CursorApi/Models/PostCursorBody.cs @@ -54,11 +54,6 @@ public class PostCursorBody /// public long? MemoryLimit { get; set; } - /// - /// Gets or set the stream transaction Id. - /// - public string TransactionId { get; set; } - /// /// The time-to-live for the cursor (in seconds). /// The cursor will be removed on the server automatically after the specified amount of time. diff --git a/arangodb-net-standard/Transport/Http/HttpApiTransport.cs b/arangodb-net-standard/Transport/Http/HttpApiTransport.cs index d1fbff58..e00a24b2 100644 --- a/arangodb-net-standard/Transport/Http/HttpApiTransport.cs +++ b/arangodb-net-standard/Transport/Http/HttpApiTransport.cs @@ -184,14 +184,10 @@ public void SetJwtToken(string jwt) /// Sends a DELETE request using . /// /// - /// Object containing a dictionary of Header keys and values. /// - public async Task DeleteAsync( - string requestUri, WebHeaderCollection webHeaderCollection = null) + public async Task DeleteAsync(string requestUri) { - var request = new HttpRequestMessage(HttpMethod.Delete, requestUri); - ApplyHeaders(webHeaderCollection, request.Headers); - var response = await _client.SendAsync(request); + var response = await _client.DeleteAsync(requestUri); return new HttpApiClientResponse(response); } @@ -234,14 +230,12 @@ public async Task PostAsync( /// /// /// The content of the request, must not be null. - /// Object containing a dictionary of Header keys and values. /// public async Task PutAsync( - string requestUri, byte[] content, WebHeaderCollection webHeaderCollection = null) + string requestUri, byte[] content) { var httpContent = new ByteArrayContent(content); httpContent.Headers.ContentType = new MediaTypeHeaderValue(_contentTypeMap[_contentType]); - ApplyHeaders(webHeaderCollection, httpContent.Headers); var response = await _client.PutAsync(requestUri, httpContent); return new HttpApiClientResponse(response); } diff --git a/arangodb-net-standard/Transport/IApiClientTransport.cs b/arangodb-net-standard/Transport/IApiClientTransport.cs index e09e1be7..480af710 100644 --- a/arangodb-net-standard/Transport/IApiClientTransport.cs +++ b/arangodb-net-standard/Transport/IApiClientTransport.cs @@ -23,9 +23,8 @@ Task PostAsync( /// Send a DELETE request. /// /// - /// Object containing a dictionary of Header keys and values. /// - Task DeleteAsync(string requestUri, WebHeaderCollection webHeaderCollection = null); + Task DeleteAsync(string requestUri); /// /// Send a DELETE request with body content. @@ -40,10 +39,9 @@ Task PostAsync( /// /// /// - /// Object containing a dictionary of Header keys and values. /// Task PutAsync( - string requestUri, byte[] content, WebHeaderCollection webHeaderCollection = null); + string requestUri, byte[] content); /// /// Send a GET request. From d15c6ce028fb8df379716e19087e7ab3216eac80 Mon Sep 17 00:00:00 2001 From: DJ Venkataraman Date: Wed, 4 Aug 2021 10:40:30 +0100 Subject: [PATCH 3/4] Reinstated the missed out ConfigureAwait for the end points. fix #322 --- arangodb-net-standard/CursorApi/CursorApiClient.cs | 6 +++--- arangodb-net-standard/Transport/Http/HttpApiTransport.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arangodb-net-standard/CursorApi/CursorApiClient.cs b/arangodb-net-standard/CursorApi/CursorApiClient.cs index 2430fb44..0523ae3c 100644 --- a/arangodb-net-standard/CursorApi/CursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/CursorApiClient.cs @@ -120,7 +120,7 @@ public virtual async Task> PostCursorAsync( { var content = GetContent(postCursorBody, new ApiClientSerializationOptions(true, true)); var headerCollection = GetHeaderCollection(headerProperties); - using (var response = await _client.PostAsync(_cursorApiPath, content, headerCollection)) + using (var response = await _client.PostAsync(_cursorApiPath, content, headerCollection).ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { @@ -140,7 +140,7 @@ public virtual async Task> PostCursorAsync( /// public virtual async Task DeleteCursorAsync(string cursorId) { - using (var response = await _client.DeleteAsync(_cursorApiPath + "/" + WebUtility.UrlEncode(cursorId))) + using (var response = await _client.DeleteAsync(_cursorApiPath + "/" + WebUtility.UrlEncode(cursorId)).ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { @@ -161,7 +161,7 @@ public virtual async Task DeleteCursorAsync(string cursorI public virtual async Task> PutCursorAsync(string cursorId) { string uri = _cursorApiPath + "/" + WebUtility.UrlEncode(cursorId); - using (var response = await _client.PutAsync(uri, new byte[0])) + using (var response = await _client.PutAsync(uri, new byte[0]).ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { diff --git a/arangodb-net-standard/Transport/Http/HttpApiTransport.cs b/arangodb-net-standard/Transport/Http/HttpApiTransport.cs index e00a24b2..c0dbbb9c 100644 --- a/arangodb-net-standard/Transport/Http/HttpApiTransport.cs +++ b/arangodb-net-standard/Transport/Http/HttpApiTransport.cs @@ -187,7 +187,7 @@ public void SetJwtToken(string jwt) /// public async Task DeleteAsync(string requestUri) { - var response = await _client.DeleteAsync(requestUri); + var response = await _client.DeleteAsync(requestUri).ConfigureAwait(false); return new HttpApiClientResponse(response); } @@ -221,7 +221,7 @@ public async Task PostAsync( var httpContent = new ByteArrayContent(content); httpContent.Headers.ContentType = new MediaTypeHeaderValue(_contentTypeMap[_contentType]); ApplyHeaders(webHeaderCollection, httpContent.Headers); - var response = await _client.PostAsync(requestUri, httpContent); + var response = await _client.PostAsync(requestUri, httpContent).ConfigureAwait(false); return new HttpApiClientResponse(response); } @@ -236,7 +236,7 @@ public async Task PutAsync( { var httpContent = new ByteArrayContent(content); httpContent.Headers.ContentType = new MediaTypeHeaderValue(_contentTypeMap[_contentType]); - var response = await _client.PutAsync(requestUri, httpContent); + var response = await _client.PutAsync(requestUri, httpContent).ConfigureAwait(false); return new HttpApiClientResponse(response); } From 9c11fba2d1176a938e4245d3fb2c8ea741e7e1c0 Mon Sep 17 00:00:00 2001 From: DJ Venkataraman Date: Mon, 16 Aug 2021 15:59:44 +0100 Subject: [PATCH 4/4] Renamed the HeaderProperties to CursorHeaderProperties. Refactored the method to get HeaderCollection. Added test to verify header properties. fix #322 --- .../CursorApi/CursorApiClientTest.cs | 52 +++++++++++++++++++ .../CursorApi/CursorApiClient.cs | 8 +-- .../CursorApi/ICursorApiClient.cs | 2 +- ...roperties.cs => CursorHeaderProperties.cs} | 4 +- 4 files changed, 59 insertions(+), 7 deletions(-) rename arangodb-net-standard/CursorApi/Models/{HeaderProperties.cs => CursorHeaderProperties.cs} (82%) diff --git a/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs b/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs index b4d1d70d..de874fd0 100644 --- a/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs +++ b/arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs @@ -216,6 +216,58 @@ public async Task PostCursorAsync_ShouldThrowException_WhenResponseDeserializati Assert.NotNull(ex.InnerException); } + [Fact] + public async Task PostCursorAsync_ShouldUseHeaderProperties() + { + // Mock the IApiClientTransport. + var mockTransport = new Mock(); + + // Mock the IApiClientResponse. + var mockResponse = new Mock(); + + // Mock the IApiClientResponseContent. + var mockResponseContent = new Mock(); + + // Setup the mocked api client response. + mockResponse.Setup(x => x.Content) + .Returns(mockResponseContent.Object); + mockResponse.Setup(x => x.IsSuccessStatusCode) + .Returns(true); + + // Setup the mocked api client transport. + WebHeaderCollection requestHeader = null; + mockTransport.Setup(x => x.PostAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) => + { + requestHeader = webHeaderCollection; + return Task.FromResult(mockResponse.Object); + }); + + string transactionHeaderKey = "x-arango-trx-id"; + string dummyTransactionId = "dummy transaction Id"; + + // Call the method to create the cursor. + var apiClient = new CursorApiClient(mockTransport.Object); + await apiClient.PostCursorAsync( + new PostCursorBody + { + Query = "FOR doc IN [{ myProperty: CONCAT('This is a ', @testString) }] LIMIT 1 RETURN doc", + BindVars = new Dictionary { ["testString"] = "robbery" } + }, + new CursorHeaderProperties + { + TransactionId = dummyTransactionId + }); + + // Check that the header and values are there. + Assert.NotNull(requestHeader); + Assert.Contains(transactionHeaderKey, requestHeader.AllKeys); + Assert.Equal(dummyTransactionId, requestHeader.Get(transactionHeaderKey)); + } + [Fact] public async Task PutCursorAsync_ShouldSucceed() { diff --git a/arangodb-net-standard/CursorApi/CursorApiClient.cs b/arangodb-net-standard/CursorApi/CursorApiClient.cs index 0523ae3c..303d7353 100644 --- a/arangodb-net-standard/CursorApi/CursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/CursorApiClient.cs @@ -48,9 +48,9 @@ public CursorApiClient(IApiClientTransport client, IApiClientSerialization seria /// /// Method to get the header collection. /// - /// The values. + /// The values. /// values. - private static WebHeaderCollection GetHeaderCollection(HeaderProperties headerProperties) + protected virtual WebHeaderCollection GetHeaderCollection(CursorHeaderProperties headerProperties) { var headerCollection = new WebHeaderCollection(); if (headerProperties != null) @@ -89,7 +89,7 @@ public virtual async Task> PostCursorAsync( int? ttl = null, string transactionId = null) { - var headerProperties = new HeaderProperties(); + var headerProperties = new CursorHeaderProperties(); if (!string.IsNullOrWhiteSpace(transactionId)) { headerProperties.TransactionId = transactionId; @@ -116,7 +116,7 @@ public virtual async Task> PostCursorAsync( /// Optional. Additional Header properties. /// public virtual async Task> PostCursorAsync( - PostCursorBody postCursorBody, HeaderProperties headerProperties = null) + PostCursorBody postCursorBody, CursorHeaderProperties headerProperties = null) { var content = GetContent(postCursorBody, new ApiClientSerializationOptions(true, true)); var headerCollection = GetHeaderCollection(headerProperties); diff --git a/arangodb-net-standard/CursorApi/ICursorApiClient.cs b/arangodb-net-standard/CursorApi/ICursorApiClient.cs index 2e3dc094..5b36d84a 100644 --- a/arangodb-net-standard/CursorApi/ICursorApiClient.cs +++ b/arangodb-net-standard/CursorApi/ICursorApiClient.cs @@ -41,7 +41,7 @@ Task> PostCursorAsync( /// Optional. Additional Header properties. /// Task> PostCursorAsync( - PostCursorBody postCursorBody, HeaderProperties headerProperties); + PostCursorBody postCursorBody, CursorHeaderProperties headerProperties); /// /// Deletes an existing cursor and frees the resources associated with it. diff --git a/arangodb-net-standard/CursorApi/Models/HeaderProperties.cs b/arangodb-net-standard/CursorApi/Models/CursorHeaderProperties.cs similarity index 82% rename from arangodb-net-standard/CursorApi/Models/HeaderProperties.cs rename to arangodb-net-standard/CursorApi/Models/CursorHeaderProperties.cs index 8853321c..0822e041 100644 --- a/arangodb-net-standard/CursorApi/Models/HeaderProperties.cs +++ b/arangodb-net-standard/CursorApi/Models/CursorHeaderProperties.cs @@ -3,11 +3,11 @@ /// /// Class representing the additional header properties for Cursor Api. /// - public class HeaderProperties + public class CursorHeaderProperties { /// /// Gets or sets the stream transaction Id. - /// + /// public string TransactionId { get; set; } } }