Skip to content

Commit 50ae694

Browse files
committed
Enable nullability on test project and support better null value handling for Set
1 parent 940e19b commit 50ae694

File tree

11 files changed

+99
-77
lines changed

11 files changed

+99
-77
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,4 +402,5 @@ ASALocalRun/
402402
.mfractor/
403403

404404
# Local History for Visual Studio
405-
.localhistory/
405+
.localhistory/
406+
/PostgrestTests/.runsettings

Postgrest/Interfaces/IPostgrestTable.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public interface IPostgrestTable<T> : IGettableHeaders
2222
Task<int> Count(Constants.CountType type, CancellationToken cancellationToken = default);
2323
Task Delete(QueryOptions? options = null, CancellationToken cancellationToken = default);
2424
Task<ModeledResponse<T>> Delete(T model, QueryOptions? options = null, CancellationToken cancellationToken = default);
25-
Table<T> Filter(string columnName, Constants.Operator op, object criterion);
26-
Table<T> Filter(Expression<Func<T, object>> predicate, Constants.Operator op, object criterion);
25+
Table<T> Filter(string columnName, Constants.Operator op, object? criterion);
26+
Table<T> Filter(Expression<Func<T, object>> predicate, Constants.Operator op, object? criterion);
2727
Task<ModeledResponse<T>> Get(CancellationToken cancellationToken = default);
2828
Task<ModeledResponse<T>> Insert(ICollection<T> models, QueryOptions? options = null, CancellationToken cancellationToken = default);
2929
Task<ModeledResponse<T>> Insert(T model, QueryOptions? options = null, CancellationToken cancellationToken = default);
@@ -47,8 +47,8 @@ public interface IPostgrestTable<T> : IGettableHeaders
4747
Table<T> Select(Expression<Func<T, object[]>> predicate);
4848
Table<T> Where(Expression<Func<T, bool>> predicate);
4949
Task<T?> Single(CancellationToken cancellationToken = default);
50-
Table<T> Set(Expression<Func<T, object>> keySelector, object value);
51-
Table<T> Set(Expression<Func<T, KeyValuePair<object, object>>> keyValuePairExpression);
50+
Table<T> Set(Expression<Func<T, object>> keySelector, object? value);
51+
Table<T> Set(Expression<Func<T, KeyValuePair<object, object?>>> keyValuePairExpression);
5252
Task<ModeledResponse<T>> Update(QueryOptions? options = null, CancellationToken cancellationToken = default);
5353
Task<ModeledResponse<T>> Update(T model, QueryOptions? options = null, CancellationToken cancellationToken = default);
5454
Task<ModeledResponse<T>> Upsert(ICollection<T> model, QueryOptions? options = null, CancellationToken cancellationToken = default);

Postgrest/Table.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ namespace Postgrest
5555
private List<QueryOrderer> orderers = new List<QueryOrderer>();
5656
private List<string> columns = new List<string>();
5757

58-
private Dictionary<object, object> setData = new Dictionary<object, object>();
58+
private Dictionary<object, object?> setData = new Dictionary<object, object?>();
5959

6060
private List<ReferenceAttribute> references = new List<ReferenceAttribute>();
6161

