Skip to content

Commit 464854e

Browse files
committed
Conforms library with other libraries in supabase:
- Adds XML docs - Changes publish process - Updates Assembly - Uses `FunctionsException` instead of a generic `RequestException` - Version bump to 1.3.0
1 parent a61fc6b commit 464854e

File tree

14 files changed

+233
-112
lines changed

14 files changed

+233
-112
lines changed

.github/icon.png

5.64 KB
Loading

.github/workflows/dotnet-core.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- name: Setup .NET Core
2222
uses: actions/setup-dotnet@v1
2323
with:
24-
dotnet-version: 3.1.301
24+
dotnet-version: '7.0.x'
2525

2626
- name: Install dependencies
2727
run: dotnet restore

.github/workflows/release.yml

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Setup .NET Core
1414
uses: actions/setup-dotnet@v1
1515
with:
16-
dotnet-version: 3.1.301
16+
dotnet-version: '7.0.x'
1717

1818
- name: Wait for tests to succeed
1919
uses: lewagon/wait-on-check-action@v1.0.0
@@ -23,28 +23,13 @@ jobs:
2323
repo-token: ${{ secrets.GITHUB_TOKEN }}
2424
wait-interval: 10
2525

26-
# Publish
27-
- name: publish on version change
28-
id: publish_nuget
29-
uses: rohith/publish-nuget@v2
30-
with:
31-
# Filepath of the project to be packaged, relative to root of repository
32-
PROJECT_FILE_PATH: Functions/Functions.csproj
33-
34-
# NuGet package id, used for version detection & defaults to project name
35-
PACKAGE_NAME: supabase/functions
36-
37-
# Filepath with version info, relative to root of repository & defaults to PROJECT_FILE_PATH
38-
# VERSION_FILE_PATH: Directory.Build.props
26+
- name: Install dependencies
27+
run: dotnet restore
3928

40-
# Regex pattern to extract version info in a capturing group
41-
VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$
29+
- name: Build Realtime
30+
run: dotnet build ./Functions/Functions.csproj --configuration Release --no-restore
4231

43-
# API key to authenticate with NuGet server
44-
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
45-
46-
# NuGet server uri hosting the packages, defaults to https://api.nuget.org
47-
NUGET_SOURCE: https://api.nuget.org
48-
49-
# Flag to toggle pushing symbols along with nuget package to the server, disabled by default
50-
INCLUDE_SYMBOLS: true
32+
# Publish
33+
- name: publish on version change
34+
run: nuget push "**/*.nupkg" -Source 'https://api.nuget.org/v3/index.json' -ApiKey ${{secrets.NUGET_API_KEY}}
35+

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 1.3.0 - 06-10-2023
4+
5+
- Rename assembly to `Supabase.Functions`
6+
- Uses `FunctionsException` instead of `RequestException`
7+
38
## 1.2.1 - 11-12-2022
49

510
- Use `supabase-core` and implement `IGettableHeaders` on `Client`

Functions/Client.cs

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,32 @@
1010
using System.Text;
1111
using System.Threading.Tasks;
1212
using System.Web;
13+
using Supabase.Functions.Exceptions;
1314

