aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-04-23 11:28:48 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-04-23 11:28:48 -0400
commit311f8cbb0ffe623134896c696b71339fb1ab3954 (patch)
treeff9bf969e5fd194ba0230ef0403b3f7a5b3985a8 /lib
parentd65aa4f010adbd6c8162eb08c5787550e27d3e47 (diff)
Rest cleint construction
Diffstat (limited to 'lib')
-rw-r--r--lib/Net.Rest.Client/src/Construction/Extensions.cs264
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestEndpointAdapter.cs51
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestEndpointBuilder.cs40
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestEndpointDefinition.cs39
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestRequestBody.cs40
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestRequestBuilder.cs60
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestSingleEndpoint.cs57
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestSiteAdapter.cs65
-rw-r--r--lib/Net.Rest.Client/src/Construction/IRestSiteEndpointStore.cs46
-rw-r--r--lib/Net.Rest.Client/src/Construction/RestSiteAdapterBase.cs72
-rw-r--r--lib/Utils/src/Native/SafeMethodHandle.cs14
11 files changed, 747 insertions, 1 deletions
diff --git a/lib/Net.Rest.Client/src/Construction/Extensions.cs b/lib/Net.Rest.Client/src/Construction/Extensions.cs
new file mode 100644
index 0000000..3f0e79b
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/Extensions.cs
@@ -0,0 +1,264 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: Extensions.cs
+*
+* Extensions.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Construction extensions
+ /// </summary>
+ public static class Extensions
+ {
+ /// <summary>
+ /// Executes a request against the site by sending the request model parameter. An <see cref="IRestEndpointAdapter{TModel}"/> must be
+ /// defined to handle requests of the given model type.
+ /// </summary>
+ /// <typeparam name="TModel"></typeparam>
+ /// <param name="site"></param>
+ /// <param name="entity">The request entity model to send to the server</param>
+ /// <param name="cancellation">A token to cancel the operation</param>
+ /// <returns>A task that resolves the response message</returns>
+ public static async Task<RestResponse> ExecuteAsync<TModel>(this IRestSiteAdapter site, TModel entity, CancellationToken cancellation = default)
+ {
+ //Get the adapter for the model
+ IRestEndpointAdapter<TModel> adapter = site.GetAdapter<TModel>();
+
+ //Get new request on adapter
+ RestRequest request = adapter.GetRequest(entity);
+
+ //Wait to exec operations if needed
+ await site.WaitAsync(cancellation);
+
+ RestResponse response;
+
+ //Get rest client
+ using (ClientContract contract = site.GetClient())
+ {
+ //Exec response
+ response = await contract.Resource.ExecuteAsync(request, cancellation);
+ }
+
+ //Site handler should not cause an exception
+ site.OnResponse(response);
+
+ //invoke response handlers
+ adapter.OnResponse(entity, response);
+
+ return response;
+ }
+
+ /// <summary>
+ /// Executes a request using a model that defines its own endpoint information, on the current site and returns the response
+ /// </summary>
+ /// <typeparam name="TModel">The request model type</typeparam>
+ /// <param name="site"></param>
+ /// <param name="model">The entity model that defines itself as an endpoint and the request information</param>
+ /// <param name="cancellation">A token to cancel the operation</param>
+ /// <returns>When completed, gets the <see cref="RestResponse"/></returns>
+ public static async Task<RestResponse> ExecuteSingleAsync<TModel>(this IRestSiteAdapter site, TModel model, CancellationToken cancellation = default) where TModel : IRestSingleEndpoint
+ {
+ //Init new request
+ RestRequest request = new(model.Url, model.Method);
+ model.OnRequest(request);
+
+ //Wait to exec operations if needed
+ await site.WaitAsync(cancellation);
+
+ RestResponse response;
+
+ //Get rest client
+ using (ClientContract contract = site.GetClient())
+ {
+ //Exec response
+ response = await contract.Resource.ExecuteAsync(request, cancellation);
+ }
+
+ //Site handler should not cause an exception
+ site.OnResponse(response);
+
+ //Allow model to handle a response
+ model.OnResponse(response);
+
+ return response;
+ }
+
+ /// <summary>
+ /// Sets the request method of a new request
+ /// </summary>
+ /// <typeparam name="TModel"></typeparam>
+ /// <param name="builder"></param>
+ /// <param name="methodCb">The callback method that will be invoked on every call to build a new request</param>
+ /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns>
+ public static IRestRequestBuilder<TModel> WithMethod<TModel>(this IRestRequestBuilder<TModel> builder, Func<TModel, Method> methodCb)
+ {
+ builder.WithModifier((m, r) => methodCb(m));
+ return builder;
+ }
+
+ /// <summary>
+ /// Sets a callback that will create a query string argument value
+ /// </summary>
+ /// <typeparam name="TModel">The request entity type</typeparam>
+ /// <param name="builder"></param>
+ /// <param name="callback">The callback method that gets the query value</param>
+ /// <param name="parameter">The query paramter value to set</param>
+ /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns>
+ public static IRestRequestBuilder<TModel> WithQuery<TModel>(this IRestRequestBuilder<TModel> builder, string parameter, Func<TModel, string> callback)
+ {
+ //Get a query item string value from the callback and sets the query paremter
+ builder.WithModifier((m, r) => r.AddQueryParameter(parameter, callback(m)));
+ return builder;
+ }
+
+ /// <summary>
+ /// Specifies a model that will handle its own request body builder
+ /// </summary>
+ /// <typeparam name="TModel"></typeparam>
+ /// <param name="builder"></param>
+ /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns>
+ public static IRestRequestBuilder<TModel> WithBodyBuilder<TModel>(this IRestRequestBuilder<TModel> builder) where TModel : IRestRequestBody
+ {
+ builder.WithModifier(static (m, r) => m.AddBody(r));
+ return builder;
+ }
+
+ /// <summary>
+ /// Builds endpoints from an <see cref="IRestEndpointDefinition"/> and stores them in the
+ /// <see cref="IRestSiteEndpointStore"/>
+ /// </summary>
+ /// <param name="site"></param>
+ /// <param name="endpoint">The endpoint definition to build endpoints from</param>
+ /// <returns>A chainable <see cref="IRestSiteEndpointStore"/></returns>
+ public static IRestSiteEndpointStore BuildEndpoints(this IRestSiteEndpointStore site, IRestEndpointDefinition endpoint)
+ {
+ EndpointAdapterBuilder builder = new(site);
+
+ //Build endpoints
+ endpoint.BuildRequest(site.Site, builder);
+
+ return site;
+ }
+
+ /// <summary>
+ /// Converts a task that resolves a <see cref="RestResponse"/> to a task that deserializes
+ /// the response data as json.
+ /// </summary>
+ /// <typeparam name="TResult">The json response entity type</typeparam>
+ /// <param name="response">The response task</param>
+ /// <returns>A task that resolves the deserialized entity type</returns>
+ public static async Task<TResult?> AsJson<TResult>(this Task<RestResponse> response)
+ {
+ RestResponse r = await response.ConfigureAwait(false);
+ return JsonSerializer.Deserialize<TResult>(r.RawBytes);
+ }
+
+ private record class EndpointAdapterBuilder(IRestSiteEndpointStore Site) : IRestEndpointBuilder
+ {
+ ///<inheritdoc/>
+ public IRestRequestBuilder<TModel> WithEndpoint<TModel>()
+ {
+ //New adapter
+ EndpointAdapter<TModel> adapter = new();
+
+ //Store adapter in site
+ Site.AddAdapter(adapter);
+
+ //Builder
+ return new RequestBuilder<TModel>(adapter);
+ }
+
+ private record class RequestBuilder<TModel>(EndpointAdapter<TModel> Adapter) : IRestRequestBuilder<TModel>
+ {
+ ///<inheritdoc/>
+ public IRestRequestBuilder<TModel> WithModifier(Action<TModel, RestRequest> requestBuilder)
+ {
+ _ = requestBuilder ?? throw new ArgumentNullException(nameof(requestBuilder));
+ //Add handler to handler chain
+ Adapter.RequestChain.AddLast(requestBuilder);
+ return this;
+ }
+
+ ///<inheritdoc/>
+ public IRestRequestBuilder<TModel> WithUrl(Func<TModel, string> uriBuilder)
+ {
+ _ = uriBuilder ?? throw new ArgumentNullException(nameof(uriBuilder));
+ //Add get url handler
+ Adapter.GetUrl = uriBuilder;
+ return this;
+ }
+
+ ///<inheritdoc/>
+ public IRestRequestBuilder<TModel> OnResponse(Action<TModel, RestResponse> onResponseBuilder)
+ {
+ _ = onResponseBuilder ?? throw new ArgumentNullException(nameof(onResponseBuilder));
+ //Add a response handler
+ Adapter.ResponseChain.AddLast(onResponseBuilder);
+ return this;
+ }
+ }
+
+ private sealed class EndpointAdapter<TModel> : IRestEndpointAdapter<TModel>
+ {
+ internal Func<TModel, string>? GetUrl { get; set; }
+
+ internal LinkedList<Action<TModel, RestRequest>> RequestChain { get; } = new();
+ internal LinkedList<Action<TModel, RestResponse>> ResponseChain { get; } = new();
+
+ RestRequest IRestEndpointAdapter<TModel>.GetRequest(TModel entity)
+ {
+ //First we need to get the url for the entity
+ string? url = GetUrl?.Invoke(entity);
+
+ //New request
+ RestRequest request = new(url);
+
+ //Invoke request modifier chain
+ foreach (Action<TModel, RestRequest> action in RequestChain)
+ {
+ action(entity, request);
+ }
+
+ return request;
+ }
+
+ void IRestEndpointAdapter<TModel>.OnResponse(TModel model, RestResponse response)
+ {
+ //Invoke request modifier chain
+ foreach (Action<TModel, RestResponse> action in ResponseChain)
+ {
+ action(model, response);
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestEndpointAdapter.cs b/lib/Net.Rest.Client/src/Construction/IRestEndpointAdapter.cs
new file mode 100644
index 0000000..2e9d26d
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestEndpointAdapter.cs
@@ -0,0 +1,51 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestEndpointAdapter.cs
+*
+* IRestEndpointAdapter.cs is part of VNLib.Net.Rest.Client which is
+* part of the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Represents a remote http endpoint that can create requests given an entity
+ /// to communicate with the remote endpoint.
+ /// </summary>
+ /// <typeparam name="TEntity">The request entity model</typeparam>
+ public interface IRestEndpointAdapter<TEntity>
+ {
+ /// <summary>
+ /// Gets a new <see cref="RestRequest"/> for the given request entity/arguments
+ /// used to make a request against a rest endpoint
+ /// </summary>
+ /// <param name="entity">The entity to get the new <see cref="RestRequest"/> for</param>
+ /// <returns>The configured <see cref="RestRequest"/> to send to the endpoint</returns>
+ RestRequest GetRequest(TEntity entity);
+
+ /// <summary>
+ /// Called when a request has successfully completed, may be used to validate a response message
+ /// </summary>
+ /// <param name="model">The original request entity that was sent</param>
+ /// <param name="response">The response message that was received</param>
+ void OnResponse(TEntity model, RestResponse response);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestEndpointBuilder.cs b/lib/Net.Rest.Client/src/Construction/IRestEndpointBuilder.cs
new file mode 100644
index 0000000..9f62ef6
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestEndpointBuilder.cs
@@ -0,0 +1,40 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestEndpointBuilder.cs
+*
+* IRestEndpointBuilder.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Represents an object used to define endpoint adapters for a <see cref="IRestSiteAdapter"/>
+ /// </summary>
+ public interface IRestEndpointBuilder
+ {
+ /// <summary>
+ /// Creates a new endpoint adapter of the given entity model type and gets a <see cref="IRestRequestBuilder{TModel}"/>
+ /// that may be used to configure the request message.
+ /// </summary>
+ /// <typeparam name="TModel"></typeparam>
+ /// <returns>A chainable <see cref="IRestRequestBuilder{TModel}"/> used to define your request</returns>
+ IRestRequestBuilder<TModel> WithEndpoint<TModel>();
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestEndpointDefinition.cs b/lib/Net.Rest.Client/src/Construction/IRestEndpointDefinition.cs
new file mode 100644
index 0000000..bdbe0f5
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestEndpointDefinition.cs
@@ -0,0 +1,39 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestEndpointDefinition.cs
+*
+* IRestEndpointDefinition.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Represents an object that will define endpoint adapters for a <see cref="IRestSiteAdapter"/>
+ /// </summary>
+ public interface IRestEndpointDefinition
+ {
+ /// <summary>
+ /// Called to build endpoint adapters for the site
+ /// </summary>
+ /// <param name="site">The site that the adapters are build built for</param>
+ /// <param name="builder">The endpoint builder instance</param>
+ void BuildRequest(IRestSiteAdapter site, IRestEndpointBuilder builder);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestRequestBody.cs b/lib/Net.Rest.Client/src/Construction/IRestRequestBody.cs
new file mode 100644
index 0000000..7ce1a42
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestRequestBody.cs
@@ -0,0 +1,40 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestRequestBody.cs
+*
+* IRestRequestBody.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Allows a request entity to configure its request message
+ /// </summary>
+ public interface IRestRequestBody
+ {
+ /// <summary>
+ /// Called when the request is being built to define a message body
+ /// </summary>
+ /// <param name="request">The request to configure</param>
+ void AddBody(RestRequest request);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestRequestBuilder.cs b/lib/Net.Rest.Client/src/Construction/IRestRequestBuilder.cs
new file mode 100644
index 0000000..be49ef3
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestRequestBuilder.cs
@@ -0,0 +1,60 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestRequestBuilder.cs
+*
+* IRestRequestBuilder.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// A type used to define operations required to generate requests to a REST endpoint
+ /// </summary>
+ /// <typeparam name="TModel">The request entity/model type</typeparam>
+ public interface IRestRequestBuilder<TModel>
+ {
+ /// <summary>
+ /// Defines a callback method that gets the request url for a given endpoint
+ /// </summary>
+ /// <param name="uriBuilder"></param>
+ /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns>
+ IRestRequestBuilder<TModel> WithUrl(Func<TModel, string> uriBuilder);
+
+ /// <summary>
+ /// Adds a request message handler/converter, callback method used to modify
+ /// the request message programatically.
+ /// </summary>
+ /// <param name="requestBuilder"></param>
+ /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns>
+ IRestRequestBuilder<TModel> WithModifier(Action<TModel, RestRequest> requestBuilder);
+
+ /// <summary>
+ /// Adds a response handler callback method that will be invoked when a response
+ /// is received from the endpoint. This method may be used to validate a response message
+ /// </summary>
+ /// <param name="onResponseBuilder"></param>
+ /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns>
+ IRestRequestBuilder<TModel> OnResponse(Action<TModel, RestResponse> onResponseBuilder);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestSingleEndpoint.cs b/lib/Net.Rest.Client/src/Construction/IRestSingleEndpoint.cs
new file mode 100644
index 0000000..dd39445
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestSingleEndpoint.cs
@@ -0,0 +1,57 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestSingleEndpoint.cs
+*
+* IRestSingleEndpoint.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Allows a request entity to configure its own endpoint definition
+ /// </summary>
+ public interface IRestSingleEndpoint
+ {
+ /// <summary>
+ /// Gets the endpoint url to execute the REST request against
+ /// </summary>
+ string Url { get; }
+
+ /// <summary>
+ /// The request method type
+ /// </summary>
+ Method Method { get; }
+
+ /// <summary>
+ /// Allows manually configuring the request before execution
+ /// </summary>
+ /// <param name="request">The request to configure</param>
+ void OnRequest(RestRequest request);
+
+ /// <summary>
+ /// Called when a response is received, and may be used to validate the response
+ /// message.
+ /// </summary>
+ /// <param name="response">The received response message</param>
+ void OnResponse(RestResponse response);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestSiteAdapter.cs b/lib/Net.Rest.Client/src/Construction/IRestSiteAdapter.cs
new file mode 100644
index 0000000..ee3c336
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestSiteAdapter.cs
@@ -0,0 +1,65 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestSiteAdapter.cs
+*
+* IRestSiteAdapter.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System.Threading;
+using System.Threading.Tasks;
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Represents a remote REST api that defines endpoints to execute request/response
+ /// operations against, and configures RestClient's for use.
+ /// </summary>
+ public interface IRestSiteAdapter
+ {
+ /// <summary>
+ /// May be called during request execution to pause an operation to avoid
+ /// resource exhaustion and avoid dropped connections from overload
+ /// </summary>
+ /// <param name="cancellation"></param>
+ /// <returns>A task that will be awaited, when complete will continue the operation</returns>
+ Task WaitAsync(CancellationToken cancellation = default);
+
+ /// <summary>
+ /// Gets a new <see cref="ClientContract"/> that has a properly configured <see cref="RestClient"/>
+ /// </summary>
+ /// <returns>The new <see cref="ClientContract"/></returns>
+ ClientContract GetClient();
+
+ /// <summary>
+ /// Gets an <see cref="IRestEndpointAdapter{TEntity}"/> for the given request entity type
+ /// </summary>
+ /// <typeparam name="TModel">The request entity model</typeparam>
+ /// <returns>The <see cref="IRestEndpointAdapter{TEntity}"/> used to build a request from an entity</returns>
+ IRestEndpointAdapter<TModel> GetAdapter<TModel>();
+
+ /// <summary>
+ /// Called after every successful REST operation.
+ /// </summary>
+ /// <param name="response">The response message returned from any endpoint</param>
+ void OnResponse(RestResponse response);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/IRestSiteEndpointStore.cs b/lib/Net.Rest.Client/src/Construction/IRestSiteEndpointStore.cs
new file mode 100644
index 0000000..11d648a
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/IRestSiteEndpointStore.cs
@@ -0,0 +1,46 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: IRestSiteEndpointStore.cs
+*
+* IRestSiteEndpointStore.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Represents a type used to store <see cref="IRestEndpointAdapter{TEntity}"/> instances
+ /// for a <see cref="IRestSiteAdapter"/>
+ /// </summary>
+ public interface IRestSiteEndpointStore
+ {
+ /// <summary>
+ /// The <see cref="IRestSiteAdapter"/> that adapters will be stored in
+ /// </summary>
+ IRestSiteAdapter Site { get; }
+
+ /// <summary>
+ /// Adds an adapter for the given request entity type to be used fore
+ /// REST operations from the given entity model.
+ /// </summary>
+ /// <typeparam name="TModel">The request entity model for the endpoint</typeparam>
+ /// <param name="adapter">The adapter to add to the store</param>
+ void AddAdapter<TModel>(IRestEndpointAdapter<TModel> adapter);
+ }
+}
diff --git a/lib/Net.Rest.Client/src/Construction/RestSiteAdapterBase.cs b/lib/Net.Rest.Client/src/Construction/RestSiteAdapterBase.cs
new file mode 100644
index 0000000..f7b234b
--- /dev/null
+++ b/lib/Net.Rest.Client/src/Construction/RestSiteAdapterBase.cs
@@ -0,0 +1,72 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Rest.Client
+* File: RestSiteAdapterBase.cs
+*
+* RestSiteAdapterBase.cs is part of VNLib.Net.Rest.Client which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Rest.Client is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Rest.Client is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Rest.Client. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using RestSharp;
+
+namespace VNLib.Net.Rest.Client.Construction
+{
+ /// <summary>
+ /// Represents a remote REST site adapter, that handles the basic housekeeping of
+ /// creating and handling operations
+ /// </summary>
+ public abstract class RestSiteAdapterBase : IRestSiteAdapter, IRestSiteEndpointStore
+ {
+ /// <summary>
+ /// The collection of stored <see cref="IRestEndpointAdapter{T}"/> by their model type
+ /// </summary>
+ protected Dictionary<Type, object> Adapters { get; } = new();
+
+ /// <summary>
+ /// The internal client pool
+ /// </summary>
+ protected abstract RestClientPool Pool { get; }
+
+ ///<inheritdoc/>
+ public IRestSiteAdapter Site => this;
+
+ ///<inheritdoc/>
+ public virtual IRestEndpointAdapter<TModel> GetAdapter<TModel>() => (IRestEndpointAdapter<TModel>)Adapters[typeof(TModel)];
+
+ ///<inheritdoc/>
+ public virtual ClientContract GetClient() => Pool.Lease();
+
+ ///<inheritdoc/>
+ public abstract void OnResponse(RestResponse response);
+
+ ///<inheritdoc/>
+ public abstract Task WaitAsync(CancellationToken cancellation = default);
+
+ /// <summary>
+ /// Adds an endpoint adapter to the store with the given model type
+ /// </summary>
+ /// <typeparam name="TModel">The endpoint model type</typeparam>
+ /// <param name="adapter">The model typed adapter to add to the store</param>
+ public virtual void AddAdapter<TModel>(IRestEndpointAdapter<TModel> adapter) => Adapters[typeof(TModel)] = adapter;
+ }
+}
diff --git a/lib/Utils/src/Native/SafeMethodHandle.cs b/lib/Utils/src/Native/SafeMethodHandle.cs
index 3ba0879..478b505 100644
--- a/lib/Utils/src/Native/SafeMethodHandle.cs
+++ b/lib/Utils/src/Native/SafeMethodHandle.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -57,5 +57,17 @@ namespace VNLib.Utils.Native
//Decrement lib handle count
Library.DangerousRelease();
}
+
+ /// <summary>
+ /// Releases the library handle on finalization
+ /// </summary>
+#pragma warning disable CA1063 // Implement IDisposable Correctly
+ ~SafeMethodHandle()
+ {
+ //Make sure the library is released on finalization
+ Library.DangerousRelease();
+ }
+#pragma warning restore CA1063 // Implement IDisposable Correctly
+
}
}