@@ -104,7 +104,7 @@ public Table(string baseUrl, JsonSerializerSettings serializerSettings, ClientOp
104104
/// <param name="criterion">Value to filter with, must be a `string`, `List<object>`, `Dictionary<string, object>`, `FullTextSearchConfig`, or `Range`</param>
105105
/// <returns></returns>
106106
/// <exception cref="ArgumentException"></exception>
107-
public Table<T> Filter(Expression<Func<T, object>> predicate, Operator op, object criterion)
107+
public Table<T> Filter(Expression<Func<T, object>> predicate, Operator op, object? criterion)
108108
{
109109
var visitor = new SelectExpressionVisitor();
110110
visitor.Visit(predicate);
@@ -126,7 +126,7 @@ public Table<T> Filter(Expression<Func<T, object>> predicate, Operator op, objec
126126
/// <param name="op">Operation to perform.</param>
127127
/// <param name="criterion">Value to filter with, must be a `string`, `List<object>`, `Dictionary<string, object>`, `FullTextSearchConfig`, or `Range`</param>
128128
/// <returns></returns>
129-
public Table<T> Filter(string columnName, Operator op, object criterion)
129+
public Table<T> Filter(string columnName, Operator op, object? criterion)
130130
{
131131
if (criterion == null)
132132
{
@@ -599,16 +599,23 @@ public Task<ModeledResponse<T>> Upsert(ICollection<T> model, QueryOptions? optio
599599
/// <param name="keySelector"></param>
600600
/// <param name="value"></param>
601601
/// <returns></returns>
602-
public Table<T> Set(Expression<Func<T, object>> keySelector, object value)
602+
public Table<T> Set(Expression<Func<T, object>> keySelector, object? value)
603603
{
604604
var visitor = new SetExpressionVisitor();
605605
visitor.Visit(keySelector);
606606

607607
if (visitor.Column == null || visitor.ExpectedType == null)
608608
throw new ArgumentException("Expression should return a KeyValuePair with a key of a Model Property and a value.");
609609

610-
if (!visitor.ExpectedType.IsAssignableFrom(value.GetType()))
610+
if (value == null)
611+
{
612+
if (Nullable.GetUnderlyingType(visitor.ExpectedType) == null)
613+
throw new ArgumentException(string.Format("Expected Value to be of Type: {0}, instead received: {1}.", visitor.ExpectedType.Name, null));
614+
}
615+
else if (!visitor.ExpectedType.IsAssignableFrom(value.GetType()))
616+
{
611617
throw new ArgumentException(string.Format("Expected Value to be of Type: {0}, instead received: {1}.", visitor.ExpectedType.Name, value.GetType().Name));
618+
}
612619

613620
setData.Add(visitor.Column, value);
614621

@@ -623,7 +630,7 @@ public Table<T> Set(Expression<Func<T, object>> keySelector, object value)
623630
/// <param name="keyValuePairExpression"></param>
624631
/// <returns></returns>
625632
/// <exception cref="ArgumentException"></exception>
626-
public Table<T> Set(Expression<Func<T, KeyValuePair<object, object>>> keyValuePairExpression)
633+
public Table<T> Set(Expression<Func<T, KeyValuePair<object, object?>>> keyValuePairExpression)
627634
{
628635
var visitor = new SetExpressionVisitor();
629636
visitor.Visit(keyValuePairExpression);

PostgrestTests/ClientApi.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,21 @@ public void TestQueryParams()
3636
}
3737
});
3838

39-
Assert.AreEqual($"{baseUrl}/users?some-param=foo&other-param=bar", (client.Table<User>() as Table<User>).GenerateUrl());
39+
Assert.AreEqual($"{baseUrl}/users?some-param=foo&other-param=bar", (client.Table<User>() as Table<User>)!.GenerateUrl());
4040
}
4141

4242
[TestMethod("will use TableAttribute")]
4343
public void TestTableAttribute()
4444
{
4545
var client = new Client(baseUrl, null);
46-
Assert.AreEqual($"{baseUrl}/users", (client.Table<User>() as Table<User>).GenerateUrl());
46+
Assert.AreEqual($"{baseUrl}/users", (client.Table<User>() as Table<User>)!.GenerateUrl());
4747
}
4848

4949
[TestMethod("will default to Class.name in absence of TableAttribute")]
5050
public void TestTableAttributeDefault()
5151
{
5252
var client = new Client(baseUrl, null);
53-
Assert.AreEqual($"{baseUrl}/Stub", (client.Table<Stub>() as Table<Stub>).GenerateUrl());
53+
Assert.AreEqual($"{baseUrl}/Stub", (client.Table<Stub>() as Table<Stub>)!.GenerateUrl());
5454
}
5555

5656
[TestMethod("will set header from options")]
@@ -71,7 +71,7 @@ public void TestQueryApiKey()
7171
{ "apikey", "some-key" }
7272
}
7373
});
74-
Assert.AreEqual($"{baseUrl}/users?apikey=some-key", (client.Table<User>() as Table<User>).GenerateUrl());
74+
Assert.AreEqual($"{baseUrl}/users?apikey=some-key", (client.Table<User>() as Table<User>)!.GenerateUrl());
7575
}
7676

