diff --git a/CHANGELOG.md b/CHANGELOG.md index 011e04a..da7b6e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,3 +26,14 @@ - Fixed HttpClientFactory usage in default request sender - Migrate to SLNX + +## 2.1.0 + +### ✨ Features + +- Added support for Versioned API routes via `VersionedEndpoint` class, and `GetBuilder(VersionedEndpoint endpoint)` method signatures. +- Added support for M2M authentication via M2MAuthenticationClient. + +### 🛠 Technical + +- Overriding `GetBuilder` now changes the behaviour of `GetBuilderAsync`. \ No newline at end of file diff --git a/NotoriousClient.Sample/Program.cs b/NotoriousClient.Sample/Program.cs index 7adedbd..4b5315c 100644 --- a/NotoriousClient.Sample/Program.cs +++ b/NotoriousClient.Sample/Program.cs @@ -36,9 +36,8 @@ public class User } public class UserClient : BaseClient { - private Endpoint GET_USERS_ENDPOINT = new Endpoint("/api/users", Method.Get); - private Endpoint GET_USER_ENDPOINT = new Endpoint("/api/users/{id}", Method.Get); - private Endpoint CREATE_USER_ENDPOINT = new Endpoint("/api/users", Method.Post); + private Endpoint GET_USERS_ENDPOINT = new Endpoint("/users", Method.Get); + private VersionedEndpoint CREATE_USER_ENDPOINT = new VersionedEndpoint("/users", Method.Post, "v1.0"); public UserClient(IRequestSender sender, string url) : base(sender, url) { } @@ -55,18 +54,6 @@ public async Task> GetUsers() return response.ReadAs>(); } - public async Task> GetUser(int id) - { - HttpRequestMessage request = GetBuilder(GET_USERS_ENDPOINT) - .WithAuthentication("username", "password") - .AddEndpointParameter("id", id.ToString()) - .Build(); - - HttpResponseMessage response = await Sender.SendAsync(request); - - return response.ReadAs>(); - } - public async Task> CreateUser(User user) { HttpRequestMessage request = GetBuilder(CREATE_USER_ENDPOINT) diff --git a/NotoriousClient.Tests.Unit/RequestBuilderURIUnitTests.cs b/NotoriousClient.Tests.Unit/RequestBuilderURIUnitTests.cs index 39bd9eb..d12481f 100644 --- a/NotoriousClient.Tests.Unit/RequestBuilderURIUnitTests.cs +++ b/NotoriousClient.Tests.Unit/RequestBuilderURIUnitTests.cs @@ -53,6 +53,22 @@ public void RequestBuilder_Should_AddUrlSlashProperly() Assert.NotNull(request.RequestUri); Assert.Equal("https://toto.com/pandas", request.RequestUri.AbsoluteUri); } + + [GWTFact(given: "a url, an endpoint, an HTTP Verb and a version", + when: "i build a request", + then: "request has right url, endpoint, verb, and version")] + public void RequestBuilder_Should_HandleVersionProperly() + { + string url = "https://toto.com/api"; + VersionedEndpoint endpoint = new VersionedEndpoint("pandas", Method.Get, "v1.0"); + + RequestBuilder requestBuilder = new RequestBuilder(url, endpoint); + HttpRequestMessage request = requestBuilder.Build(); + + Assert.Equal(HttpMethod.Get, request.Method); + Assert.NotNull(request.RequestUri); + Assert.Equal("https://toto.com/api/v1.0/pandas", request.RequestUri.AbsoluteUri); + } #endregion #region QueryParams diff --git a/NotoriousClient/Builder/RequestBuilder.Uri.cs b/NotoriousClient/Builder/RequestBuilder.Uri.cs index e96e4d2..8e23061 100644 --- a/NotoriousClient/Builder/RequestBuilder.Uri.cs +++ b/NotoriousClient/Builder/RequestBuilder.Uri.cs @@ -4,6 +4,7 @@ public partial class RequestBuilder : IRequestBuilder { private string _url; private string _route; + private string? _version; private Dictionary _queryParams = new Dictionary(); private Dictionary _endpointParams = new Dictionary(); private const char START_ENDPOINT_PARAM = '{'; @@ -113,9 +114,10 @@ private string GetReplacementToken(string key) private string GetUri() { var url = !_url.EndsWith("/") ? _url : _url.Substring(0, _url.Length - 1); + var version = !string.IsNullOrEmpty(_version) ? $"/{_version}" : string.Empty; var endpoint = _route.StartsWith("/") ? _route : $"/{_route}"; - return url + endpoint; + return url + version + endpoint; } #endregion } diff --git a/NotoriousClient/Builder/RequestBuilder.cs b/NotoriousClient/Builder/RequestBuilder.cs index 81ebd8a..4158730 100644 --- a/NotoriousClient/Builder/RequestBuilder.cs +++ b/NotoriousClient/Builder/RequestBuilder.cs @@ -3,8 +3,8 @@ /// public partial class RequestBuilder : IRequestBuilder { - private Method _method; - + private Method _method; + /// /// Initialize a new instance of . /// @@ -20,14 +20,28 @@ public RequestBuilder(string url, string route, Method method) /// /// Initialize a new instance of . /// - public RequestBuilder(string url, Endpoint endpoint) + public RequestBuilder(string url, Endpoint endpoint) : this(url, endpoint.Route, endpoint.Method) { - if (string.IsNullOrEmpty(url)) throw new ArgumentNullException(); - ArgumentNullException.ThrowIfNull(endpoint, nameof(endpoint)); + } + /// + /// Initialize a new instance of . + /// + public RequestBuilder(string url, string route, string? version, Method method) + { + if (string.IsNullOrEmpty(url)) throw new ArgumentNullException(); + if (string.IsNullOrEmpty(route)) throw new ArgumentNullException(); _url = url; - _route = endpoint.Route; - _method = endpoint.Method; + _route = route; + _method = method; + _version = version; + } + + /// + /// Initialize a new instance of . + /// + public RequestBuilder(string url, VersionedEndpoint endpoint) : this(url, endpoint.Route, endpoint.Version, endpoint.Method) + { } /// diff --git a/NotoriousClient/Builder/VersionedEndpoint.cs b/NotoriousClient/Builder/VersionedEndpoint.cs new file mode 100644 index 0000000..c135d84 --- /dev/null +++ b/NotoriousClient/Builder/VersionedEndpoint.cs @@ -0,0 +1,11 @@ +namespace NotoriousClient.Builder +{ + public class VersionedEndpoint : Endpoint + { + public string Version { get; set; } + public VersionedEndpoint(string route, Method method, string version) : base(route, method) + { + Version = version; + } + } +} diff --git a/NotoriousClient/Clients/BaseClient.cs b/NotoriousClient/Clients/BaseClient.cs index f4815ee..575861c 100644 --- a/NotoriousClient/Clients/BaseClient.cs +++ b/NotoriousClient/Clients/BaseClient.cs @@ -16,7 +16,7 @@ public abstract class BaseClient protected IRequestSender Sender { get; } /// - /// Initialize a new instance of . + /// Initialize a new instance of . /// /// Class used to send . /// Base URL of api (ex: https://myapi.com/). @@ -34,8 +34,8 @@ protected BaseClient(IRequestSender sender, string url) /// /// Request's Route. /// Request's Method (GET, POST, PUT, DELETE...). - protected virtual IRequestBuilder GetBuilder(string route, Method method = Method.Get) - => new RequestBuilder(_url, route, method); + protected virtual IRequestBuilder GetBuilder(string route, Method method = Method.Get, string? version = null) + => new RequestBuilder(_url, route, version, method); /// /// Get preconfigured . @@ -44,13 +44,20 @@ protected virtual IRequestBuilder GetBuilder(string route, Method method = Metho protected IRequestBuilder GetBuilder(Endpoint endpoint) => GetBuilder(endpoint.Route, endpoint.Method); + /// + /// Get preconfigured with versioning. + /// + /// Request's . + protected IRequestBuilder GetBuilder(VersionedEndpoint endpoint) + => GetBuilder(endpoint.Route, endpoint.Method, endpoint.Version); + /// /// Get preconfigured . /// /// Request's Route. /// Request's Method (GET, POST, PUT, DELETE...). - protected virtual async Task GetBuilderAsync(string route, Method method = Method.Get) - => new RequestBuilder(_url, route, method); + protected virtual async Task GetBuilderAsync(string route, Method method = Method.Get, string? version = null) + => GetBuilder(route, method, version); /// /// Get preconfigured . @@ -58,5 +65,12 @@ protected virtual async Task GetBuilderAsync(string route, Meth /// Request's . protected Task GetBuilderAsync(Endpoint endpoint) => GetBuilderAsync(endpoint.Route, endpoint.Method); + + /// + /// Get preconfigured with versioning. + /// + /// Request's . + protected Task GetBuilderAsync(VersionedEndpoint endpoint) + => GetBuilderAsync(endpoint.Route, endpoint.Method, endpoint.Version); } } diff --git a/NotoriousClient/Clients/BasicAuthBaseClient.cs b/NotoriousClient/Clients/BasicAuthBaseClient.cs index cb694c8..04d19c5 100644 --- a/NotoriousClient/Clients/BasicAuthBaseClient.cs +++ b/NotoriousClient/Clients/BasicAuthBaseClient.cs @@ -33,7 +33,7 @@ protected BasicAuthBaseClient(IRequestSender sender, string url, string login, s /// /// Request's Route. /// Request's method (GET, POST, PUT, DELETE...). - protected override IRequestBuilder GetBuilder(string route, Method method = Method.Get) + protected override IRequestBuilder GetBuilder(string route, Method method = Method.Get, string? version = null) => base.GetBuilder(route, method).WithAuthentication(_login, _password); } } diff --git a/NotoriousClient/NotoriousClient.csproj b/NotoriousClient/NotoriousClient.csproj index 3fffd8b..74c393f 100644 --- a/NotoriousClient/NotoriousClient.csproj +++ b/NotoriousClient/NotoriousClient.csproj @@ -1,11 +1,11 @@  - net8.0 + net8.0;net9.0 A fluent HTTPResponseMessage builder with fully extendable API Client implementation. NotoriousClient enable enable - 2.0.3 + 2.1.0 README.md Brice SCHUMACHER https://github.com/Notorious-Coding/Notorious-Client/ diff --git a/README.md b/README.md index fc7d81e..d375862 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,21 @@ Lets dive into the possibity of the RequestBuilder ! new RequestBuilder("https://toto.com", "/users", Method.Get); ``` +Or with `Endpoint` class : + +```csharp +Endpoint endpoint = new Endpoint("/users", Method.Get); +new RequestBuilder("https://toto.com", endpoint); +``` + +or with `VersionedEndpoint` class : + +```csharp +VersionedEndpoint endpoint = new VersionedEndpoint("/users", Method.Get, "v1.0"); +new RequestBuilder("https://toto.com/api", endpoint); // Will result in "https://toto.com/api/v1.0/users" +``` + + ### Configure URI parameters **Add URL parameters**