From c86f60404d2caed5566ed2129c59458f9c3c3a11 Mon Sep 17 00:00:00 2001 From: MichaelL79 <53481075+MichaelL79@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:12:46 +0100 Subject: [PATCH 1/3] Added target version for DataAPI endpoints --- src/FMData.Rest/FileMakerRestClient.cs | 45 +++++++++---- src/FMData/RestTargetVersion.cs | 21 ++++++ tests/FMData.Rest.Tests/GeneralTests.cs | 87 +++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 src/FMData/RestTargetVersion.cs diff --git a/src/FMData.Rest/FileMakerRestClient.cs b/src/FMData.Rest/FileMakerRestClient.cs index 44e945f..33022f0 100644 --- a/src/FMData.Rest/FileMakerRestClient.cs +++ b/src/FMData.Rest/FileMakerRestClient.cs @@ -52,6 +52,7 @@ public class FileMakerRestClient : FileMakerApiClientBase, IFileMakerRestClient private readonly IAuthTokenProvider _authTokenProvider; private readonly bool _useNewClientForContainers = false; + private readonly string _targetVersion = "v1"; #region Constructors /// @@ -61,17 +62,19 @@ public class FileMakerRestClient : FileMakerApiClientBase, IFileMakerRestClient /// Name of the FileMaker Database to connect to. /// Account to connect with. /// Account to connect with. + /// Version of the DataAPI endpoint to use. Default is v1. [Obsolete("Creates a new HttpClient for this instance, and is generally not good. Inject a managed client.")] - public FileMakerRestClient(string fmsUri, string file, string user, string pass) - : this(new HttpClient(), new ConnectionInfo { FmsUri = fmsUri, Database = file, Username = user, Password = pass }) { } + public FileMakerRestClient(string fmsUri, string file, string user, string pass, RestTargetVersion targetVersion = RestTargetVersion.v1) + : this(new HttpClient(), new ConnectionInfo { FmsUri = fmsUri, Database = file, Username = user, Password = pass }, targetVersion) { } /// /// FM Data Constructor with HttpClient and ConnectionInfo. Useful for Dependency Injection situations. /// /// The HttpClient instance to use. /// The connection information for FMS. - public FileMakerRestClient(HttpClient client, ConnectionInfo conn) - : this(client, new DefaultAuthTokenProvider(conn)) + /// Version of the DataAPI endpoint to use. Default is v1. + public FileMakerRestClient(HttpClient client, ConnectionInfo conn, RestTargetVersion targetVersion = RestTargetVersion.v1) + : this(client, new DefaultAuthTokenProvider(conn), targetVersion) { } /// @@ -79,9 +82,11 @@ public FileMakerRestClient(HttpClient client, ConnectionInfo conn) /// /// The HttpClient instance to use. /// Authentication provider + /// Version of the DataAPI endpoint to use. Default is v1. public FileMakerRestClient( HttpClient client, - IAuthTokenProvider authTokenProvider) : this(client, authTokenProvider, false) + IAuthTokenProvider authTokenProvider, + RestTargetVersion targetVersion = RestTargetVersion.v1) : this(client, authTokenProvider, false, targetVersion) { } /// @@ -90,14 +95,28 @@ public FileMakerRestClient( /// The HttpClient instance to use. /// Authentication provider /// When set to true, will use a new http client to load container data that has isolated cookies and can work with ASP.NET Core DI/HttpClientFactory. + /// Version of the DataAPI endpoint to use. Default is v1. public FileMakerRestClient( HttpClient client, IAuthTokenProvider authTokenProvider, - bool useNewClientForContainers) + bool useNewClientForContainers, + RestTargetVersion targetVersion = RestTargetVersion.v1) : base(client, authTokenProvider.ConnectionInfo) { _authTokenProvider = authTokenProvider; _useNewClientForContainers = useNewClientForContainers; + switch (targetVersion) + { + case RestTargetVersion.v1: + _targetVersion = "v1"; + break; + case RestTargetVersion.v2: + _targetVersion = "v2"; + break; + default: + _targetVersion = "vLatest"; + break; + } #if NETSTANDARD1_3 var header = new System.Net.Http.Headers.ProductHeaderValue("FMData.Rest", "4"); var userAgent = new System.Net.Http.Headers.ProductInfoHeaderValue(header); @@ -116,7 +135,7 @@ public FileMakerRestClient( /// /// Note we assume _fmsUri has no trailing slash as its cut off in the constructor. /// - private string BaseEndPoint => $"{FmsUri}/fmi/data/v1/databases/{FileName}"; + private string BaseEndPoint => $"{FmsUri}/fmi/data/{_targetVersion}/databases/{FileName}"; /// /// Generate the appropriate Authentication endpoint uri for this instance of the data client. @@ -639,7 +658,7 @@ public override async Task RunScriptAsync(string layout, string script, await UpdateTokenDateAsync().ConfigureAwait(false); // we're about to use the token so update date used // generate request url - var uri = $"{FmsUri}/fmi/data/v1" + var uri = $"{FmsUri}/fmi/data/{_targetVersion}" + $"/databases/{Uri.EscapeDataString(FileName)}" + $"/layouts/{Uri.EscapeDataString(layout)}" + $"/script/{Uri.EscapeDataString(script)}"; @@ -835,7 +854,7 @@ public override async Task SetGlobalFieldAsync(string baseTable, stri public override async Task GetProductInformationAsync() { // generate request url - var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{FmsUri}/fmi/data/v1/productinfo"); + var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{FmsUri}/fmi/data/{_targetVersion}/productinfo"); // run the patch action var response = await Client.SendAsync(requestMessage).ConfigureAwait(false); @@ -869,7 +888,7 @@ public override async Task> GetDatabasesAsync() // don't need to refresh the token, because this is a basic authentication request // generate request url - var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{FmsUri}/fmi/data/v1/databases"); + var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{FmsUri}/fmi/data/{_targetVersion}/databases"); // special non-token auth to list databases requestMessage.Headers.Authorization = await _authTokenProvider.GetAuthenticationHeaderValue().ConfigureAwait(false); @@ -906,7 +925,7 @@ public override async Task> GetLayoutsAsync( await UpdateTokenDateAsync().ConfigureAwait(false); // we're about to use the token so update date used // generate request url - var uri = $"{FmsUri}/fmi/data/v1/" + var uri = $"{FmsUri}/fmi/data/{_targetVersion}/" + $"databases/{Uri.EscapeDataString(FileName)}/layouts"; var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri); @@ -945,7 +964,7 @@ public override async Task> GetScriptsAsync( await UpdateTokenDateAsync().ConfigureAwait(false); // we're about to use the token so update date used // generate request url - var uri = $"{FmsUri}/fmi/data/v1" + var uri = $"{FmsUri}/fmi/data/{_targetVersion}" + $"/databases/{Uri.EscapeDataString(FileName)}/scripts"; var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri); @@ -986,7 +1005,7 @@ public override async Task GetLayoutAsync(string layout, int? re await UpdateTokenDateAsync().ConfigureAwait(false); // we're about to use the token so update date used // generate request url - var uri = $"{FmsUri}/fmi/data/v1" + var uri = $"{FmsUri}/fmi/data/{_targetVersion}" + $"/databases/{Uri.EscapeDataString(FileName)}" + $"/layouts/{Uri.EscapeDataString(layout)}"; if (recordId.HasValue) diff --git a/src/FMData/RestTargetVersion.cs b/src/FMData/RestTargetVersion.cs new file mode 100644 index 0000000..2ba0b9f --- /dev/null +++ b/src/FMData/RestTargetVersion.cs @@ -0,0 +1,21 @@ +namespace FMData +{ + /// + /// All supported FileMaker DataAPI endpoint versions. + /// + public enum RestTargetVersion + { + /// + /// Uses v1 endpoint of the DataAPI. + /// + v1, + /// + /// Uses v2 endpoint of the DataAPI. + /// + v2, + /// + /// Uses the latest endpoint version of the DataAPI. + /// + vLatest + } +} diff --git a/tests/FMData.Rest.Tests/GeneralTests.cs b/tests/FMData.Rest.Tests/GeneralTests.cs index 4134d2d..0392deb 100644 --- a/tests/FMData.Rest.Tests/GeneralTests.cs +++ b/tests/FMData.Rest.Tests/GeneralTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net.Http; @@ -6,6 +7,7 @@ using System.Threading.Tasks; using FMData.Rest.Requests; using FMData.Rest.Tests.TestModels; +using Newtonsoft.Json.Linq; using RichardSzalay.MockHttp; using Xunit; @@ -197,5 +199,90 @@ public async Task Test_DateTime_To_Timestamp_Parsing() var responseDataContainsResult = response.Any(r => r.Created == DateTime.ParseExact("03/29/2018 15:22:09", "MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture)); Assert.True(responseDataContainsResult); } + + + [Fact] + public void Test_EndpointVersion_Default() + { + // arrange + var expected = "http://localhost/fmi/data/v1/databases/test-file/sessions"; + var testClients = CreateEndpointTestClients(null); + + // assert + foreach (var client in testClients) + { + Assert.Equal(expected, client.AuthEndpoint()); + } + } + + [Fact] + public void Test_EndpointVersion_v1() + { + // arrange + var expected = "http://localhost/fmi/data/v1/databases/test-file/sessions"; + var testClients = CreateEndpointTestClients(RestTargetVersion.v1); + + // assert + foreach (var client in testClients) + { + Assert.Equal(expected, client.AuthEndpoint()); + } + } + + [Fact] + public void Test_EndpointVersion_v2() + { + // arrange + var expected = "http://localhost/fmi/data/v2/databases/test-file/sessions"; + var testClients = CreateEndpointTestClients(RestTargetVersion.v2); + + // assert + foreach (var client in testClients) + { + Assert.Equal(expected, client.AuthEndpoint()); + } + } + + [Fact] + public void Test_EndpointVersion_vLatest() + { + // arrange + var expected = "http://localhost/fmi/data/vLatest/databases/test-file/sessions"; + var testClients = CreateEndpointTestClients(RestTargetVersion.vLatest); + + // assert + foreach (var client in testClients) + { + Assert.Equal(expected, client.AuthEndpoint()); + } + } + + private IEnumerable CreateEndpointTestClients(RestTargetVersion? targetVersion) + { + + var mockHttp = new MockHttpMessageHandler(); + var server = "http://localhost"; + var file = "test-file"; + var user = "unit"; + var pass = "test"; + var connectionInfo = new ConnectionInfo { FmsUri = server, Database = file, Username = user, Password = pass }; + + if (targetVersion != null) + { + return new List(){ + new(server, file, user, pass, targetVersion.Value), + new(mockHttp.ToHttpClient(), connectionInfo, targetVersion.Value), + new(mockHttp.ToHttpClient(), new DefaultAuthTokenProvider(connectionInfo), targetVersion.Value) + }; + } + else + { + return new List(){ + new(server, file, user, pass), + new(mockHttp.ToHttpClient(), connectionInfo), + new(mockHttp.ToHttpClient(), new DefaultAuthTokenProvider(connectionInfo)) + }; + } + } } } From 4b5446f357be1ccdafbcacbbce3c1cd525a37dc8 Mon Sep 17 00:00:00 2001 From: MichaelL79 <53481075+MichaelL79@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:31:11 +0100 Subject: [PATCH 2/3] Moved target version to connection info object --- src/FMData.Rest/FileMakerRestClient.cs | 18 +++++++----------- src/FMData/ConnectionInfo.cs | 4 ++++ tests/FMData.Rest.Tests/GeneralTests.cs | 22 +++++----------------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/FMData.Rest/FileMakerRestClient.cs b/src/FMData.Rest/FileMakerRestClient.cs index 33022f0..97a6f91 100644 --- a/src/FMData.Rest/FileMakerRestClient.cs +++ b/src/FMData.Rest/FileMakerRestClient.cs @@ -65,16 +65,15 @@ public class FileMakerRestClient : FileMakerApiClientBase, IFileMakerRestClient /// Version of the DataAPI endpoint to use. Default is v1. [Obsolete("Creates a new HttpClient for this instance, and is generally not good. Inject a managed client.")] public FileMakerRestClient(string fmsUri, string file, string user, string pass, RestTargetVersion targetVersion = RestTargetVersion.v1) - : this(new HttpClient(), new ConnectionInfo { FmsUri = fmsUri, Database = file, Username = user, Password = pass }, targetVersion) { } + : this(new HttpClient(), new ConnectionInfo { FmsUri = fmsUri, Database = file, Username = user, Password = pass, RestTargetVersion = targetVersion }) { } /// /// FM Data Constructor with HttpClient and ConnectionInfo. Useful for Dependency Injection situations. /// /// The HttpClient instance to use. /// The connection information for FMS. - /// Version of the DataAPI endpoint to use. Default is v1. - public FileMakerRestClient(HttpClient client, ConnectionInfo conn, RestTargetVersion targetVersion = RestTargetVersion.v1) - : this(client, new DefaultAuthTokenProvider(conn), targetVersion) + public FileMakerRestClient(HttpClient client, ConnectionInfo conn) + : this(client, new DefaultAuthTokenProvider(conn)) { } /// @@ -82,11 +81,9 @@ public FileMakerRestClient(HttpClient client, ConnectionInfo conn, RestTargetVer /// /// The HttpClient instance to use. /// Authentication provider - /// Version of the DataAPI endpoint to use. Default is v1. public FileMakerRestClient( HttpClient client, - IAuthTokenProvider authTokenProvider, - RestTargetVersion targetVersion = RestTargetVersion.v1) : this(client, authTokenProvider, false, targetVersion) + IAuthTokenProvider authTokenProvider) : this(client, authTokenProvider, false) { } /// @@ -95,19 +92,18 @@ public FileMakerRestClient( /// The HttpClient instance to use. /// Authentication provider /// When set to true, will use a new http client to load container data that has isolated cookies and can work with ASP.NET Core DI/HttpClientFactory. - /// Version of the DataAPI endpoint to use. Default is v1. public FileMakerRestClient( HttpClient client, IAuthTokenProvider authTokenProvider, - bool useNewClientForContainers, - RestTargetVersion targetVersion = RestTargetVersion.v1) + bool useNewClientForContainers) : base(client, authTokenProvider.ConnectionInfo) { _authTokenProvider = authTokenProvider; _useNewClientForContainers = useNewClientForContainers; - switch (targetVersion) + switch (_authTokenProvider.ConnectionInfo?.RestTargetVersion) { case RestTargetVersion.v1: + case null: _targetVersion = "v1"; break; case RestTargetVersion.v2: diff --git a/src/FMData/ConnectionInfo.cs b/src/FMData/ConnectionInfo.cs index 856c204..8f08e5e 100644 --- a/src/FMData/ConnectionInfo.cs +++ b/src/FMData/ConnectionInfo.cs @@ -21,6 +21,10 @@ public class ConnectionInfo /// Password to use when making the connection. /// public string Password { get; set; } + /// + /// Gets or sets the to use. + /// + public RestTargetVersion? RestTargetVersion { get; set; } #region FileMaker Cloud diff --git a/tests/FMData.Rest.Tests/GeneralTests.cs b/tests/FMData.Rest.Tests/GeneralTests.cs index 0392deb..01a3818 100644 --- a/tests/FMData.Rest.Tests/GeneralTests.cs +++ b/tests/FMData.Rest.Tests/GeneralTests.cs @@ -265,24 +265,12 @@ private IEnumerable CreateEndpointTestClients(RestTargetVer var file = "test-file"; var user = "unit"; var pass = "test"; - var connectionInfo = new ConnectionInfo { FmsUri = server, Database = file, Username = user, Password = pass }; + var connectionInfo = new ConnectionInfo { FmsUri = server, Database = file, Username = user, Password = pass, RestTargetVersion = targetVersion }; - if (targetVersion != null) - { - return new List(){ - new(server, file, user, pass, targetVersion.Value), - new(mockHttp.ToHttpClient(), connectionInfo, targetVersion.Value), - new(mockHttp.ToHttpClient(), new DefaultAuthTokenProvider(connectionInfo), targetVersion.Value) - }; - } - else - { - return new List(){ - new(server, file, user, pass), - new(mockHttp.ToHttpClient(), connectionInfo), - new(mockHttp.ToHttpClient(), new DefaultAuthTokenProvider(connectionInfo)) - }; - } + return [ + new(mockHttp.ToHttpClient(), connectionInfo), + new(mockHttp.ToHttpClient(), new DefaultAuthTokenProvider(connectionInfo)) + ]; } } } From c38245f9e220c9377d8780c0cc991231c06ab6bf Mon Sep 17 00:00:00 2001 From: MichaelL79 <53481075+MichaelL79@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:12:46 +0100 Subject: [PATCH 3/3] Implemented suggested changes --- src/FMData.Rest/FileMakerRestClient.cs | 11 ++++++----- tests/FMData.Rest.Tests/GeneralTests.cs | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FMData.Rest/FileMakerRestClient.cs b/src/FMData.Rest/FileMakerRestClient.cs index 97a6f91..ec75a79 100644 --- a/src/FMData.Rest/FileMakerRestClient.cs +++ b/src/FMData.Rest/FileMakerRestClient.cs @@ -62,10 +62,9 @@ public class FileMakerRestClient : FileMakerApiClientBase, IFileMakerRestClient /// Name of the FileMaker Database to connect to. /// Account to connect with. /// Account to connect with. - /// Version of the DataAPI endpoint to use. Default is v1. [Obsolete("Creates a new HttpClient for this instance, and is generally not good. Inject a managed client.")] - public FileMakerRestClient(string fmsUri, string file, string user, string pass, RestTargetVersion targetVersion = RestTargetVersion.v1) - : this(new HttpClient(), new ConnectionInfo { FmsUri = fmsUri, Database = file, Username = user, Password = pass, RestTargetVersion = targetVersion }) { } + public FileMakerRestClient(string fmsUri, string file, string user, string pass) + : this(new HttpClient(), new ConnectionInfo { FmsUri = fmsUri, Database = file, Username = user, Password = pass }) { } /// /// FM Data Constructor with HttpClient and ConnectionInfo. Useful for Dependency Injection situations. @@ -103,15 +102,17 @@ public FileMakerRestClient( switch (_authTokenProvider.ConnectionInfo?.RestTargetVersion) { case RestTargetVersion.v1: - case null: _targetVersion = "v1"; break; case RestTargetVersion.v2: _targetVersion = "v2"; break; - default: + case RestTargetVersion.vLatest: _targetVersion = "vLatest"; break; + default: + _targetVersion = "v1"; + break; } #if NETSTANDARD1_3 var header = new System.Net.Http.Headers.ProductHeaderValue("FMData.Rest", "4"); diff --git a/tests/FMData.Rest.Tests/GeneralTests.cs b/tests/FMData.Rest.Tests/GeneralTests.cs index 01a3818..ffa243c 100644 --- a/tests/FMData.Rest.Tests/GeneralTests.cs +++ b/tests/FMData.Rest.Tests/GeneralTests.cs @@ -259,7 +259,6 @@ public void Test_EndpointVersion_vLatest() private IEnumerable CreateEndpointTestClients(RestTargetVersion? targetVersion) { - var mockHttp = new MockHttpMessageHandler(); var server = "http://localhost"; var file = "test-file";