7777
[TestMethod("filters: simple")]
@@ -92,7 +92,7 @@ public void TestFiltersSimple()
9292
foreach (var pair in dict)
9393
{
9494
var filter = new QueryFilter("foo", pair.Key, "bar");
95-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
95+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
9696
Assert.AreEqual("foo", result.Key);
9797
Assert.AreEqual(pair.Value, result.Value);
9898
}
@@ -111,7 +111,7 @@ public void TestFiltersLike()
111111
foreach (var pair in dict)
112112
{
113113
var filter = new QueryFilter("foo", pair.Key, "%bar%");
114-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
114+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
115115
Assert.AreEqual("foo", result.Key);
116116
Assert.AreEqual(pair.Value, result.Value);
117117
}
@@ -136,7 +136,7 @@ public void TestFiltersArraysWithLists()
136136
{
137137
var list = new List<object> { "bar", "buzz" };
138138
var filter = new QueryFilter("foo", pair.Key, list);
139-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
139+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
140140
Assert.AreEqual("foo", result.Key);
141141
Assert.AreEqual(pair.Value, result.Value);
142142
}
@@ -163,7 +163,7 @@ public void TestFiltersContainsArraysWithLists()
163163
{
164164
var list = new List<object> { "bar", "buzz" };
165165
var filter = new QueryFilter("foo", pair.Key, list);
166-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
166+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
167167
Assert.AreEqual("foo", result.Key);
168168
Assert.AreEqual(pair.Value, result.Value);
169169
}
@@ -187,7 +187,7 @@ public void TestFiltersArraysWithDictionaries()
187187
{
188188
var value = new Dictionary<string, object> { { "bar", 100 }, { "buzz", "zap" } };
189189
var filter = new QueryFilter("foo", pair.Key, value);
190-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
190+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
191191
Assert.AreEqual("foo", result.Key);
192192
Assert.AreEqual(pair.Value, result.Value);
193193
}
@@ -212,7 +212,7 @@ public void TestFiltersFullTextSearch()
212212
{
213213
var config = new FullTextSearchConfig("bar", "english");
214214
var filter = new QueryFilter("foo", pair.Key, config);
215-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
215+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
216216
Assert.AreEqual("foo", result.Key);
217217
Assert.AreEqual(pair.Value, result.Value);
218218
}
@@ -237,7 +237,7 @@ public void TestFiltersRanges()
237237
{
238238
var config = new IntRange(2, 3);
239239
var filter = new QueryFilter("foo", pair.Key, config);
240-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
240+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
241241
Assert.AreEqual("foo", result.Key);
242242
Assert.AreEqual(pair.Value, result.Value);
243243
}
@@ -249,7 +249,7 @@ public void TestFiltersNot()
249249
var client = new Client(baseUrl);
250250
var filter = new QueryFilter("foo", Operator.Equals, "bar");
251251
var notFilter = new QueryFilter(Operator.Not, filter);
252-
var result = (client.Table<User>() as Table<User>).PrepareFilter(notFilter);
252+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(notFilter);
253253

254254
Assert.AreEqual("foo", result.Key);
255255
Assert.AreEqual("not.eq.bar", result.Value);
@@ -275,7 +275,7 @@ public void TestFiltersAndOr()
275275
foreach (var pair in dict)
276276
{
277277
var filter = new QueryFilter(pair.Key, subfilters);
278-
var result = (client.Table<User>() as Table<User>).PrepareFilter(filter);
278+
var result = (client.Table<User>() as Table<User>)!.PrepareFilter(filter);
279279
Assert.AreEqual(pair.Value, $"{result.Key}={result.Value}");
280280
}
281281
}
@@ -395,7 +395,7 @@ public async Task TestInsertWithUpsert()
395395
var ks1 = await client.Table<KitchenSink>().OnConflict("unique_value").Upsert(kitchenSink1);
396396
var uks1 = ks1.Models.First();
397397
uks1.StringValue = "Testing 1";
398-
var ks3 = await client.Table<KitchenSink>().OnConflict(x => x.UniqueValue).Upsert(uks1);
398+
var ks3 = await client.Table<KitchenSink>().OnConflict(x => x.UniqueValue!).Upsert(uks1);
399399

400400
var updatedUser = response.Models.First();
401401

@@ -701,7 +701,7 @@ public async Task TestLikeFilter()
701701
var messagesResponse = await client.Table<Message>().Get();
702702

