Skip to content

Commit

Permalink
Stream transaction on Document API (#327)
Browse files Browse the repository at this point in the history
* Implement changes to Document API to include the stream transaction id to the request headers.

fix #322

* Added test to verify the TransactionId HeadDocumentHeader value.

Moved the TransactionId header string to a constant class.

fix #322

* Add Trait attribute to the new tests added in DocumentApiClientTest to skip 3.4 ArangoDB test run.

fix #322
  • Loading branch information
DiscJockeyDJ authored Sep 15, 2021
1 parent cc09728 commit 9d1ac5e
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 127 deletions.
5 changes: 2 additions & 3 deletions arangodb-net-standard.Test/CursorApi/CursorApiClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ public async Task PostCursorAsync_ShouldUseHeaderProperties()
return Task.FromResult(mockResponse.Object);
});

string transactionHeaderKey = "x-arango-trx-id";
string dummyTransactionId = "dummy transaction Id";

// Call the method to create the cursor.
Expand All @@ -264,8 +263,8 @@ await apiClient.PostCursorAsync<MyModel>(

// Check that the header and values are there.
Assert.NotNull(requestHeader);
Assert.Contains(transactionHeaderKey, requestHeader.AllKeys);
Assert.Equal(dummyTransactionId, requestHeader.Get(transactionHeaderKey));
Assert.Contains(CustomHttpHeaders.StreamTransactionHeader, requestHeader.AllKeys);
Assert.Equal(dummyTransactionId, requestHeader.Get(CustomHttpHeaders.StreamTransactionHeader));
}

[Fact]
Expand Down
84 changes: 68 additions & 16 deletions arangodb-net-standard.Test/DocumentApi/DocumentApiClientTest.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
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.TransactionApi.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
Expand Down Expand Up @@ -108,8 +109,8 @@ public async Task DeleteDocument_ShouldUseQueryParameters_WhenProvided()

string requestUri = null;