1415
[assembly: InternalsVisibleTo("FunctionsTests")]
16+
1517
namespace Supabase.Functions
1618
{
17-
19+
/// <inheritdoc />
1820
public partial class Client : IFunctionsClient
1921
{
20-
private static readonly HttpClient client = new HttpClient();
21-
private string baseUrl;
22+
private static readonly HttpClient HttpClient = new HttpClient();
23+
private readonly string _baseUrl;
2224

2325
/// <summary>
2426
/// Function that can be set to return dynamic headers.
2527
///
26-
/// Headers specified in the method parameters will ALWAYS take precendece over headers returned by this function.
28+
/// Headers specified in the method parameters will ALWAYS take precedence over headers returned by this function.
2729
/// </summary>
2830
public Func<Dictionary<string, string>>? GetHeaders { get; set; }
2931

32+
/// <summary>
33+
/// Initializes a functions client
34+
/// </summary>
35+
/// <param name="baseUrl"></param>
3036
public Client(string baseUrl)
3137
{
32-
this.baseUrl = baseUrl;
38+
_baseUrl = baseUrl;
3339
}
3440

3541
/// <summary>
@@ -39,9 +45,10 @@ public Client(string baseUrl)
3945
/// <param name="token">Anon Key.</param>
4046
/// <param name="options">Options</param>
4147
/// <returns></returns>
42-
public async Task<HttpContent> RawInvoke(string functionName, string? token = null, InvokeFunctionOptions? options = null)
48+
public async Task<HttpContent> RawInvoke(string functionName, string? token = null,
49+
InvokeFunctionOptions? options = null)
4350
{
44-
var url = $"{baseUrl}/{functionName}";
51+
var url = $"{_baseUrl}/{functionName}";
4552

4653
return (await HandleRequest(url, token, options)).Content;
4754
}
@@ -53,9 +60,10 @@ public async Task<HttpContent> RawInvoke(string functionName, string? token = nu
5360
/// <param name="token">Anon Key.</param>
5461
/// <param name="options">Options</param>
5562
/// <returns></returns>
56-
public async Task<string> Invoke(string functionName, string? token = null, InvokeFunctionOptions? options = null)
63+
public async Task<string> Invoke(string functionName, string? token = null,
64+
InvokeFunctionOptions? options = null)
5765
{
58-
var url = $"{baseUrl}/{functionName}";
66+
var url = $"{_baseUrl}/{functionName}";
5967
var response = await HandleRequest(url, token, options);
6068

6169
return await response.Content.ReadAsStringAsync();
@@ -65,13 +73,14 @@ public async Task<string> Invoke(string functionName, string? token = null, Invo
6573
/// Invokes a function and returns a JSON Deserialized object according to the supplied generic Type <typeparamref name="T"/>
6674
/// </summary>
6775
/// <typeparam name="T"></typeparam>
68-
/// <param name="functionsName">Function Name, will be appended to BaseUrl</param>
76+
/// <param name="functionName">Function Name, will be appended to BaseUrl</param>
6977
/// <param name="token">Anon Key.</param>
7078
/// <param name="options">Options</param>
7179
/// <returns></returns>
72-
public async Task<T?> Invoke<T>(string functionName, string? token = null, InvokeFunctionOptions? options = null) where T : class
80+
public async Task<T?> Invoke<T>(string functionName, string? token = null,
81+
InvokeFunctionOptions? options = null) where T : class
7382
{
74-
var url = $"{baseUrl}/{functionName}";
83+
var url = $"{_baseUrl}/{functionName}";
7584
var response = await HandleRequest(url, token, options);
7685

7786
var content = await response.Content.ReadAsStringAsync();
@@ -86,13 +95,11 @@ public async Task<string> Invoke(string functionName, string? token = null, Invo
8695
/// <param name="token"></param>
8796
/// <param name="options"></param>
8897
/// <returns></returns>
89-
/// <exception cref="RequestException"></exception>
90-
internal async Task<HttpResponseMessage> HandleRequest(string url, string? token = null, InvokeFunctionOptions? options = null)
98+
/// <exception cref="FunctionsException"></exception>
99+
private async Task<HttpResponseMessage> HandleRequest(string url, string? token = null,
100+
InvokeFunctionOptions? options = null)
91101
{
92-
if (options == null)
93-
{
94-
options = new InvokeFunctionOptions();
95-
}
102+
options ??= new InvokeFunctionOptions();
96103

97104
if (GetHeaders != null)
98105
{
@@ -111,46 +118,29 @@ internal async Task<HttpResponseMessage> HandleRequest(string url, string? token
111118

112119
builder.Query = query.ToString();
113120

114-
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, builder.Uri))
115-
{
116-
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(options.Body), Encoding.UTF8, "application/json");
117-
118-
if (options.Headers != null)
119-
{
120-
foreach (var kvp in options.Headers)
121-
{
122-
requestMessage.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
123-
}
124-
}
125-
126-
var response = await client.SendAsync(requestMessage);
127-
128-
if (!response.IsSuccessStatusCode || response.Headers.Contains("x-relay-error"))
129-
{
130-
var content = await response.Content.ReadAsStringAsync();
131-
132-
var obj = new ErrorResponse
133-
{
134-
Content = content,
135-
Message = content
136-
};
137-
throw new RequestException(response, obj);
138-
}
121+
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, builder.Uri);
122+
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(options.Body), Encoding.UTF8,
123+
"application/json");
139124

140-
return response;
125+
foreach (var kvp in options.Headers)
126+
{
127+
requestMessage.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
141128
}
142-
}
143129

144-
public class RequestException : Exception
145-
{
146-
public HttpResponseMessage Response { get; private set; }
147-
public ErrorResponse Error { get; private set; }
130+
var response = await HttpClient.SendAsync(requestMessage);
148131

149-
public RequestException(HttpResponseMessage response, ErrorResponse error) : base(error.Message)
132+
if (response.IsSuccessStatusCode && !response.Headers.Contains("x-relay-error"))
133+
return response;
134+
135+
var content = await response.Content.ReadAsStringAsync();
136+
var exception = new FunctionsException(content)
150137
{
151-
Response = response;
152-
Error = error;
153-
}
138+
Content = content,
139+
Response = response,
140+
StatusCode = (int)response.StatusCode
141+
};
142+
exception.AddReason();
143+
throw exception;
154144
}
155145
}
156-
}
146+
}

Functions/Exceptions/FailureHint.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using static Supabase.Functions.Exceptions.FailureHint.Reason;
2+
3+
namespace Supabase.Functions.Exceptions
4+
{
5+
/// <summary>
6+
/// A hint as to why a request failed.
7+
/// </summary>
8+
public static class FailureHint
9+
{
10+
/// <summary>
11+
/// A failure reason
12+
/// </summary>
13+
public enum Reason
14+
{
15+
/// <summary>
16+
/// An unknown reason
17+
/// </summary>
18+
Unknown,
19+
/// <summary>
20+
/// Request was not authorized
21+
/// </summary>
22+
NotAuthorized,
23+
/// <summary>
24+
/// An internal error occurred, check your supabase logs.
25+
/// </summary>
26+
Internal,
27+
}
28+
29+
/// <summary>
30+
/// Attempts to detect a reason given an exception.
31+
/// </summary>
32+
/// <param name="ex"></param>
33+
/// <returns></returns>
34+
public static Reason DetectReason(FunctionsException ex)
35+
{
36+
if (ex.Content == null)
37+
return Unknown;
38+
39+
return ex.StatusCode switch
40+
{
41+
401 => NotAuthorized,
42+
403 when ex.Content.Contains("apikey") => NotAuthorized,
43+
500 => Internal,
44+
_ => Unknown
45+
};
46+
}
47+
}
48+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Net.Http;
3+
4+
namespace Supabase.Functions.Exceptions
5+
{
6+
/// <summary>
7+
/// An Exception thrown within <see cref="Functions"/>
8+
/// </summary>
9+
public class FunctionsException : Exception
10+
{
11+
/// <inheritdoc />
12+
public FunctionsException(string? message) : base(message) { }
13+
14+
/// <inheritdoc />
15+
public FunctionsException(string? message, Exception? innerException) : base(message, innerException) { }
16+
17+
/// <summary>
18+
/// The Http Response
19+
/// </summary>
20+
public HttpResponseMessage? Response { get; internal set; }
21+
22+
/// <summary>
23+
/// The Http response content
24+
/// </summary>
25+
public string? Content { get; internal set; }
26+
27+
/// <summary>
28+
/// The Http Status code
29+
/// </summary>
30+
public int StatusCode { get; internal set; }
31+
32+
/// <summary>
33+
/// A parsed reason for a given failure
34+
/// </summary>
35+
public FailureHint.Reason Reason { get; internal set; }
36+
37+
/// <summary>
38+
/// Attempts to detect a reason for this exception
39+
/// </summary>
40+
public void AddReason()
41+
{
42+
Reason = FailureHint.DetectReason(this);
43+
}
44+
}
45+
}

Functions/Functions.csproj

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,37 @@
77
<Authors>Joseph Schultz &lt;joseph@acupofjose.com&gt;</Authors>
88
<Copyright>MIT</Copyright>
99
<NeutralLanguage>en</NeutralLanguage>
10+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1011
<Owners>Joseph Schultz &lt;joseph@acupofjose.com&gt;</Owners>
11-
<Summary>A C# client for Supabase functions</Summary>
12+
<Summary>A C# client for Supabase Functions</Summary>
1213
<Title>Function</Title>
13-
<Description>A C# client for Supabase functions</Description>
14+
<Description>A C# client for Supabase Functions</Description>
1415
<RootNamespace>Supabase.Functions</RootNamespace>
1516
<PackageIconUrl>https://avatars.githubusercontent.com/u/54469796?s=200&amp;v=4</PackageIconUrl>
16-
<PackageLicenseUrl>https://github.com/supabase-community/functions-csharp/blob/master/LICENSE</PackageLicenseUrl>
1717
<PackageProjectUrl>https://github.com/supabase-community/functions-csharp</PackageProjectUrl>
1818
<PackageTags>supabase, functions</PackageTags>
19-
<PackageVersion>1.2.1</PackageVersion>
20-
<ReleaseVersion>1.2.1</ReleaseVersion>
19+
<PackageVersion>1.3.0</PackageVersion>
20+
<ReleaseVersion>1.3.0</ReleaseVersion>
2121
<AssemblyName>Supabase.Functions</AssemblyName>
22+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
23+
<PackageIcon>icon.png</PackageIcon>
24+
<PackageReadmeFile>README.md</PackageReadmeFile>
25+
<RepositoryUrl>https://github.com/supabase-community/functions-csharp</RepositoryUrl>
26+
</PropertyGroup>
27+
<PropertyGroup>
28+
<IncludeSymbols>true</IncludeSymbols>
29+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
30+
</PropertyGroup>
31+
<PropertyGroup>
32+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
2233
</PropertyGroup>
2334
<PropertyGroup>
2435
<Nullable>enable</Nullable>
2536
<LangVersion>8.0</LangVersion>
2637
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
2738
</PropertyGroup>
2839
<PropertyGroup Condition=" '$(Version)' == '' ">
29-
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.2.1</VersionPrefix>
40+
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.3.0</VersionPrefix>
3041
<VersionSuffix Condition=" '$(VersionSuffix)' == '' ">
3142
</VersionSuffix>
3243
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix)-$(VersionSuffix)</Version>
@@ -42,4 +53,8 @@
4253
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
4354
<PackageReference Include="supabase-core" Version="0.0.2" />
4455
</ItemGroup>
56+
<ItemGroup>
57+
<None Include="..\.github\icon.png" Pack="true" Link="icon.png" PackagePath="\" />
58+
<None Include="..\README.md" Pack="true" Link="README.md" PackagePath="\" />
59+
</ItemGroup>
4560
</Project>

0 commit comments

Comments
 (0)