Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
17 changes: 2 additions & 15 deletions NotoriousClient.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@
}
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)
{
}
Expand All @@ -55,18 +54,6 @@
return response.ReadAs<IEnumerable<User>>();
}

public async Task<IEnumerable<User>> 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<IEnumerable<User>>();
}

public async Task<IEnumerable<User>> CreateUser(User user)
{
HttpRequestMessage request = GetBuilder(CREATE_USER_ENDPOINT)
Expand Down Expand Up @@ -113,7 +100,7 @@
{
}

public async Task GetToto()

Check warning on line 103 in NotoriousClient.Sample/Program.cs

View workflow job for this annotation

GitHub Actions / publish

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
}

Expand Down
16 changes: 16 additions & 0 deletions NotoriousClient.Tests.Unit/RequestBuilderURIUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion NotoriousClient/Builder/RequestBuilder.Uri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public partial class RequestBuilder : IRequestBuilder
{
private string _url;
private string _route;
private string? _version;
private Dictionary<string, string> _queryParams = new Dictionary<string, string>();
private Dictionary<string, string> _endpointParams = new Dictionary<string, string>();
private const char START_ENDPOINT_PARAM = '{';
Expand Down Expand Up @@ -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
}
Expand Down
28 changes: 21 additions & 7 deletions NotoriousClient/Builder/RequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
///<inheritdoc/>
public partial class RequestBuilder : IRequestBuilder
{
private Method _method;
private Method _method;

/// <summary>
/// Initialize a new instance of <see cref="RequestBuilder"/>.
/// </summary>
Expand All @@ -20,14 +20,28 @@ public RequestBuilder(string url, string route, Method method)
/// <summary>
/// Initialize a new instance of <see cref="RequestBuilder"/>.
/// </summary>
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));
}

/// <summary>
/// Initialize a new instance of <see cref="RequestBuilder"/>.
/// </summary>
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;
}

/// <summary>
/// Initialize a new instance of <see cref="RequestBuilder"/>.
/// </summary>
public RequestBuilder(string url, VersionedEndpoint endpoint) : this(url, endpoint.Route, endpoint.Version, endpoint.Method)
{
}

///<inheritdoc/>
Expand Down
11 changes: 11 additions & 0 deletions NotoriousClient/Builder/VersionedEndpoint.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
24 changes: 19 additions & 5 deletions NotoriousClient/Clients/BaseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
protected IRequestSender Sender { get; }

/// <summary>
/// Initialize a new instance of <see cref="SynchronousBaseClient"/>.
/// Initialize a new instance of <see cref="BaseClient"/>.
/// </summary>
/// <param name="sender">Class used to send <see cref="HttpRequestMessage"/>.</param>
/// <param name="url">Base URL of api (ex: https://myapi.com/).</param>
Expand All @@ -34,8 +34,8 @@
/// </summary>
/// <param name="route">Request's Route.</param>
/// <param name="method">Request's Method (GET, POST, PUT, DELETE...).</param>
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);

/// <summary>
/// Get preconfigured <see cref="IRequestBuilder"/>.
Expand All @@ -44,19 +44,33 @@
protected IRequestBuilder GetBuilder(Endpoint endpoint)
=> GetBuilder(endpoint.Route, endpoint.Method);

/// <summary>
/// Get preconfigured <see cref="IRequestBuilder"/> with versioning.
/// </summary>
/// <param name="endpoint">Request's <see cref="Endpoint"/>.</param>
protected IRequestBuilder GetBuilder(VersionedEndpoint endpoint)
=> GetBuilder(endpoint.Route, endpoint.Method, endpoint.Version);

/// <summary>
/// Get preconfigured <see cref="IRequestBuilder"/>.
/// </summary>
/// <param name="route">Request's Route.</param>
/// <param name="method">Request's Method (GET, POST, PUT, DELETE...).</param>
protected virtual async Task<IRequestBuilder> GetBuilderAsync(string route, Method method = Method.Get)
=> new RequestBuilder(_url, route, method);
protected virtual async Task<IRequestBuilder> GetBuilderAsync(string route, Method method = Method.Get, string? version = null)

Check warning on line 59 in NotoriousClient/Clients/BaseClient.cs

View workflow job for this annotation

GitHub Actions / publish

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 59 in NotoriousClient/Clients/BaseClient.cs

View workflow job for this annotation

GitHub Actions / publish

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 59 in NotoriousClient/Clients/BaseClient.cs

View workflow job for this annotation

GitHub Actions / publish

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 59 in NotoriousClient/Clients/BaseClient.cs

View workflow job for this annotation

GitHub Actions / publish

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
=> GetBuilder(route, method, version);

/// <summary>
/// Get preconfigured <see cref="IRequestBuilder"/>.
/// </summary>
/// <param name="endpoint">Request's <see cref="Endpoint"/>.</param>
protected Task<IRequestBuilder> GetBuilderAsync(Endpoint endpoint)
=> GetBuilderAsync(endpoint.Route, endpoint.Method);

/// <summary>
/// Get preconfigured <see cref="IRequestBuilder"/> with versioning.
/// </summary>
/// <param name="endpoint">Request's <see cref="VersionedEndpoint"/>.</param>
protected Task<IRequestBuilder> GetBuilderAsync(VersionedEndpoint endpoint)
=> GetBuilderAsync(endpoint.Route, endpoint.Method, endpoint.Version);
}
}
2 changes: 1 addition & 1 deletion NotoriousClient/Clients/BasicAuthBaseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected BasicAuthBaseClient(IRequestSender sender, string url, string login, s
/// </summary>
/// <param name="route">Request's Route.</param>
/// <param name="method">Request's method (GET, POST, PUT, DELETE...).</param>
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);
}
}
4 changes: 2 additions & 2 deletions NotoriousClient/NotoriousClient.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<Description>A fluent HTTPResponseMessage builder with fully extendable API Client implementation.</Description>
<PackageId>NotoriousClient</PackageId>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<VersionPrefix>2.0.3</VersionPrefix>
<VersionPrefix>2.1.0</VersionPrefix>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Authors>Brice SCHUMACHER</Authors>
<RepositoryUrl>https://github.com/Notorious-Coding/Notorious-Client/</RepositoryUrl>
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
Loading