From b5a10cf9d62f422bf9f160024b81177be0299bea Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sun, 3 May 2026 19:41:36 +0100 Subject: [PATCH 1/2] Refactor: introduce ClientBase and update client usage Add abstract ClientBase for reusable HTTP logic with generic methods for all HTTP verbs, error handling, and custom headers. Refactor TransactionProcessorClient to inherit from ClientBase and use its methods, replacing SendHttp*Request calls. Serialization is now handled via injected delegates using Newtonsoft.Json, improving maintainability and consistency. --- TransactionProcessor.Client/ClientBase.cs | 712 ++++++++++++++++++ .../TransactionProcessorClient.cs | 128 ++-- 2 files changed, 782 insertions(+), 58 deletions(-) create mode 100644 TransactionProcessor.Client/ClientBase.cs diff --git a/TransactionProcessor.Client/ClientBase.cs b/TransactionProcessor.Client/ClientBase.cs new file mode 100644 index 00000000..2028cf2c --- /dev/null +++ b/TransactionProcessor.Client/ClientBase.cs @@ -0,0 +1,712 @@ +using SimpleResults; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace SecurityService.Client +{ + public abstract class ClientBase + { + private readonly HttpClient HttpClient; + private readonly Func Serialise; + private readonly Func Deserialise; + + public ClientBase( + HttpClient httpClient, + Func serialise, + Func deserialise) + { + HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + Serialise = serialise ?? throw new ArgumentNullException(nameof(serialise)); + Deserialise = deserialise ?? throw new ArgumentNullException(nameof(deserialise)); + } + + public Task> Get(string requestUri, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Get, requestUri, body: null, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Get(string requestUri, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Get, requestUri, body: null, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Get(string requestUri, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Get, requestUri, body: null, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Get(string requestUri, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Get, requestUri, body: null, accessToken, additionalHeaders, cancellationToken); + + public Task Delete(string requestUri, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Delete, requestUri, body: null, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Delete(string requestUri, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Delete, requestUri, body: null, accessToken: null, additionalHeaders, cancellationToken); + + public Task Delete(string requestUri, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Delete, requestUri, body: null, accessToken, additionalHeaders: null, cancellationToken); + + public Task Delete(string requestUri, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Delete, requestUri, body: null, accessToken, additionalHeaders, cancellationToken); + + public Task Post(string requestUri, TRequest request, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, request, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Post(string requestUri, TRequest request, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, request, accessToken: null, additionalHeaders, cancellationToken); + + public Task Post(string requestUri, TRequest request, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, request, accessToken, additionalHeaders: null, cancellationToken); + + public Task Post(string requestUri, TRequest request, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, request, accessToken, additionalHeaders, cancellationToken); + + public Task Post(string requestUri, HttpContent content, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, content, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Post(string requestUri, HttpContent content, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, content, accessToken: null, additionalHeaders, cancellationToken); + + public Task Post(string requestUri, HttpContent content, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, content, accessToken, additionalHeaders: null, cancellationToken); + + public Task Post(string requestUri, HttpContent content, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Post, requestUri, content, accessToken, additionalHeaders, cancellationToken); + + public Task Post(string requestUri, byte[] fileData, string fileName, List<(string field, string value)> formFields, string accessToken, CancellationToken cancellationToken = default) + => SendMultipartWithoutResponse(requestUri, fileData, fileName, formFields, accessToken, additionalHeaders: null, cancellationToken); + + public Task Post(string requestUri, byte[] fileData, string fileName, List<(string field, string value)> formFields, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendMultipartWithoutResponse(requestUri, fileData, fileName, formFields, accessToken, additionalHeaders, cancellationToken); + + public Task> Post(string requestUri, TRequest request, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, request, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Post(string requestUri, TRequest request, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, request, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Post(string requestUri, TRequest request, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, request, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Post(string requestUri, TRequest request, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, request, accessToken, additionalHeaders, cancellationToken); + + public Task> Post(string requestUri, HttpContent content, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, content, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Post(string requestUri, HttpContent content, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, content, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Post(string requestUri, HttpContent content, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, content, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Post(string requestUri, HttpContent content, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Post, requestUri, content, accessToken, additionalHeaders, cancellationToken); + + public Task Put(string requestUri, TRequest request, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, request, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Put(string requestUri, TRequest request, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, request, accessToken: null, additionalHeaders, cancellationToken); + + public Task Put(string requestUri, TRequest request, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, request, accessToken, additionalHeaders: null, cancellationToken); + + public Task Put(string requestUri, TRequest request, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, request, accessToken, additionalHeaders, cancellationToken); + + public Task Put(string requestUri, HttpContent content, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, content, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Put(string requestUri, HttpContent content, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, content, accessToken: null, additionalHeaders, cancellationToken); + + public Task Put(string requestUri, HttpContent content, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, content, accessToken, additionalHeaders: null, cancellationToken); + + public Task Put(string requestUri, HttpContent content, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Put, requestUri, content, accessToken, additionalHeaders, cancellationToken); + + public Task> Put(string requestUri, TRequest request, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, request, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Put(string requestUri, TRequest request, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, request, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Put(string requestUri, TRequest request, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, request, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Put(string requestUri, TRequest request, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, request, accessToken, additionalHeaders, cancellationToken); + + public Task> Put(string requestUri, HttpContent content, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, content, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Put(string requestUri, HttpContent content, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, content, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Put(string requestUri, HttpContent content, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, content, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Put(string requestUri, HttpContent content, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Put, requestUri, content, accessToken, additionalHeaders, cancellationToken); + + public Task Patch(string requestUri, TRequest request, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, request, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Patch(string requestUri, TRequest request, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, request, accessToken: null, additionalHeaders, cancellationToken); + + public Task Patch(string requestUri, TRequest request, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, request, accessToken, additionalHeaders: null, cancellationToken); + + public Task Patch(string requestUri, TRequest request, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, request, accessToken, additionalHeaders, cancellationToken); + + public Task Patch(string requestUri, HttpContent content, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, content, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task Patch(string requestUri, HttpContent content, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, content, accessToken: null, additionalHeaders, cancellationToken); + + public Task Patch(string requestUri, HttpContent content, string accessToken, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, content, accessToken, additionalHeaders: null, cancellationToken); + + public Task Patch(string requestUri, HttpContent content, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendWithoutResponse(HttpMethod.Patch, requestUri, content, accessToken, additionalHeaders, cancellationToken); + + public Task> Patch(string requestUri, TRequest request, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, request, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Patch(string requestUri, TRequest request, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, request, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Patch(string requestUri, TRequest request, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, request, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Patch(string requestUri, TRequest request, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, request, accessToken, additionalHeaders, cancellationToken); + + public Task> Patch(string requestUri, HttpContent content, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, content, accessToken: null, additionalHeaders: null, cancellationToken); + + public Task> Patch(string requestUri, HttpContent content, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, content, accessToken: null, additionalHeaders, cancellationToken); + + public Task> Patch(string requestUri, HttpContent content, string accessToken, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, content, accessToken, additionalHeaders: null, cancellationToken); + + public Task> Patch(string requestUri, HttpContent content, string accessToken, List<(string header, string value)> additionalHeaders, CancellationToken cancellationToken = default) + => SendForResponse(HttpMethod.Patch, requestUri, content, accessToken, additionalHeaders, cancellationToken); + + private async Task SendWithoutResponse(HttpMethod method, + string requestUri, + TRequest? body, + string? accessToken, + List<(string header, string value)>? additionalHeaders, + CancellationToken cancellationToken) + { + try + { + using var request = CreateRequest(method, requestUri, body, accessToken, additionalHeaders); + using var response = await HttpClient.SendAsync(request, cancellationToken); + return await MapNonGenericResult(method, response, cancellationToken); + } + catch (InvalidOperationException exception) + { + return Result.Failure(exception.Message); + } + catch (HttpRequestException exception) + { + return Result.CriticalError(exception.Message); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + return Result.CriticalError("The HTTP request timed out."); + } + } + + private async Task SendWithoutResponse(HttpMethod method, + string requestUri, + HttpContent content, + string? accessToken, + List<(string header, string value)>? additionalHeaders, + CancellationToken cancellationToken) + { + try + { + using var request = CreateRequest(method, requestUri, content, accessToken, additionalHeaders); + using var response = await HttpClient.SendAsync(request, cancellationToken); + return await MapNonGenericResult(method, response, cancellationToken); + } + catch (InvalidOperationException exception) + { + return Result.Failure(exception.Message); + } + catch (HttpRequestException exception) + { + return Result.CriticalError(exception.Message); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + return Result.CriticalError("The HTTP request timed out."); + } + } + + private async Task SendMultipartWithoutResponse( + string requestUri, + byte[] fileData, + string fileName, + List<(string field, string value)> formFields, + string accessToken, + List<(string header, string value)>? additionalHeaders, + CancellationToken cancellationToken) + { + try + { + using var request = CreateMultipartRequest(requestUri, fileData, fileName, formFields, accessToken, additionalHeaders); + using var response = await HttpClient.SendAsync(request, cancellationToken); + return await MapNonGenericResult(HttpMethod.Post, response, cancellationToken); + } + catch (InvalidOperationException exception) + { + return Result.Failure(exception.Message); + } + catch (HttpRequestException exception) + { + return Result.CriticalError(exception.Message); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + return Result.CriticalError("The HTTP request timed out."); + } + } + + private async Task> SendForResponse(HttpMethod method, + string requestUri, + object? body, + string? accessToken, + List<(string header, string value)>? additionalHeaders, + CancellationToken cancellationToken) + { + try + { + using var request = CreateRequest(method, requestUri, body, accessToken, additionalHeaders); + using var response = await HttpClient.SendAsync(request, cancellationToken); + return await MapGenericResult(method, response, cancellationToken); + } + catch (InvalidOperationException exception) + { + return Result.Failure(exception.Message); + } + catch (HttpRequestException exception) + { + return Result.CriticalError(exception.Message); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + return Result.CriticalError("The HTTP request timed out."); + } + } + + private async Task> SendForResponse(HttpMethod method, + string requestUri, + HttpContent content, + string? accessToken, + List<(string header, string value)>? additionalHeaders, + CancellationToken cancellationToken) + { + try + { + using var request = CreateRequest(method, requestUri, content, accessToken, additionalHeaders); + using var response = await HttpClient.SendAsync(request, cancellationToken); + return await MapGenericResult(method, response, cancellationToken); + } + catch (InvalidOperationException exception) + { + return Result.Failure(exception.Message); + } + catch (HttpRequestException exception) + { + return Result.CriticalError(exception.Message); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + return Result.CriticalError("The HTTP request timed out."); + } + } + + private HttpRequestMessage CreateRequest(HttpMethod method, + string requestUri, + object? body, + string? accessToken, + List<(string header, string value)>? additionalHeaders) + { + if (string.IsNullOrWhiteSpace(requestUri)) + { + throw new ArgumentException("Request URI cannot be null, empty, or whitespace.", nameof(requestUri)); + } + + var request = new HttpRequestMessage(method, requestUri); + + if (accessToken is not null) + { + if (string.IsNullOrWhiteSpace(accessToken)) + { + throw new ArgumentException("Access token cannot be null, empty, or whitespace.", nameof(accessToken)); + } + + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + } + + if (body is HttpContent httpContent) + { + request.Content = httpContent; + } + else if (body is not null) + { + string serialisedBody; + + try + { + serialisedBody = Serialise(body); + } + catch (Exception exception) + { + throw new InvalidOperationException($"The request body could not be serialized: {exception.Message}", exception); + } + + if (serialisedBody is null) + { + throw new InvalidOperationException("The request body could not be serialized."); + } + + request.Content = new StringContent(serialisedBody, Encoding.UTF8, "application/json"); + } + + if (additionalHeaders is not null) + { + AddHeaders(request, additionalHeaders); + } + + return request; + } + + private HttpRequestMessage CreateRequest(HttpMethod method, + string requestUri, + HttpContent content, + string? accessToken, + List<(string header, string value)>? additionalHeaders) + { + if (content is null) + { + throw new ArgumentNullException(nameof(content)); + } + + var request = CreateRequest(method, requestUri, body: null, accessToken, additionalHeaders: null); + request.Content = content; + + if (additionalHeaders is not null) + { + AddHeaders(request, additionalHeaders); + } + + return request; + } + + private HttpRequestMessage CreateMultipartRequest( + string requestUri, + byte[] fileData, + string fileName, + List<(string field, string value)> formFields, + string accessToken, + List<(string header, string value)>? additionalHeaders) + { + if (fileData is null) + { + throw new ArgumentNullException(nameof(fileData)); + } + + if (string.IsNullOrWhiteSpace(fileName)) + { + throw new ArgumentException("File name cannot be null, empty, or whitespace.", nameof(fileName)); + } + + if (formFields is null) + { + throw new ArgumentNullException(nameof(formFields)); + } + + var request = CreateRequest(HttpMethod.Post, requestUri, body: null, accessToken, additionalHeaders: null); + request.Content = CreateMultipartContent(fileData, fileName, formFields); + + if (additionalHeaders is not null) + { + AddHeaders(request, additionalHeaders); + } + + return request; + } + + private static void AddHeaders(HttpRequestMessage request, List<(string header, string value)> additionalHeaders) + { + foreach (var (header, value) in additionalHeaders) + { + if (string.IsNullOrWhiteSpace(header)) + { + throw new ArgumentException("Header name cannot be null, empty, or whitespace.", nameof(additionalHeaders)); + } + + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException($"Header value for '{header}' cannot be null, empty, or whitespace.", nameof(additionalHeaders)); + } + + if (request.Headers.TryAddWithoutValidation(header, value)) + { + continue; + } + + if (request.Content is not null && request.Content.Headers.TryAddWithoutValidation(header, value)) + { + continue; + } + + throw new InvalidOperationException($"The header '{header}' could not be added to the request."); + } + } + + private static MultipartFormDataContent CreateMultipartContent( + byte[] fileData, + string fileName, + List<(string field, string value)> formFields) + { + var multipartContent = new MultipartFormDataContent(); + + var fileContent = new ByteArrayContent(fileData); + fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data"); + multipartContent.Add(fileContent, "file", fileName); + + foreach (var (field, value) in formFields) + { + if (string.IsNullOrWhiteSpace(field)) + { + throw new ArgumentException("Form field name cannot be null, empty, or whitespace.", nameof(formFields)); + } + + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException($"Form field value for '{field}' cannot be null, empty, or whitespace.", nameof(formFields)); + } + + multipartContent.Add(new StringContent(value), field); + } + + return multipartContent; + } + + private async Task MapNonGenericResult(HttpMethod method, + HttpResponseMessage response, + CancellationToken cancellationToken) + { + if (response.IsSuccessStatusCode) + { + return MapSuccessfulResult(method, response.StatusCode); + } + + return await MapFailedResult(response, cancellationToken); + } + + private async Task> MapGenericResult(HttpMethod method, + HttpResponseMessage response, + CancellationToken cancellationToken) + { + if (!response.IsSuccessStatusCode) + { + return await MapFailedResult(response, cancellationToken); + } + + var rawContent = await ReadContent(response, cancellationToken); + if (string.IsNullOrWhiteSpace(rawContent)) + { + return Result.Failure("The response content was empty."); + } + + if (typeof(TResponse) == typeof(string)) + { + return Result.Success((TResponse)(object)rawContent); + } + + object deserialisedValue; + + try + { + deserialisedValue = Deserialise(rawContent, typeof(TResponse)); + } + catch (Exception exception) + { + return Result.Failure($"The response body could not be deserialized: {exception.Message}"); + } + + if (deserialisedValue is null) + { + return Result.Failure("The response body could not be deserialized."); + } + + if (deserialisedValue is not TResponse value) + { + return Result.Failure( + $"The deserialized response type '{deserialisedValue.GetType().FullName}' is not assignable to '{typeof(TResponse).FullName}'."); + } + + return method == HttpMethod.Get + ? Result.ObtainedResource(value) + : Result.Success(value); + } + + private static Result MapSuccessfulResult(HttpMethod method, HttpStatusCode statusCode) + { + if (method == HttpMethod.Delete) + { + return Result.DeletedResource(); + } + + if (method == HttpMethod.Post && statusCode == HttpStatusCode.Created) + { + return Result.CreatedResource(); + } + + if (method == HttpMethod.Put || method == HttpMethod.Patch) + { + return Result.UpdatedResource(); + } + + return Result.Success(); + } + + private static async Task MapFailedResult(HttpResponseMessage response, CancellationToken cancellationToken) + { + var rawContent = await ReadContent(response, cancellationToken); + var errorMessages = ExtractErrors(rawContent); + var primaryMessage = BuildPrimaryMessage(response, errorMessages); + + return response.StatusCode switch + { + HttpStatusCode.BadRequest => Result.Invalid(primaryMessage, errorMessages), + HttpStatusCode.Unauthorized => Result.Unauthorized(primaryMessage, errorMessages), + HttpStatusCode.Forbidden => Result.Forbidden(primaryMessage, errorMessages), + HttpStatusCode.NotFound => Result.NotFound(primaryMessage, errorMessages), + HttpStatusCode.Conflict => Result.Conflict(primaryMessage, errorMessages), + _ when (int)response.StatusCode >= 500 => Result.CriticalError(primaryMessage, errorMessages), + _ => Result.Failure(primaryMessage, errorMessages) + }; + } + + private static async Task ReadContent(HttpResponseMessage response, CancellationToken cancellationToken) + { + if (response.Content is null) + { + return null; + } + + return await response.Content.ReadAsStringAsync(cancellationToken); + } + + private static IReadOnlyList ExtractErrors(string? rawContent) + { + if (string.IsNullOrWhiteSpace(rawContent)) + { + return []; + } + + try + { + using var document = JsonDocument.Parse(rawContent); + var errors = new List(); + AppendErrors(document.RootElement, errors); + + return errors.Count == 0 ? [rawContent] : errors; + } + catch (JsonException) + { + return [rawContent]; + } + } + + private static void AppendErrors(JsonElement element, ICollection errors) + { + if (element.ValueKind == JsonValueKind.Object) + { + if (element.TryGetProperty("title", out var title) && title.ValueKind == JsonValueKind.String) + { + AddIfPresent(errors, title.GetString()); + } + + if (element.TryGetProperty("detail", out var detail) && detail.ValueKind == JsonValueKind.String) + { + AddIfPresent(errors, detail.GetString()); + } + + if (element.TryGetProperty("message", out var message) && message.ValueKind == JsonValueKind.String) + { + AddIfPresent(errors, message.GetString()); + } + + if (element.TryGetProperty("errors", out var nestedErrors)) + { + AppendErrors(nestedErrors, errors); + } + + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("title") || property.NameEquals("detail") || property.NameEquals("message") || property.NameEquals("errors")) + { + continue; + } + + if (property.Value.ValueKind is JsonValueKind.Array or JsonValueKind.Object) + { + AppendErrors(property.Value, errors); + } + } + + return; + } + + if (element.ValueKind == JsonValueKind.Array) + { + foreach (var item in element.EnumerateArray()) + { + AppendErrors(item, errors); + } + + return; + } + + if (element.ValueKind == JsonValueKind.String) + { + AddIfPresent(errors, element.GetString()); + } + } + + private static void AddIfPresent(ICollection errors, string? value) + { + if (!string.IsNullOrWhiteSpace(value)) + { + errors.Add(value); + } + } + + private static string BuildPrimaryMessage(HttpResponseMessage response, IReadOnlyList errors) + { + if (errors.Count > 0) + { + return errors[0]; + } + + return response.ReasonPhrase ?? $"Request failed with status code {(int)response.StatusCode}."; + } + } +} diff --git a/TransactionProcessor.Client/TransactionProcessorClient.cs b/TransactionProcessor.Client/TransactionProcessorClient.cs index 45d68728..24a8bf6f 100644 --- a/TransactionProcessor.Client/TransactionProcessorClient.cs +++ b/TransactionProcessor.Client/TransactionProcessorClient.cs @@ -1,3 +1,4 @@ +using SecurityService.Client; using SimpleResults; using TransactionProcessor.DataTransferObjects.Requests.Contract; using TransactionProcessor.DataTransferObjects.Requests.Estate; @@ -24,9 +25,19 @@ namespace TransactionProcessor.Client; using Newtonsoft.Json; using Shared.Results; -public class TransactionProcessorClient : ClientProxyBase, ITransactionProcessorClient { +public class TransactionProcessorClient : ClientBase, ITransactionProcessorClient { + private static String Serialise(Object arg) + { + return JsonConvert.SerializeObject(arg); + } + + private static Object Deserialise(String arg, Type type) + { + return JsonConvert.DeserializeObject(arg, type); + } + #region Fields - + private readonly Func BaseAddressResolver; #endregion @@ -34,11 +45,12 @@ public class TransactionProcessorClient : ClientProxyBase, ITransactionProcessor #region Constructors public TransactionProcessorClient(Func baseAddressResolver, - HttpClient httpClient) : base(httpClient) { + HttpClient httpClient) : base(httpClient, Serialise, Deserialise) + { this.BaseAddressResolver = baseAddressResolver; // Add the API version header - this.HttpClient.DefaultRequestHeaders.Add("api-version", "1.0"); + //this.HttpClient.DefaultRequestHeaders.Add("api-version", "1.0"); } #endregion @@ -49,7 +61,7 @@ public async Task> GetEstate(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}"); try { - Result result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -70,7 +82,7 @@ public async Task CreateEstate(String accessToken, String requestUri = this.BuildRequestUrl("/api/estates/"); try { - var result = await this.SendHttpPostRequest(requestUri, createEstateRequest,accessToken, cancellationToken); + var result = await this.Post(requestUri, createEstateRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -91,7 +103,7 @@ public async Task>> GetEstates(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/all"); try { - Result> result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + Result> result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -113,7 +125,7 @@ public async Task CreateFloatForContractProduct(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/floats"); try { - var result = await this.SendHttpPostRequest(requestUri, createFloatForContractProductRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, createFloatForContractProductRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -137,7 +149,7 @@ public async Task UpdateMerchantOpeningHours(String accessToken, try { - var result = await this.SendHttpPatchRequest(requestUri, merchantOpeningRequest, accessToken, cancellationToken); + var result = await this.Patch(requestUri, merchantOpeningRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -163,7 +175,7 @@ public async Task> GetMerchantBalance(String acc if (liveBalance) requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/livebalance"); try { - Result result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -187,7 +199,7 @@ public async Task>> GetMerchant String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/balancehistory?startDate={startDate:yyyy-MM-dd}&endDate={endDate:yyyy-MM-dd}"); try { - Result> result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + Result> result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -209,7 +221,7 @@ public async Task> GetSettlementByDate(String accessT CancellationToken cancellationToken) { String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/settlements/{settlementDate.Date:yyyy-MM-dd}/merchants/{merchantId}/pending"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -231,7 +243,7 @@ public async Task> GetVoucherByCode(String accessToke String requestUri = this.BuildRequestUrl($"/api/vouchers?estateId={estateId}&voucherCode={voucherCode}"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -253,7 +265,7 @@ public async Task> GetVoucherByTransactionId(String a String requestUri = this.BuildRequestUrl($"/api/vouchers?estateId={estateId}&transactionId={transactionId}"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -273,7 +285,7 @@ public async Task> PerformTransaction(String accessTok CancellationToken cancellationToken) { String requestUri = this.BuildRequestUrl($"/api/transactions"); try { - var result = await this.SendHttpPostRequest(requestUri, transactionRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, transactionRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -296,7 +308,7 @@ public async Task ProcessSettlement(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/settlements/{settlementDate.Date:yyyy-MM-dd}/merchants/{merchantId}"); try { - Result result = await this.SendHttpPostRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Post(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -324,7 +336,7 @@ public async Task RecordFloatCreditPurchase(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/floats"); try { - var result = await this.SendHttpPostRequest(requestUri, recordFloatCreditPurchaseRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, recordFloatCreditPurchaseRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -346,7 +358,7 @@ public async Task RedeemVoucher(String accessToken, try { - Result result = await this.SendHttpPutRequest(requestUri, redeemVoucherRequest, accessToken, cancellationToken); + Result result = await this.Put(requestUri, redeemVoucherRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -369,7 +381,7 @@ public async Task ResendEmailReceipt(String accessToken, String requestUri = this.BuildRequestUrl($"/api/{estateId}/transactions/{transactionId}/resendreceipt"); try { - var result = await this.SendHttpPostRequest(requestUri, accessToken, cancellationToken); + var result = await this.Post(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -392,7 +404,7 @@ public async Task AddContractToMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/contracts"); try { - Result result = await this.SendHttpPatchRequest(requestUri, addMerchantContractRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, addMerchantContractRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -415,7 +427,7 @@ public async Task AddDeviceToMerchant(String accessToken, try { - Result result = await this.SendHttpPatchRequest(requestUri, addMerchantDeviceRequest,accessToken, cancellationToken); + Result result = await this.Patch(requestUri, addMerchantDeviceRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -438,7 +450,7 @@ public async Task AddMerchantAddress(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/addresses"); try { - Result result = await this.SendHttpPatchRequest(requestUri, newAddressRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, newAddressRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -461,7 +473,7 @@ public async Task AddMerchantContact(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/contacts"); try { - Result result = await this.SendHttpPatchRequest(requestUri, newContactRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, newContactRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -484,7 +496,7 @@ public async Task AddProductToContract(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/contracts/{contractId}/products"); try { - Result result = await this.SendHttpPatchRequest(requestUri, addProductToContractRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, addProductToContractRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -508,7 +520,7 @@ public async Task AddTransactionFeeForProductToContract(String accessTok String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/contracts/{contractId}/products/{productId}/transactionFees"); try { - Result result = await this.SendHttpPatchRequest(requestUri, addTransactionFeeForProductToContractRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, addTransactionFeeForProductToContractRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -531,7 +543,7 @@ public async Task AssignOperatorToMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/operators"); try { - Result result = await this.SendHttpPatchRequest(requestUri, assignOperatorRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, assignOperatorRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -554,7 +566,7 @@ public async Task RemoveOperatorFromMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/operators/{operatorId}"); try { - Result result = await this.SendHttpDeleteRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Delete(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -576,7 +588,7 @@ public async Task RemoveOperatorFromEstate(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/operators/{operatorId}"); try { - Result result = await this.SendHttpDeleteRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Delete(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -599,7 +611,7 @@ public async Task RemoveContractFromMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/contracts/{contractId}"); try { - Result result = await this.SendHttpDeleteRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Delete(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -621,7 +633,7 @@ public async Task CreateContract(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/contracts/"); try { - var result = await this.SendHttpPostRequest(requestUri, createContractRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, createContractRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -643,7 +655,7 @@ public async Task CreateOperator(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/operators"); try { - var result = await this.SendHttpPostRequest(requestUri, createOperatorRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, createOperatorRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -665,7 +677,7 @@ public async Task CreateEstateUser(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/users"); try { - Result result = await this.SendHttpPatchRequest(requestUri, createEstateUserRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, createEstateUserRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -688,7 +700,7 @@ public async Task CreateMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants"); try { - var result = await this.SendHttpPostRequest(requestUri, createMerchantRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, createMerchantRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -711,7 +723,7 @@ public async Task CreateMerchantSchedule(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/schedules"); try { - var result = await this.SendHttpPostRequest(requestUri, createMerchantScheduleRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, createMerchantScheduleRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -735,7 +747,7 @@ public async Task UpdateMerchantSchedule(String accessToken, try { - var result = await this.SendHttpPatchRequest(requestUri, updateMerchantScheduleRequest, accessToken, cancellationToken); + var result = await this.Patch(requestUri, updateMerchantScheduleRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -757,7 +769,7 @@ public async Task> GetMerchantSchedule(String a String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/schedules/{year}"); try { - Result result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -779,7 +791,7 @@ public async Task> GetMerchantScheduleFromReadM String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/schedules/{year}/readmodel"); try { - Result result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -801,7 +813,7 @@ public async Task CreateMerchantUser(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/users"); try { - Result result = await this.SendHttpPatchRequest(requestUri, createMerchantUserRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, createMerchantUserRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -823,7 +835,7 @@ public async Task AssignOperatorToEstate(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/operators"); try { - Result result = await this.SendHttpPatchRequest(requestUri, assignOperatorRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, assignOperatorRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -847,7 +859,7 @@ public async Task DisableTransactionFeeForProduct(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/contracts/{contractId}/products/{productId}/transactionFees/{transactionFeeId}"); try { - Result result = await this.SendHttpDeleteRequest(requestUri, accessToken, cancellationToken); + Result result = await this.Delete(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -869,7 +881,7 @@ public async Task> GetContract(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/contracts/{contractId}"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -890,7 +902,7 @@ public async Task>> GetContracts(String accessToke String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/contracts"); try { - var result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + var result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -912,7 +924,7 @@ public async Task> GetMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -934,7 +946,7 @@ public async Task>> GetMerchantContracts(String ac String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/contracts"); try { - var result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + var result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -955,7 +967,7 @@ public async Task>> GetMerchants(String accessToke String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants"); try { - var result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + var result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -978,7 +990,7 @@ public async Task>> GetMerchants(String accessToke String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/settlements/{settlementId}?merchantId={merchantId}"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1002,7 +1014,7 @@ public async Task>> GetMerchants(String accessToke String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/settlements?merchantId={merchantId}&start_date={startDate}&end_date={endDate}"); try { - var result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + var result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1026,7 +1038,7 @@ public async Task>> GetTransactionFee String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/contracts/{contractId}/products/{productId}/transactionFees"); try { - var result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + var result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1049,7 +1061,7 @@ public async Task MakeMerchantDeposit(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/deposits"); try { - var result = await this.SendHttpPostRequest(requestUri, makeMerchantDepositRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, makeMerchantDepositRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1072,7 +1084,7 @@ public async Task MakeMerchantWithdrawal(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/withdrawals"); try { - var result = await this.SendHttpPostRequest(requestUri, makeMerchantWithdrawalRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, makeMerchantWithdrawalRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1095,7 +1107,7 @@ public async Task SetMerchantSettlementSchedule(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}"); try { - Result result = await this.SendHttpPatchRequest(requestUri, setSettlementScheduleRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, setSettlementScheduleRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1119,7 +1131,7 @@ public async Task SwapDeviceForMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/devices/{deviceIdentifier}"); try { - Result result = await this.SendHttpPatchRequest(requestUri, swapMerchantDeviceRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, swapMerchantDeviceRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1142,7 +1154,7 @@ public async Task UpdateMerchant(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}"); try { - Result result = await this.SendHttpPatchRequest(requestUri, updateMerchantRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, updateMerchantRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1166,7 +1178,7 @@ public async Task UpdateMerchantAddress(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/addresses/{addressId}"); try { - Result result = await this.SendHttpPatchRequest(requestUri, updatedAddressRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, updatedAddressRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1190,7 +1202,7 @@ public async Task UpdateMerchantContact(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/merchants/{merchantId}/contacts/{contactId}"); try { - Result result = await this.SendHttpPatchRequest(requestUri, updatedContactRequest, accessToken, cancellationToken); + Result result = await this.Patch(requestUri, updatedContactRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1213,7 +1225,7 @@ public async Task UpdateOperator(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/operators/{operatorId}"); try { - var result = await this.SendHttpPostRequest(requestUri, updateOperatorRequest, accessToken, cancellationToken); + var result = await this.Post(requestUri, updateOperatorRequest, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1235,7 +1247,7 @@ public async Task> GetOperator(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/operators/{operatorId}"); try { - var result = await this.SendHttpGetRequest(requestUri, accessToken, cancellationToken); + var result = await this.Get(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -1256,7 +1268,7 @@ public async Task>> GetOperators(String accessToke String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/operators"); try { - var result = await this.SendHttpGetRequest>(requestUri, accessToken, cancellationToken); + var result = await this.Get>(requestUri, accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); From 297a78e68867cca0c0ee04c5c1534b53e764cd9b Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 4 May 2026 11:05:32 +0100 Subject: [PATCH 2/2] Refactor test setup to remove messagingService scope - Remove all references to messagingService scope, resource, and client from feature files and generated code - Update transaction tables to leave CustomerEmailAddress empty for some transactions - Comment out receipt resend scenario in feature and generated files - Send empty StringContent in ProcessSettlement POST request - Add result success assertion in settlement processing step - Rename table variables in generated code for clarity - Ensure only transactionProcessor scope is used in test setup and data --- .../TransactionProcessorClient.cs | 2 +- .../TransactionProcessorSteps.cs | 4 +- .../Features/SaleTransactionFeature.feature | 16 +- .../SaleTransactionFeature.feature.cs | 449 +++++++++--------- 4 files changed, 226 insertions(+), 245 deletions(-) diff --git a/TransactionProcessor.Client/TransactionProcessorClient.cs b/TransactionProcessor.Client/TransactionProcessorClient.cs index 24a8bf6f..91ff21a0 100644 --- a/TransactionProcessor.Client/TransactionProcessorClient.cs +++ b/TransactionProcessor.Client/TransactionProcessorClient.cs @@ -308,7 +308,7 @@ public async Task ProcessSettlement(String accessToken, String requestUri = this.BuildRequestUrl($"/api/estates/{estateId}/settlements/{settlementDate.Date:yyyy-MM-dd}/merchants/{merchantId}"); try { - Result result = await this.Post(requestUri, accessToken, cancellationToken); + Result result = await this.Post(requestUri, new StringContent(""), accessToken, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); diff --git a/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs b/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs index 91bc1557..b5e4a506 100644 --- a/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs +++ b/TransactionProcessor.IntegrationTesting.Helpers/TransactionProcessorSteps.cs @@ -505,12 +505,14 @@ await Retry.For(async () => public async Task WhenIProcessTheSettlementForOnEstateThenFeesAreMarkedAsSettledAndTheSettlementIsCompleted(String accessToken, ReqnrollExtensions.ProcessSettlementRequest request, Int32 expectedNumberFeesSettled) { - await this.TransactionProcessorClient.ProcessSettlement(accessToken, + var result = await this.TransactionProcessorClient.ProcessSettlement(accessToken, request.SettlementDate, request.EstateDetails.EstateId, request.MerchantId, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + await Retry.For(async () => { Result? getSettlementByDateResult = await this.TransactionProcessorClient.GetSettlementByDate(accessToken, diff --git a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature index b36ef2c8..04341952 100644 --- a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature +++ b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature @@ -6,16 +6,16 @@ Background: Given I create the following api scopes | Name | DisplayName | Description | | transactionProcessor | Transaction Processor REST Scope | A scope for Transaction Processor REST | - | messagingService | Scope for Messaging REST | Scope for Messaging REST | + #| messagingService | Scope for Messaging REST | Scope for Messaging REST | Given the following api resources exist | Name | DisplayName | Secret | Scopes | UserClaims | | transactionProcessor | Estate Managememt REST | Secret1 | transactionProcessor | MerchantId, EstateId, role | - | messagingService | Messaging REST | Secret | messagingService | | + #| messagingService | Messaging REST | Secret | messagingService | | Given the following clients exist | ClientId | ClientName | Secret | Scopes | GrantTypes | - | serviceClient | Service Client | Secret1 | transactionProcessor,messagingService | client_credentials | + | serviceClient | Service Client | Secret1 | transactionProcessor | client_credentials | Given I have a token to access the estate management and transaction processor resources | ClientId | @@ -137,10 +137,10 @@ Scenario: Sale Transactions When I perform the following transactions | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | RecipientEmail | RecipientMobile | MessageType | AccountNumber | CustomerName | MeterNumber | - | Today | 1 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 110.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | | | | | + | Today | 1 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 110.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | | Today | 2 | Sale | 1 | Test Merchant 2 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | | Today | 3 | Sale | 2 | Test Merchant 3 | 123456782 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | - | Today | 4 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 90.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | | | | | + | Today | 4 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 90.00 | 123456789 | | Safaricom Contract | Variable Topup | | | | | | | | Today | 5 | Sale | 1 | Test Merchant 1 | 123456780 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | | | | | | Today | 6 | Sale | 1 | Test Merchant 2 | 123456781 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | | 123456789 | | | | | | Today | 7 | Sale | 2 | Test Merchant 3 | 123456782 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | | | | | @@ -193,9 +193,9 @@ Scenario: Sale Transactions | Today | Transaction Fee Processed | C | 0.00 | 0.85 | 0.50 | 20.00 | | Today | Opening Balance | C | 0.00 | 0.00 | 0.00 | 20.00 | - When I request the receipt is resent - | EstateName | MerchantName | TransactionNumber | - | Test Estate 1 | Test Merchant 1 | 1 | + #When I request the receipt is resent + #| EstateName | MerchantName | TransactionNumber | + #| Test Estate 1 | Test Merchant 1 | 1 | When I perform the following transactions | DateTime | TransactionNumber | TransactionType | TransactionSource | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | diff --git a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs index 3723880c..62807b1c 100644 --- a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs +++ b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature.cs @@ -111,179 +111,169 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table45 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Description"}); - table45.AddRow(new string[] { + table1.AddRow(new string[] { "transactionProcessor", "Transaction Processor REST Scope", "A scope for Transaction Processor REST"}); - table45.AddRow(new string[] { - "messagingService", - "Scope for Messaging REST", - "Scope for Messaging REST"}); #line 6 - await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table45, "Given "); + await testRunner.GivenAsync("I create the following api scopes", ((string)(null)), table1, "Given "); #line hidden - global::Reqnroll.Table table46 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { "Name", "DisplayName", "Secret", "Scopes", "UserClaims"}); - table46.AddRow(new string[] { + table2.AddRow(new string[] { "transactionProcessor", "Estate Managememt REST", "Secret1", - "estateManagement", + "transactionProcessor", "MerchantId, EstateId, role"}); - table46.AddRow(new string[] { - "messagingService", - "Messaging REST", - "Secret", - "messagingService", - ""}); #line 11 - await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table46, "Given "); + await testRunner.GivenAsync("the following api resources exist", ((string)(null)), table2, "Given "); #line hidden - global::Reqnroll.Table table47 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { "ClientId", "ClientName", "Secret", "Scopes", "GrantTypes"}); - table47.AddRow(new string[] { + table3.AddRow(new string[] { "serviceClient", "Service Client", "Secret1", - "transactionProcessor,messagingService", + "transactionProcessor", "client_credentials"}); #line 16 - await testRunner.GivenAsync("the following clients exist", ((string)(null)), table47, "Given "); + await testRunner.GivenAsync("the following clients exist", ((string)(null)), table3, "Given "); #line hidden - global::Reqnroll.Table table48 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { "ClientId"}); - table48.AddRow(new string[] { + table4.AddRow(new string[] { "serviceClient"}); #line 20 await testRunner.GivenAsync("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table48, "Given "); + "s", ((string)(null)), table4, "Given "); #line hidden - global::Reqnroll.Table table49 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { "AccountNumber", "AccountName", "DueDate", "Amount"}); - table49.AddRow(new string[] { + table5.AddRow(new string[] { "12345678", "Test Account 1", "Today", "100.00"}); #line 24 - await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table49, "Given "); + await testRunner.GivenAsync("the following bills are available at the PataPawa PostPaid Host", ((string)(null)), table5, "Given "); #line hidden - global::Reqnroll.Table table50 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { "Username", "Password"}); - table50.AddRow(new string[] { + table6.AddRow(new string[] { "operatora", "1234567898"}); #line 28 - await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table50, "Given "); + await testRunner.GivenAsync("the following users are available at the PataPawa PrePay Host", ((string)(null)), table6, "Given "); #line hidden - global::Reqnroll.Table table51 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { "MeterNumber", "CustomerName"}); - table51.AddRow(new string[] { + table7.AddRow(new string[] { "00000001", "Customer 1"}); - table51.AddRow(new string[] { + table7.AddRow(new string[] { "00000002", "Customer 2"}); - table51.AddRow(new string[] { + table7.AddRow(new string[] { "00000003", "Customer 3"}); #line 32 - await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table51, "Given "); + await testRunner.GivenAsync("the following meters are available at the PataPawa PrePay Host", ((string)(null)), table7, "Given "); #line hidden - global::Reqnroll.Table table52 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table8 = new global::Reqnroll.Table(new string[] { "EstateName"}); - table52.AddRow(new string[] { + table8.AddRow(new string[] { "Test Estate 1"}); #line 38 - await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table52, "Given "); + await testRunner.GivenAsync("I have created the following estates", ((string)(null)), table8, "Given "); #line hidden - global::Reqnroll.Table table53 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table9 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "RequireCustomMerchantNumber", "RequireCustomTerminalNumber"}); - table53.AddRow(new string[] { + table9.AddRow(new string[] { "Test Estate 1", "Safaricom", "False", "False"}); - table53.AddRow(new string[] { + table9.AddRow(new string[] { "Test Estate 1", "Voucher", "False", "False"}); - table53.AddRow(new string[] { + table9.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "False", "False"}); - table53.AddRow(new string[] { + table9.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "False", "False"}); #line 42 - await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table53, "Given "); + await testRunner.GivenAsync("I have created the following operators", ((string)(null)), table9, "Given "); #line hidden - global::Reqnroll.Table table54 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table10 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName"}); - table54.AddRow(new string[] { + table10.AddRow(new string[] { "Test Estate 1", "Safaricom"}); - table54.AddRow(new string[] { + table10.AddRow(new string[] { "Test Estate 1", "Voucher"}); - table54.AddRow(new string[] { + table10.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay"}); - table54.AddRow(new string[] { + table10.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay"}); #line 49 - await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table54, "And "); + await testRunner.AndAsync("I have assigned the following operators to the estates", ((string)(null)), table10, "And "); #line hidden - global::Reqnroll.Table table55 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table11 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription"}); - table55.AddRow(new string[] { + table11.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract"}); - table55.AddRow(new string[] { + table11.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract"}); - table55.AddRow(new string[] { + table11.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract"}); - table55.AddRow(new string[] { + table11.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract"}); #line 56 - await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table55, "Given "); + await testRunner.GivenAsync("I create a contract with the following values", ((string)(null)), table11, "Given "); #line hidden - global::Reqnroll.Table table56 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table12 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -291,7 +281,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "DisplayText", "Value", "ProductType"}); - table56.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -299,7 +289,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Custom", "", "MobileTopup"}); - table56.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "Voucher", "Hospital 1 Contract", @@ -307,7 +297,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10 KES", "", "Voucher"}); - table56.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -315,7 +305,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Bill Pay (Post)", "", "BillPayment"}); - table56.AddRow(new string[] { + table12.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -324,9 +314,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "BillPayment"}); #line 63 - await testRunner.WhenAsync("I create the following Products", ((string)(null)), table56, "When "); + await testRunner.WhenAsync("I create the following Products", ((string)(null)), table12, "When "); #line hidden - global::Reqnroll.Table table57 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table13 = new global::Reqnroll.Table(new string[] { "EstateName", "OperatorName", "ContractDescription", @@ -334,7 +324,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CalculationType", "FeeDescription", "Value"}); - table57.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "Safaricom", "Safaricom Contract", @@ -342,7 +332,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table57.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "PataPawa PostPay", "PataPawa PostPay Contract", @@ -350,7 +340,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Percentage", "Merchant Commission", "0.50"}); - table57.AddRow(new string[] { + table13.AddRow(new string[] { "Test Estate 1", "PataPawa PrePay", "PataPawa PrePay Contract", @@ -359,9 +349,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Commission", "0.50"}); #line 70 - await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table57, "When "); + await testRunner.WhenAsync("I add the following Transaction Fees", ((string)(null)), table13, "When "); #line hidden - global::Reqnroll.Table table58 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table14 = new global::Reqnroll.Table(new string[] { "MerchantName", "AddressLine1", "Town", @@ -371,7 +361,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "ContactName", "EmailAddress", "EstateName"}); - table58.AddRow(new string[] { + table14.AddRow(new string[] { "Test Merchant 1", "Address Line 1", "TestTown", @@ -381,7 +371,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 1", "testcontact1@merchant1.co.uk", "Test Estate 1"}); - table58.AddRow(new string[] { + table14.AddRow(new string[] { "Test Merchant 2", "Address Line 1", "TestTown", @@ -391,7 +381,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 2", "testcontact2@merchant2.co.uk", "Test Estate 1"}); - table58.AddRow(new string[] { + table14.AddRow(new string[] { "Test Merchant 3", "Address Line 1", "TestTown", @@ -401,7 +391,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Test Contact 3", "testcontact3@merchant3.co.uk", "Test Estate 1"}); - table58.AddRow(new string[] { + table14.AddRow(new string[] { "Test Merchant 4", "Address Line 1", "TestTown", @@ -412,239 +402,239 @@ await testRunner.GivenAsync("I have a token to access the estate management and "testcontact4@merchant4.co.uk", "Test Estate 1"}); #line 76 - await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table58, "Given "); + await testRunner.GivenAsync("I create the following merchants", ((string)(null)), table14, "Given "); #line hidden - global::Reqnroll.Table table59 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table15 = new global::Reqnroll.Table(new string[] { "OperatorName", "MerchantName", "MerchantNumber", "TerminalNumber", "EstateName"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Safaricom", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Voucher", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 1", "00000001", "10000001", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Safaricom", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Voucher", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 2", "00000002", "10000002", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 2", "00000001", "10000001", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Safaricom", "Test Merchant 3", "00000003", "10000003", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Voucher", "Test Merchant 3", "00000003", "10000003", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 3", "00000003", "10000003", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 3", "00000001", "10000001", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Safaricom", "Test Merchant 4", "00000004", "10000004", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "Voucher", "Test Merchant 4", "00000004", "10000004", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PostPay", "Test Merchant 4", "00000004", "10000004", "Test Estate 1"}); - table59.AddRow(new string[] { + table15.AddRow(new string[] { "PataPawa PrePay", "Test Merchant 4", "00000001", "10000001", "Test Estate 1"}); #line 83 - await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table59, "Given "); + await testRunner.GivenAsync("I have assigned the following operator to the merchants", ((string)(null)), table15, "Given "); #line hidden - global::Reqnroll.Table table60 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table16 = new global::Reqnroll.Table(new string[] { "DeviceIdentifier", "MerchantName", "EstateName"}); - table60.AddRow(new string[] { + table16.AddRow(new string[] { "123456780", "Test Merchant 1", "Test Estate 1"}); - table60.AddRow(new string[] { + table16.AddRow(new string[] { "123456781", "Test Merchant 2", "Test Estate 1"}); - table60.AddRow(new string[] { + table16.AddRow(new string[] { "123456782", "Test Merchant 3", "Test Estate 1"}); - table60.AddRow(new string[] { + table16.AddRow(new string[] { "123456783", "Test Merchant 4", "Test Estate 1"}); #line 102 - await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table60, "Given "); + await testRunner.GivenAsync("I have assigned the following devices to the merchants", ((string)(null)), table16, "Given "); #line hidden - global::Reqnroll.Table table61 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table17 = new global::Reqnroll.Table(new string[] { "Reference", "Amount", "DateTime", "MerchantName", "EstateName"}); - table61.AddRow(new string[] { + table17.AddRow(new string[] { "Deposit1", "265.00", "Today", "Test Merchant 1", "Test Estate 1"}); - table61.AddRow(new string[] { + table17.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 2", "Test Estate 1"}); - table61.AddRow(new string[] { + table17.AddRow(new string[] { "Deposit1", "110.00", "Today", "Test Merchant 3", "Test Estate 1"}); - table61.AddRow(new string[] { + table17.AddRow(new string[] { "Deposit1", "100.00", "Today", "Test Merchant 4", "Test Estate 1"}); #line 109 - await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table61, "Given "); + await testRunner.GivenAsync("I make the following manual merchant deposits", ((string)(null)), table17, "Given "); #line hidden - global::Reqnroll.Table table62 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table18 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "ContractDescription"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Safaricom Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "Hospital 1 Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "PataPawa PostPay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "PataPawa PrePay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Safaricom Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "Hospital 1 Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "PataPawa PostPay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "PataPawa PrePay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "Safaricom Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "Hospital 1 Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "PataPawa PostPay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "PataPawa PrePay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "Safaricom Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "Hospital 1 Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "PataPawa PostPay Contract"}); - table62.AddRow(new string[] { + table18.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "PataPawa PrePay Contract"}); #line 116 - await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table62, "When "); + await testRunner.WhenAsync("I add the following contracts to the following merchants", ((string)(null)), table18, "When "); #line hidden } @@ -678,7 +668,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table63 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table19 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -698,7 +688,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "AccountNumber", "CustomerName", "MeterNumber"}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "1", "Sale", @@ -709,7 +699,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom", "110.00", "123456789", - "testcustomer@customer.co.uk", + "", "Safaricom Contract", "Variable Topup", "", @@ -718,7 +708,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "2", "Sale", @@ -738,7 +728,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "3", "Sale", @@ -758,7 +748,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "4", "Sale", @@ -769,7 +759,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom", "90.00", "123456789", - "testcustomer@customer.co.uk", + "", "Safaricom Contract", "Variable Topup", "", @@ -778,7 +768,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "5", "Sale", @@ -798,7 +788,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "6", "Sale", @@ -818,7 +808,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "7", "Sale", @@ -838,7 +828,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "8", "Sale", @@ -858,7 +848,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "9", "Sale", @@ -878,7 +868,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "12345678", "Mr Test Customer", ""}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "10", "Sale", @@ -898,7 +888,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "", "", "00000001"}); - table63.AddRow(new string[] { + table19.AddRow(new string[] { "Today", "11", "Sale", @@ -919,84 +909,84 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Customer 1", "00000001"}); #line 138 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table63, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table19, "When "); #line hidden - global::Reqnroll.Table table64 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table20 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "1", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "2", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "3", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "4", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "5", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 2", "6", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 3", "7", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "8", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "9", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "10", "0000", "SUCCESS"}); - table64.AddRow(new string[] { + table20.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "11", "0000", "SUCCESS"}); #line 152 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table64, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table20, "Then "); #line hidden - global::Reqnroll.Table table65 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table21 = new global::Reqnroll.Table(new string[] { "DateTime", "Reference", "EntryType", @@ -1004,7 +994,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Out", "ChangeAmount", "Balance"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Merchant Deposit", "C", @@ -1012,7 +1002,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "265.00", "230.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1020,7 +1010,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "110.00", "110.00", "130.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1028,7 +1018,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "90.00", "90.00", "30.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1036,7 +1026,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10.00", "10.00", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1044,7 +1034,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "20.00", "20.00", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1052,7 +1042,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "20.00", "25.00", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1060,7 +1050,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.55", "0.55", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1068,7 +1058,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.45", "0.45", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1076,7 +1066,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.01", "0.10", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1084,7 +1074,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.01", "0.10", "20.00"}); - table65.AddRow(new string[] { + table21.AddRow(new string[] { "Today", "Opening Balance", "C", @@ -1094,9 +1084,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "20.00"}); #line 166 await testRunner.ThenAsync("the following entries appear in the merchants balance history for estate \'Test Es" + - "tate 1\' and merchant \'Test Merchant 1\'", ((string)(null)), table65, "Then "); + "tate 1\' and merchant \'Test Merchant 1\'", ((string)(null)), table21, "Then "); #line hidden - global::Reqnroll.Table table66 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table22 = new global::Reqnroll.Table(new string[] { "DateTime", "Reference", "EntryType", @@ -1104,7 +1094,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Out", "ChangeAmount", "Balance"}); - table66.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "Merchant Deposit", "C", @@ -1112,7 +1102,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "110.00", "230.00"}); - table66.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1120,7 +1110,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "100.00", "100.00", "130.00"}); - table66.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1128,7 +1118,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10.00", "10.00", "30.00"}); - table66.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1136,7 +1126,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.50", "0.50", "20.00"}); - table66.AddRow(new string[] { + table22.AddRow(new string[] { "Today", "Opening Balance", "C", @@ -1146,9 +1136,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "20.00"}); #line 180 await testRunner.ThenAsync("the following entries appear in the merchants balance history for estate \'Test Es" + - "tate 1\' and merchant \'Test Merchant 2\'", ((string)(null)), table66, "Then "); + "tate 1\' and merchant \'Test Merchant 2\'", ((string)(null)), table22, "Then "); #line hidden - global::Reqnroll.Table table67 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table23 = new global::Reqnroll.Table(new string[] { "DateTime", "Reference", "EntryType", @@ -1156,7 +1146,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Out", "ChangeAmount", "Balance"}); - table67.AddRow(new string[] { + table23.AddRow(new string[] { "Today", "Merchant Deposit", "C", @@ -1164,7 +1154,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.00", "110.00", "230.00"}); - table67.AddRow(new string[] { + table23.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1172,7 +1162,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "100.00", "100.00", "130.00"}); - table67.AddRow(new string[] { + table23.AddRow(new string[] { "Today", "Transaction Completed", "D", @@ -1180,7 +1170,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "10.00", "10.00", "30.00"}); - table67.AddRow(new string[] { + table23.AddRow(new string[] { "Today", "Transaction Fee Processed", "C", @@ -1188,7 +1178,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "0.85", "0.50", "20.00"}); - table67.AddRow(new string[] { + table23.AddRow(new string[] { "Today", "Opening Balance", "C", @@ -1198,20 +1188,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "20.00"}); #line 188 await testRunner.ThenAsync("the following entries appear in the merchants balance history for estate \'Test Es" + - "tate 1\' and merchant \'Test Merchant 3\'", ((string)(null)), table67, "Then "); -#line hidden - global::Reqnroll.Table table68 = new global::Reqnroll.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber"}); - table68.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "1"}); -#line 196 - await testRunner.WhenAsync("I request the receipt is resent", ((string)(null)), table68, "When "); + "tate 1\' and merchant \'Test Merchant 3\'", ((string)(null)), table23, "Then "); #line hidden - global::Reqnroll.Table table69 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table24 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1225,7 +1204,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table69.AddRow(new string[] { + table24.AddRow(new string[] { "Today", "12", "Sale", @@ -1240,24 +1219,24 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom Contract", "Variable Topup"}); #line 200 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table69, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table24, "When "); #line hidden - global::Reqnroll.Table table70 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table25 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table70.AddRow(new string[] { + table25.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "12", "1000", "Device Identifier 123456781 not valid for Merchant Test Merchant 1"}); #line 204 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table70, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table25, "Then "); #line hidden - global::Reqnroll.Table table71 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table26 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1271,7 +1250,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table71.AddRow(new string[] { + table26.AddRow(new string[] { "Today", "13", "Sale", @@ -1286,24 +1265,24 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom Contract", "Variable Topup"}); #line 208 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table71, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table26, "When "); #line hidden - global::Reqnroll.Table table72 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table27 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table72.AddRow(new string[] { + table27.AddRow(new string[] { "InvalidEstate", "Test Merchant 1", "13", "1001", "Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate"}); #line 212 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table72, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table27, "Then "); #line hidden - global::Reqnroll.Table table73 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table28 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1317,7 +1296,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table73.AddRow(new string[] { + table28.AddRow(new string[] { "Today", "14", "Sale", @@ -1332,15 +1311,15 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom Contract", "Variable Topup"}); #line 216 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table73, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table28, "When "); #line hidden - global::Reqnroll.Table table74 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table74.AddRow(new string[] { + table29.AddRow(new string[] { "Test Estate 1", "InvalidMerchant", "14", @@ -1348,9 +1327,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for es" + "tate [Test Estate 1]"}); #line 220 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table74, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table29, "Then "); #line hidden - global::Reqnroll.Table table75 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1364,7 +1343,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table75.AddRow(new string[] { + table30.AddRow(new string[] { "Today", "15", "Sale", @@ -1379,15 +1358,15 @@ await testRunner.GivenAsync("I have a token to access the estate management and "EmptyContract", "Variable Topup"}); #line 224 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table75, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table30, "When "); #line hidden - global::Reqnroll.Table table76 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table76.AddRow(new string[] { + table31.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "15", @@ -1395,9 +1374,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Contract Id [00000000-0000-0000-0000-000000000000] must be set for a sale transac" + "tion"}); #line 228 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table76, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table31, "Then "); #line hidden - global::Reqnroll.Table table77 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1411,7 +1390,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table77.AddRow(new string[] { + table32.AddRow(new string[] { "Today", "16", "Sale", @@ -1426,15 +1405,15 @@ await testRunner.GivenAsync("I have a token to access the estate management and "InvalidContract", "Variable Topup"}); #line 232 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table77, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table32, "When "); #line hidden - global::Reqnroll.Table table78 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table78.AddRow(new string[] { + table33.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "16", @@ -1442,9 +1421,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Contract Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test M" + "erchant 1]"}); #line 236 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table78, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table33, "Then "); #line hidden - global::Reqnroll.Table table79 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1458,7 +1437,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table79.AddRow(new string[] { + table34.AddRow(new string[] { "Today", "17", "Sale", @@ -1473,15 +1452,15 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom Contract", "EmptyProduct"}); #line 240 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table79, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table34, "When "); #line hidden - global::Reqnroll.Table table80 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table35 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table80.AddRow(new string[] { + table35.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "17", @@ -1489,9 +1468,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Product Id [00000000-0000-0000-0000-000000000000] must be set for a sale transact" + "ion"}); #line 244 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table80, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table35, "Then "); #line hidden - global::Reqnroll.Table table81 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table36 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1505,7 +1484,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table81.AddRow(new string[] { + table36.AddRow(new string[] { "Today", "18", "Sale", @@ -1520,15 +1499,15 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom Contract", "InvalidProduct"}); #line 248 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table81, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table36, "When "); #line hidden - global::Reqnroll.Table table82 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table37 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table82.AddRow(new string[] { + table37.AddRow(new string[] { "Test Estate 1", "Test Merchant 1", "18", @@ -1536,9 +1515,9 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Product Id [934d8164-f36a-448e-b27b-4d671d41d180] not valid for Merchant [Test Me" + "rchant 1]"}); #line 252 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table82, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table37, "Then "); #line hidden - global::Reqnroll.Table table83 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table38 = new global::Reqnroll.Table(new string[] { "DateTime", "TransactionNumber", "TransactionType", @@ -1552,7 +1531,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "CustomerEmailAddress", "ContractDescription", "ProductName"}); - table83.AddRow(new string[] { + table38.AddRow(new string[] { "Today", "19", "Sale", @@ -1567,15 +1546,15 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Safaricom Contract", "Variable Topup"}); #line 256 - await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table83, "When "); + await testRunner.WhenAsync("I perform the following transactions", ((string)(null)), table38, "When "); #line hidden - global::Reqnroll.Table table84 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table39 = new global::Reqnroll.Table(new string[] { "EstateName", "MerchantName", "TransactionNumber", "ResponseCode", "ResponseMessage"}); - table84.AddRow(new string[] { + table39.AddRow(new string[] { "Test Estate 1", "Test Merchant 4", "19", @@ -1583,7 +1562,7 @@ await testRunner.GivenAsync("I have a token to access the estate management and "Merchant [Test Merchant 4] does not have enough credit available [100.00] to perf" + "orm transaction amount [300.00]"}); #line 260 - await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table84, "Then "); + await testRunner.ThenAsync("transaction response should contain the following information", ((string)(null)), table39, "Then "); #line hidden } await this.ScenarioCleanupAsync();