diff --git a/Postgrest/Attributes/ColumnAttribute.cs b/Postgrest/Attributes/ColumnAttribute.cs
index 0dd1ab6..85238c7 100644
--- a/Postgrest/Attributes/ColumnAttribute.cs
+++ b/Postgrest/Attributes/ColumnAttribute.cs
@@ -21,22 +21,22 @@ public class ColumnAttribute : Attribute
///
/// The name in postgres of this column.
///
- public string ColumnName { get; set; }
+ public string ColumnName { get; }
///
- /// Specifies what should be serialied in the event this column's value is NULL
+ /// Specifies what should be serialized in the event this column's value is NULL
///
public NullValueHandling NullValueHandling { get; set; }
///
/// If the performed query is an Insert or Upsert, should this value be ignored?
///
- public bool IgnoreOnInsert { get; set; }
+ public bool IgnoreOnInsert { get; }
///
/// If the performed query is an Update, should this value be ignored?
///
- public bool IgnoreOnUpdate { get; set; }
+ public bool IgnoreOnUpdate { get; }
public ColumnAttribute([CallerMemberName] string? columnName = null, NullValueHandling nullValueHandling = NullValueHandling.Include, bool ignoreOnInsert = false, bool ignoreOnUpdate = false)
{
diff --git a/Postgrest/Attributes/PrimaryKeyAttribute.cs b/Postgrest/Attributes/PrimaryKeyAttribute.cs
index db17300..a65e6e4 100644
--- a/Postgrest/Attributes/PrimaryKeyAttribute.cs
+++ b/Postgrest/Attributes/PrimaryKeyAttribute.cs
@@ -17,12 +17,12 @@ namespace Postgrest.Attributes
[AttributeUsage(AttributeTargets.Property)]
public class PrimaryKeyAttribute : Attribute
{
- public string ColumnName { get; set; }
+ public string ColumnName { get; }
///
/// Would be set to false in the event that the database handles the generation of this property.
///
- public bool ShouldInsert { get; set; }
+ public bool ShouldInsert { get; }
public PrimaryKeyAttribute([CallerMemberName] string? columnName = null, bool shouldInsert = false)
{
diff --git a/Postgrest/Attributes/ReferenceAttribute.cs b/Postgrest/Attributes/ReferenceAttribute.cs
index 02cf326..e3e8cab 100644
--- a/Postgrest/Attributes/ReferenceAttribute.cs
+++ b/Postgrest/Attributes/ReferenceAttribute.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Runtime.CompilerServices;
-using Newtonsoft.Json;
using Postgrest.Extensions;
using Postgrest.Models;
@@ -28,12 +28,12 @@ public class ReferenceAttribute : Attribute
///
/// Table name of model
///
- public string TableName { get; private set; }
+ public string TableName { get; }
///
/// Columns that exist on the model we will select from.
///
- public List Columns { get; } = new List();
+ public List Columns { get; } = new();
///
/// If the performed query is an Insert or Upsert, should this value be ignored? (DEFAULT TRUE)
@@ -48,14 +48,14 @@ public class ReferenceAttribute : Attribute
///
/// If Reference should automatically be included in queries on this reference. (DEFAULT TRUE)
///
- public bool IncludeInQuery { get; private set; }
+ public bool IncludeInQuery { get; }
///
/// As to whether the query will filter top-level rows.
///
/// See: https://postgrest.org/en/stable/api.html#resource-embedding
///
- public bool ShouldFilterTopLevel { get; private set; }
+ public bool ShouldFilterTopLevel { get; }
/// Model referenced
/// Should referenced be included in queries?
@@ -64,11 +64,12 @@ public class ReferenceAttribute : Attribute
/// As to whether the query will filter top-level rows.
///
///
- public ReferenceAttribute(Type model, bool includeInQuery = true, bool ignoreOnInsert = true, bool ignoreOnUpdate = true, bool shouldFilterTopLevel = true, [CallerMemberName] string propertyName = "")
+ public ReferenceAttribute(Type model, bool includeInQuery = true, bool ignoreOnInsert = true,
+ bool ignoreOnUpdate = true, bool shouldFilterTopLevel = true, [CallerMemberName] string propertyName = "")
{
if (!IsDerivedFromBaseModel(model))
{
- throw new Exception("RefernceAttribute must be used with Postgrest BaseModels.");
+ throw new Exception("ReferenceAttribute must be used with Postgrest BaseModels.");
}
Model = model;
@@ -102,36 +103,17 @@ public ReferenceAttribute(Type model, bool includeInQuery = true, bool ignoreOnI
{
Columns.Add(pk.ColumnName);
}
- else if (item is ReferenceAttribute refAttr)
+ else if (item is ReferenceAttribute { IncludeInQuery: true } refAttr)
{
- if (refAttr.IncludeInQuery)
- {
- if (ShouldFilterTopLevel)
- {
- Columns.Add($"{refAttr.TableName}!inner({string.Join(",", refAttr.Columns.ToArray())})");
- }
- else
- {
- Columns.Add($"{refAttr.TableName}({string.Join(",", refAttr.Columns.ToArray())})");
- }
- }
+ Columns.Add(ShouldFilterTopLevel
+ ? $"{refAttr.TableName}!inner({string.Join(",", refAttr.Columns.ToArray())})"
+ : $"{refAttr.TableName}({string.Join(",", refAttr.Columns.ToArray())})");
}
}
}
}
- private bool IsDerivedFromBaseModel(Type type)
- {
- var isDerived = false;
- foreach (var t in type.GetInheritanceHierarchy())
- {
- if (t == typeof(BaseModel))
- {
- isDerived = true;
- break;
- }
- }
- return isDerived;
- }
+ private bool IsDerivedFromBaseModel(Type type) =>
+ type.GetInheritanceHierarchy().Any(t => t == typeof(BaseModel));
}
-}
+}
\ No newline at end of file
diff --git a/Postgrest/Client.cs b/Postgrest/Client.cs
index d0fb698..262bd07 100644
--- a/Postgrest/Client.cs
+++ b/Postgrest/Client.cs
@@ -55,7 +55,7 @@ public static JsonSerializerSettings SerializerSettings(ClientOptions? options =
///
/// Function that can be set to return dynamic headers.
///
- /// Headers specified in the constructor options will ALWAYS take precendece over headers returned by this function.
+ /// Headers specified in the constructor options will ALWAYS take precedence over headers returned by this function.
///
public Func>? GetHeaders { get; set; }
@@ -81,9 +81,11 @@ public Client(string baseUrl, ClientOptions? options = null)
///
public IPostgrestTable Table() where T : BaseModel, new()
{
- var table = new Table(BaseUrl, SerializerSettings(Options), Options);
- table.GetHeaders = GetHeaders;
-
+ var table = new Table(BaseUrl, SerializerSettings(Options), Options)
+ {
+ GetHeaders = GetHeaders
+ };
+
return table;
}
@@ -111,9 +113,7 @@ public Task Rpc(string procedureName, Dictionary p
new Dictionary(Options.Headers), Options);
if (GetHeaders != null)
- {
headers = GetHeaders().MergeLeft(headers);
- }
// Send request
var request = Helpers.MakeRequest(Options, HttpMethod.Post, canonicalUri, serializerSettings, data, headers);
diff --git a/Postgrest/Constants.cs b/Postgrest/Constants.cs
index f9909e9..4e4e66c 100644
--- a/Postgrest/Constants.cs
+++ b/Postgrest/Constants.cs
@@ -1,5 +1,4 @@
-using Postgrest.Attributes;
-using Supabase.Core.Attributes;
+using Supabase.Core.Attributes;
namespace Postgrest
{
diff --git a/Postgrest/Exceptions/FailureHint.cs b/Postgrest/Exceptions/FailureHint.cs
new file mode 100644
index 0000000..773b1a9
--- /dev/null
+++ b/Postgrest/Exceptions/FailureHint.cs
@@ -0,0 +1,39 @@
+using static Postgrest.Exceptions.FailureHint.Reason;
+
+namespace Postgrest.Exceptions
+{
+ ///
+ /// https://postgrest.org/en/v10.2/errors.html?highlight=exception#http-status-codes
+ ///
+ public static class FailureHint
+ {
+ public enum Reason
+ {
+ Unknown,
+ NotAuthorized,
+ ForeignKeyViolation,
+ UniquenessViolation,
+ Internal,
+ UndefinedTable,
+ UndefinedFunction
+ }
+
+ public static Reason DetectReason(PostgrestException pgex)
+ {
+ if (pgex.Content == null)
+ return Unknown;
+
+ return pgex.StatusCode switch
+ {
+ 401 => NotAuthorized,
+ 403 when pgex.Content.Contains("apikey") => NotAuthorized,
+ 404 when pgex.Content.Contains("42883") => UndefinedTable,
+ 404 when pgex.Content.Contains("42P01") => UndefinedFunction,
+ 409 when pgex.Content.Contains("23503") => ForeignKeyViolation,
+ 409 when pgex.Content.Contains("23505") => UniquenessViolation,
+ 500 => Internal,
+ _ => Unknown
+ };
+ }
+ }
+}
diff --git a/Postgrest/Exceptions/PostgrestException.cs b/Postgrest/Exceptions/PostgrestException.cs
new file mode 100644
index 0000000..483c906
--- /dev/null
+++ b/Postgrest/Exceptions/PostgrestException.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Net.Http;
+
+namespace Postgrest.Exceptions
+{
+ ///
+ /// Errors from Postgrest are wrapped by this exception
+ ///
+ public class PostgrestException : Exception
+ {
+ public PostgrestException(string? message) : base(message) { }
+ public PostgrestException(string? message, Exception? innerException) : base(message, innerException) { }
+
+ public HttpResponseMessage? Response { get; internal set; }
+
+ public string? Content { get; internal set; }
+
+ public int StatusCode { get; internal set; }
+
+ public FailureHint.Reason Reason { get; private set; }
+
+ public void AddReason()
+ {
+ Reason = FailureHint.DetectReason(this);
+ }
+
+ }
+}
+
\ No newline at end of file
diff --git a/Postgrest/Extensions/TypeExtensions.cs b/Postgrest/Extensions/TypeExtensions.cs
index 0c515da..827ea25 100644
--- a/Postgrest/Extensions/TypeExtensions.cs
+++ b/Postgrest/Extensions/TypeExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Text;
namespace Postgrest.Extensions
{
diff --git a/Postgrest/Extensions/UriExtensions.cs b/Postgrest/Extensions/UriExtensions.cs
index b5e0f75..d9a22bd 100644
--- a/Postgrest/Extensions/UriExtensions.cs
+++ b/Postgrest/Extensions/UriExtensions.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Text;
namespace Postgrest.Extensions
{
diff --git a/Postgrest/Helpers.cs b/Postgrest/Helpers.cs
index 589983c..7327f26 100644
--- a/Postgrest/Helpers.cs
+++ b/Postgrest/Helpers.cs
@@ -9,170 +9,143 @@
using System.Runtime.CompilerServices;
using System.Threading;
using Newtonsoft.Json.Linq;
-using Postgrest.Extensions;
using Postgrest.Models;
using Supabase.Core.Extensions;
+using Postgrest.Exceptions;
[assembly: InternalsVisibleTo("PostgrestTests")]
namespace Postgrest
{
- internal static class Helpers
- {
- public static T GetPropertyValue(object obj, string propName) =>
- (T)obj.GetType().GetProperty(propName).GetValue(obj, null);
-
- public static T GetCustomAttribute(object obj) where T : Attribute =>
- (T)Attribute.GetCustomAttribute(obj.GetType(), typeof(T));
-
- public static T GetCustomAttribute(Type type) where T : Attribute =>
- (T)Attribute.GetCustomAttribute(type, typeof(T));
-
- private static readonly HttpClient Client = new HttpClient();
-
- ///
- /// Helper to make a request using the defined parameters to an API Endpoint and coerce into a model.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static async Task> MakeRequest(ClientOptions clientOptions, HttpMethod method, string url, JsonSerializerSettings serializerSettings, object? data = null, Dictionary? headers = null, Func>? getHeaders = null, CancellationToken cancellationToken = default) where T : BaseModel, new()
- {
- var baseResponse = await MakeRequest(clientOptions, method, url, serializerSettings, data, headers, cancellationToken);
- return new ModeledResponse(baseResponse, serializerSettings, getHeaders);
- }
-
- ///
- /// Helper to make a request using the defined parameters to an API Endpoint.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static async Task MakeRequest(ClientOptions clientOptions, HttpMethod method, string url, JsonSerializerSettings serializerSettings, object? data = null, Dictionary? headers = null, CancellationToken cancellationToken = default)
- {
- var builder = new UriBuilder(url);
- var query = HttpUtility.ParseQueryString(builder.Query);
-
- if (data != null && method == HttpMethod.Get)
- {
- // Case if it's a Get request the data object is a dictionary
- if (data is Dictionary reqParams)
- {
- foreach (var param in reqParams)
- query[param.Key] = param.Value;
- }
- }
-
- builder.Query = query.ToString();
-
- using var requestMessage = new HttpRequestMessage(method, builder.Uri);
-
- if (data != null && method != HttpMethod.Get)
- {
- var stringContent = JsonConvert.SerializeObject(data, serializerSettings);
-
- if (!string.IsNullOrWhiteSpace(stringContent) && JToken.Parse(stringContent).HasValues)
- {
- requestMessage.Content = new StringContent(stringContent,
- Encoding.UTF8, "application/json");
- }
- }
-
- if (headers != null)
- {
- foreach (var kvp in headers)
- {
- requestMessage.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
- }
- }
-
- var response = await Client.SendAsync(requestMessage, cancellationToken);
- var content = await response.Content.ReadAsStringAsync();
-
- if (!response.IsSuccessStatusCode)
- {
- ErrorResponse? obj = null;
-
- try
- {
- obj = JsonConvert.DeserializeObject(content);
- }
- catch (JsonSerializationException)
- {
- obj = new ErrorResponse(clientOptions, response, content)
- {
- Message =
- "Invalid or Empty response received. Are you trying to update or delete a record that does not exist?"
- };
- }
-
- throw new RequestException(response, obj!);
- }
-
- return new BaseResponse(clientOptions, response, content);
- }
-
- ///
- /// Prepares the request with appropriate HTTP headers expected by Postgrest.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static Dictionary PrepareRequestHeaders(HttpMethod method, Dictionary? headers = null, ClientOptions? options = null, int rangeFrom = int.MinValue, int rangeTo = int.MinValue)
- {
- options ??= new ClientOptions();
-
- headers = headers == null
- ? new Dictionary(options.Headers)
- : options.Headers.MergeLeft(headers);
-
- if (!string.IsNullOrEmpty(options.Schema))
- {
- headers.Add(method == HttpMethod.Get
- ? "Accept-Profile"
- : "Content-Profile", options.Schema);
- }
-
- if (rangeFrom != int.MinValue)
- {
- var formatRangeTo = rangeTo != int.MinValue
- ? rangeTo.ToString()
- : null;
-
- headers.Add("Range-Unit", "items");
- headers.Add("Range", $"{rangeFrom}-{formatRangeTo}");
- }
-
- if (!headers.ContainsKey("X-Client-Info"))
- {
- headers.Add("X-Client-Info", Supabase.Core.Util.GetAssemblyVersion(typeof(Client)));
- }
-
- return headers;
- }
- }
-
- public class RequestException : Exception
- {
- public HttpResponseMessage Response { get; }
- public ErrorResponse Error { get; }
-
- public RequestException(HttpResponseMessage response, ErrorResponse error) : base(error.Message)
- {
- Response = response;
- Error = error;
- }
- }
+ internal static class Helpers
+ {
+ private static readonly HttpClient Client = new HttpClient();
+
+ ///
+ /// Helper to make a request using the defined parameters to an API Endpoint and coerce into a model.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task> MakeRequest(ClientOptions clientOptions, HttpMethod method, string url, JsonSerializerSettings serializerSettings, object? data = null, Dictionary? headers = null, Func>? getHeaders = null, CancellationToken cancellationToken = default) where T : BaseModel, new()
+ {
+ var baseResponse = await MakeRequest(clientOptions, method, url, serializerSettings, data, headers, cancellationToken);
+ return new ModeledResponse(baseResponse, serializerSettings, getHeaders);
+ }
+
+ ///
+ /// Helper to make a request using the defined parameters to an API Endpoint.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task MakeRequest(ClientOptions clientOptions, HttpMethod method, string url, JsonSerializerSettings serializerSettings, object? data = null, Dictionary? headers = null, CancellationToken cancellationToken = default)
+ {
+ var builder = new UriBuilder(url);
+ var query = HttpUtility.ParseQueryString(builder.Query);
+
+ if (data != null && method == HttpMethod.Get)
+ {
+ // Case if it's a Get request the data object is a dictionary
+ if (data is Dictionary reqParams)
+ {
+ foreach (var param in reqParams)
+ query[param.Key] = param.Value;
+ }
+ }
+
+ builder.Query = query.ToString();
+
+ using var requestMessage = new HttpRequestMessage(method, builder.Uri);
+
+ if (data != null && method != HttpMethod.Get)
+ {
+ var stringContent = JsonConvert.SerializeObject(data, serializerSettings);
+
+ if (!string.IsNullOrWhiteSpace(stringContent) && JToken.Parse(stringContent).HasValues)
+ {
+ requestMessage.Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
+ }
+ }
+
+ if (headers != null)
+ {
+ foreach (var kvp in headers)
+ {
+ requestMessage.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
+ }
+ }
+
+ var response = await Client.SendAsync(requestMessage, cancellationToken);
+ var content = await response.Content.ReadAsStringAsync();
+
+ if (!response.IsSuccessStatusCode)
+ {
+ var e = new PostgrestException("Request Failed")
+ {
+ Content = content,
+ Response = response,
+ StatusCode = (int)response.StatusCode
+ };
+ e.AddReason();
+ throw e;
+ }
+
+ return new BaseResponse(clientOptions, response, content);
+ }
+
+ ///
+ /// Prepares the request with appropriate HTTP headers expected by Postgrest.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Dictionary PrepareRequestHeaders(HttpMethod method, Dictionary? headers = null, ClientOptions? options = null, int rangeFrom = int.MinValue, int rangeTo = int.MinValue)
+ {
+ options ??= new ClientOptions();
+
+ headers = headers == null
+ ? new Dictionary(options.Headers)
+ : options.Headers.MergeLeft(headers);
+
+ if (!string.IsNullOrEmpty(options.Schema))
+ {
+ headers.Add(method == HttpMethod.Get
+ ? "Accept-Profile"
+ : "Content-Profile", options.Schema);
+ }
+
+ if (rangeFrom != int.MinValue)
+ {
+ var formatRangeTo = rangeTo != int.MinValue
+ ? rangeTo.ToString()
+ : null;
+
+ headers.Add("Range-Unit", "items");
+ headers.Add("Range", $"{rangeFrom}-{formatRangeTo}");
+ }
+
+ if (!headers.ContainsKey("X-Client-Info"))
+ {
+ headers.Add("X-Client-Info", Supabase.Core.Util.GetAssemblyVersion(typeof(Client)));
+ }
+
+ return headers;
+ }
+ }
}
\ No newline at end of file
diff --git a/Postgrest/IntRange.cs b/Postgrest/IntRange.cs
index 34b6394..7fd8fb7 100644
--- a/Postgrest/IntRange.cs
+++ b/Postgrest/IntRange.cs
@@ -1,10 +1,9 @@
-// https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Index.cs
-// https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Range.cs
-
-using System;
+using System;
using System.Runtime.CompilerServices;
using Postgrest;
+// https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Index.cs
+// https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Range.cs
namespace Postgrest
{
/// Represent a type can be used to index a collection either from the start or the end.
@@ -121,7 +120,7 @@ public int GetOffset(int length)
/// Indicates whether the current Index object is equal to another object of the same type.
/// An object to compare with this object
- public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value;
+ public override bool Equals(object? value) => value is Index index && _value == index._value;
/// Indicates whether the current Index object is equal to another Index object.
/// An object to compare with this object
diff --git a/Postgrest/Interfaces/IPostgrestQueryFilter.cs b/Postgrest/Interfaces/IPostgrestQueryFilter.cs
index 6e8f03a..afb92db 100644
--- a/Postgrest/Interfaces/IPostgrestQueryFilter.cs
+++ b/Postgrest/Interfaces/IPostgrestQueryFilter.cs
@@ -2,7 +2,7 @@
{
public interface IPostgrestQueryFilter
{
- object Criteria { get; }
+ object? Criteria { get; }
Constants.Operator Op { get; }
string? Property { get; }
}
diff --git a/Postgrest/Linq/SelectExpressionVisitor.cs b/Postgrest/Linq/SelectExpressionVisitor.cs
index 658b257..02e2f4d 100644
--- a/Postgrest/Linq/SelectExpressionVisitor.cs
+++ b/Postgrest/Linq/SelectExpressionVisitor.cs
@@ -1,92 +1,97 @@
using Postgrest.Attributes;
using System;
using System.Collections.Generic;
-using System.Linq;
+using System.Diagnostics;
using System.Linq.Expressions;
-using System.Reflection;
-using System.Text;
-using static Postgrest.Constants;
namespace Postgrest.Linq
{
- ///
- /// Helper class for parsing Select linq queries.
- ///
- internal class SelectExpressionVisitor : ExpressionVisitor
- {
- ///
- /// The columns that have been selected from this linq expression.
- ///
- public List Columns { get; private set; } = new List();
+ ///
+ /// Helper class for parsing Select linq queries.
+ ///
+ internal class SelectExpressionVisitor : ExpressionVisitor
+ {
+ ///
+ /// The columns that have been selected from this linq expression.
+ ///
+ public List Columns { get; } = new();
- ///
- /// The root call that will be looped through to populate .
- ///
- /// Called like: `Table().Select(x => new[] { x.Id, x.Name, x.CreatedAt }).Get()`
- ///
- ///
- ///
- protected override Expression VisitNewArray(NewArrayExpression node)
- {
- foreach (var expression in node.Expressions)
- Visit(expression);
+ ///
+ /// The root call that will be looped through to populate .
+ ///
+ /// Called like: `Table().Select(x => new[] { x.Id, x.Name, x.CreatedAt }).Get()`
+ ///
+ ///
+ ///
+ protected override Expression VisitNewArray(NewArrayExpression node)
+ {
+ foreach (var expression in node.Expressions)
+ Visit(expression);
- return node;
- }
+ return node;
+ }
- ///
- /// A Member Node, representing a property on a BaseModel.
- ///
- ///
- ///
- protected override Expression VisitMember(MemberExpression node)
- {
- var column = GetColumnFromMemberExpression(node);
+ ///
+ /// A Member Node, representing a property on a BaseModel.
+ ///
+ ///
+ ///
+ protected override Expression VisitMember(MemberExpression node)
+ {
+ var column = GetColumnFromMemberExpression(node);
- if (column != null)
- Columns.Add(column);
+ if (column != null)
+ Columns.Add(column);
- return node;
- }
+ return node;
+ }
- ///
- /// A Unary Node, delved into to represent a property on a BaseModel.
- ///
- ///
- ///
- protected override Expression VisitUnary(UnaryExpression node)
- {
- if (node.Operand is MemberExpression memberExpression)
- {
- var column = GetColumnFromMemberExpression(memberExpression);
+ ///
+ /// A Unary Node, delved into to represent a property on a BaseModel.
+ ///
+ ///
+ ///
+ protected override Expression VisitUnary(UnaryExpression node)
+ {
+ if (node.Operand is MemberExpression memberExpression)
+ {
+ var column = GetColumnFromMemberExpression(memberExpression);
- if (column != null)
- Columns.Add(column);
- }
+ if (column != null)
+ Columns.Add(column);
+ }
- return node;
- }
+ return node;
+ }
- ///
- /// Gets a column name from property based on it's supplied attributes.
- ///
- ///
- ///
- private string? GetColumnFromMemberExpression(MemberExpression node)
- {
- var type = node.Member.ReflectedType;
- var prop = type.GetProperty(node.Member.Name);
- var attrs = prop.GetCustomAttributes(true);
+ ///
+ /// Gets a column name from property based on it's supplied attributes.
+ ///
+ ///
+ ///
+ private string? GetColumnFromMemberExpression(MemberExpression node)
+ {
+ var type = node.Member.ReflectedType;
+ var prop = type?.GetProperty(node.Member.Name);
+ var attrs = prop?.GetCustomAttributes(true);
- foreach (var attr in attrs)
- {
- if (attr is ColumnAttribute columnAttr)
- return columnAttr.ColumnName;
- else if (attr is PrimaryKeyAttribute primaryKeyAttr)
- return primaryKeyAttr.ColumnName;
- }
+ if (attrs == null)
+ throw new ArgumentException(
+ $"Unknown argument '{node.Member.Name}' provided, does it have a `Column` or `PrimaryKey` attribute?");
- throw new ArgumentException(string.Format("Unknown argument '{0}' provided, does it have a `Column` or `PrimaryKey` attribute?", node.Member.Name));
- }
- }
-}
+ foreach (var attr in attrs)
+ {
+ switch (attr)
+ {
+ case ColumnAttribute columnAttr:
+ return columnAttr.ColumnName;
+ case PrimaryKeyAttribute primaryKeyAttr:
+ return primaryKeyAttr.ColumnName;
+ }
+ }
+
+ throw new ArgumentException(
+ $"Unknown argument '{node.Member.Name}' provided, does it have a `Column` or `PrimaryKey` attribute?");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Postgrest/Linq/SetExpressionVisitor.cs b/Postgrest/Linq/SetExpressionVisitor.cs
index d9dc533..99e3e68 100644
--- a/Postgrest/Linq/SetExpressionVisitor.cs
+++ b/Postgrest/Linq/SetExpressionVisitor.cs
@@ -1,12 +1,7 @@
-using Newtonsoft.Json.Linq;
-using Postgrest.Attributes;
+using Postgrest.Attributes;
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Linq.Expressions;
-using System.Reflection;
-using System.Text;
-using static Postgrest.Constants;
namespace Postgrest.Linq
{
@@ -37,16 +32,12 @@ internal class SetExpressionVisitor : ExpressionVisitor
///
protected override Expression VisitUnary(UnaryExpression node)
{
- if (node.Operand is MemberExpression memberExpression)
- {
- var column = GetColumnFromMemberExpression(memberExpression);
+ if (node.Operand is not MemberExpression memberExpression) return node;
+
+ var column = GetColumnFromMemberExpression(memberExpression);
- if (column != null)
- {
- Column = column;
- ExpectedType = memberExpression.Type;
- }
- }
+ Column = column;
+ ExpectedType = memberExpression.Type;
return node;
}
@@ -60,11 +51,8 @@ protected override Expression VisitMember(MemberExpression node)
{
var column = GetColumnFromMemberExpression(node);
- if (column != null)
- {
- Column = column;
- ExpectedType = node.Type;
- }
+ Column = column;
+ ExpectedType = node.Type;
return node;
}
@@ -115,8 +103,9 @@ private void HandleKeyValuePair(NewExpression node)
var valueArgument = Expression.Lambda(right).Compile().DynamicInvoke();
Value = valueArgument;
- if (!ExpectedType!.IsAssignableFrom(Value.GetType()))
- throw new ArgumentException(string.Format("Expected Value to be of Type: {0}, instead received: {1}.", ExpectedType.Name, Value.GetType().Name));
+ if (!ExpectedType!.IsInstanceOfType(Value))
+ throw new ArgumentException(
+ $"Expected Value to be of Type: {ExpectedType.Name}, instead received: {Value.GetType().Name}.");
}
///
@@ -127,18 +116,26 @@ private void HandleKeyValuePair(NewExpression node)
private string GetColumnFromMemberExpression(MemberExpression node)
{
var type = node.Member.ReflectedType;
- var prop = type.GetProperty(node.Member.Name);
- var attrs = prop.GetCustomAttributes(true);
+ var prop = type?.GetProperty(node.Member.Name);
+ var attrs = prop?.GetCustomAttributes(true);
+ if (attrs == null)
+ throw new ArgumentException(
+ $"Unknown argument '{node.Member.Name}' provided, does it have a Column or PrimaryKey attribute?");
+
foreach (var attr in attrs)
{
- if (attr is ColumnAttribute columnAttr)
- return columnAttr.ColumnName;
- else if (attr is PrimaryKeyAttribute primaryKeyAttr)
- return primaryKeyAttr.ColumnName;
+ switch (attr)
+ {
+ case ColumnAttribute columnAttr:
+ return columnAttr.ColumnName;
+ case PrimaryKeyAttribute primaryKeyAttr:
+ return primaryKeyAttr.ColumnName;
+ }
}
- throw new ArgumentException(string.Format("Unknown argument '{0}' provided, does it have a Column or PrimaryKey attribute?", node.Member.Name));
+ throw new ArgumentException(
+ $"Unknown argument '{node.Member.Name}' provided, does it have a Column or PrimaryKey attribute?");
}
}
}
diff --git a/Postgrest/Linq/WhereExpressionVisitor.cs b/Postgrest/Linq/WhereExpressionVisitor.cs
index 5464634..1a7301e 100644
--- a/Postgrest/Linq/WhereExpressionVisitor.cs
+++ b/Postgrest/Linq/WhereExpressionVisitor.cs
@@ -5,8 +5,8 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
-using System.Text;
using static Postgrest.Constants;
+// ReSharper disable InvalidXmlDocComment
namespace Postgrest.Linq
{
@@ -54,13 +54,14 @@ protected override Expression VisitBinary(BinaryExpression node)
// Otherwise, the base case.
- Expression left = Visit(node.Left);
- Expression right = Visit(node.Right);
+ var left = Visit(node.Left);
+ var right = Visit(node.Right);
var column = left is MemberExpression leftMember ? GetColumnFromMemberExpression(leftMember) : null;
if (column == null)
- throw new ArgumentException(string.Format("Left side of expression: '{0}' is expected to be property with a ColumnAttribute or PrimaryKeyAttribute", node.ToString()));
+ throw new ArgumentException(
+ $"Left side of expression: '{node}' is expected to be property with a ColumnAttribute or PrimaryKeyAttribute");
if (right is ConstantExpression rightConstant)
{
@@ -94,12 +95,14 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
var obj = node.Object as MemberExpression;
if (obj == null)
- throw new ArgumentException(string.Format("Calling context '{0}' is expected to be a member of or derived from `BaseModel`", node.Object));
+ throw new ArgumentException(
+ $"Calling context '{node.Object}' is expected to be a member of or derived from `BaseModel`");
var column = GetColumnFromMemberExpression(obj);
if (column == null)
- throw new ArgumentException(string.Format("Left side of expression: '{0}' is expected to be property with a ColumnAttribute or PrimaryKeyAttribute", node.ToString()));
+ throw new ArgumentException(
+ $"Left side of expression: '{node.ToString()}' is expected to be property with a ColumnAttribute or PrimaryKeyAttribute");
switch (node.Method.Name)
{
@@ -141,7 +144,6 @@ private void HandleMemberExpression(string column, Operator op, MemberExpression
Filter = new QueryFilter(column, op, GetMemberExpressionValue(memberExpression));
}
-
///
/// A unary expression parser (i.e. => x.Id == 1 <- where both `1` is considered unary)
///
@@ -197,15 +199,20 @@ private void HandleNewExpression(string column, Operator op, NewExpression newEx
private string GetColumnFromMemberExpression(MemberExpression node)
{
var type = node.Member.ReflectedType;
- var prop = type.GetProperty(node.Member.Name);
- var attrs = prop.GetCustomAttributes(true);
+ var prop = type?.GetProperty(node.Member.Name);
+ var attrs = prop?.GetCustomAttributes(true);
+ if (attrs == null) return node.Member.Name;
+
foreach (var attr in attrs)
{
- if (attr is ColumnAttribute columnAttr)
- return columnAttr.ColumnName;
- else if (attr is PrimaryKeyAttribute primaryKeyAttr)
- return primaryKeyAttr.ColumnName;
+ switch (attr)
+ {
+ case ColumnAttribute columnAttr:
+ return columnAttr.ColumnName;
+ case PrimaryKeyAttribute primaryKeyAttr:
+ return primaryKeyAttr.ColumnName;
+ }
}
return node.Member.Name;
@@ -223,12 +230,10 @@ private object GetMemberExpressionValue(MemberExpression member)
var obj = Expression.Lambda(member.Expression).Compile().DynamicInvoke();
return field.GetValue(obj);
}
- else
- {
- var lambda = Expression.Lambda(member);
- var func = lambda.Compile();
- return func.DynamicInvoke();
- }
+
+ var lambda = Expression.Lambda(member);
+ var func = lambda.Compile();
+ return func.DynamicInvoke();
}
///
@@ -238,7 +243,7 @@ private object GetMemberExpressionValue(MemberExpression member)
///
private Operator GetMappedOperator(Expression node)
{
- Operator op = Operator.Equals;
+ var op = Operator.Equals;
switch (node.NodeType)
{
diff --git a/Postgrest/Models/BaseModel.cs b/Postgrest/Models/BaseModel.cs
index 514c6d4..3d2e89c 100644
--- a/Postgrest/Models/BaseModel.cs
+++ b/Postgrest/Models/BaseModel.cs
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
-using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Postgrest.Attributes;
using Postgrest.Responses;
-using Supabase.Core.Extensions;
-using Supabase.Core.Interfaces;
namespace Postgrest.Models
{
diff --git a/Postgrest/PostgrestContractResolver.cs b/Postgrest/PostgrestContractResolver.cs
index 06652a6..91ba0d9 100644
--- a/Postgrest/PostgrestContractResolver.cs
+++ b/Postgrest/PostgrestContractResolver.cs
@@ -4,9 +4,10 @@
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
+using Postgrest.Attributes;
using Postgrest.Converters;
-namespace Postgrest.Attributes
+namespace Postgrest
{
///
/// A custom resolver that handles mapping column names and property names as well
@@ -14,13 +15,16 @@ namespace Postgrest.Attributes
///
public class PostgrestContractResolver : DefaultContractResolver
{
- public bool IsUpdate { get; private set; } = false;
- public bool IsInsert { get; private set; } = false;
+ public bool IsUpdate { get; private set; }
+ public bool IsInsert { get; private set; }
- public void SetState(bool isInsert = false, bool isUpdate = false)
+ public bool IsUpsert { get; private set; }
+
+ public void SetState(bool isInsert = false, bool isUpdate = false, bool isUpsert = false)
{
IsUpdate = isUpdate;
IsInsert = isInsert;
+ IsUpsert = isUpsert;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
@@ -93,7 +97,7 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ
}
prop.PropertyName = primaryKeyAttribute.ColumnName;
- prop.ShouldSerialize = instance => primaryKeyAttribute.ShouldInsert;
+ prop.ShouldSerialize = instance => primaryKeyAttribute.ShouldInsert || (IsUpsert && instance != null);
return prop;
}
}
diff --git a/Postgrest/QueryFilter.cs b/Postgrest/QueryFilter.cs
index c5a4a52..76e51b9 100644
--- a/Postgrest/QueryFilter.cs
+++ b/Postgrest/QueryFilter.cs
@@ -9,16 +9,16 @@ namespace Postgrest
public class QueryFilter : IPostgrestQueryFilter
{
///
- /// String value to be subsituted for a null criterion
+ /// String value to be substituted for a null criterion
///
public const string NullVal = "null";
public string? Property { get; private set; }
public Operator Op { get; private set; }
- public object Criteria { get; private set; }
+ public object? Criteria { get; private set; }
///
- /// Contructor to use single value filtering.
+ /// Contractor to use single value filtering.
///
/// Column name
/// Operation: And, Equals, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, NotEqual, Is, Adjacent, Not, Like, ILike
@@ -46,7 +46,6 @@ public QueryFilter(string property, Operator op, object criteria)
default:
throw new Exception("Advanced filters require a constructor with more specific arguments");
}
-
}
///
@@ -68,7 +67,8 @@ public QueryFilter(string property, Operator op, List
public class FullTextSearchConfig
{
- [JsonProperty("queryText")]
- public string QueryText { get; private set; }
+ [JsonProperty("queryText")] public string QueryText { get; private set; }
- [JsonProperty("config")]
- public string Config { get; private set; } = "english";
+ [JsonProperty("config")] public string Config { get; private set; } = "english";
- public FullTextSearchConfig(string queryText, string config)
+ public FullTextSearchConfig(string queryText, string? config)
{
QueryText = queryText;
- Config = config;
+
+ if (!string.IsNullOrEmpty(config))
+ Config = config!;
}
}
-}
+}
\ No newline at end of file
diff --git a/Postgrest/QueryOptions.cs b/Postgrest/QueryOptions.cs
index ab2bdcc..00f4fcb 100644
--- a/Postgrest/QueryOptions.cs
+++ b/Postgrest/QueryOptions.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using Postgrest.Attributes;
using Postgrest.Extensions;
using Supabase.Core.Attributes;
diff --git a/Postgrest/Responses/ErrorResponse.cs b/Postgrest/Responses/ErrorResponse.cs
deleted file mode 100644
index a594b0e..0000000
--- a/Postgrest/Responses/ErrorResponse.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Newtonsoft.Json;
-using System.Net.Http;
-
-namespace Postgrest.Responses
-{
- ///
- /// A representation of Postgrest's API error response.
- ///
- public class ErrorResponse : BaseResponse
- {
- public ErrorResponse(ClientOptions clientOptions, HttpResponseMessage? responseMessage, string? content) : base(clientOptions, responseMessage, content)
- { }
-
- [JsonProperty("hint")]
- public object? Hint { get; set; }
-
- [JsonProperty("details")]
- public object? Details { get; set; }
-
- [JsonProperty("code")]
- public string? Code { get; set; }
-
- [JsonProperty("message")]
- public string? Message { get; set; }
- }
-}
diff --git a/Postgrest/Responses/ModeledResponse.cs b/Postgrest/Responses/ModeledResponse.cs
index c4d0282..f594d03 100644
--- a/Postgrest/Responses/ModeledResponse.cs
+++ b/Postgrest/Responses/ModeledResponse.cs
@@ -1,12 +1,9 @@
using System;
-using System.Collections;
using System.Collections.Generic;
-using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Postgrest.Extensions;
using Postgrest.Models;
-using Supabase.Core.Extensions;
namespace Postgrest.Responses
{
@@ -16,13 +13,10 @@ namespace Postgrest.Responses
///
public class ModeledResponse : BaseResponse where T : BaseModel, new()
{
- private JsonSerializerSettings SerializerSettings { get; set; }
-
- public List Models { get; private set; } = new List();
+ public List Models { get; } = new();
public ModeledResponse(BaseResponse baseResponse, JsonSerializerSettings serializerSettings, Func>? getHeaders = null, bool shouldParse = true) : base(baseResponse.ClientOptions, baseResponse.ResponseMessage, baseResponse.Content)
{
- SerializerSettings = serializerSettings;
Content = baseResponse.Content;
ResponseMessage = baseResponse.ResponseMessage;
@@ -37,14 +31,11 @@ public ModeledResponse(BaseResponse baseResponse, JsonSerializerSettings seriali
if (deserialized != null)
Models = deserialized;
- if (Models != null)
+ foreach (var model in Models)
{
- foreach (var model in Models)
- {
- model.BaseUrl = baseResponse.ResponseMessage!.RequestMessage.RequestUri.GetInstanceUrl().Replace(model.TableName, "").TrimEnd('/');
- model.RequestClientOptions = ClientOptions;
- model.GetHeaders = getHeaders;
- }
+ model.BaseUrl = baseResponse.ResponseMessage!.RequestMessage.RequestUri.GetInstanceUrl().Replace(model.TableName, "").TrimEnd('/');
+ model.RequestClientOptions = ClientOptions;
+ model.GetHeaders = getHeaders;
}
}
else if (token is JObject)
diff --git a/Postgrest/Table.cs b/Postgrest/Table.cs
index cdabdee..c883054 100644
--- a/Postgrest/Table.cs
+++ b/Postgrest/Table.cs
@@ -9,8 +9,8 @@
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using Postgrest.Attributes;
+using Postgrest.Exceptions;
using Postgrest.Extensions;
using Postgrest.Interfaces;
using Postgrest.Linq;
@@ -20,1147 +20,1114 @@
using Supabase.Core.Extensions;
using static Postgrest.Constants;
+// ReSharper disable InvalidXmlDocComment
+
namespace Postgrest
{
- ///
- /// Class created from a model derived from `BaseModel` that can generate query requests to a Postgrest Endpoint.
- ///
- /// Representative of a `USE $TABLE` command.
- ///
- /// Model derived from `BaseModel`.
- public class Table : IPostgrestTable where T : BaseModel, new()
- {
- public string BaseUrl { get; }
-
- ///
- /// Name of the Table parsed by the Model.
- ///
- public string TableName { get; }
-
- ///
- /// Function that can be set to return dynamic headers.
- ///
- /// Headers specified in the constructor will ALWAYS take precendece over headers returned by this function.
- ///
- public Func>? GetHeaders { get; set; }
-
- private ClientOptions options;
- private JsonSerializerSettings serializerSettings;
-
- private HttpMethod method = HttpMethod.Get;
-
- private string? columnQuery;
-
- private List filters = new List();
- private List orderers = new List();
- private List columns = new List();
-
- private Dictionary