mockTransport.Setup(x => x.DeleteAsync(It.IsAny<string>()))
.Returns((string uri) =>
mockTransport.Setup(x => x.DeleteAsync(It.IsAny<string>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -289,8 +290,8 @@ public async Task DeleteDocuments_ShouldUseQueryParameters_WhenProvided()

string requestUri = null;

mockTransport.Setup(x => x.DeleteAsync(It.IsAny<string>(), It.IsAny<byte[]>()))
.Returns((string uri, byte[] content) =>
mockTransport.Setup(x => x.DeleteAsync(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -619,8 +620,8 @@ public async Task PutDocument_ShouldUseQueryParameters_WhenProvided()

string requestUri = null;

mockTransport.Setup(x => x.PutAsync(It.IsAny<string>(), It.IsAny<byte[]>()))
.Returns((string uri, byte[] content) =>
mockTransport.Setup(x => x.PutAsync(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -725,8 +726,8 @@ public async Task PutDocumentsAsync_ShouldUseQueryParameters_WhenProvided()

string requestUri = null;

mockTransport.Setup(x => x.PutAsync(It.IsAny<string>(), It.IsAny<byte[]>()))
.Returns((string uri, byte[] content) =>
mockTransport.Setup(x => x.PutAsync(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -852,8 +853,8 @@ public async Task PatchDocumentsAsync_ShouldUseQueryParameters_WhenProvided()

string requestUri = null;

mockTransport.Setup(x => x.PatchAsync(It.IsAny<string>(), It.IsAny<byte[]>()))
.Returns((string uri, byte[] content) =>
mockTransport.Setup(x => x.PatchAsync(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, byte[] content, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -1086,6 +1087,57 @@ public async Task ReadDocumentHeaderAsync_ShouldReturnPreconditionFailed_WhenIfM
Assert.NotEqual($"\"{docResponse._rev}\"", response.Etag.Tag);
}

[Fact]
[Trait("Feature", "StreamTransaction")]
public async Task ReadDocumentHeaderAsync_ShouldReturnOk_WhenTransactionIdIsGivenAndIsTheSame()
{
// Post a single document.
var docResponse =
await _docClient.PostDocumentAsync(_testCollection, new Dictionary<string, object> { ["key"] = "value" });

// Begin a transaction.
var beginTransaction = await _adb.Transaction.BeginTransaction(
new StreamTransactionBody
{
Collections = new PostTransactionRequestCollections
{
Write = new[] { _testCollection }
}
});

// Get the header fields.
var response = await _docClient.HeadDocumentAsync(
_testCollection,
docResponse._key,
new HeadDocumentHeader { TransactionId = beginTransaction.Result.Id });

// Check for the expected status.
Assert.Equal(HttpStatusCode.OK, response.Code);

// Abort the transaction.
await _adb.Transaction.AbortTransaction(beginTransaction.Result.Id);
}

[Fact]
[Trait("Feature", "StreamTransaction")]
public async Task ReadDocumentHeaderAsync_ShouldReturnNotFound_WhenTransctionIdIsGiveAndIsNotTheSame()
{
string dummyTransactionId = "Bogus transaction Id";

// Post a single document.
var docResponse =
await _docClient.PostDocumentAsync(_testCollection, new Dictionary<string, object> { ["key"] = "value" });

// Get the header fields.
var response = await _docClient.HeadDocumentAsync(
_testCollection,
docResponse._key,
new HeadDocumentHeader { TransactionId = dummyTransactionId });

// Check for the expected status.
Assert.Equal(HttpStatusCode.BadRequest, response.Code);
}

[Fact]
public async Task ReadDocumentHeaderAsync_ShouldReturnNotFound_WhenCollectionDoesNotExist()
{
Expand Down
12 changes: 6 additions & 6 deletions arangodb-net-standard.Test/UserApi/UserApiClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ public async Task GetDatabaseAccessLevelAsync_ShouldThrow_WhenErrorResponseRetur

string requestUri = null;

mockTransport.Setup(x => x.GetAsync(It.IsAny<string>()))
.Returns((string uri) =>
mockTransport.Setup(x => x.GetAsync(It.IsAny<string>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -375,8 +375,8 @@ public async Task GetAccessibleDatabasesAsync_ShouldUseQueryParameters_WhenProvi

string requestUri = null;

mockTransport.Setup(x => x.GetAsync(It.IsAny<string>()))
.Returns((string uri) =>
mockTransport.Setup(x => x.GetAsync(It.IsAny<string>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down Expand Up @@ -512,8 +512,8 @@ public async Task GetCollectionAccessLevelAsync_ShouldThrow_WhenErrorResponseRet

string requestUri = null;

mockTransport.Setup(x => x.GetAsync(It.IsAny<string>()))
.Returns((string uri) =>
mockTransport.Setup(x => x.GetAsync(It.IsAny<string>(), It.IsAny<WebHeaderCollection>()))
.Returns((string uri, WebHeaderCollection webHeaderCollection) =>
{
requestUri = uri;
return Task.FromResult(mockResponse.Object);
Expand Down
2 changes: 1 addition & 1 deletion arangodb-net-standard/CursorApi/CursorApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ protected virtual WebHeaderCollection GetHeaderCollection(CursorHeaderProperties
{
if (!string.IsNullOrWhiteSpace(headerProperties.TransactionId))
{
headerCollection.Add("x-arango-trx-id", headerProperties.TransactionId);
headerCollection.Add(CustomHttpHeaders.StreamTransactionHeader, headerProperties.TransactionId);
}
}

Expand Down
13 changes: 13 additions & 0 deletions arangodb-net-standard/CustomHttpHeaders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace ArangoDBNetStandard
{
/// <summary>
/// The custom HttpHeaders that may be specified in a client request.
/// </summary>
public static class CustomHttpHeaders
{
/// <summary>
/// The header string used for Stream Transaction.
/// </summary>
public const string StreamTransactionHeader = "x-arango-trx-id";
}
}
Loading

0 comments on commit 9d1ac5e

Please sign in to comment.