703703
var supaFilteredMessages = filteredResponse.Models;
704-
var linqFilteredMessages = messagesResponse.Models.Where(m => m.UserName.StartsWith('s')).ToList();
704+
var linqFilteredMessages = messagesResponse.Models.Where(m => m.UserName!.StartsWith('s')).ToList();
705705

706706
CollectionAssert.AreEqual(linqFilteredMessages, supaFilteredMessages);
707707
}
@@ -821,7 +821,7 @@ public async Task TestILikeFilter()
821821
var messagesResponse = await client.Table<Message>().Get();
822822

823823
var supaFilteredMessages = filteredResponse.Models;
824-
var linqFilteredMessages = messagesResponse.Models.Where(m => m.UserName.Contains("SUPA", StringComparison.OrdinalIgnoreCase)).ToList();
824+
var linqFilteredMessages = messagesResponse.Models.Where(m => m.UserName!.Contains("SUPA", StringComparison.OrdinalIgnoreCase)).ToList();
825825

826826
CollectionAssert.AreEqual(linqFilteredMessages, supaFilteredMessages);
827827
}
@@ -859,7 +859,7 @@ public async Task TestPhrasetoFullTextSearch()
859859
var filteredResponse = await client.Table<User>().Filter("catchphrase", Operator.PHFTS, config).Get();
860860
var usersResponse = await client.Table<User>().Filter("catchphrase", Operator.NotEqual, null).Get();
861861

862-
var testAgainst = usersResponse.Models.Where(u => u.Catchphrase.Contains("'cat'")).ToList();
862+
var testAgainst = usersResponse.Models.Where(u => u.Catchphrase!.Contains("'cat'")).ToList();
863863
CollectionAssert.AreEqual(testAgainst, filteredResponse.Models);
864864
}
865865

@@ -1002,8 +1002,8 @@ public async Task TestStoredProcedure()
10021002
var response = await client.Rpc("get_status", parameters);
10031003

10041004
//Assert
1005-
Assert.AreEqual(true, response.ResponseMessage.IsSuccessStatusCode);
1006-
Assert.AreEqual(true, response.Content.Contains("OFFLINE"));
1005+
Assert.AreEqual(true, response?.ResponseMessage?.IsSuccessStatusCode);
1006+
Assert.AreEqual(true, response?.Content?.Contains("OFFLINE"));
10071007
}
10081008

10091009
[TestMethod("switch schema")]
@@ -1064,7 +1064,7 @@ public async Task TestCancellationToken()
10641064
DateTimeValue1 = now
10651065
};
10661066

1067-
ModeledResponse<KitchenSink> insertResponse = null;
1067+
ModeledResponse<KitchenSink>? insertResponse = null;
10681068

10691069
try
10701070
{
@@ -1089,7 +1089,7 @@ public async Task TestReferences()
10891089
Assert.IsTrue(movies.Models.Count > 0);
10901090

10911091
var first = movies.Models.First();
1092-
Assert.IsTrue(first.Persons.Count > 0);
1092+
Assert.IsTrue(first.Persons?.Count > 0);
10931093

10941094
var people = first.Persons.First();
10951095
Assert.IsNotNull(people.Profile);
@@ -1098,7 +1098,7 @@ public async Task TestReferences()
10981098
.Filter("first_name", Operator.Equals, "Bob")
10991099
.Single();
11001100

1101-
Assert.IsNotNull(person.Profile);
1101+
Assert.IsNotNull(person?.Profile);
11021102

11031103
var byEmail = await client.Table<Person>()
11041104
.Order(x => x.CreatedAt, Ordering.Ascending)

PostgrestTests/Coercion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public async Task CanCoerceData()
7272
CollectionAssert.AreEquivalent(model.ListOfDateTimes, actual.ListOfDateTimes);
7373
CollectionAssert.AreEquivalent(model.ListOfInts, actual.ListOfInts);
7474
CollectionAssert.AreEquivalent(model.ListOfFloats, actual.ListOfFloats);
75-
Assert.AreEqual(model.IntRange.Start, actual.IntRange.Start);
75+
Assert.AreEqual(model.IntRange.Start, actual.IntRange!.Start);
7676
Assert.AreEqual(model.IntRange.End, actual.IntRange.End);
7777

7878
}

0 commit comments

Comments
 (0)