Skip to content

Commit 14997a4

Browse files
committed
Client now initializes with a baseUrl and method calls arguments are only the functionName.
1 parent 563f37b commit 14997a4

File tree

4 files changed

+82
-14
lines changed

4 files changed

+82
-14
lines changed

Functions/Client.cs

+31-7
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,55 @@
11
using Newtonsoft.Json;
2+
using Supabase.Functions.Extensions;
23
using Supabase.Functions.Interfaces;
34
using Supabase.Functions.Responses;
45
using System;
56
using System.Collections.Generic;
67
using System.Net.Http;
8+
using System.Runtime.CompilerServices;
79
using System.Text;
810
using System.Threading.Tasks;
911
using System.Web;
1012

13+
[assembly: InternalsVisibleTo("FunctionsTests")]
1114
namespace Supabase.Functions
1215
{
16+
1317
public class Client : IFunctionsClient
1418
{
1519
private static readonly HttpClient client = new HttpClient();
20+
private string baseUrl;
21+
22+
public Func<Dictionary<string, string>>? GetHeaders { get; set; }
23+
24+
public Client(string baseUrl)
25+
{
26+
this.baseUrl = baseUrl;
27+
}
1628

1729
/// <summary>
1830
/// Returns an <see cref="HttpContent"/> response, allowing for coersion into Streams, Strings, and byte[]
1931
/// </summary>
20-
/// <param name="url">Url of function to invoke</param>
32+
/// <param name="functionName">Function Name, will be appended to BaseUrl</param>
2133
/// <param name="token">Anon Key.</param>
2234
/// <param name="options">Options</param>
2335
/// <returns></returns>
24-
public async Task<HttpContent> RawInvoke(string url, string? token = null, InvokeFunctionOptions? options = null) => (await HandleRequest(url, token, options)).Content;
36+
public async Task<HttpContent> RawInvoke(string functionName, string? token = null, InvokeFunctionOptions? options = null)
37+
{
38+
var url = $"{baseUrl}/{functionName}";
39+
40+
return (await HandleRequest(url, token, options)).Content;
41+
}
2542

2643
/// <summary>
2744
/// Invokes a function and returns the Text content of the response.
2845
/// </summary>
29-
/// <param name="url">Url of the function to invoke</param>
46+
/// <param name="functionName">Function Name, will be appended to BaseUrl</param>
3047
/// <param name="token">Anon Key.</param>
3148
/// <param name="options">Options</param>
3249
/// <returns></returns>
33-
public async Task<string> Invoke(string url, string? token = null, InvokeFunctionOptions? options = null)
50+
public async Task<string> Invoke(string functionName, string? token = null, InvokeFunctionOptions? options = null)
3451
{
52+
var url = $"{baseUrl}/{functionName}";
3553
var response = await HandleRequest(url, token, options);
3654

3755
return await response.Content.ReadAsStringAsync();
@@ -41,12 +59,13 @@ public async Task<string> Invoke(string url, string? token = null, InvokeFunctio
4159
/// Invokes a function and returns a JSON Deserialized object according to the supplied generic Type <typeparamref name="T"/>
4260
/// </summary>
4361
/// <typeparam name="T"></typeparam>
44-
/// <param name="url">Url of function to invoke</param>
62+
/// <param name="functionsName">Function Name, will be appended to BaseUrl</param>
4563
/// <param name="token">Anon Key.</param>
4664
/// <param name="options">Options</param>
4765
/// <returns></returns>
48-
public async Task<T?> Invoke<T>(string url, string? token = null, InvokeFunctionOptions? options = null) where T : class
66+
public async Task<T?> Invoke<T>(string functionName, string? token = null, InvokeFunctionOptions? options = null) where T : class
4967
{
68+
var url = $"{baseUrl}/{functionName}";
5069
var response = await HandleRequest(url, token, options);
5170

5271
var content = await response.Content.ReadAsStringAsync();
@@ -62,13 +81,18 @@ public async Task<string> Invoke(string url, string? token = null, InvokeFunctio
6281
/// <param name="options"></param>
6382
/// <returns></returns>
6483
/// <exception cref="RequestException"></exception>
65-
private async Task<HttpResponseMessage> HandleRequest(string url, string? token = null, InvokeFunctionOptions? options = null)
84+
internal async Task<HttpResponseMessage> HandleRequest(string url, string? token = null, InvokeFunctionOptions? options = null)
6685
{
6786
if (options == null)
6887
{
6988
options = new InvokeFunctionOptions();
7089
}
7190

91+
if (GetHeaders != null)
92+
{
93+
options.Headers = GetHeaders().MergeLeft(options.Headers);
94+
}
95+
7296
if (!string.IsNullOrEmpty(token))
7397
{
7498
options.Headers["Authorization"] = $"Bearer {token}";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace Supabase.Functions.Extensions
7+
{
8+
internal static class DictionaryExtensions
9+
{
10+
// Works in C#3/VS2008:
11+
// Returns a new dictionary of this ... others merged leftward.
12+
// Keeps the type of 'this', which must be default-instantiable.
13+
// Example:
14+
// result = map.MergeLeft(other1, other2, ...)
15+
// From: https://stackoverflow.com/a/2679857/3629438
16+
public static T MergeLeft<T, K, V>(this T me, params IDictionary<K, V>[] others)
17+
where T : IDictionary<K, V>, new()
18+
{
19+
T newMap = new T();
20+
foreach (IDictionary<K, V> src in (new List<IDictionary<K, V>> { me }).Concat(others))
21+
{
22+
foreach (KeyValuePair<K, V> p in src)
23+
{
24+
newMap[p.Key] = p.Value;
25+
}
26+
}
27+
return newMap;
28+
}
29+
30+
}
31+
}

Functions/Interfaces/IFunctionsClient.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
using System.Net.Http;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
24
using System.Threading.Tasks;
35

46
namespace Supabase.Functions.Interfaces
57
{
68
public interface IFunctionsClient
79
{
10+
Func<Dictionary<string, string>>? GetHeaders { get; set; }
811
Task<string> Invoke(string url, string? token = null, Client.InvokeFunctionOptions? options = null);
912
Task<T?> Invoke<T>(string url, string? token = null, Client.InvokeFunctionOptions? options = null) where T : class;
1013
Task<HttpContent> RawInvoke(string url, string? token = null, Client.InvokeFunctionOptions? options = null);

FunctionsTests/ClientTests.cs

+16-6
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,24 @@ namespace FunctionsTests
1010
[TestClass]
1111
public class ClientTests
1212
{
13+
Client client;
14+
string token;
15+
16+
[TestInitialize]
17+
public void Initialize()
18+
{
19+
var endpoint = Environment.GetEnvironmentVariable("FUNCTION_ENDPOINT");
20+
21+
token = Environment.GetEnvironmentVariable("TOKEN");
22+
client = new Client(endpoint);
23+
}
24+
1325
[TestMethod("Invokes a function.")]
1426
public async Task Invokes()
1527
{
16-
var token = Environment.GetEnvironmentVariable("TOKEN");
17-
var endpoint = Environment.GetEnvironmentVariable("FUNCTION_ENDPOINT");
18-
var client = new Client();
28+
var function = "hello";
1929

20-
var result = await client.Invoke(endpoint, token, new InvokeFunctionOptions
30+
var result = await client.Invoke(function, token, new InvokeFunctionOptions
2131
{
2232
Body = new Dictionary<string, object>
2333
{
@@ -28,7 +38,7 @@ public async Task Invokes()
2838
Assert.IsTrue(result.Contains("supabase"));
2939

3040

31-
var result2 = await client.Invoke<Dictionary<string, string>>(endpoint, token, new InvokeFunctionOptions
41+
var result2 = await client.Invoke<Dictionary<string, string>>(function, token, new InvokeFunctionOptions
3242
{
3343
Body = new Dictionary<string, object>
3444
{
@@ -41,7 +51,7 @@ public async Task Invokes()
4151
Assert.IsTrue(result2["message"].Contains("functions"));
4252

4353

44-
var result3 = await client.RawInvoke(endpoint, token, new InvokeFunctionOptions
54+
var result3 = await client.RawInvoke(function, token, new InvokeFunctionOptions
4555
{
4656
Body = new Dictionary<string, object>
4757
{

0 commit comments

Comments
 (0)