├── .documentation
├── .gitignore
├── docfx.json
├── index.md
└── toc.yml
├── .github
├── icon.png
├── logo.png
├── postgrest-relationship-example.drawio
├── postgrest-relationship-example.drawio.png
└── workflows
│ ├── build-and-test.yml
│ ├── build-documentation.yaml
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Postgrest
├── Attributes
│ ├── ColumnAttribute.cs
│ ├── PrimaryKeyAttribute.cs
│ ├── ReferenceAttribute.cs
│ └── TableAttribute.cs
├── Client.cs
├── ClientOptions.cs
├── Constants.cs
├── Converters
│ ├── DateTimeConverter.cs
│ ├── IntConverter.cs
│ └── RangeConverter.cs
├── Debugger.cs
├── Exceptions
│ ├── FailureHint.cs
│ └── PostgrestException.cs
├── Extensions
│ ├── EnumExtensions.cs
│ ├── RangeExtensions.cs
│ ├── TypeExtensions.cs
│ └── UriExtensions.cs
├── Helpers.cs
├── Hooks.cs
├── IntRange.cs
├── Interfaces
│ ├── IPostgrestCacheProvider.cs
│ ├── IPostgrestClient.cs
│ ├── IPostgrestDebugger.cs
│ ├── IPostgrestQueryFilter.cs
│ ├── IPostgrestTable.cs
│ └── IPostgrestTableWithCache.cs
├── Linq
│ ├── SelectExpressionVisitor.cs
│ ├── SetExpressionVisitor.cs
│ └── WhereExpressionVisitor.cs
├── Models
│ ├── BaseModel.cs
│ └── CachedModel.cs
├── Postgrest.csproj
├── PostgrestContractResolver.cs
├── QueryFilter.cs
├── QueryOptions.cs
├── QueryOrderer.cs
├── Requests
│ └── CacheBackedRequest.cs
├── Responses
│ ├── BaseResponse.cs
│ └── ModeledResponse.cs
├── Table.cs
└── TableWithCache.cs
├── PostgrestExample
├── Models
│ ├── Message.cs
│ ├── Movie.cs
│ └── User.cs
├── PostgrestExample.csproj
└── Program.cs
├── PostgrestTests
├── ClientTests.cs
├── CoercionTests.cs
├── ConverterTests.cs
├── ExceptionTests.cs
├── ExtensionTests.cs
├── Helpers.cs
├── LinqTests.cs
├── Models
│ ├── ForeignKeyTestModel.cs
│ ├── KitchenSink.cs
│ ├── LinkedModels.cs
│ ├── Message.cs
│ ├── NestedForeignKeyTestModel.cs
│ ├── Stub.cs
│ ├── Todo.cs
│ └── User.cs
├── PostgrestTests.csproj
├── ReferenceTests.cs
├── TableWithCacheTests.cs
└── db
│ ├── 00-schema.sql
│ └── 01-dummy-data.sql
├── README.md
├── Supabase.Postgrest.sln
└── docker-compose.yml
/.documentation/.gitignore:
--------------------------------------------------------------------------------
1 | ###############
2 | # folder #
3 | ###############
4 | /**/DROP/
5 | /**/TEMP/
6 | /**/packages/
7 | /**/bin/
8 | /**/obj/
9 | /_site
10 | /api
11 |
--------------------------------------------------------------------------------
/.documentation/docfx.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": [
3 | {
4 | "src": [
5 | {
6 | "src": "../Postgrest",
7 | "files": [
8 | "**/*.csproj"
9 | ]
10 | }
11 | ],
12 | "dest": "api"
13 | }
14 | ],
15 | "build": {
16 | "content": [
17 | {
18 | "files": [
19 | "**/*.{md,yml}"
20 | ],
21 | "exclude": [
22 | "_site/**"
23 | ]
24 | }
25 | ],
26 | "resource": [
27 | {
28 | "files": [
29 | "images/**"
30 | ]
31 | }
32 | ],
33 | "output": "_site",
34 | "template": [
35 | "default",
36 | "modern"
37 | ],
38 | "globalMetadata": {
39 | "_appName": "postgrest-csharp",
40 | "_appTitle": "postgrest-csharp",
41 | "_enableSearch": true
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/.documentation/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | _layout: landing
3 | ---
4 |
5 | # postgrest-csharp
6 |
7 | 
8 | [](https://www.nuget.org/packages/postgrest-csharp/)
9 |
10 | Postgrest-csharp is written primarily as a helper library for [supabase/supabase-csharp](https://github.com/supabase/supabase-csharp), however, it should be easy enough to use outside of the supabase ecosystem.
11 |
12 | The bulk of this library is a translation and c-sharp-ification of the [supabase/postgrest-js](https://github.com/supabase/postgrest-js) library.
13 |
--------------------------------------------------------------------------------
/.documentation/toc.yml:
--------------------------------------------------------------------------------
1 | - name: API
2 | href: api/
3 |
--------------------------------------------------------------------------------
/.github/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase-community/postgrest-csharp/879920a16211b41e97bf2c0dc96aba8aa21e1f85/.github/icon.png
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase-community/postgrest-csharp/879920a16211b41e97bf2c0dc96aba8aa21e1f85/.github/logo.png
--------------------------------------------------------------------------------
/.github/postgrest-relationship-example.drawio:
--------------------------------------------------------------------------------
1 | 7Vldb9owFP01SN1DUb7I6GP5ah82TWonbX1CJrkkXh07cxxK9utnxw5JSKmoNChDvLTxude+1/c4xxfoueNkfcdRGn9lIZCeY4XrnjvpOY7vePKvAgoN3Fg3Gog4DjVk18Aj/gMGtAya4xCylqNgjAictsGAUQqBaGGIc/bSdlsy0o6aogg6wGOASBf9gUMRa3To+DV+DziKq8i2b/a3QMFzxFlOTTzKKGhLgqplzB6zGIXspQG505475owJ/ZSsx0BUVauK6XmzHdZNyhyo2GcCG0/49+CJezd3eTF7uCuiVXFt25/1OitEclMM2u/3qclZFFWJIJQVM0PGRcwiRhGZ1uioLAOoaJYc1T5fGEslaEvwFwhRGPpRLpiEYpEQY80E4uJW0SmB6UOCaFGhM0yIWRho2PGRWMND564S3lkqA2Us54Hx2hw8xCMQb9bM29AnXwhgCQheyJkcCBJ41Y6KzNGMNn41R/LB0PQuyoYdymxJmf0RlMmC8uLnhhk5eFKD/qAaTtZN46Qwoy7V4TcKe7Bt3I5M+OBIhFdrNKjtOT6RyY3SFrf+71zpxiiRuWPac2+l1UrXZS0sDV4LxaEyeJVBwFpcI4IjMyOQ5QHesIUQMC63xIyDOh2c4LLkdVD5FJn/ZWqZ4IxGFZrmC4KDfgo8k8sYF7nxtlfDkG5jMd9G3r95AkuhLUNj2c5e1vyNHA4RcYl5JuYUJXDsyAR9UOCAAxIQzpF4PfKWYq2ACyyv5Vt9RiflER6ZEzvR4UZMei1JKQnLUgNGS0aFkSjbMeMZSjBRcnMPZAVq1e3LhrNnGDPCeBnbtSzX9X1lqS5qu+SMkIbTxJvas4EJ0Z28S3bUvmDdgLpaYqye6RZMI+VW3cNL3ZbYvsHiZktiWQe7b/xLi2DkeXByLUKV0hncGAlb4aZAXS6Mi2L/f4ote3Hf20+zh8O+6x9Mtb3zEob5paHsRNSFKeOiRL13dJGl2t4ac1iCPOEBZGp1NekKh5+Ona0m8JV0W+lpr4/Ir1p70TktR6sQx9JXacszFFeqUOOKsHk12lTx0775nqNid+T5FRHfqdj2oKXY9qDbY2++mWz12PbheuzzaeNSziShp9zImQzLV8o6EfGBBGFy6fVOXDmcmz0+ng//0cdzOax/HShtjR9f3Olf
--------------------------------------------------------------------------------
/.github/postgrest-relationship-example.drawio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase-community/postgrest-csharp/879920a16211b41e97bf2c0dc96aba8aa21e1f85/.github/postgrest-relationship-example.drawio.png
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | build-and-test:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Setup .NET
16 | uses: actions/setup-dotnet@v3
17 | with:
18 | dotnet-version: 8.x
19 |
20 | - name: Restore dependencies
21 | run: dotnet restore
22 |
23 | - name: Build
24 | run: dotnet build --configuration Release --no-restore
25 |
26 | - name: Initialize Testing Stack
27 | run: docker compose up -d
28 |
29 | - name: Test
30 | run: dotnet test --no-restore
31 |
--------------------------------------------------------------------------------
/.github/workflows/build-documentation.yaml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy Documentation
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release/* # Default release branch
8 |
9 | jobs:
10 | docs:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | with:
15 | persist-credentials: false
16 |
17 | - name: Setup .NET
18 | uses: actions/setup-dotnet@v3
19 | with:
20 | dotnet-version: 8.x
21 |
22 | - name: Install docfx
23 | run: dotnet tool update -g docfx
24 |
25 | - name: Build documentation
26 | run: docfx .documentation/docfx.json
27 |
28 | - name: Deploy 🚀
29 | uses: JamesIves/github-pages-deploy-action@v4
30 | with:
31 | folder: .documentation/_site
32 | token: ${{ secrets.GITHUB_TOKEN }}
33 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Publish NuGet Package
2 |
3 | on:
4 | push:
5 | branches:
6 | - release/* # Default release branch
7 |
8 | jobs:
9 | publish:
10 | name: build, pack & publish
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Setup .NET
16 | uses: actions/setup-dotnet@v3
17 | with:
18 | dotnet-version: 8.x
19 |
20 | - name: Wait for tests to succeed
21 | uses: lewagon/wait-on-check-action@v1.3.1
22 | with:
23 | ref: ${{ github.ref }}
24 | check-name: build-and-test
25 | repo-token: ${{ secrets.GITHUB_TOKEN }}
26 | wait-interval: 10
27 |
28 | - name: Restore dependencies
29 | run: dotnet restore
30 |
31 | - name: Build
32 | run: dotnet build ./Postgrest/Postgrest.csproj --configuration Release --no-restore
33 |
34 | - name: Generate package
35 | run: dotnet pack ./Postgrest/Postgrest.csproj --configuration Release
36 |
37 | - name: Publish on version change
38 | run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # globs
2 | Makefile.in
3 | *.userprefs
4 | *.usertasks
5 | config.make
6 | config.status
7 | aclocal.m4
8 | install-sh
9 | autom4te.cache/
10 | *.tar.gz
11 | tarballs/
12 | test-results/
13 |
14 | # Mac bundle stuff
15 | *.dmg
16 | *.app
17 |
18 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
19 | # General
20 | .DS_Store
21 | .AppleDouble
22 | .LSOverride
23 |
24 | # Icon must end with two \r
25 | Icon
26 |
27 |
28 | # Thumbnails
29 | ._*
30 |
31 | # Files that might appear in the root of a volume
32 | .DocumentRevisions-V100
33 | .fseventsd
34 | .Spotlight-V100
35 | .TemporaryItems
36 | .Trashes
37 | .VolumeIcon.icns
38 | .com.apple.timemachine.donotpresent
39 |
40 | # Directories potentially created on remote AFP share
41 | .AppleDB
42 | .AppleDesktop
43 | Network Trash Folder
44 | Temporary Items
45 | .apdisk
46 |
47 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
48 | # Windows thumbnail cache files
49 | Thumbs.db
50 | ehthumbs.db
51 | ehthumbs_vista.db
52 |
53 | # Dump file
54 | *.stackdump
55 |
56 | # Folder config file
57 | [Dd]esktop.ini
58 |
59 | # Recycle Bin used on file shares
60 | $RECYCLE.BIN/
61 |
62 | # Windows Installer files
63 | *.cab
64 | *.msi
65 | *.msix
66 | *.msm
67 | *.msp
68 |
69 | # Windows shortcuts
70 | *.lnk
71 |
72 | # content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
73 | ## Ignore Visual Studio temporary files, build results, and
74 | ## files generated by popular Visual Studio add-ons.
75 | ##
76 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
77 |
78 | # User-specific files
79 | *.suo
80 | *.user
81 | *.userosscache
82 | *.sln.docstates
83 |
84 | # User-specific files (MonoDevelop/Xamarin Studio)
85 | *.userprefs
86 |
87 | # Build results
88 | [Dd]ebug/
89 | [Dd]ebugPublic/
90 | [Rr]elease/
91 | [Rr]eleases/
92 | x64/
93 | x86/
94 | bld/
95 | [Bb]in/
96 | [Oo]bj/
97 | [Ll]og/
98 |
99 | # Visual Studio 2015/2017 cache/options directory
100 | .vs/
101 | # Uncomment if you have tasks that create the project's static files in wwwroot
102 | #wwwroot/
103 |
104 | # Visual Studio 2017 auto generated files
105 | Generated\ Files/
106 |
107 | # MSTest test Results
108 | [Tt]est[Rr]esult*/
109 | [Bb]uild[Ll]og.*
110 |
111 | # NUNIT
112 | *.VisualState.xml
113 | TestResult.xml
114 |
115 | # Build Results of an ATL Project
116 | [Dd]ebugPS/
117 | [Rr]eleasePS/
118 | dlldata.c
119 |
120 | # Benchmark Results
121 | BenchmarkDotNet.Artifacts/
122 |
123 | # .NET Core
124 | project.lock.json
125 | project.fragment.lock.json
126 | artifacts/
127 |
128 | # StyleCop
129 | StyleCopReport.xml
130 |
131 | # Files built by Visual Studio
132 | *_i.c
133 | *_p.c
134 | *_h.h
135 | *.ilk
136 | *.meta
137 | *.obj
138 | *.iobj
139 | *.pch
140 | *.pdb
141 | *.ipdb
142 | *.pgc
143 | *.pgd
144 | *.rsp
145 | *.sbr
146 | *.tlb
147 | *.tli
148 | *.tlh
149 | *.tmp
150 | *.tmp_proj
151 | *_wpftmp.csproj
152 | *.log
153 | *.vspscc
154 | *.vssscc
155 | .builds
156 | *.pidb
157 | *.svclog
158 | *.scc
159 |
160 | # Chutzpah Test files
161 | _Chutzpah*
162 |
163 | # Visual C++ cache files
164 | ipch/
165 | *.aps
166 | *.ncb
167 | *.opendb
168 | *.opensdf
169 | *.sdf
170 | *.cachefile
171 | *.VC.db
172 | *.VC.VC.opendb
173 |
174 | # Visual Studio profiler
175 | *.psess
176 | *.vsp
177 | *.vspx
178 | *.sap
179 |
180 | # Visual Studio Trace Files
181 | *.e2e
182 |
183 | # TFS 2012 Local Workspace
184 | $tf/
185 |
186 | # Guidance Automation Toolkit
187 | *.gpState
188 |
189 | # ReSharper is a .NET coding add-in
190 | _ReSharper*/
191 | *.[Rr]e[Ss]harper
192 | *.DotSettings.user
193 |
194 | # JustCode is a .NET coding add-in
195 | .JustCode
196 |
197 | # TeamCity is a build add-in
198 | _TeamCity*
199 |
200 | # DotCover is a Code Coverage Tool
201 | *.dotCover
202 |
203 | # AxoCover is a Code Coverage Tool
204 | .axoCover/*
205 | !.axoCover/settings.json
206 |
207 | # Visual Studio code coverage results
208 | *.coverage
209 | *.coveragexml
210 |
211 | # NCrunch
212 | _NCrunch_*
213 | .*crunch*.local.xml
214 | nCrunchTemp_*
215 |
216 | # MightyMoose
217 | *.mm.*
218 | AutoTest.Net/
219 |
220 | # Web workbench (sass)
221 | .sass-cache/
222 |
223 | # Installshield output folder
224 | [Ee]xpress/
225 |
226 | # DocProject is a documentation generator add-in
227 | DocProject/buildhelp/
228 | DocProject/Help/*.HxT
229 | DocProject/Help/*.HxC
230 | DocProject/Help/*.hhc
231 | DocProject/Help/*.hhk
232 | DocProject/Help/*.hhp
233 | DocProject/Help/Html2
234 | DocProject/Help/html
235 |
236 | # Click-Once directory
237 | publish/
238 |
239 | # Publish Web Output
240 | *.[Pp]ublish.xml
241 | *.azurePubxml
242 | # Note: Comment the next line if you want to checkin your web deploy settings,
243 | # but database connection strings (with potential passwords) will be unencrypted
244 | *.pubxml
245 | *.publishproj
246 |
247 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
248 | # checkin your Azure Web App publish settings, but sensitive information contained
249 | # in these scripts will be unencrypted
250 | PublishScripts/
251 |
252 | # NuGet Packages
253 | *.nupkg
254 | # The packages folder can be ignored because of Package Restore
255 | **/[Pp]ackages/*
256 | # except build/, which is used as an MSBuild target.
257 | !**/[Pp]ackages/build/
258 | # Uncomment if necessary however generally it will be regenerated when needed
259 | #!**/[Pp]ackages/repositories.config
260 | # NuGet v3's project.json files produces more ignorable files
261 | *.nuget.props
262 | *.nuget.targets
263 |
264 | # Microsoft Azure Build Output
265 | csx/
266 | *.build.csdef
267 |
268 | # Microsoft Azure Emulator
269 | ecf/
270 | rcf/
271 |
272 | # Windows Store app package directories and files
273 | AppPackages/
274 | BundleArtifacts/
275 | Package.StoreAssociation.xml
276 | _pkginfo.txt
277 | *.appx
278 |
279 | # Visual Studio cache files
280 | # files ending in .cache can be ignored
281 | *.[Cc]ache
282 | # but keep track of directories ending in .cache
283 | !*.[Cc]ache/
284 |
285 | # Others
286 | ClientBin/
287 | ~$*
288 | *~
289 | *.dbmdl
290 | *.dbproj.schemaview
291 | *.jfm
292 | *.pfx
293 | *.publishsettings
294 | orleans.codegen.cs
295 |
296 | # Including strong name files can present a security risk
297 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
298 | #*.snk
299 |
300 | # Since there are multiple workflows, uncomment next line to ignore bower_components
301 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
302 | #bower_components/
303 |
304 | # RIA/Silverlight projects
305 | Generated_Code/
306 |
307 | # Backup & report files from converting an old project file
308 | # to a newer Visual Studio version. Backup files are not needed,
309 | # because we have git ;-)
310 | _UpgradeReport_Files/
311 | Backup*/
312 | UpgradeLog*.XML
313 | UpgradeLog*.htm
314 | ServiceFabricBackup/
315 | *.rptproj.bak
316 |
317 | # SQL Server files
318 | *.mdf
319 | *.ldf
320 | *.ndf
321 |
322 | # Business Intelligence projects
323 | *.rdl.data
324 | *.bim.layout
325 | *.bim_*.settings
326 | *.rptproj.rsuser
327 |
328 | # Microsoft Fakes
329 | FakesAssemblies/
330 |
331 | # GhostDoc plugin setting file
332 | *.GhostDoc.xml
333 |
334 | # Node.js Tools for Visual Studio
335 | .ntvs_analysis.dat
336 | node_modules/
337 |
338 | # Visual Studio 6 build log
339 | *.plg
340 |
341 | # Visual Studio 6 workspace options file
342 | *.opt
343 |
344 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
345 | *.vbw
346 |
347 | # Visual Studio LightSwitch build output
348 | **/*.HTMLClient/GeneratedArtifacts
349 | **/*.DesktopClient/GeneratedArtifacts
350 | **/*.DesktopClient/ModelManifest.xml
351 | **/*.Server/GeneratedArtifacts
352 | **/*.Server/ModelManifest.xml
353 | _Pvt_Extensions
354 |
355 | # Paket dependency manager
356 | .paket/paket.exe
357 | paket-files/
358 |
359 | # FAKE - F# Make
360 | .fake/
361 |
362 | # JetBrains Rider
363 | .idea/
364 | *.sln.iml
365 |
366 | # CodeRush personal settings
367 | .cr/personal
368 |
369 | # Python Tools for Visual Studio (PTVS)
370 | __pycache__/
371 | *.pyc
372 |
373 | # Cake - Uncomment if you are using it
374 | # tools/**
375 | # !tools/packages.config
376 |
377 | # Tabs Studio
378 | *.tss
379 |
380 | # Telerik's JustMock configuration file
381 | *.jmconfig
382 |
383 | # BizTalk build output
384 | *.btp.cs
385 | *.btm.cs
386 | *.odx.cs
387 | *.xsd.cs
388 |
389 | # OpenCover UI analysis results
390 | OpenCover/
391 |
392 | # Azure Stream Analytics local run output
393 | ASALocalRun/
394 |
395 | # MSBuild Binary and Structured Log
396 | *.binlog
397 |
398 | # NVidia Nsight GPU debugger configuration file
399 | *.nvuser
400 |
401 | # MFractors (Xamarin productivity tool) working folder
402 | .mfractor/
403 |
404 | # Local History for Visual Studio
405 | .localhistory/
406 | /PostgrestTests/.runsettings
407 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 4.1.0 - 2025-02-03
4 |
5 | - Added count to ModeledResponse [#103](https://github.com/supabase-community/postgrest-csharp/pull/103) by [DanielW093]https://github.com/DanielW093
6 | - Add support for long, DateTime, and DateTimeOffset criteria in filter expressions [#101](https://github.com/supabase-community/postgrest-csharp/pull/101) by [sbarnes-ellenbytech](https://github.com/sbarnes-ellenbytech)
7 |
8 | ## 4.0.3 - 2024-05-23
9 |
10 | - Re: [#97](https://github.com/supabase-community/postgrest-csharp/pull/97) Fix set null value on string property.
11 | Thanks [@alustrement-bob](https://github.com/alustrement-bob)!
12 |
13 | ## 4.0.2 - 2024-05-16
14 |
15 | - Re: [#96](https://github.com/supabase-community/postgrest-csharp/pull/96) Set `ConfigureAwait(false)` the response to
16 | prevent deadlocking applications. Thanks [@pur3extreme](https://github.com/pur3extreme)!
17 |
18 | ## 4.0.1 - 2024-05-07
19 |
20 | - Re: [#92](https://github.com/supabase-community/postgrest-csharp/issues/92) Changes `IPostgrestTable<>` contract to
21 | return the interface rather than a concrete type.
22 |
23 | ## 4.0.0 - 2024-04-21
24 |
25 | - [MAJOR] Moves namespaces from `Postgrest` to `Supabase.Postgrest`
26 | - Re: [#135](https://github.com/supabase-community/supabase-csharp/issues/135) Update nuget package
27 | name `postgrest-csharp` to `Supabase.Postgrest`
28 |
29 | ## 3.5.1 - 2024-03-15
30 |
31 | - Re: [#147](https://github.com/supabase-community/supabase-csharp/issues/147) - Supports `Rpc` specifying a generic
32 | type for its return.
33 |
34 | ## 3.5.0 - 2024-01-14
35 |
36 | - Re: [#78](https://github.com/supabase-community/postgrest-csharp/issues/78), Generalize query filtering creation
37 | in `Table` so that it matches new generic signatures.
38 | - Move from `QueryFilter` parameters to a more generic `IPostgrestQueryFilter` to support constructing new QueryFilters
39 | from a LINQ expression.
40 | - Note: Lists of `QueryFilter`s will now need to be defined
41 | as: `new List { new QueryFilter(), ... }`
42 | - Adjust serialization of timestamps within a `QueryFilter` to support `DateTime` and `DateTimeOffset` using the
43 | ISO-8601 (https://stackoverflow.com/a/115002)
44 |
45 | ## 3.4.1 - 2024-01-08
46 |
47 | - Re: [#85](https://github.com/supabase-community/postgrest-csharp/issues/85) Fixes problem when using multiple .Order()
48 | methods by merging [#86](https://github.com/supabase-community/postgrest-csharp/pull/86).
49 | Thanks [@hunsra](https://github.com/hunsra)!
50 |
51 | ## 3.4.0 - 2024-01-03
52 |
53 | - Re: [#81](https://github.com/supabase-community/postgrest-csharp/issues/81)
54 | - [Minor] Removes `IgnoreOnInsert`and `IgnoreOnUpdate` from `ReferenceAttribute` as changing these properties
55 | to `false` does not currently provide the expected functionality.
56 | - Fixes `Insert` and `Update` not working on models that have `Reference` specified on a property with a non-null
57 | value.
58 |
59 | ## 3.3.0 - 2023-11-28
60 |
61 | - Re: [#78](https://github.com/supabase-community/postgrest-csharp/issues/78) Updates signatures for `Not` and `Filter`
62 | to include generic types for a better development experience.
63 | - Updates internal generic type names to be more descriptive.
64 | - Add support for LINQ predicates on `Table.Not()` signatures
65 |
66 | ## 3.2.10 - 2023-11-13
67 |
68 | - Re: [#76](https://github.com/supabase-community/postgrest-csharp/issues/76) Removes the incorrect `ToUniversalTime`
69 | conversion in the LINQ `Where` parser.
70 |
71 | ## 3.2.9 - 2023-10-09
72 |
73 | - Re: [supabase-csharp#115](https://github.com/supabase-community/supabase-csharp/discussions/115) Additional support
74 | for a model referencing another model with multiple foreign keys.
75 |
76 | ## 3.2.8 - 2023-10-08
77 |
78 | - Re: [supabase-csharp#115](https://github.com/supabase-community/supabase-csharp/discussions/115) Adds support for
79 | multiple references attached to the same model (foreign keys) on a single C# Model.
80 |
81 | ## 3.2.7 - 2023-09-15
82 |
83 | - Implements a `TableWithCache` for `Get` requests that can pull reactive Models from cache before making a remote
84 | request.
85 | - Re: [supabase-csharp#85](https://github.com/supabase-community/supabase-csharp/issues/85) Includes sourcelink support.
86 |
87 | ## 3.2.6 - 2023-09-04
88 |
89 | - Re: [#75](https://github.com/supabase-community/postgrest-csharp/pull/75) Fix issue with marshalling of stored
90 | procedure arguments. Big thank you to [@corrideat](https://github.com/corrideat)!
91 |
92 | ## 3.2.5 - 2023-07-13
93 |
94 | - Re: [supabase-community/supabase-csharp#81](https://github.com/supabase-community/supabase-csharp/discussions/81) -
95 | Clarifies `ReferenceAttribute` by changing `shouldFilterTopLevel` to `useInnerJoin` and adds an additional
96 | constructor for `ReferenceAttribute` with a shortcut for specifying the `JoinType`
97 |
98 | ## 3.2.4 - 2023-06-29
99 |
100 | - [#70](https://github.com/supabase-community/postgrest-csharp/pull/70) Minor Unity related fixes
101 |
102 | ## 3.2.3 - 2023-06-25
103 |
104 | - [#69](https://github.com/supabase-community/postgrest-csharp/pull/69) Locks language version to C#9
105 | - [#68](https://github.com/supabase-community/postgrest-csharp/pull/68) Makes RPC parameters optional
106 |
107 | Thanks [@wiverson](https://github.com/wiverson) for the work in this release!
108 |
109 | ## 3.2.2 - 2023-06-10
110 |
111 | - Uses new assembly name of `Supabase.Core`
112 |
113 | ## 3.2.1 - 2023-06-10
114 |
115 | - Changes Assembly output to be `Supabase.Postgrest`
116 |
117 | ## 3.2.0 - 2023-05-23
118 |
119 | - General codebase and QOL improvements. Exceptions are generally thrown through `PostgrestException` now instead
120 | of `Exception`. A `FailureHint.Reason` is provided with failures if possible to parse.
121 | - `AddDebugListener` is now available on the client to help with debugging
122 | - Merges [#65](https://github.com/supabase-community/postgrest-csharp/pull/65) Cleanup + Add better exception handling
123 | - Merges [#66](https://github.com/supabase-community/postgrest-csharp/pull/66) Local test Fixes
124 | - Fixes [#67](https://github.com/supabase-community/postgrest-csharp/issues/67) Postgrest Reference attribute is
125 | producing StackOverflow for circular references
126 |
127 | ## 3.1.3 - 2023-01-28
128 |
129 | - Fix [#61](https://github.com/supabase-community/postgrest-csharp/issues/61) which further typechecks nullable values.
130 |
131 | ## 3.1.2 - 2023-01-27
132 |
133 | - Fix [#61](https://github.com/supabase-community/postgrest-csharp/issues/61) which did not correctly parse Linq `Where`
134 | when encountering a nullable type.
135 | - Add missing support for transforming for `== null` and `!= null`
136 |
137 | ## 3.1.1 - 2023-01-17
138 |
139 | - Fix issue from supabase-community/supabase-csharp#48 where boolean model properties would not be evaluated in
140 | predicate expressions
141 |
142 | ## 3.1.0 - 2023-01-16
143 |
144 | - [Minor] Breaking API Change: `PrimaryKey` attribute defaults to `shouldInsert: false` as most uses will have the
145 | Database generate the primary key.
146 | - Merged [#60](https://github.com/supabase-community/postgrest-csharp/pull/60) which Added linq support
147 | for `Select`, `Where`, `OnConflict`, `Columns`, `Order`, `Update`, `Set`, and `Delete`
148 |
149 | ## 3.0.4 - 2022-11-22
150 |
151 | ## 3.0.3 - 2022-11-22
152 |
153 | - `GetHeaders` is now passed to `ModeledResponse` and `BaseModel` so that the default `Update` and `Delete` methods use
154 | the latest credentials
155 | - `GetHeaders` is used in `Rpc` calls (re: [#39](https://github.com/supabase-community/supabase-csharp/issues/39))
156 |
157 | ## 3.0.2 - 2022-11-12
158 |
159 | - `IPostgrestClient` and `IPostgrestAPI` now implement `IGettableHeaders`
160 |
161 | ## 3.0.1 - 2022-11-10
162 |
163 | - Make `SerializerSettings` publicly accessible.
164 |
165 | ## 3.0.0 - 2022-11-08
166 |
167 | - Re: [#54](https://github.com/supabase-community/postgrest-csharp/pull/54) Restructure Project to support DI and enable
168 | Nullity
169 | - `Client` is no longer a singleton class.
170 | - `StatelessClient` has been removed as `Client` performs the same essential functions.
171 | - `Table` default constructor requires reference to `JsonSerializerSettings`
172 | - `BaseModel` now keeps track of `BaseUrl` and `RequestClientOptions`. These are now used in the default (and
173 | overridable) `BaseModel.Update` and `BaseModel.Delete` methods (as they previously referenced the singleton).
174 | - All publicly facing classes (that offer functionality) now include an Interface.
175 | - `RequestException` is no longer thrown for attempting to update a record that does not exist, instead an
176 | empty `ModeledResponse` is returned.
177 |
178 | ## 2.1.1 - 2022-10-19
179 |
180 | -
181 |
182 | Re: [#50](https://github.com/supabase-community/postgrest-csharp/issues/50) & [#51](https://github.com/supabase-community/postgrest-csharp/pull/51)
183 | Adds `shouldFilterTopRows` as constructor parameter for `ReferenceAttribute` which defaults to `true` to match current
184 | API expectations.
185 |
186 | ## 2.1.0 - 2022-10-11
187 |
188 | - [Minor] Breaking API change: Remove `BaseModel.PrimaryKeyValue` and `BaseModel.PrimaryKeyColumn` in favor of
189 | a `PrimaryKey` dictionary with support for composite keys.
190 | - Re: [#48](https://github.com/supabase-community/postgrest-csharp/issues/48) - Add support for derived models
191 | on `ReferenceAttribute`
192 | - Re: [#49](https://github.com/supabase-community/postgrest-csharp/issues/49) - Added `Match(T model)`
193 |
194 | ## 2.0.12 - 2022-09-13
195 |
196 | - Merged [#47](https://github.com/supabase-community/postgrest-csharp/pull/47) which added cancellation token support
197 | to `Table` methods. Thanks [@devpikachu](https://github.com/devpikachu)!
198 |
199 | ## 2.0.11 - 2022-08-01
200 |
201 | - Additional `OnConflict` Access via `QueryOptions` with reference
202 | to [supabase-community/supabase-csharp#29](https://github.com/supabase-community/supabase-csharp/issues/29)
203 |
204 | ## 2.0.10 - 2022-08-01
205 |
206 | - Added `OnConflict` parameter for UNIQUE resolution with reference
207 | to [supabase-community/supabase-csharp#29](https://github.com/supabase-community/supabase-csharp/issues/29)
208 |
209 | ## 2.0.9 - 2022-07-17
210 |
211 | - Merged [#44](https://github.com/supabase-community/postgrest-csharp/pull/44) Fixing zero length content when sending
212 | requests without body. Thanks [@SameerOmar](https://github.com/sameeromar)!
213 |
214 | ## 2.0.8 - 2022-05-24
215 |
216 | - Implements [#41](https://github.com/supabase-community/postgrest-csharp/issues/41), which adds support for `infinity`
217 | and `-infinity` as readable values.
218 |
219 | ## 2.0.7 - 2022-04-09
220 |
221 | - Merged [#39](https://github.com/supabase-community/postgrest-csharp/pull/39), which a fixed shadowed variable
222 | in `Table.And` and `Table.Or`. Thanks [@erichards3](https://github.com/erichards3)!
223 |
224 | ## 2.0.6 - 2021-12-30
225 |
226 | - Fix for [#38](https://github.com/supabase-community/postgrest-csharp/issues/38), Add support for `NullValueHandling`
227 | to be specified on a `Column` Attribute and for it to be honored on Inserts and Updates. Defaults
228 | to: `NullValueHandling.Include`.
229 |
230 | ## 2.0.5 - 2021-12-26
231 |
232 | - Fix for [#37](https://github.com/supabase-community/postgrest-csharp/issues/37) - Fixes #37 - Return Type `minimal`
233 | would fail to resolve because of incorrect `Accept` headers. Added header and test to verify for future.
234 |
235 | ## 2.0.4 - 2021-12-26
236 |
237 | - Fix for [#36](https://github.com/supabase-community/postgrest-csharp/issues/36) - Inserting/Upserting bulk records
238 | would fail while doing an unnecessary generic coercion.
239 |
240 | ## 2.0.3 - 2021-11-26
241 |
242 | - Add a `StatelessClient` static class (re: [#7](https://github.com/supabase-community/supabase-csharp/issues/7)) that
243 | enables API interactions through specifying `StatelessClientOptions`
244 | - Fix for [#35](https://github.com/supabase-community/postgrest-csharp/issues/35) - Client now handles DateTime[]
245 | serialization and deserialization.
246 | - Added tests for `StatelessClient`
247 | - Added "Kitchen Sink" tests for roundtrip serialization and deserialization data coersion.
248 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Joseph Schultz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Postgrest/Attributes/ColumnAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using Newtonsoft.Json;
4 | namespace Supabase.Postgrest.Attributes
5 | {
6 |
7 | ///
8 | /// Used to map a C# property to a Postgrest Column.
9 | ///
10 | ///
11 | ///
12 | /// class User : BaseModel {
13 | /// [ColumnName("firstName")]
14 | /// public string FirstName {get; set;}
15 | /// }
16 | ///
17 | ///
18 | [AttributeUsage(AttributeTargets.Property)]
19 | public class ColumnAttribute : Attribute
20 | {
21 | ///
22 | /// The name in postgres of this column.
23 | ///
24 | public string ColumnName { get; }
25 |
26 | ///
27 | /// Specifies what should be serialized in the event this column's value is NULL
28 | ///
29 | public NullValueHandling NullValueHandling { get; set; }
30 |
31 | ///
32 | /// If the performed query is an Insert or Upsert, should this value be ignored?
33 | ///
34 | public bool IgnoreOnInsert { get; }
35 |
36 | ///
37 | /// If the performed query is an Update, should this value be ignored?
38 | ///
39 | public bool IgnoreOnUpdate { get; }
40 |
41 | ///
42 | public ColumnAttribute([CallerMemberName] string? columnName = null, NullValueHandling nullValueHandling = NullValueHandling.Include, bool ignoreOnInsert = false, bool ignoreOnUpdate = false)
43 | {
44 | ColumnName = columnName!; // Will either be user specified or given by runtime compiler.
45 | NullValueHandling = nullValueHandling;
46 | IgnoreOnInsert = ignoreOnInsert;
47 | IgnoreOnUpdate = ignoreOnUpdate;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Postgrest/Attributes/PrimaryKeyAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | #pragma warning disable CS1591
4 |
5 | namespace Supabase.Postgrest.Attributes
6 | {
7 |
8 | ///
9 | /// Used to map a C# property to a Postgrest PrimaryKey.
10 | ///
11 | ///
12 | ///
13 | /// class User : BaseModel {
14 | /// [PrimaryKey("id")]
15 | /// public string Id {get; set;}
16 | /// }
17 | ///
18 | ///
19 | [AttributeUsage(AttributeTargets.Property)]
20 | public class PrimaryKeyAttribute : Attribute
21 | {
22 | public string ColumnName { get; }
23 |
24 | ///
25 | /// Would be set to false in the event that the database handles the generation of this property.
26 | ///
27 | public bool ShouldInsert { get; }
28 |
29 | public PrimaryKeyAttribute([CallerMemberName] string? columnName = null, bool shouldInsert = false)
30 | {
31 | ColumnName = columnName!; // Either given by user or specified by runtime compiler.
32 | ShouldInsert = shouldInsert;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Postgrest/Attributes/ReferenceAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using Supabase.Postgrest.Extensions;
6 | using Supabase.Postgrest.Exceptions;
7 | using Supabase.Postgrest.Models;
8 |
9 | namespace Supabase.Postgrest.Attributes
10 | {
11 | ///
12 | /// Used to specify that a foreign key relationship exists in PostgreSQL
13 | ///
14 | /// See: https://postgrest.org/en/stable/api.html#resource-embedding
15 | ///
16 | [AttributeUsage(AttributeTargets.Property)]
17 | public class ReferenceAttribute : Attribute
18 | {
19 | ///
20 | /// Specifies the Join type on this reference. PostgREST only allows for a LEFT join and an INNER join.
21 | ///
22 | public enum JoinType
23 | {
24 | ///
25 | /// INNER JOIN: returns rows when there is a match on both the source and the referenced tables.
26 | ///
27 | Inner,
28 |
29 | ///
30 | /// LEFT JOIN: returns all rows from the source table, even if there are no matches in the referenced table
31 | ///
32 | Left
33 | }
34 |
35 | ///
36 | /// Type of the model referenced
37 | ///
38 | public Type Model { get; }
39 |
40 | ///
41 | /// Column this attribute references as specified in Postgres, DOES NOT need to be set if is set.
42 | ///
43 | public string? ColumnName { get; private set; }
44 |
45 | ///
46 | /// The explicit SQL defined foreign key that this references.
47 | ///
48 | public string? ForeignKey { get; private set; }
49 |
50 | ///
51 | /// Table name of model
52 | ///
53 | public string TableName { get; }
54 |
55 | ///
56 | /// Columns that exist on the model we will select from.
57 | ///
58 | public List Columns { get; private set; } = new();
59 |
60 | ///
61 | /// If the performed query is an Insert or Upsert, should this value be ignored? (DEFAULT TRUE)
62 | ///
63 | public bool IgnoreOnInsert { get; private set; }
64 |
65 | ///
66 | /// If the performed query is an Update, should this value be ignored? (DEFAULT TRUE)
67 | ///
68 | public bool IgnoreOnUpdate { get; private set; }
69 |
70 | ///
71 | /// If Reference should automatically be included in queries on this reference. (DEFAULT TRUE)
72 | ///
73 | public bool IncludeInQuery { get; }
74 |
75 | ///
76 | /// As to whether the query will filter top-level rows.
77 | ///
78 | /// See: https://postgrest.org/en/stable/api.html#resource-embedding
79 | ///
80 | public bool UseInnerJoin { get; }
81 |
82 | /// Establishes a reference between two tables
83 | /// Model referenced
84 | /// Should referenced be included in queries?
85 | /// Specifies the join type for this relationship
86 | /// Column this attribute references as specified in Postgres, DOES NOT need to be set if <see cref="ForeignKey"/> is set.
87 | /// Foreign Key this attribute references as specified in Postgres (only required if the model references the same table multiple times)
88 | ///
89 | public ReferenceAttribute(Type model, JoinType joinType, bool includeInQuery = true,
90 | [CallerMemberName] string columnName = "", string? foreignKey = null)
91 | : this(model, includeInQuery, joinType == JoinType.Inner, columnName,
92 | foreignKey)
93 | {
94 | }
95 |
96 | /// Establishes a reference between two tables
97 | /// Model referenced
98 | /// Should referenced be included in queries?
99 | /// As to whether the query will filter top-level rows.
100 | /// Column this attribute references as specified in Postgres, DOES NOT need to be set if is set.
101 | /// Foreign Key this attribute references as specified in Postgres (only required if the model references the same table multiple times)
102 | ///
103 | public ReferenceAttribute(Type model, bool includeInQuery = true, bool useInnerJoin = true,
104 | [CallerMemberName] string? columnName = null, string? foreignKey = null)
105 | {
106 | if (!IsDerivedFromBaseModel(model))
107 | throw new PostgrestException("ReferenceAttribute must be used with Postgrest BaseModels.")
108 | { Reason = FailureHint.Reason.InvalidArgument };
109 |
110 | Model = model;
111 | IncludeInQuery = includeInQuery;
112 | IgnoreOnInsert = true;
113 | IgnoreOnUpdate = true;
114 | ColumnName = columnName;
115 | UseInnerJoin = useInnerJoin;
116 | ForeignKey = foreignKey;
117 |
118 | var attr = GetCustomAttribute(model, typeof(TableAttribute));
119 | TableName = attr is TableAttribute tableAttr ? tableAttr.Name : model.Name;
120 | }
121 |
122 | internal void ParseProperties(List? seenRefs = null)
123 | {
124 | seenRefs ??= new List();
125 |
126 | ParseColumns(ref seenRefs);
127 | ParseRelationships(seenRefs);
128 | }
129 |
130 | private void ParseColumns(ref List seenRefs)
131 | {
132 | foreach (var property in Model.GetProperties())
133 | {
134 | var attrs = property.GetCustomAttributes(true);
135 |
136 | foreach (var item in attrs)
137 | {
138 | switch (item)
139 | {
140 | case ColumnAttribute colAttr:
141 | Columns.Add(colAttr.ColumnName);
142 | break;
143 | case PrimaryKeyAttribute pkAttr:
144 | Columns.Add(pkAttr.ColumnName);
145 | break;
146 | }
147 | }
148 | }
149 | }
150 |
151 | ///
152 | public override bool Equals(object? obj)
153 | {
154 | if (obj is ReferenceAttribute attribute)
155 | {
156 | return TableName == attribute.TableName && ColumnName == attribute.ColumnName &&
157 | Model == attribute.Model;
158 | }
159 |
160 | return false;
161 | }
162 |
163 |
164 | ///
165 | /// Parses relationships that exist on this model. Called by
166 | ///
167 | ///
168 | private void ParseRelationships(List seenRefs)
169 | {
170 | foreach (var property in Model.GetProperties())
171 | {
172 | var attrs = property.GetCustomAttributes(true);
173 |
174 | foreach (var attr in attrs)
175 | {
176 | if (attr is not ReferenceAttribute { IncludeInQuery: true } refAttr) continue;
177 |
178 | if (seenRefs.FirstOrDefault(r => r.Equals(refAttr)) != null) continue;
179 |
180 | seenRefs.Add(refAttr);
181 | refAttr.ParseProperties(seenRefs);
182 |
183 | if (!string.IsNullOrEmpty(refAttr.ForeignKey))
184 | {
185 | Columns.Add(UseInnerJoin
186 | ? $"{refAttr.ColumnName}:{refAttr.ForeignKey}!inner({string.Join(",", refAttr.Columns.ToArray())})"
187 | : $"{refAttr.ColumnName}:{refAttr.ForeignKey}({string.Join(",", refAttr.Columns.ToArray())})");
188 | }
189 | else
190 | {
191 | Columns.Add(UseInnerJoin
192 | ? $"{refAttr.TableName}!inner({string.Join(",", refAttr.Columns.ToArray())})"
193 | : $"{refAttr.TableName}({string.Join(",", refAttr.Columns.ToArray())})");
194 | }
195 | }
196 | }
197 | }
198 |
199 | private static bool IsDerivedFromBaseModel(Type type) =>
200 | type.GetInheritanceHierarchy().Any(t => t == typeof(BaseModel));
201 | }
202 | }
--------------------------------------------------------------------------------
/Postgrest/Attributes/TableAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | #pragma warning disable CS1591
3 | namespace Supabase.Postgrest.Attributes
4 | {
5 |
6 | ///
7 | /// Used to map a C# Model to a Postgres Table.
8 | ///
9 | ///
10 | ///
11 | /// [Table("user")]
12 | /// class User : BaseModel {
13 | /// [ColumnName("firstName")]
14 | /// public string FirstName {get; set;}
15 | /// }
16 | ///
17 | ///
18 | [AttributeUsage(AttributeTargets.Class)]
19 | public class TableAttribute : Attribute
20 | {
21 | public string Name { get; set; }
22 |
23 | public TableAttribute(string tableName)
24 | {
25 | Name = tableName;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Postgrest/Client.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Converters;
7 | using Supabase.Core.Extensions;
8 | using Supabase.Postgrest.Interfaces;
9 | using Supabase.Postgrest.Models;
10 | using Supabase.Postgrest.Responses;
11 |
12 | namespace Supabase.Postgrest
13 | {
14 | ///
15 | public class Client : IPostgrestClient
16 | {
17 | ///
18 | /// Custom Serializer resolvers and converters that will be used for encoding and decoding Postgrest JSON responses.
19 | ///
20 | /// By default, Postgrest seems to use a date format that C# and Newtonsoft do not like, so this initial
21 | /// configuration handles that.
22 | ///
23 | public static JsonSerializerSettings SerializerSettings(ClientOptions? options = null)
24 | {
25 | options ??= new ClientOptions();
26 |
27 | return new JsonSerializerSettings
28 | {
29 | ContractResolver = new PostgrestContractResolver(),
30 | Converters =
31 | {
32 | // 2020-08-28T12:01:54.763231
33 | new IsoDateTimeConverter
34 | {
35 | DateTimeStyles = options.DateTimeStyles,
36 | DateTimeFormat = ClientOptions.DATE_TIME_FORMAT
37 | }
38 | }
39 | };
40 | }
41 |
42 | ///
43 | public string BaseUrl { get; }
44 |
45 | ///
46 | public ClientOptions Options { get; }
47 |
48 | ///
49 | public void AddRequestPreparedHandler(OnRequestPreparedEventHandler handler) =>
50 | Hooks.Instance.AddRequestPreparedHandler(handler);
51 |
52 | ///
53 | public void RemoveRequestPreparedHandler(OnRequestPreparedEventHandler handler) =>
54 | Hooks.Instance.AddRequestPreparedHandler(handler);
55 |
56 | ///
57 | public void ClearRequestPreparedHandlers() =>
58 | Hooks.Instance.ClearRequestPreparedHandlers();
59 |
60 | ///
61 | public void AddDebugHandler(IPostgrestDebugger.DebugEventHandler handler) =>
62 | Debugger.Instance.AddDebugHandler(handler);
63 |
64 | ///
65 | public void RemoveDebugHandler(IPostgrestDebugger.DebugEventHandler handler) =>
66 | Debugger.Instance.RemoveDebugHandler(handler);
67 |
68 | ///
69 | public void ClearDebugHandlers() => Debugger.Instance.ClearDebugHandlers();
70 |
71 | ///
72 | /// Function that can be set to return dynamic headers.
73 | ///
74 | /// Headers specified in the constructor options will ALWAYS take precedence over headers returned by this function.
75 | ///
76 | public Func>? GetHeaders { get; set; }
77 |
78 | ///
79 | /// Should be the first call to this class to initialize a connection with a Postgrest API Server
80 | ///
81 | /// Api Endpoint (ex: "http://localhost:8000"), no trailing slash required.
82 | /// Optional client configuration.
83 | ///
84 | public Client(string baseUrl, ClientOptions? options = null)
85 | {
86 | BaseUrl = baseUrl;
87 | Options = options ?? new ClientOptions();
88 | }
89 |
90 |
91 | ///
92 | public IPostgrestTable Table() where T : BaseModel, new() =>
93 | new Table(BaseUrl, SerializerSettings(Options), Options)
94 | {
95 | GetHeaders = GetHeaders
96 | };
97 |
98 | ///
99 | public IPostgrestTableWithCache Table(IPostgrestCacheProvider cacheProvider)
100 | where T : BaseModel, new() =>
101 | new TableWithCache(BaseUrl, cacheProvider, SerializerSettings(Options), Options)
102 | {
103 | GetHeaders = GetHeaders
104 | };
105 |
106 |
107 | ///
108 | public async Task Rpc(string procedureName, object? parameters = null)
109 | {
110 | var response = await Rpc(procedureName, parameters);
111 |
112 | return string.IsNullOrEmpty(response.Content) ? default : JsonConvert.DeserializeObject(response.Content!);
113 | }
114 |
115 | ///
116 | public Task Rpc(string procedureName, object? parameters = null)
117 | {
118 | // Build Uri
119 | var builder = new UriBuilder($"{BaseUrl}/rpc/{procedureName}");
120 |
121 | var canonicalUri = builder.Uri.ToString();
122 |
123 | var serializerSettings = SerializerSettings(Options);
124 |
125 | // Prepare parameters
126 | Dictionary? data = null;
127 | if (parameters != null)
128 | data = JsonConvert.DeserializeObject>(
129 | JsonConvert.SerializeObject(parameters, serializerSettings));
130 |
131 | // Prepare headers
132 | var headers = Helpers.PrepareRequestHeaders(HttpMethod.Post,
133 | new Dictionary(Options.Headers), Options);
134 |
135 | if (GetHeaders != null)
136 | headers = GetHeaders().MergeLeft(headers);
137 |
138 | // Send request
139 | var request =
140 | Helpers.MakeRequest(Options, HttpMethod.Post, canonicalUri, serializerSettings, data, headers);
141 | return request;
142 | }
143 | }
144 | }
--------------------------------------------------------------------------------
/Postgrest/ClientOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Globalization;
3 | #pragma warning disable CS1591
4 | namespace Supabase.Postgrest
5 | {
6 |
7 | ///
8 | /// Options that can be passed to the Client configuration
9 | ///
10 | public class ClientOptions
11 | {
12 | public string Schema { get; set; } = "public";
13 |
14 | public readonly DateTimeStyles DateTimeStyles = DateTimeStyles.AdjustToUniversal;
15 |
16 | public const string DATE_TIME_FORMAT = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFK";
17 |
18 | public Dictionary Headers { get; set; } = new Dictionary();
19 |
20 | public Dictionary QueryParams { get; set; } = new Dictionary();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Postgrest/Constants.cs:
--------------------------------------------------------------------------------
1 | using Supabase.Core.Attributes;
2 | #pragma warning disable CS1591
3 | namespace Supabase.Postgrest
4 | {
5 |
6 | public static class Constants
7 | {
8 | ///
9 | /// See: https://postgrest.org/en/v7.0.0/api.html?highlight=operators#operators
10 | ///
11 | public enum Operator
12 | {
13 | [MapTo("and")]
14 | And,
15 | [MapTo("or")]
16 | Or,
17 | [MapTo("eq")]
18 | Equals,
19 | [MapTo("gt")]
20 | GreaterThan,
21 | [MapTo("gte")]
22 | GreaterThanOrEqual,
23 | [MapTo("lt")]
24 | LessThan,
25 | [MapTo("lte")]
26 | LessThanOrEqual,
27 | [MapTo("neq")]
28 | NotEqual,
29 | [MapTo("like")]
30 | Like,
31 | [MapTo("ilike")]
32 | ILike,
33 | [MapTo("in")]
34 | In,
35 | [MapTo("is")]
36 | Is,
37 | [MapTo("fts")]
38 | FTS,
39 | [MapTo("plfts")]
40 | PLFTS,
41 | [MapTo("phfts")]
42 | PHFTS,
43 | [MapTo("wfts")]
44 | WFTS,
45 | [MapTo("cs")]
46 | Contains,
47 | [MapTo("cd")]
48 | ContainedIn,
49 | [MapTo("ov")]
50 | Overlap,
51 | [MapTo("sl")]
52 | StrictlyLeft,
53 | [MapTo("sr")]
54 | StrictlyRight,
55 | [MapTo("nxr")]
56 | NotRightOf,
57 | [MapTo("nxl")]
58 | NotLeftOf,
59 | [MapTo("adj")]
60 | Adjacent,
61 | [MapTo("not")]
62 | Not,
63 | }
64 |
65 | public enum Ordering
66 | {
67 | [MapTo("asc")]
68 | Ascending,
69 | [MapTo("desc")]
70 | Descending,
71 | }
72 |
73 | ///
74 | /// See: https://postgrest.org/en/v7.0.0/api.html?highlight=nulls%20first#ordering
75 | ///
76 | public enum NullPosition
77 | {
78 | [MapTo("nullsfirst")]
79 | First,
80 | [MapTo("nullslast")]
81 | Last
82 | }
83 |
84 | ///
85 | /// See: https://postgrest.org/en/v7.0.0/api.html?highlight=count#estimated-count
86 | ///
87 | public enum CountType
88 | {
89 | [MapTo("exact")]
90 | Exact,
91 | [MapTo("planned")]
92 | Planned,
93 | [MapTo("estimated")]
94 | Estimated
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Postgrest/Converters/DateTimeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 | namespace Supabase.Postgrest.Converters
7 | {
8 |
9 | ///
10 | public class DateTimeConverter : JsonConverter
11 | {
12 | ///
13 | public override bool CanConvert(Type objectType)
14 | {
15 | throw new NotImplementedException();
16 | }
17 |
18 | ///
19 | public override bool CanWrite => false;
20 |
21 | ///
22 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
23 | {
24 | if (reader.Value != null)
25 | {
26 | var str = reader.Value.ToString();
27 |
28 | var infinity = ParseInfinity(str);
29 |
30 | if (infinity != null)
31 | {
32 | return (DateTime)infinity;
33 | }
34 |
35 | var date = DateTime.Parse(str);
36 | return date;
37 | }
38 |
39 | var result = new List();
40 |
41 | try
42 | {
43 | var jo = JArray.Load(reader);
44 |
45 | foreach (var item in jo.ToArray())
46 | {
47 | var inner = item.ToString();
48 |
49 | var infinity = ParseInfinity(inner);
50 |
51 | if (infinity != null)
52 | {
53 | result.Add((DateTime)infinity);
54 | }
55 |
56 | var date = DateTime.Parse(inner);
57 | result.Add(date);
58 | }
59 | }
60 | catch (JsonReaderException)
61 | {
62 | return null;
63 | }
64 |
65 |
66 | return result;
67 | }
68 |
69 | private static DateTime? ParseInfinity(string input)
70 | {
71 | if (input.Contains("infinity"))
72 | {
73 | return input.Contains("-") ? DateTime.MinValue : DateTime.MaxValue;
74 | }
75 |
76 | return null;
77 | }
78 |
79 | ///
80 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
81 | {
82 | throw new NotImplementedException();
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Postgrest/Converters/IntConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Newtonsoft.Json;
4 | namespace Supabase.Postgrest.Converters
5 | {
6 |
7 | ///
8 | public class IntArrayConverter : JsonConverter
9 | {
10 | ///
11 | public override bool CanConvert(Type objectType)
12 | {
13 | throw new NotImplementedException();
14 | }
15 |
16 | ///
17 | public override bool CanRead => false;
18 |
19 | ///
20 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
21 | {
22 | throw new NotImplementedException();
23 | }
24 |
25 | ///
26 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
27 | {
28 | if (value is List list)
29 | {
30 | writer.WriteValue($"{{{string.Join(",", list)}}}");
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Postgrest/Converters/RangeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 | using Newtonsoft.Json;
4 | using Supabase.Postgrest.Extensions;
5 | using Supabase.Postgrest.Exceptions;
6 |
7 | namespace Supabase.Postgrest.Converters
8 | {
9 |
10 | ///
11 | /// Used by Newtonsoft.Json to convert a C# range into a Postgrest range.
12 | ///
13 | internal class RangeConverter : JsonConverter
14 | {
15 | public override bool CanConvert(Type objectType)
16 | {
17 | throw new NotImplementedException();
18 | }
19 |
20 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
21 | {
22 | return reader.Value != null ? ParseIntRange(reader.Value.ToString()) : null;
23 | }
24 |
25 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
26 | {
27 | if (value == null) return;
28 |
29 | var val = (IntRange)value;
30 | writer.WriteValue(val.ToPostgresString());
31 | }
32 |
33 | public static IntRange ParseIntRange(string value)
34 | {
35 | //int4range (0,1] , [123,4123], etc. etc.
36 | const string pattern = @"^(\[|\()(\d+),(\d+)(\]|\))$";
37 | var matches = Regex.Matches(value, pattern);
38 |
39 | if (matches.Count <= 0)
40 | throw new PostgrestException("Unknown Range format.") { Reason = FailureHint.Reason.InvalidArgument };
41 |
42 | var groups = matches[0].Groups;
43 | var isInclusiveLower = groups[1].Value == "[";
44 | var isInclusiveUpper = groups[4].Value == "]";
45 | var value1 = int.Parse(groups[2].Value);
46 | var value2 = int.Parse(groups[3].Value);
47 |
48 | var start = isInclusiveLower ? value1 : value1 + 1;
49 | var count = isInclusiveUpper ? value2 : value2 - 1;
50 |
51 | // Edge-case, includes no points
52 | return count < start ? new IntRange(0, 0) : new IntRange(start, count);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Postgrest/Debugger.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Supabase.Postgrest.Exceptions;
3 | using Supabase.Postgrest.Interfaces;
4 |
5 | namespace Supabase.Postgrest
6 | {
7 |
8 | ///
9 | /// A Singleton used for debug notifications
10 | ///
11 | internal class Debugger
12 | {
13 | private static Debugger? _instance { get; set; }
14 |
15 | ///
16 | /// Returns the Singleton Instance.
17 | ///
18 | public static Debugger Instance
19 | {
20 | get
21 | {
22 | _instance ??= new Debugger();
23 | return _instance;
24 | }
25 | }
26 |
27 | private Debugger()
28 | { }
29 |
30 | private readonly List _debugListeners = new();
31 |
32 | ///
33 | /// Adds a debug listener
34 | ///
35 | ///
36 | public void AddDebugHandler(IPostgrestDebugger.DebugEventHandler handler)
37 | {
38 | if (!_debugListeners.Contains(handler))
39 | _debugListeners.Add(handler);
40 | }
41 |
42 | ///
43 | /// Removes a debug handler.
44 | ///
45 | ///
46 | public void RemoveDebugHandler(IPostgrestDebugger.DebugEventHandler handler)
47 | {
48 | if (_debugListeners.Contains(handler))
49 | _debugListeners.Remove(handler);
50 | }
51 |
52 | ///
53 | /// Clears debug handlers.
54 | ///
55 | public void ClearDebugHandlers() =>
56 | _debugListeners.Clear();
57 |
58 | ///
59 | /// Notifies debug listeners.
60 | ///
61 | ///
62 | ///
63 | ///
64 | public void Log(object? sender, string message, PostgrestException? exception = null)
65 | {
66 | foreach (var l in _debugListeners.ToArray())
67 | l.Invoke(sender, message, exception);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Postgrest/Exceptions/FailureHint.cs:
--------------------------------------------------------------------------------
1 | using static Supabase.Postgrest.Exceptions.FailureHint.Reason;
2 | #pragma warning disable CS1591
3 |
4 | namespace Supabase.Postgrest.Exceptions
5 | {
6 |
7 | ///
8 | /// https://postgrest.org/en/v10.2/errors.html?highlight=exception#http-status-codes
9 | ///
10 | public static class FailureHint
11 | {
12 | public enum Reason
13 | {
14 | Unknown,
15 | NotAuthorized,
16 | ForeignKeyViolation,
17 | UniquenessViolation,
18 | ServerError,
19 | UndefinedTable,
20 | UndefinedFunction,
21 | InvalidArgument
22 | }
23 |
24 | public static Reason DetectReason(PostgrestException pgex)
25 | {
26 | if (pgex.Content == null)
27 | return Unknown;
28 |
29 | return pgex.StatusCode switch
30 | {
31 | 401 => NotAuthorized,
32 | 403 when pgex.Content.Contains("apikey") => NotAuthorized,
33 | 404 when pgex.Content.Contains("42883") => UndefinedTable,
34 | 404 when pgex.Content.Contains("42P01") => UndefinedFunction,
35 | 409 when pgex.Content.Contains("23503") => ForeignKeyViolation,
36 | 409 when pgex.Content.Contains("23505") => UniquenessViolation,
37 | 500 => ServerError,
38 | _ => Unknown
39 | };
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Postgrest/Exceptions/PostgrestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | namespace Supabase.Postgrest.Exceptions
4 | {
5 | ///
6 | /// Errors from Postgrest are wrapped by this exception
7 | ///
8 | public class PostgrestException : Exception
9 | {
10 | ///
11 | public PostgrestException(string? message) : base(message) { }
12 | ///
13 | public PostgrestException(string? message, Exception? innerException) : base(message, innerException) { }
14 |
15 | ///
16 | /// The response object from Postgrest
17 | ///
18 | public HttpResponseMessage? Response { get; internal set; }
19 |
20 | ///
21 | /// The content of the response object from Postgrest
22 | ///
23 | public string? Content { get; internal set; }
24 |
25 | ///
26 | /// The HTTP status code of the response object from Postgrest
27 | ///
28 | public int StatusCode { get; internal set; }
29 |
30 | ///
31 | /// Postgres client's best effort at decoding the error from the GoTrue server.
32 | ///
33 | public FailureHint.Reason Reason { get; internal set; }
34 |
35 | ///
36 | /// Attempts to decode the error from the GoTrue server.
37 | ///
38 | public void AddReason()
39 | {
40 | Reason = FailureHint.DetectReason(this);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Postgrest/Extensions/EnumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace Supabase.Postgrest.Extensions
3 | {
4 |
5 | ///
6 | /// Adds functionality to get a typed Attribute attached to an enum value.
7 | ///
8 | public static class EnumExtensions
9 | {
10 | ///
11 | /// Gets a typed Attribute attached to an enum value.
12 | ///
13 | ///
14 | ///
15 | ///
16 | internal static T? GetAttribute(this Enum value) where T : Attribute
17 | {
18 | var type = value.GetType();
19 | var name = Enum.GetName(type, value);
20 |
21 | if (name == null)
22 | {
23 | return null;
24 | }
25 |
26 | var fieldInfo = type.GetField(name);
27 |
28 | if (fieldInfo == null)
29 | {
30 | return null;
31 | }
32 |
33 | if (Attribute.GetCustomAttribute(fieldInfo, typeof(T)) is T attribute)
34 | {
35 | return attribute;
36 | }
37 |
38 | return null;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Postgrest/Extensions/RangeExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Supabase.Postgrest.Extensions
2 | {
3 |
4 | ///
5 | /// Adds functionality to transform a C# Range to a Postgrest String.
6 | ///
7 | ///
8 | /// https://www.postgresql.org/docs/14/rangetypes.html
9 | ///
10 | ///
11 | public static class RangeExtensions
12 | {
13 | ///
14 | /// Transforms a C# Range to a Postgrest String.
15 | ///
16 | ///
17 | ///
18 | internal static string ToPostgresString(this IntRange range) => $"[{range.Start},{range.End}]";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Postgrest/Extensions/TypeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | namespace Supabase.Postgrest.Extensions
4 | {
5 | internal static class TypeExtensions
6 | {
7 | internal static IEnumerable GetInheritanceHierarchy(this Type type)
8 | {
9 | for (var current = type; current != null; current = current.BaseType)
10 | yield return current;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Postgrest/Extensions/UriExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace Supabase.Postgrest.Extensions
3 | {
4 | ///
5 | /// Pull the instance info out of the Uri
6 | ///
7 | public static class UriExtensions
8 | {
9 | ///
10 | /// Pull the instance info out of the Uri
11 | ///
12 | ///
13 | ///
14 | public static string GetInstanceUrl(this Uri uri) =>
15 | uri.GetLeftPart(UriPartial.Authority) + uri.LocalPath;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Postgrest/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Runtime.CompilerServices;
5 | using System.Text;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using System.Web;
9 | using Newtonsoft.Json;
10 | using Newtonsoft.Json.Linq;
11 | using Supabase.Core;
12 | using Supabase.Core.Extensions;
13 | using Supabase.Postgrest.Exceptions;
14 | using Supabase.Postgrest.Models;
15 | using Supabase.Postgrest.Responses;
16 |
17 | [assembly: InternalsVisibleTo("PostgrestTests")]
18 |
19 | namespace Supabase.Postgrest
20 | {
21 |
22 | internal static class Helpers
23 | {
24 | private static readonly HttpClient Client = new HttpClient();
25 |
26 | private static readonly Guid AppSession = Guid.NewGuid();
27 |
28 | ///
29 | /// Helper to make a request using the defined parameters to an API Endpoint and coerce into a model.
30 | ///
31 | ///
32 | ///
33 | ///
34 | ///
35 | ///
36 | ///
37 | ///
38 | ///
39 | ///
40 | ///
41 | public static async Task> MakeRequest(ClientOptions clientOptions, HttpMethod method, string url, JsonSerializerSettings serializerSettings, object? data = null,
42 | Dictionary? headers = null, Func>? getHeaders = null, CancellationToken cancellationToken = default) where T : BaseModel, new()
43 | {
44 | var baseResponse = await MakeRequest(clientOptions, method, url, serializerSettings, data, headers, cancellationToken);
45 | return new ModeledResponse(baseResponse, serializerSettings, getHeaders);
46 | }
47 |
48 | ///
49 | /// Helper to make a request using the defined parameters to an API Endpoint.
50 | ///
51 | ///
52 | ///
53 | ///
54 | ///
55 | ///
56 | ///
57 | ///
58 | ///
59 | public static async Task MakeRequest(ClientOptions clientOptions, HttpMethod method, string url, JsonSerializerSettings serializerSettings, object? data = null,
60 | Dictionary? headers = null, CancellationToken cancellationToken = default)
61 | {
62 | var builder = new UriBuilder(url);
63 | var query = HttpUtility.ParseQueryString(builder.Query);
64 |
65 | if (data != null && method == HttpMethod.Get)
66 | {
67 | // Case if it's a Get request the data object is a dictionary
68 | if (data is Dictionary reqParams)
69 | {
70 | foreach (var param in reqParams)
71 | query[param.Key] = param.Value;
72 | }
73 | }
74 |
75 | builder.Query = query.ToString();
76 |
77 | using var requestMessage = new HttpRequestMessage(method, builder.Uri);
78 |
79 | if (data != null && method != HttpMethod.Get)
80 | {
81 | var stringContent = JsonConvert.SerializeObject(data, serializerSettings);
82 |
83 | if (!string.IsNullOrWhiteSpace(stringContent) && JToken.Parse(stringContent).HasValues)
84 | {
85 | requestMessage.Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
86 | }
87 | }
88 |
89 | if (headers != null)
90 | {
91 | foreach (var kvp in headers)
92 | {
93 | requestMessage.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
94 | }
95 | }
96 |
97 | using var response = await Client.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
98 | var content = await response.Content.ReadAsStringAsync();
99 |
100 | if (response.IsSuccessStatusCode)
101 | return new BaseResponse(clientOptions, response, content);
102 |
103 | var exception = new PostgrestException(content)
104 | {
105 | Content = content,
106 | Response = response,
107 | StatusCode = (int)response.StatusCode
108 | };
109 | exception.AddReason();
110 | throw exception;
111 | }
112 |
113 | ///
114 | /// Prepares the request with appropriate HTTP headers expected by Postgrest.
115 | ///
116 | ///
117 | ///
118 | ///
119 | ///
120 | ///
121 | ///
122 | public static Dictionary PrepareRequestHeaders(HttpMethod method, Dictionary? headers = null, ClientOptions? options = null, int rangeFrom = int.MinValue, int rangeTo = int.MinValue)
123 | {
124 | options ??= new ClientOptions();
125 |
126 | headers = headers == null ? new Dictionary(options.Headers) : options.Headers.MergeLeft(headers);
127 |
128 | if (!string.IsNullOrEmpty(options.Schema))
129 | {
130 | headers.Add(method == HttpMethod.Get ? "Accept-Profile" : "Content-Profile", options.Schema);
131 | }
132 |
133 | if (rangeFrom != int.MinValue)
134 | {
135 | var formatRangeTo = rangeTo != int.MinValue ? rangeTo.ToString() : null;
136 |
137 | headers.Add("Range-Unit", "items");
138 | headers.Add("Range", $"{rangeFrom}-{formatRangeTo}");
139 | }
140 |
141 | if (!headers.ContainsKey("X-Client-Info"))
142 | {
143 | try
144 | {
145 | // Default version to match other clients
146 | // https://github.com/search?q=org%3Asupabase-community+x-client-info&type=code
147 | headers.Add("X-Client-Info", $"postgrest-csharp/{Util.GetAssemblyVersion(typeof(Client))}");
148 | }
149 | catch (Exception)
150 | {
151 | // Fallback for when the version can't be found
152 | // e.g. running in the Unity Editor, ILL2CPP builds, etc.
153 | headers.Add("X-Client-Info", $"postgrest-csharp/session-{AppSession}");
154 | }
155 | }
156 |
157 | return headers;
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/Postgrest/Hooks.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Net.Http;
4 | using Newtonsoft.Json;
5 |
6 | namespace Supabase.Postgrest
7 | {
8 | ///
9 | /// Delegate representing the request to be sent to the remote server.
10 | ///
11 | public delegate void OnRequestPreparedEventHandler(object sender, ClientOptions clientOptions,
12 | HttpMethod method, string url,
13 | JsonSerializerSettings serializerSettings, object? data = null,
14 | Dictionary? headers = null);
15 |
16 | ///
17 | /// A internal singleton used for hooks applied to and
18 | ///
19 | internal class Hooks
20 | {
21 | private static Hooks? _instance { get; set; }
22 |
23 | ///
24 | /// Returns the Singleton Instance.
25 | ///
26 | public static Hooks Instance
27 | {
28 | get
29 | {
30 | _instance ??= new Hooks();
31 | return _instance;
32 | }
33 | }
34 |
35 | private readonly List _requestPreparedEventHandlers =
36 | new List();
37 |
38 | private Hooks()
39 | {
40 | }
41 |
42 | ///
43 | /// Adds a handler that is called prior to a request being sent.
44 | ///
45 | ///
46 | public void AddRequestPreparedHandler(OnRequestPreparedEventHandler handler)
47 | {
48 | if (!_requestPreparedEventHandlers.Contains(handler))
49 | _requestPreparedEventHandlers.Add(handler);
50 | }
51 |
52 | ///
53 | /// Removes an handler.
54 | ///
55 | ///
56 | public void RemoveRequestPreparedHandler(OnRequestPreparedEventHandler handler)
57 | {
58 | if (_requestPreparedEventHandlers.Contains(handler))
59 | _requestPreparedEventHandlers.Remove(handler);
60 | }
61 |
62 | ///
63 | /// Clears all handlers.
64 | ///
65 | public void ClearRequestPreparedHandlers()
66 | {
67 | _requestPreparedEventHandlers.Clear();
68 | }
69 |
70 | ///
71 | /// Notifies all listeners.
72 | ///
73 | ///
74 | ///
75 | ///
76 | ///
77 | ///
78 | ///
79 | ///
80 | public void NotifyOnRequestPreparedHandlers(object sender, ClientOptions clientOptions, HttpMethod method,
81 | string url,
82 | JsonSerializerSettings serializerSettings, object? data = null,
83 | Dictionary? headers = null)
84 | {
85 | Debugger.Instance.Log(this, $"{nameof(NotifyOnRequestPreparedHandlers)} called for [{method}] to {url}");
86 |
87 | foreach (var handler in _requestPreparedEventHandlers.ToList())
88 | handler.Invoke(sender, clientOptions, method, url, serializerSettings, data, headers);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/Postgrest/IntRange.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using Supabase.Postgrest;
4 |
5 | // https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Index.cs
6 | // https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Range.cs
7 | namespace Supabase.Postgrest
8 | {
9 | /// Represent a type can be used to index a collection either from the start or the end.
10 | ///
11 | /// Index is used by the C# compiler to support the new index syntax
12 | ///
13 | /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
14 | /// int lastElement = someArray[^1]; // lastElement = 5
15 | ///
16 | ///
17 | public class Index : IEquatable
18 | {
19 | private readonly int _value;
20 |
21 | /// Construct an Index using a value and indicating if the index is from the start or from the end.
22 | /// The index value. it has to be zero or positive number.
23 | /// Indicating if the index is from the start or from the end.
24 | ///
25 | /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
26 | ///
27 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
28 | public Index(int value, bool fromEnd = false)
29 | {
30 | if (value < 0)
31 | {
32 | throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative");
33 | }
34 |
35 | if (fromEnd)
36 | _value = ~value;
37 | else
38 | _value = value;
39 | }
40 |
41 | // The following private constructors mainly created for perf reason to avoid the checks
42 | private Index(int value)
43 | {
44 | _value = value;
45 | }
46 |
47 | /// Create an Index pointing at first element.
48 | public static Index Start => new Index(0);
49 |
50 | /// Create an Index pointing at beyond last element.
51 | public static Index End => new Index(~0);
52 |
53 | /// Create an Index from the start at the position indicated by the value.
54 | /// The index value from the start.
55 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
56 | public static Index FromStart(int value)
57 | {
58 | if (value < 0)
59 | {
60 | throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative");
61 | }
62 |
63 | return new Index(value);
64 | }
65 |
66 | /// Create an Index from the end at the position indicated by the value.
67 | /// The index value from the end.
68 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
69 | public static Index FromEnd(int value)
70 | {
71 | if (value < 0)
72 | {
73 | throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative");
74 | }
75 |
76 | return new Index(~value);
77 | }
78 |
79 | /// Returns the index value.
80 | public int Value
81 | {
82 | get
83 | {
84 | if (_value < 0)
85 | {
86 | return ~_value;
87 | }
88 | else
89 | {
90 | return _value;
91 | }
92 | }
93 | }
94 |
95 | /// Indicates whether the index is from the start or the end.
96 | public bool IsFromEnd => _value < 0;
97 |
98 | /// Calculate the offset from the start using the giving collection length.
99 | /// The length of the collection that the Index will be used with. length has to be a positive value
100 | ///
101 | /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
102 | /// we don't validate either the returned offset is greater than the input length.
103 | /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
104 | /// then used to index a collection will get out of range exception which will be same affect as the validation.
105 | ///
106 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
107 | public int GetOffset(int length)
108 | {
109 | var offset = _value;
110 | if (IsFromEnd)
111 | {
112 | // offset = length - (~value)
113 | // offset = length + (~(~value) + 1)
114 | // offset = length + value + 1
115 |
116 | offset += length + 1;
117 | }
118 | return offset;
119 | }
120 |
121 | /// Indicates whether the current Index object is equal to another object of the same type.
122 | /// An object to compare with this object
123 | public override bool Equals(object? value) => value is Index index && _value == index._value;
124 |
125 | /// Indicates whether the current Index object is equal to another Index object.
126 | /// An object to compare with this object
127 | public bool Equals(Index other) => _value == other._value;
128 |
129 | /// Returns the hash code for this instance.
130 | public override int GetHashCode() => _value;
131 |
132 | /// Converts integer number to an Index.
133 | public static implicit operator Index(int value) => FromStart(value);
134 |
135 | /// Converts the value of the current Index object to its equivalent string representation.
136 | public override string ToString()
137 | {
138 | if (IsFromEnd)
139 | return "^" + ((uint)Value).ToString();
140 |
141 | return ((uint)Value).ToString();
142 | }
143 | }
144 |
145 | /// Represent a range has start and end indexes.
146 | ///
147 | /// Range is used by the C# compiler to support the range syntax.
148 | ///
149 | /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 };
150 | /// int[] subArray1 = someArray[0..2]; // { 1, 2 }
151 | /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
152 | ///
153 | ///
154 | public class IntRange : IEquatable
155 | {
156 | /// Represent the inclusive start index of the Range.
157 | public Index Start { get; }
158 |
159 | /// Represent the exclusive end index of the Range.
160 | public Index End { get; }
161 |
162 | /// Construct a Range object using the start and end indexes.
163 | /// Represent the inclusive start index of the range.
164 | /// Represent the exclusive end index of the range.
165 | public IntRange(Index start, Index end)
166 | {
167 | Start = start;
168 | End = end;
169 | }
170 |
171 | /// Indicates whether the current Range object is equal to another object of the same type.
172 | /// An object to compare with this object
173 | public override bool Equals(object? value) =>
174 | value is IntRange r &&
175 | r.Start.Equals(Start) &&
176 | r.End.Equals(End);
177 |
178 | /// Indicates whether the current Range object is equal to another Range object.
179 | /// An object to compare with this object
180 | public bool Equals(IntRange other) => other.Start.Equals(Start) && other.End.Equals(End);
181 |
182 | /// Returns the hash code for this instance.
183 | public override int GetHashCode()
184 | {
185 | return Start.GetHashCode() * 31 + End.GetHashCode();
186 | }
187 |
188 | /// Converts the value of the current Range object to its equivalent string representation.
189 | public override string ToString()
190 | {
191 | return Start + ".." + End;
192 | }
193 |
194 | /// Create a Range object starting from start index to the end of the collection.
195 | public static IntRange StartAt(Index start) => new IntRange(start, Index.End);
196 |
197 | /// Create a Range object starting from first element in the collection to the end Index.
198 | public static IntRange EndAt(Index end) => new IntRange(Index.Start, end);
199 |
200 | /// Create a Range object starting from first element to the end.
201 | public static IntRange All => new IntRange(Index.Start, Index.End);
202 |
203 | /// Calculate the start offset and length of range object using a collection length.
204 | /// The length of the collection that the range will be used with. length has to be a positive value.
205 | ///
206 | /// For performance reason, we don't validate the input length parameter against negative values.
207 | /// It is expected Range will be used with collections which always have non negative length/count.
208 | /// We validate the range is inside the length scope though.
209 | ///
210 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
211 | public (int Offset, int Length) GetOffsetAndLength(int length)
212 | {
213 | int start;
214 | var startIndex = Start;
215 | if (startIndex.IsFromEnd)
216 | start = length - startIndex.Value;
217 | else
218 | start = startIndex.Value;
219 |
220 | int end;
221 | var endIndex = End;
222 | if (endIndex.IsFromEnd)
223 | end = length - endIndex.Value;
224 | else
225 | end = endIndex.Value;
226 |
227 | if ((uint)end > (uint)length || (uint)start > (uint)end)
228 | {
229 | throw new ArgumentOutOfRangeException(nameof(length));
230 | }
231 |
232 | return (start, end - start);
233 | }
234 | }
235 | }
236 |
237 | namespace System.Runtime.CompilerServices
238 | {
239 | internal static class RuntimeHelpers
240 | {
241 | ///
242 | /// Slices the specified array using the specified range.
243 | ///
244 | public static T[] GetSubArray(T[] array, IntRange range)
245 | {
246 | if (array == null)
247 | {
248 | throw new ArgumentNullException(nameof(array));
249 | }
250 |
251 | (int offset, int length) = range.GetOffsetAndLength(array.Length);
252 |
253 | if (default(T) != null || typeof(T[]) == array.GetType())
254 | {
255 | // We know the type of the array to be exactly T[].
256 |
257 | if (length == 0)
258 | {
259 | return Array.Empty();
260 | }
261 |
262 | var dest = new T[length];
263 | Array.Copy(array, offset, dest, 0, length);
264 | return dest;
265 | }
266 | else
267 | {
268 | // The array is actually a U[] where U:T.
269 | var dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), length);
270 | Array.Copy(array, offset, dest, 0, length);
271 | return dest;
272 | }
273 | }
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/Postgrest/Interfaces/IPostgrestCacheProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Supabase.Postgrest.Interfaces
4 | {
5 | ///
6 | /// A caching provider than can be used by postgrest to store requests.
7 | ///
8 | public interface IPostgrestCacheProvider
9 | {
10 | ///
11 | /// Gets an item from a caching solution, should coerce into a datatype.
12 | ///
13 | /// This will most likely be a JSON deserialization approach.
14 | ///
15 | /// A reproducible key for a defined query.
16 | ///
17 | ///
18 | public Task GetItem(string key);
19 |
20 | ///
21 | /// Sets an item within a caching solution, should store in a way that the data can be retrieved and coerced into a generic type by
22 | ///
23 | /// This will most likely be a JSON serialization approach.
24 | ///
25 | /// A reproducible key for a defined query.
26 | /// An object of serializable data.
27 | ///
28 | public Task SetItem(string key, object value);
29 |
30 | ///
31 | /// Clear an item within a caching solution by a key.
32 | ///
33 | /// A reproducible key for a defined query.
34 | ///
35 | public Task ClearItem(string key);
36 |
37 | ///
38 | /// An empty/clear cache implementation.
39 | ///
40 | ///
41 | public Task Empty();
42 | }
43 | }
--------------------------------------------------------------------------------
/Postgrest/Interfaces/IPostgrestClient.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Supabase.Core.Interfaces;
3 | using Supabase.Postgrest.Models;
4 | using Supabase.Postgrest.Responses;
5 |
6 | namespace Supabase.Postgrest.Interfaces
7 | {
8 | ///
9 | /// Client interface for Postgrest
10 | ///
11 | public interface IPostgrestClient : IGettableHeaders
12 | {
13 | ///
14 | /// API Base Url for subsequent calls.
15 | ///
16 | string BaseUrl { get; }
17 |
18 | ///
19 | /// The Options was initialized with.
20 | ///
21 | ClientOptions Options { get; }
22 |
23 | ///
24 | /// Adds a handler that is called prior to a request being sent.
25 | ///
26 | ///
27 | void AddRequestPreparedHandler(OnRequestPreparedEventHandler handler);
28 |
29 | ///
30 | /// Removes an handler.
31 | ///
32 | ///
33 | void RemoveRequestPreparedHandler(OnRequestPreparedEventHandler handler);
34 |
35 | ///
36 | /// Clears all handlers.
37 | ///
38 | void ClearRequestPreparedHandlers();
39 |
40 | ///
41 | /// Adds a debug handler
42 | ///
43 | ///
44 | void AddDebugHandler(IPostgrestDebugger.DebugEventHandler handler);
45 |
46 | ///
47 | /// Removes a debug handler
48 | ///
49 | /// ///
50 | void RemoveDebugHandler(IPostgrestDebugger.DebugEventHandler handler);
51 |
52 | ///
53 | /// Clears debug handlers
54 | ///
55 | void ClearDebugHandlers();
56 |
57 | ///
58 | /// Perform a stored procedure call.
59 | ///
60 | /// The function name to call
61 | /// The parameters to pass to the function call
62 | ///
63 | Task Rpc(string procedureName, object? parameters);
64 |
65 | ///
66 | /// Perform a stored procedure call.
67 | ///
68 | /// The function name to call
69 | /// The parameters to pass to the function call
70 | /// A type used for hydrating the HTTP response content (hydration through JSON.NET)
71 | /// A hydrated model
72 | Task Rpc(string procedureName, object? parameters = null);
73 |
74 | ///
75 | /// Returns a Table Query Builder instance for a defined model - representative of `USE $TABLE`
76 | ///
77 | /// Custom Model derived from `BaseModel`
78 | ///
79 | IPostgrestTable Table() where T : BaseModel, new();
80 |
81 | ///
82 | /// Returns a Table Query Builder instance with a Cache Provider for a defined model - representative of `USE #$TABLE`
83 | ///
84 | ///
85 | ///
86 | ///
87 | IPostgrestTableWithCache Table(IPostgrestCacheProvider cacheProvider) where T : BaseModel, new();
88 | }
89 | }
--------------------------------------------------------------------------------
/Postgrest/Interfaces/IPostgrestDebugger.cs:
--------------------------------------------------------------------------------
1 | using Supabase.Postgrest.Exceptions;
2 |
3 | namespace Supabase.Postgrest.Interfaces
4 | {
5 | ///
6 | /// Interface for getting debug info from Postgrest
7 | ///
8 | public interface IPostgrestDebugger
9 | {
10 | ///
11 | delegate void DebugEventHandler(object? sender, string message, PostgrestException? exception);
12 |
13 | ///
14 | /// Adds a debug handler
15 | ///
16 | ///
17 | void AddDebugHandler(DebugEventHandler handler);
18 | ///
19 | /// Removes a debug handler
20 | ///
21 | ///
22 | void RemoveDebugHandler(DebugEventHandler handler);
23 | ///
24 | /// Clears debug handlers
25 | ///
26 | void ClearDebugHandlers();
27 | ///
28 | /// Logs a message
29 | ///
30 | ///
31 | ///
32 | ///
33 | void Log(object? sender, string message, PostgrestException? exception = null);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Postgrest/Interfaces/IPostgrestQueryFilter.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS1591
2 | namespace Supabase.Postgrest.Interfaces
3 | {
4 | public interface IPostgrestQueryFilter
5 | {
6 | object? Criteria { get; }
7 | Constants.Operator Op { get; }
8 | string? Property { get; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Postgrest/Interfaces/IPostgrestTableWithCache.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Supabase.Postgrest.Models;
4 | using Supabase.Postgrest.Requests;
5 |
6 | namespace Supabase.Postgrest.Interfaces
7 | {
8 | ///
9 | /// Client interface for Postgrest
10 | ///
11 | ///
12 | public interface IPostgrestTableWithCache : IPostgrestTable where T : BaseModel, new()
13 | {
14 | ///
15 | /// Performs a Get request, returning a which populates from the cache, if applicable.
16 | ///
17 | ///
18 | ///
19 | public new Task> Get(CancellationToken cancellationToken = default);
20 | }
21 | }
--------------------------------------------------------------------------------
/Postgrest/Linq/SelectExpressionVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq.Expressions;
4 | using Supabase.Postgrest.Attributes;
5 |
6 | namespace Supabase.Postgrest.Linq
7 |
8 | {
9 |
10 | ///
11 | /// Helper class for parsing Select linq queries.
12 | ///
13 | internal class SelectExpressionVisitor : ExpressionVisitor
14 | {
15 | ///
16 | /// The columns that have been selected from this linq expression.
17 | ///
18 | public List Columns { get; } = new();
19 |
20 | ///
21 | /// The root call that will be looped through to populate .
22 | ///
23 | /// Called like: `Table<Movies>().Select(x => new[] { x.Id, x.Name, x.CreatedAt }).Get()`
24 | ///
25 | ///
26 | ///
27 | protected override Expression VisitNewArray(NewArrayExpression node)
28 | {
29 | foreach (var expression in node.Expressions)
30 | Visit(expression);
31 |
32 | return node;
33 | }
34 |
35 | ///
36 | /// A Member Node, representing a property on a BaseModel.
37 | ///
38 | ///
39 | ///
40 | protected override Expression VisitMember(MemberExpression node)
41 | {
42 | var column = GetColumnFromMemberExpression(node);
43 |
44 | if (column != null)
45 | Columns.Add(column);
46 |
47 | return node;
48 | }
49 |
50 | ///
51 | /// A Unary Node, delved into to represent a property on a BaseModel.
52 | ///
53 | ///
54 | ///
55 | protected override Expression VisitUnary(UnaryExpression node)
56 | {
57 | if (node.Operand is MemberExpression memberExpression)
58 | {
59 | var column = GetColumnFromMemberExpression(memberExpression);
60 |
61 | if (column != null)
62 | Columns.Add(column);
63 | }
64 |
65 | return node;
66 | }
67 |
68 | ///
69 | /// Gets a column name from property based on it's supplied attributes.
70 | ///
71 | ///
72 | ///
73 | private string? GetColumnFromMemberExpression(MemberExpression node)
74 | {
75 | var type = node.Member.ReflectedType;
76 | var prop = type?.GetProperty(node.Member.Name);
77 | var attrs = prop?.GetCustomAttributes(true);
78 |
79 | if (attrs == null)
80 | throw new ArgumentException($"Unknown argument '{node.Member.Name}' provided, does it have a `Column` or `PrimaryKey` attribute?");
81 |
82 | foreach (var attr in attrs)
83 | {
84 | switch (attr)
85 | {
86 | case ColumnAttribute columnAttr:
87 | return columnAttr.ColumnName;
88 | case PrimaryKeyAttribute primaryKeyAttr:
89 | return primaryKeyAttr.ColumnName;
90 | }
91 | }
92 |
93 | throw new ArgumentException($"Unknown argument '{node.Member.Name}' provided, does it have a `Column` or `PrimaryKey` attribute?");
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Postgrest/Linq/SetExpressionVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq.Expressions;
4 | using Supabase.Postgrest.Attributes;
5 |
6 | namespace Supabase.Postgrest.Linq
7 |
8 | {
9 | ///
10 | /// Helper class for parsing Set linq queries.
11 | ///
12 | internal class SetExpressionVisitor : ExpressionVisitor
13 | {
14 | ///
15 | /// The column that have been selected from this linq expression.
16 | ///
17 | public string? Column { get; private set; }
18 |
19 | ///
20 | /// The Column's type that value should be checked against.
21 | ///
22 | public Type? ExpectedType { get; private set; }
23 |
24 | ///
25 | /// Value to be updated.
26 | ///
27 | public object? Value { get; private set; }
28 |
29 | ///
30 | /// A Unary Node, delved into to represent a property on a BaseModel.
31 | ///
32 | ///
33 | ///
34 | protected override Expression VisitUnary(UnaryExpression node)
35 | {
36 | if (node.Operand is not MemberExpression memberExpression) return node;
37 |
38 | var column = GetColumnFromMemberExpression(memberExpression);
39 |
40 | Column = column;
41 | ExpectedType = memberExpression.Type;
42 |
43 | return node;
44 | }
45 |
46 | ///
47 | /// A Member Node, representing a property on a BaseModel.
48 | ///
49 | ///
50 | ///
51 | protected override Expression VisitMember(MemberExpression node)
52 | {
53 | var column = GetColumnFromMemberExpression(node);
54 |
55 | Column = column;
56 | ExpectedType = node.Type;
57 |
58 | return node;
59 | }
60 |
61 | ///
62 | /// Called when visiting a the expected new KeyValuePair().
63 | ///
64 | ///
65 | ///
66 | ///
67 | protected override Expression VisitNew(NewExpression node)
68 | {
69 | if (typeof(KeyValuePair