-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathClient.cs
152 lines (129 loc) · 5.66 KB
/
Client.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using Newtonsoft.Json;
using Supabase.Core;
using Supabase.Core.Extensions;
using Supabase.Functions.Interfaces;
using Supabase.Functions.Responses;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Supabase.Functions.Exceptions;
[assembly: InternalsVisibleTo("FunctionsTests")]
namespace Supabase.Functions
{
/// <inheritdoc />
public partial class Client : IFunctionsClient
{
private HttpClient _httpClient = new HttpClient();
private readonly string _baseUrl;
/// <summary>
/// Function that can be set to return dynamic headers.
///
/// Headers specified in the method parameters will ALWAYS take precedence over headers returned by this function.
/// </summary>
public Func<Dictionary<string, string>>? GetHeaders { get; set; }
/// <summary>
/// Initializes a functions client
/// </summary>
/// <param name="baseUrl"></param>
public Client(string baseUrl)
{
_baseUrl = baseUrl;
}
/// <summary>
/// Returns an <see cref="HttpContent"/> response, allowing for coersion into Streams, Strings, and byte[]
/// </summary>
/// <param name="functionName">Function Name, will be appended to BaseUrl</param>
/// <param name="token">Anon Key.</param>
/// <param name="options">Options</param>
/// <returns></returns>
public async Task<HttpContent> RawInvoke(string functionName, string? token = null,
InvokeFunctionOptions? options = null)
{
var url = $"{_baseUrl}/{functionName}";
return (await HandleRequest(url, token, options)).Content;
}
/// <summary>
/// Invokes a function and returns the Text content of the response.
/// </summary>
/// <param name="functionName">Function Name, will be appended to BaseUrl</param>
/// <param name="token">Anon Key.</param>
/// <param name="options">Options</param>
/// <returns></returns>
public async Task<string> Invoke(string functionName, string? token = null,
InvokeFunctionOptions? options = null)
{
var url = $"{_baseUrl}/{functionName}";
var response = await HandleRequest(url, token, options);
return await response.Content.ReadAsStringAsync();
}
/// <summary>
/// Invokes a function and returns a JSON Deserialized object according to the supplied generic Type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="functionName">Function Name, will be appended to BaseUrl</param>
/// <param name="token">Anon Key.</param>
/// <param name="options">Options</param>
/// <returns></returns>
public async Task<T?> Invoke<T>(string functionName, string? token = null,
InvokeFunctionOptions? options = null) where T : class
{
var url = $"{_baseUrl}/{functionName}";
var response = await HandleRequest(url, token, options);
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(content);
}
/// <summary>
/// Internal request handling
/// </summary>
/// <param name="url"></param>
/// <param name="token"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <exception cref="FunctionsException"></exception>
private async Task<HttpResponseMessage> HandleRequest(string url, string? token = null,
InvokeFunctionOptions? options = null)
{
options ??= new InvokeFunctionOptions();
if (GetHeaders != null)
{
options.Headers = GetHeaders().MergeLeft(options.Headers);
}
if (!string.IsNullOrEmpty(token))
{
options.Headers["Authorization"] = $"Bearer {token}";
}
options.Headers["X-Client-Info"] = Util.GetAssemblyVersion(typeof(Client));
var builder = new UriBuilder(url);
var query = HttpUtility.ParseQueryString(builder.Query);
builder.Query = query.ToString();
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, builder.Uri);
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(options.Body), Encoding.UTF8,
"application/json");
foreach (var kvp in options.Headers)
{
requestMessage.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
}
if (_httpClient.Timeout != options.HttpTimeout)
{
_httpClient = new HttpClient();
_httpClient.Timeout = options.HttpTimeout;
}
var response = await _httpClient.SendAsync(requestMessage);
if (response.IsSuccessStatusCode && !response.Headers.Contains("x-relay-error"))
return response;
var content = await response.Content.ReadAsStringAsync();
var exception = new FunctionsException(content)
{
Content = content,
Response = response,
StatusCode = (int)response.StatusCode
};
exception.AddReason();
throw exception;
}
}
}