Compare commits

...
31 Commits
Author SHA1 Message Date
steve-chavez 806579e659 bump version to 14.7 2026-03-20 14:22:31 -05:00
steve-chavez 1856434a74 fix: not logging termination unix signals
Under container environments like ECS, it's hard to know when PostgREST
is being terminated.
2026-03-20 13:38:40 -05:00
Taimoor ZaeemandSteve Chavez dade15acc4 nix(test): add test suite for observability tests
- Create separate test suite for observability tests

- Create wrapper script `postgrest-test-observability`

- Add to CI and `postgrest-check`

- Move JWT cache tests under observability tests

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
2026-03-20 09:13:47 -05:00
Michał KłeczekandSteve Chavez 79531a612d test(spec): Move metrics state helpers from JwtCacheSpec to SpecHelpers
Refactoring: State validation helpers used in JwtCacheSpec moved to SpecHelper
to make them available in other Spec modules.

(cherry picked from commit 85a313a8cc)
2026-03-19 14:30:18 -05:00
renovate[bot]andWolfgang Walther 9d0e13d961 chore(deps): update ubuntu:noble docker digest to 186072b 2026-03-19 09:15:59 +00:00
renovate[bot]andWolfgang Walther 3e61dc024e chore(deps): update cachix/cachix-action action to v17 2026-03-18 21:24:50 +00:00
renovate[bot]andWolfgang Walther 1bdf773417 chore(deps): update all dependencies 2026-03-18 20:01:41 +00:00
renovate[bot]andWolfgang Walther 55868351f3 chore(deps): update actions/cache action to v5.0.4 2026-03-18 19:09:06 +00:00
renovate[bot]andWolfgang Walther 8c3cb76fbd chore(deps): update ubuntu:noble docker digest to 0d39fcc 2026-03-17 10:22:17 +00:00
renovate[bot]andWolfgang Walther 3bdb69bae7 chore(deps): update actions/create-github-app-token action to v3 2026-03-14 18:06:45 +00:00
renovate[bot]andWolfgang Walther 1ed3c2c197 chore(deps): update all dependencies 2026-03-13 17:23:12 +00:00
renovate[bot]andWolfgang Walther dde145b685 chore(deps): update all dependencies 2026-03-13 17:19:11 +00:00
Wolfgang Walther 720705e32b docs: fix prometheus text format link
Reported by linkcheck.
2026-03-11 09:45:21 +01:00
steve-chavez 6813ceaf31 bump version to 14.6 2026-03-06 16:30:04 -05:00
Taimoor Zaeemandsteve-chavez 73c8ae0bbc fix(error): leaking table and function names when calculating hint
Increase similarity score to 0.75 from 0.33 for table and functions
error hint.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
2026-03-06 16:16:05 -05:00
Laurence Isla 6f356e32b6 chore: update sponsors
* The list of sponsors is now ordered by total contribution value (highest first)
* Removed former backers from sponsor pages
2026-03-03 23:11:22 -05:00
Laurence Isla c4772184e4 chore: update sponsor 2026-03-03 23:11:22 -05:00
dshukertjrandWolfgang Walther 0602080acb docs: Update the Supabase logo to a correct one 2026-02-19 10:54:29 +01:00
steve-chavez dc67e4d3f5 chore: fix changelog entries 2026-02-13 13:40:38 -05:00
steve-chavez d89826b982 bump version to 14.5 2026-02-12 20:12:52 -05:00
steve-chavez 94350fdde6 fix: don't hide async exceptions in logs
Fixes #4646. Using the repro on #4646, this now produces the log:

```
11/Feb/2026:09:40:08 -0500: Warp server error: stack overflow
```

When:
```
$ curl localhost:3000/
curl: (52) Empty reply from server
```

(cherry picked from commit e95e815483)
2026-02-12 20:11:44 -05:00
Taimoor ZaeemandWolfgang Walther 230eb3630c docs(install): update postgresql minimum supported version
PostgREST dropped support for PostgreSQL version 12 however,
it was not reflected in the docs.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
2026-02-12 10:23:29 +01:00
steve-chavez 5485b8ca9a bump version to 14.4 2026-01-29 14:13:49 -05:00
steve-chavez 895e9c536c chore: remove wrong entry in CHANGELOG 2026-01-29 13:24:20 -05:00
Michal Kleczekandsteve-chavez 16c767134c fix: listener running with exception masked after first failure 2026-01-29 13:22:46 -05:00
Laurence IslaandSteve Chavez 0a8b836435 fix: filtering the returned representation whenn using or/and filters on mutations
(cherry picked from commit 1682677297)
2026-01-29 09:16:08 -05:00
Michal KleczekandSteve Chavez 5796f86100 fix: ensure Listener connections are released
retryingListen function potentially leaks database connections. This patch ensures the connections are released in case of listen/notify errors.

(cherry picked from commit 00c7cb1a22)
2026-01-28 18:26:15 -05:00
Wolfgang Walther 101eac1cce docs: fix links
datrium.com doesn't exist anymore, while euronodes.com seems to only
fail SSL in CI.
2026-01-28 09:57:23 +01:00
renovate[bot]andWolfgang Walther 1ae14afdf2 chore(deps): update haskell-actions/setup action to v2.10.2 2026-01-11 17:36:28 +00:00
Wolfgang Walther 69090bd224 ci: pin backport action to version instead of default branch 2026-01-11 18:34:43 +01:00
renovate[bot]andWolfgang Walther 5d5160fbd7 chore(deps): update haskell-actions/setup action to v2.10.1 2026-01-05 19:01:32 +00:00
54 changed files with 697 additions and 207 deletions
@@ -112,7 +112,7 @@ runs:
echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT"
- name: Save artifact to GitHub Actions
if: steps.find-task.outputs.task_found
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ inputs.upload }}
path: ${{ steps.download.outputs.artifacts }}
+2 -2
View File
@@ -19,14 +19,14 @@ inputs:
runs:
using: composite
steps:
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
with:
path: ${{ inputs.path }}
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
restore-keys: |
${{ runner.os }}-${{ inputs.prefix }}-
- uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
- uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
with:
path: ${{ inputs.path }}
+1 -1
View File
@@ -16,7 +16,7 @@ runs:
nix_conf: |-
always-allow-substitutes = true
max-jobs = auto
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: postgrest
authToken: ${{ inputs.authToken }}
+3 -3
View File
@@ -28,7 +28,7 @@ jobs:
# This actions creates the github token using the postgrest app secrets
- name: Create Github App Token
id: app-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
with:
app-id: ${{ vars.POSTGREST_CI_APP_ID }}
private-key: ${{ secrets.POSTGREST_CI_PRIVATE_KEY }}
@@ -38,14 +38,14 @@ jobs:
# This is required for backport action to cherry-pick the PR
- name: Fetch PR ref
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ steps.app-token.outputs.token }}
# Backport action that creates the PR with given settings
- name: Create backport PR
uses: korthout/backport-action@c656f5d5851037b2b38fb5db2691a03fa229e3b2 # v4.0.1
uses: korthout/backport-action@4aaf0e03a94ff0a619c9a511b61aeb42adea5b02 # v4.2.0
with:
github_token: ${{ steps.app-token.outputs.token }}
pull_description: 'Backport for #${pull_number}.'
+10 -10
View File
@@ -33,7 +33,7 @@ jobs:
name: Nix - Linux x86-64 static
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
@@ -42,7 +42,7 @@ jobs:
- name: Build static executable
run: nix-build -A postgrestStatic -A postgrestStatic.tests
- name: Save built executable as artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: postgrest-linux-static-x86-64
path: result/bin/postgrest
@@ -51,7 +51,7 @@ jobs:
- name: Build Docker image
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
- name: Save built Docker image as artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: postgrest-docker-x86-64
path: postgrest-docker.tar.gz
@@ -62,7 +62,7 @@ jobs:
name: Nix - MacOS
runs-on: macos-15
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
@@ -117,8 +117,8 @@ jobs:
name: Stack - ${{ matrix.name }}
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: haskell-actions/setup@0512451d82f3ca8c147db62e30464e7c4ca63d30 # v2.9.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: haskell-actions/setup@f9150cb1d140e9a9271700670baa38991e6fa25c # v2.10.3
with:
# This must match the version in stack.yaml's resolver
ghc-version: 9.6.7
@@ -146,7 +146,7 @@ jobs:
- name: Strip Executable
run: strip result/postgrest*
- name: Save built executable as artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ matrix.artifact }}
path: |
@@ -159,7 +159,7 @@ jobs:
name: Stack - FreeBSD from CirrusCI
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/artifact-from-cirrus
with:
token: ${{ github.token }}
@@ -176,8 +176,8 @@ jobs:
name: Cabal - Linux x86-64 - GHC ${{ matrix.ghc }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: haskell-actions/setup@0512451d82f3ca8c147db62e30464e7c4ca63d30 # v2.9.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: haskell-actions/setup@f9150cb1d140e9a9271700670baa38991e6fa25c # v2.10.3
with:
ghc-version: ${{ matrix.ghc }}
- name: Cache .cabal
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
name: Lint & Style
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
@@ -36,7 +36,7 @@ jobs:
name: Commit
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 100 # fetch history (last 100 commits) instead of default shallow clone history, this is deemed enough for a PR history
- name: Setup Nix Environment
+1 -1
View File
@@ -50,7 +50,7 @@ jobs:
- test
- build
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
- name: Tag latest commit
+2 -2
View File
@@ -27,7 +27,7 @@ jobs:
name: Build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
@@ -41,7 +41,7 @@ jobs:
name: Spellcheck
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
linkcheck:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
+11 -11
View File
@@ -26,7 +26,7 @@ jobs:
needs:
- build
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check the version to be released
run: |
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
@@ -49,7 +49,7 @@ jobs:
echo "Relevant extract from CHANGELOG.md:"
cat CHANGES.md
- name: Save CHANGES.md as artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: release-changes
path: CHANGES.md
@@ -64,9 +64,9 @@ jobs:
needs:
- prepare
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download all artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: artifacts
- name: Create release bundle with archives for all builds
@@ -91,7 +91,7 @@ jobs:
artifacts/postgrest-windows-x86-64/postgrest.exe
- name: Save release bundle
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: release-bundle
path: release-bundle
@@ -135,17 +135,17 @@ jobs:
env:
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download x86-64 Docker image
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: postgrest-docker-x86-64
- name: Download aarch64 binary
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: postgrest-ubuntu-aarch64
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
username: ${{ vars.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
@@ -191,7 +191,7 @@ jobs:
vars.DOCKER_REPO && vars.DOCKER_USER &&
github.ref == 'refs/tags/devel'
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
with:
username: ${{ vars.DOCKER_USER }}
+11 -7
View File
@@ -39,7 +39,7 @@ jobs:
# https://github.com/actions/runner/issues/241#issuecomment-842566950
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
@@ -51,7 +51,7 @@ jobs:
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
run: postgrest-coverage
- name: Upload coverage to codecov
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3
with:
files: ./coverage/codecov.json
token: ${{ secrets.CODECOV_TOKEN }}
@@ -78,12 +78,12 @@ jobs:
# https://github.com/actions/runner/issues/241#issuecomment-842566950
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
tools: tests.testSpec.bin tests.testIO.bin tests.testBigSchema.bin withTools.pg-${{ matrix.pgVersion }}.bin cabalTools.update.bin
tools: tests.testSpec.bin tests.testObservability.bin tests.testIO.bin tests.testBigSchema.bin withTools.pg-${{ matrix.pgVersion }}.bin cabalTools.update.bin
- run: postgrest-cabal-update
@@ -91,6 +91,10 @@ jobs:
if: always()
run: postgrest-with-pg-${{ matrix.pgVersion }} postgrest-test-spec
- name: Run observability tests
if: always()
run: postgrest-with-pg-${{ matrix.pgVersion }} postgrest-test-observability
- name: Run IO tests
if: always()
run: postgrest-with-pg-${{ matrix.pgVersion }} postgrest-test-io -vv
@@ -104,7 +108,7 @@ jobs:
name: Memory
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Nix Environment
uses: ./.github/actions/setup-nix
with:
@@ -124,7 +128,7 @@ jobs:
name: Loadtest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Setup Nix Environment
@@ -158,7 +162,7 @@ jobs:
name: Flake Check
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Setup Nix Environment
+13 -8
View File
@@ -13,26 +13,26 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
</a>
</td>
<td align="center" valign="middle">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/neon.jpg">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="static/supabase.svg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/tembo.png">
<a href="https://www.euronodes.com/postgrest" target="_blank">
<img width="296px" src="static/euronodes.svg">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.euronodes.com/postgrest" target="_blank">
<img width="296px" src="static/euronodes.svg">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/neon.jpg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="static/supabase.png">
<a href="https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/bytebase.svg">
</a>
</td>
</tr>
@@ -93,6 +93,11 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
<img width="222px" src="static/code-build.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/tembo.png">
</a>
</td>
</tr>
</tbody>
</table>
+24 -2
View File
@@ -4,9 +4,31 @@ All notable changes to this project will be documented in this file. From versio
## Unreleased
### Added
## [14.7] - 2026-03-20
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
### Fixed
- Fix not logging SIGTERM and SIGINT by @steve-chavez in #4728
## [14.6] - 2026-03-06
### Fixed
- Fix leaking table and function names when calculating error hint by @taimoorzaeem in #4675
## [14.5] - 2026-02-12
### Fixed
- Don't hide async exceptions in logs by @stevechavez in #4646
## [14.4] - 2026-01-29
### Fixed
- Ensure Listener connections are released by @mkleczek in #4614
- Fix incorrectly filtering the returned representation for PATCH requests when using `or/and` filters by @laurenceisla in #3707
- Fix listener running with exception masked after first failure by @mkleczek #4615
## [14.3] - 2026-01-03
+1 -1
View File
@@ -2,7 +2,7 @@
# The x86-64 is a single-static-binary image built via Nix, see:
# nix/tools/docker/README.md
FROM ubuntu:noble@sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472dfc9e54 AS postgrest
FROM ubuntu:noble@sha256:186072bba1b2f436cbb91ef2567abca677337cfc786c86e107d25b7072feef0c AS postgrest
RUN apt-get update -y \
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
+8 -8
View File
@@ -22,26 +22,26 @@ API than you are likely to write from scratch.
</a>
</td>
<td align="center" valign="middle">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/neon.jpg">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="static/supabase.svg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/tembo.png">
<a href="https://www.euronodes.com/postgrest" target="_blank">
<img width="296px" src="static/euronodes.svg">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.euronodes.com/postgrest" target="_blank">
<img width="296px" src="static/euronodes.svg">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/neon.jpg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="static/supabase.png">
<a href="https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="static/bytebase.svg">
</a>
</td>
</tr>
+8 -8
View File
@@ -19,26 +19,26 @@ write from scratch.
</a>
</td>
<td align="center" valign="middle">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/neon.jpg">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.svg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/tembo.png">
<a href="https://www.euronodes.com/postgrest" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/euronodes.svg">
</a>
</td>
</tr>
<tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.euronodes.com/postgrest" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/euronodes.svg">
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/neon.jpg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
<a href="https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/bytebase.svg">
</a>
</td>
</tr>
+1
View File
@@ -302,6 +302,7 @@ linkcheck_ignore = [
r"https://www.cybertec-postgresql.com/.*",
# Odd SSL error
r"https://www.dripdepot.com",
r"https://www.euronodes.com",
# New GitHub UI delays comment load, so anchor fails
r"https://github.com/.*#issuecomment",
# Random 500 Internal Server Error
+1 -1
View File
@@ -16,7 +16,7 @@ Supported PostgreSQL versions
=============================
=============== =================================
**Supported** PostgreSQL >= 12
**Supported** PostgreSQL >= 13
=============== =================================
PostgREST works with all PostgreSQL versions still `officially supported <https://www.postgresql.org/support/versioning/>`_.
+26 -20
View File
@@ -38,6 +38,28 @@ Sponsors
.. image:: ../static/cybertec.svg
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
.. container:: img-dark
.. image:: ../static/supabase-dark.svg
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
.. container:: img-light
.. image:: ../static/supabase.svg
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
.. container:: img-dark
.. image:: ../static/euronodes.svg
:target: https://www.euronodes.com/postgrest
.. container:: img-light
.. image:: ../static/euronodes.svg
:target: https://www.euronodes.com/postgrest
|
.. container:: img-dark
.. image:: ../static/neon-dark.jpg
@@ -48,30 +70,15 @@ Sponsors
.. image:: ../static/neon.jpg
:target: https://neon.com/?utm_source=sponsor&utm_campaign=postgrest
.. image:: ../static/tembo.png
:target: https://www.tembo.io/?utm_source=sponsor&utm_campaign=postgrest
|
.. container:: img-dark
.. image:: ../static/euronodes.svg
:target: https://www.euronodes.com/postgrest
.. image:: ../static/bytebase-dark.svg
:target: https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest
.. container:: img-light
.. image:: ../static/euronodes.svg
:target: https://www.euronodes.com/postgrest
.. container:: img-dark
.. image:: ../static/supabase-dark.png
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
.. container:: img-light
.. image:: ../static/supabase.png
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
.. image:: ../static/bytebase.svg
:target: https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest
.. The static/empty.png(created with `convert -size 320x95 xc:#fcfcfc empty.png`) is an ugly workaround
to create space and center the logos. It's not easy to layout with restructuredText.
@@ -213,7 +220,6 @@ In Production
Here are some companies that use PostgREST in production.
* `Catarse <https://www.catarse.me>`_
* `Datrium <https://www.datrium.com>`_
* `Drip Depot <https://www.dripdepot.com>`_
* `Image-charts <https://www.image-charts.com>`_
* `Netwo <https://www.netwo.io>`_
+1 -1
View File
@@ -120,7 +120,7 @@ Restart the database and watch the log file in real-time to understand how HTTP
Metrics
=======
The ``metrics`` endpoint on the :ref:`admin_server` endpoint provides metrics in `Prometheus text format <https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format>`_.
The ``metrics`` endpoint on the :ref:`admin_server` endpoint provides metrics in `Prometheus text format <https://prometheus.io/docs/instrumenting/exposition_formats/#prometheus-text-format>`_.
.. code-block:: bash
+1
View File
@@ -82,6 +82,7 @@ let
}
''
${tests}/bin/postgrest-test-spec
${tests}/bin/postgrest-test-observability
${tests}/bin/postgrest-test-doctests
${tests}/bin/postgrest-test-io
${tests}/bin/postgrest-test-big-schema
+22 -2
View File
@@ -32,6 +32,20 @@ let
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec -- "''${_arg_leftovers[@]}"
'';
testObservability =
checkedShellScript
{
name = "postgrest-test-observability";
docs = "Run the Haskell observability test suite.";
args = [ "ARG_LEFTOVERS([hspec arguments])" ];
workingDir = "/";
withEnv = postgrest.env;
}
''
${withTools.withPg} -f test/observability/fixtures/load.sql \
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:observability -- "''${_arg_leftovers[@]}"
'';
testDoctests =
checkedShellScript
{
@@ -155,7 +169,7 @@ let
rm -rf coverage/*
# build once before running all the tests
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec test:observability
(
trap 'echo Found dead code: Check file list above.' ERR ;
@@ -179,11 +193,16 @@ let
${withTools.withPg} -f test/spec/fixtures/load.sql \
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
HPCTIXFILE="$tmpdir"/observability.tix \
${withTools.withPg} -f test/observability/fixtures/load.sql \
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:observability
# Note: No coverage for doctests, as doctests leverage GHCi and GHCi does not support hpc
# collect all the tix files
${ghc}/bin/hpc sum --union --exclude=Paths_postgrest --output="$tmpdir"/tests.tix \
"$tmpdir"/io*.tix "$tmpdir"/big_schema*.tix "$tmpdir"/replica*.tix "$tmpdir"/spec.tix
"$tmpdir"/io*.tix "$tmpdir"/big_schema*.tix "$tmpdir"/replica*.tix "$tmpdir"/spec.tix \
"$tmpdir"/observability.tix
# prepare the overlay
${ghc}/bin/hpc overlay --output="$tmpdir"/overlay.tix test/coverage.overlay
@@ -250,6 +269,7 @@ buildToolbox
tools = {
inherit
testSpec
testObservability
testDoctests
testSpecIdempotence
testIO
+32 -2
View File
@@ -1,5 +1,5 @@
name: postgrest
version: 14.3
version: 14.7
synopsis: REST API for any Postgres database
description: Reads the schema of a PostgreSQL database and creates RESTful routes
for tables, views, and functions, supporting all HTTP methods that security
@@ -211,7 +211,6 @@ test-suite spec
Feature.Auth.AudienceJwtSecretSpec
Feature.Auth.AuthSpec
Feature.Auth.BinaryJwtSecretSpec
Feature.Auth.JwtCacheSpec
Feature.Auth.NoAnonSpec
Feature.Auth.NoJwtSecretSpec
Feature.ConcurrentSpec
@@ -294,6 +293,37 @@ test-suite spec
-- https://github.com/PostgREST/postgrest/issues/387
-with-rtsopts=-K33K
test-suite observability
type: exitcode-stdio-1.0
default-language: Haskell2010
default-extensions: OverloadedStrings
QuasiQuotes
NoImplicitPrelude
hs-source-dirs: test/observability
main-is: Main.hs
other-modules: ObsHelper
Observation.JwtCache
build-depends: base >= 4.9 && < 4.20
, base64-bytestring >= 1 && < 1.3
, bytestring >= 0.10.8 && < 0.13
, hasql-pool >= 1.0.1 && < 1.1
, hasql-transaction >= 1.0.1 && < 1.2
, hspec >= 2.3 && < 2.12
, hspec-expectations >= 0.8.4 && < 0.9
, hspec-wai >= 0.10 && < 0.12
, hspec-wai-json >= 0.10 && < 0.12
, http-types >= 0.12.3 && < 0.13
, jose-jwt >= 0.9.6 && < 0.11
, postgrest
, prometheus-client >= 1.1.1 && < 1.2.0
, protolude >= 0.3.1 && < 0.4
, wai >= 3.2.1 && < 3.3
ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path
-fwrite-ide-info
-- https://github.com/PostgREST/postgrest/issues/387
-with-rtsopts=-K33K
test-suite doctests
type: exitcode-stdio-1.0
default-language: Haskell2010
+27 -5
View File
@@ -9,18 +9,24 @@ Some of its functionality includes:
- Producing HTTP Headers according to RFCs.
- Content Negotiation
-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns #-}
module PostgREST.App
( postgrest
, run
) where
import GHC.IO.Exception (IOErrorType (..))
import System.IO.Error (ioeGetErrorType)
import Control.Monad.Except (liftEither)
import Data.Either.Combinators (mapLeft, whenLeft)
import Data.Maybe (fromJust)
import Data.String (IsString (..))
import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort,
import Network.Wai.Handler.Warp (defaultSettings, setHost,
setOnException, setPort,
setServerName)
import qualified Data.Text.Encoding as T
@@ -63,11 +69,10 @@ type Handler = ExceptT Error
run :: AppState -> IO ()
run appState = do
let observer = AppState.getObserver appState
conf@AppConfig{..} <- AppState.getConfig appState
AppState.schemaCacheLoader appState -- Loads the initial SchemaCache
Unix.installSignalHandlers (AppState.getMainThreadId appState) (AppState.schemaCacheLoader appState) (AppState.readInDbConfig False appState)
Unix.installSignalHandlers observer (AppState.getMainThreadId appState) (AppState.schemaCacheLoader appState) (AppState.readInDbConfig False appState)
Listener.runListener appState
@@ -79,7 +84,24 @@ run appState = do
address <- resolveSocketToAddress (AppState.getSocketREST appState)
observer $ AppServerAddressObs address
Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app
Warp.runSettingsSocket (serverSettings conf & setOnException onWarpException) (AppState.getSocketREST appState) app
where
observer = AppState.getObserver appState
onWarpException :: Maybe Wai.Request -> SomeException -> IO ()
onWarpException _ ex =
when (shouldDisplayException ex) $
observer $ WarpErrorObs $ show ex
-- Similar to wai defaultShouldDisplayException in
-- https://github.com/yesodweb/wai//blob/8c3882c60f6abe043889fc20c7efd3fa9747fa4a/warp/Network/Wai/Handler/Warp/Settings.hs#L251-L258
-- but without omitting AsyncException since it's important to log for ThreadKilled, StackOverflow and other cases.
-- We want to reuse this to avoid flooding the logs for some transient failure cases.
shouldDisplayException :: SomeException -> Bool
shouldDisplayException se
| Just (_ :: Warp.InvalidRequest) <- fromException se = False
| Just (ioeGetErrorType -> et) <- fromException se, et == ResourceVanished || et == InvalidArgument = False
| otherwise = True
serverSettings :: AppConfig -> Warp.Settings
serverSettings AppConfig{..} =
+18 -4
View File
@@ -386,7 +386,7 @@ noRelBetweenHint parent child schema allRels = ("Perhaps you meant '" <>) <$>
-- Just "Perhaps you meant to call the function api.test"
--
-- >>> noRpcHint "api" "other" [] procs []
-- Just "Perhaps you meant to call the function api.another"
-- Nothing
--
-- >>> noRpcHint "api" "noclosealternative" [] procs []
-- Nothing
@@ -423,8 +423,8 @@ noRpcHint schema procName params allProcs overloadedProcs =
-- E.g. ["val", "param", "name"] into "(name, param, val)"
listToText = ("(" <>) . (<> ")") . T.intercalate ", " . sort
possibleProcs
| null overloadedProcs = Fuzzy.getOne fuzzySetOfProcs procName
| otherwise = (procName <>) <$> Fuzzy.getOne fuzzySetOfParams (listToText params)
| null overloadedProcs = getFuzzyHint HintProcedure fuzzySetOfProcs procName
| otherwise = (procName <>) <$> getFuzzyHint HintParams fuzzySetOfParams (listToText params)
-- |
-- Do a fuzzy search in all tables in the same schema and return closest result
@@ -432,7 +432,21 @@ tableNotFoundHint :: Text -> Text -> SchemaCache -> Maybe Text
tableNotFoundHint schema tblName SchemaCache{dbTablesFuzzyIndex}
= fmap (\tbl -> "Perhaps you meant the table '" <> schema <> "." <> tbl <> "'") perhapsTable
where
perhapsTable = (`Fuzzy.getOne` tblName) =<< HM.lookup schema dbTablesFuzzyIndex
perhapsTable = (\fuzzySet -> getFuzzyHint HintTable fuzzySet tblName) =<< HM.lookup schema dbTablesFuzzyIndex
data HintType
= HintTable
| HintProcedure
| HintParams
-- | Get hint using Fuzzy Search with at least 0.75 similarity score
getFuzzyHint :: HintType -> Fuzzy.FuzzySet -> Text -> Maybe Text
getFuzzyHint hintType =
let minScore = 0.75 :: Double -- used for table and procedure name hints
in case hintType of
HintTable -> Fuzzy.getOneWithMinScore minScore
HintProcedure -> Fuzzy.getOneWithMinScore minScore
HintParams -> Fuzzy.getOne -- For params, we stick to `getOne` which defaults to 0.33 min score, not a security risk to reveal params
compressedRel :: Relationship -> JSON.Value
-- An ambiguousness error cannot happen for computed relationships TODO refactor so this mempty is not needed
+36 -21
View File
@@ -1,3 +1,4 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE RecordWildCards #-}
@@ -15,6 +16,7 @@ import PostgREST.Version (prettyVersion)
import qualified PostgREST.AppState as AppState
import qualified PostgREST.Config as Config
import Data.Either.Combinators (whenRight)
import Protolude
-- | Starts the Listener in a thread
@@ -22,15 +24,16 @@ runListener :: AppState -> IO ()
runListener appState = do
AppConfig{..} <- getConfig appState
when configDbChannelEnabled $
void . forkIO $ retryingListen appState
void . forkIO . void $ retryingListen appState
-- | Starts a LISTEN connection and handles notifications. It recovers with exponential backoff with a cap of 32 seconds, if the LISTEN connection is lost.
retryingListen :: AppState -> IO ()
-- | This function never returns (but can throw) and return type enforces that.
retryingListen :: AppState -> IO Void
retryingListen appState = do
AppConfig{..} <- AppState.getConfig appState
let
dbChannel = toS configDbChannel
handleFinally err = do
onError err = do
AppState.putIsListenerOn appState False
observer $ DBListenFail dbChannel (Right err)
unless configDbPoolAutomaticRecovery $
@@ -42,29 +45,39 @@ retryingListen appState = do
threadDelay (delay * oneSecondInMicro)
unless (delay == maxDelay) $
AppState.putNextListenerDelay appState (delay * 2)
-- loop running the listener
retryingListen appState
-- forkFinally allows to detect if the thread dies
void . flip forkFinally handleFinally $ do
dbOrError <- SQL.acquire $ toUtf8 (Config.addTargetSessionAttrs $ Config.addFallbackAppName prettyVersion configDbUri)
case dbOrError of
Right db -> do
SQL.listen db $ SQL.toPgIdentifier dbChannel
AppState.putIsListenerOn appState True
-- Execute the listener with with error handling
handle onError $ do
-- Make sure we don't leak connections on errors
bracket
-- acquire connection
(SQL.acquire $ toUtf8 (Config.addTargetSessionAttrs $ Config.addFallbackAppName prettyVersion configDbUri))
-- release connection
(`whenRight` releaseConnection) $
-- use connection
\case
Right db -> do
SQL.listen db $ SQL.toPgIdentifier dbChannel
AppState.putIsListenerOn appState True
delay <- AppState.getNextListenerDelay appState
when (delay > 1) $ do -- if we did a retry
-- assume we lost notifications, refresh the schema cache
AppState.schemaCacheLoader appState
-- reset the delay
AppState.putNextListenerDelay appState 1
delay <- AppState.getNextListenerDelay appState
when (delay > 1) $ do -- if we did a retry
-- assume we lost notifications, refresh the schema cache
AppState.schemaCacheLoader appState
-- reset the delay
AppState.putNextListenerDelay appState 1
observer $ DBListenStart dbChannel
SQL.waitForNotifications handleNotification db
observer $ DBListenStart dbChannel
Left err -> do
observer $ DBListenFail dbChannel (Left err)
exitFailure
-- wait for notifications
-- this will never return, in case of an error it will throw and be caught by onError
forever $ SQL.waitForNotifications handleNotification db
Left err -> do
observer $ DBListenFail dbChannel (Left err)
exitFailure
where
observer = AppState.getObserver appState
mainThreadId = AppState.getMainThreadId appState
@@ -79,3 +92,5 @@ retryingListen appState = do
cacheReloader =
AppState.schemaCacheLoader appState
releaseConnection = void . forkIO . handle (observer . DBListenerConnectionCleanupFail) . SQL.release
+12 -4
View File
@@ -44,10 +44,11 @@ data Observation
| SchemaCacheLoadedObs Double
| ConnectionRetryObs Int
| DBListenStart Text
| DBListenFail Text (Either SQL.ConnectionError (Either SomeException ()))
| DBListenFail Text (Either SQL.ConnectionError SomeException)
| DBListenRetry Int
| DBListenerGotSCacheMsg ByteString
| DBListenerGotConfigMsg ByteString
| DBListenerConnectionCleanupFail SomeException
| QueryObs MainQuery Status
| ConfigReadErrorObs SQL.UsageError
| ConfigInvalidObs Text
@@ -62,6 +63,8 @@ data Observation
| PoolRequestFullfilled
| JwtCacheLookup Bool
| JwtCacheEviction
| TerminationUnixSignalObs Text
| WarpErrorObs Text
data ObsFatalError = ServerAuthError | ServerPgrstBug | ServerError42P05 | ServerError08P01
@@ -118,6 +121,8 @@ observationMessage = \case
"Received a schema cache reload message on the " <> show channel <> " channel"
DBListenerGotConfigMsg channel ->
"Received a config reload message on the " <> show channel <> " channel"
DBListenerConnectionCleanupFail ex ->
"Failed during listener connection cleanup: " <> showOnSingleLine '\t' (show ex)
QueryObs{} ->
mempty -- TODO pending refactor: The logic for printing the query cannot be done here. Join the observationMessage function into observationLogger to avoid this mempty.
ConfigReadErrorObs usageErr ->
@@ -154,6 +159,10 @@ observationMessage = \case
"Looked up a JWT in JWT cache"
JwtCacheEviction ->
"Evicted entry from JWT cache"
TerminationUnixSignalObs signal ->
"Received termination unix signal " <> signal
WarpErrorObs txt ->
"Warp server error: " <> txt
where
showMillis :: Double -> Text
showMillis x = toS $ showFFloat (Just 1) x ""
@@ -164,9 +173,8 @@ observationMessage = \case
showListenerConnError :: SQL.ConnectionError -> Text
showListenerConnError = maybe "Connection error" (showOnSingleLine '\t' . T.decodeUtf8)
showListenerException :: Either SomeException () -> Text
showListenerException (Right _) = "Failed getting notifications" -- should not happen as the listener will never finish (hasql-notifications uses `forever` internally) with a Right result
showListenerException (Left e) = showOnSingleLine '\t' $ show e
showListenerException :: SomeException -> Text
showListenerException = showOnSingleLine '\t' . show
showOnSingleLine :: Char -> Text -> Text
+8 -1
View File
@@ -965,10 +965,17 @@ addRanges ApiRequest{..} rReq =
addLogicTrees :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either Error ReadPlanTree
addLogicTrees ctx ApiRequest{..} rReq =
foldr addLogicTreeToNode (Right rReq) qsLogic
foldr addLogicTreeToNode (Right rReq) logic
where
QueryParams.QueryParams{..} = iQueryParams
logic =
case iAction of
ActDb (ActRelationRead _ _) -> qsLogic
ActDb (ActRoutine _ _) -> qsLogic
-- For mutations, take the non-root logic filters. These will only affect the embeddings and not the top level of the returned representation.
_ -> filter (not . null . fst) qsLogic
addLogicTreeToNode :: (EmbedPath, LogicTree) -> Either Error ReadPlanTree -> Either Error ReadPlanTree
addLogicTreeToNode = updateNode (\t (Node q@ReadPlan{from=fromTable, where_=lf} f) -> Node q{ReadPlan.where_=resolveLogicTree ctx{qi=fromTable} t:lf} f)
+10 -9
View File
@@ -11,26 +11,27 @@ import qualified System.Posix.Signals as Signals
import System.Posix.Types (FileMode)
import System.PosixCompat.Files (setFileMode)
import Data.String (String)
import qualified Network.Socket as NS
import Data.String (String)
import qualified Network.Socket as NS
import qualified PostgREST.Observation as Observation
import Protolude
import System.Directory (removeFile)
import System.IO.Error (isDoesNotExistError)
import System.Directory (removeFile)
import System.IO.Error (isDoesNotExistError)
-- | Set signal handlers, only for systems with signals
installSignalHandlers :: ThreadId -> IO () -> IO () -> IO ()
installSignalHandlers :: Observation.ObservationHandler -> ThreadId -> IO () -> IO () -> IO ()
#ifndef mingw32_HOST_OS
installSignalHandlers tid usr1 usr2 = do
installSignalHandlers observer tid usr1 usr2 = do
let interrupt = throwTo tid UserInterrupt
install Signals.sigINT interrupt
install Signals.sigTERM interrupt
install Signals.sigINT $ observer (Observation.TerminationUnixSignalObs "SIGINT") >> interrupt
install Signals.sigTERM $ observer (Observation.TerminationUnixSignalObs "SIGTERM") >> interrupt
install Signals.sigUSR1 usr1
install Signals.sigUSR2 usr2
where
install signal handler =
void $ Signals.installHandler signal (Signals.Catch handler) Nothing
#else
installSignalHandlers _ _ _ = pass
installSignalHandlers _ _ _ _ = pass
#endif
-- | Create a unix domain socket and bind it to the given path.
+7
View File
@@ -0,0 +1,7 @@
<svg width="1024" height="220" viewBox="0 0 1024 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M267.945 167.731H314.905C342.254 167.731 359.527 154.103 359.527 133.123C359.527 117.522 352.69 106.404 332.178 101.563C347.652 95.8246 352.51 87.2173 352.51 75.203C352.51 55.6573 337.756 42.2085 310.407 42.2085H267.945V167.731ZM293.134 145.137V114.474H312.566C326.241 114.474 333.978 120.032 333.978 129.178C333.978 140.116 326.241 145.137 312.566 145.137H293.134ZM293.134 94.0314V64.6232H308.068C321.743 64.6232 326.96 70.5407 326.96 79.148C326.96 89.1898 321.743 94.0314 308.068 94.0314H293.134ZM378.999 205.208C400.95 205.208 412.645 192.477 420.562 173.649L461.945 74.4857H433.157L414.984 127.743C413.365 132.226 411.925 137.785 411.026 142.627C410.126 137.785 408.687 132.226 407.067 127.743L388.895 74.4857H359.567L396.632 163.427L395.372 167.372C392.673 175.621 386.736 181.539 377.38 181.539C373.421 181.539 370.003 181.18 366.404 180.283L362.806 203.415C367.664 204.85 374.141 205.208 378.999 205.208Z" fill="white"/>
<path d="M509.624 167.881H522.918V146.401H512.858C508.187 146.401 505.312 143.716 505.312 137.809V95.3848H525.074V74.7993H505.312V53.4979L481.059 56.0039V74.7993H467.945V95.3848H481.059V139.062C481.059 159.289 491.12 167.881 509.624 167.881ZM575.459 170.208C593.963 170.208 609.592 161.974 617.138 150.339L598.274 136.198C593.963 143.358 585.878 148.728 575.459 148.728C563.242 148.728 554.978 141.031 552.283 130.112H622.168C622.707 127.069 622.887 124.204 622.887 121.519C622.887 94.6688 603.484 72.4723 575.459 72.4723C547.433 72.4723 527.132 94.6688 527.132 121.519C527.132 148.37 546.535 170.208 575.459 170.208ZM552.283 112.032C554.978 100.934 563.602 93.2368 575.099 93.2368C586.956 93.2368 595.759 101.65 598.454 112.032H552.283ZM687.249 170.208C712.939 170.208 733.42 148.549 733.42 121.698C733.42 94.8478 712.939 72.8303 687.249 72.8303C671.26 72.8303 662.817 79.2744 657.786 89.1197V37.2085H633.533V167.881H657.068V152.666C662.098 163.227 670.542 170.208 687.249 170.208ZM657.068 121.698C657.068 107.199 667.308 94.6688 683.297 94.6688C698.747 94.6688 709.167 107.02 709.167 121.519C709.167 135.661 698.747 148.37 683.297 148.37C667.308 148.37 657.068 136.198 657.068 121.698ZM785.937 170.208C802.645 170.208 811.088 163.227 816.119 152.666V167.881H839.653V74.7993H815.58V89.4777C810.549 79.4534 802.106 72.8303 785.937 72.8303C760.247 72.8303 739.767 94.8478 739.767 121.698C739.767 148.549 760.247 170.208 785.937 170.208ZM764.02 121.519C764.02 107.02 774.439 94.6688 789.889 94.6688C805.878 94.6688 816.119 107.199 816.119 121.698C816.119 136.198 805.878 148.37 789.889 148.37C774.439 148.37 764.02 135.661 764.02 121.519ZM886.04 170.208C906.7 170.208 922.33 157.32 922.33 140.136C922.33 126.173 916.042 116.149 888.915 109.168C877.776 106.304 876.339 103.44 876.339 100.218C876.339 94.4898 881.369 91.9837 888.016 91.9837C894.304 91.9837 900.053 94.8478 904.544 102.008L922.15 88.5827C912.629 76.2314 902.568 72.4723 887.477 72.4723C865.56 72.4723 852.625 84.6446 852.625 100.934C852.625 112.748 856.398 124.025 881.369 129.933C896.101 133.334 898.436 136.019 898.436 140.852C898.436 146.401 893.765 150.518 885.501 150.518C877.597 150.518 870.411 146.759 863.584 139.062L847.235 153.919C857.476 164.838 870.231 170.208 886.04 170.208ZM976.517 170.208C995.021 170.208 1010.65 161.974 1018.2 150.339L999.332 136.198C995.021 143.358 986.936 148.728 976.517 148.728C964.3 148.728 956.036 141.031 953.342 130.112H1023.23C1023.77 127.069 1023.94 124.204 1023.94 121.519C1023.94 94.6688 1004.54 72.4723 976.517 72.4723C948.491 72.4723 928.19 94.6688 928.19 121.519C928.19 148.37 947.593 170.208 976.517 170.208ZM953.342 112.032C956.036 100.934 964.66 93.2368 976.157 93.2368C988.014 93.2368 996.817 101.65 999.512 112.032H953.342Z" fill="white"/>
<path d="M63.3182 209.286C40.9935 198.863 22.8237 181.221 11.7365 159.201C0.649395 137.179 -2.70929 112.063 2.20168 87.8978C7.11264 63.733 20.0073 41.927 38.8077 25.9936C57.6082 10.0604 81.2196 0.927572 105.837 0.0669197C130.455 -0.793732 154.645 6.66803 174.511 21.2495C194.375 35.8312 208.758 56.6835 215.339 80.4466C221.924 104.21 220.322 129.499 210.797 152.241C201.271 174.982 184.374 193.851 162.831 205.806L109.664 109.786L63.3182 209.286Z" fill="white"/>
<path d="M162.726 205.864C147.617 214.226 130.748 218.901 113.494 219.504C96.24 220.108 79.0873 216.625 63.4319 209.338L109.665 109.785L162.726 205.864Z" fill="#111827"/>
<path d="M47.9785 64.5504H172.557V117.247C172.557 130.949 169.34 141.254 162.907 148.162C156.59 155.19 148.402 158.703 138.342 158.703C131.908 158.703 126.293 157.123 121.497 153.961C116.818 150.915 113.251 146.7 110.794 141.314C108.572 147.167 105.062 151.793 100.266 155.19C95.4705 158.703 89.7972 160.46 83.2466 160.46C72.2509 160.46 63.5947 156.947 57.2781 149.92C51.0784 142.894 47.9785 132.764 47.9785 119.531V64.5504ZM149.572 89.4936H122.55V117.072C122.55 122.107 123.661 125.972 125.884 128.665C128.224 131.476 131.616 132.88 136.061 132.88C140.038 132.88 143.255 131.535 145.711 128.842C148.284 126.265 149.572 122.341 149.572 117.072V89.4936ZM99.3891 89.4936H70.9642V118.828C70.9642 124.098 72.1924 128.022 74.6489 130.598C77.1054 133.291 80.6146 134.637 85.1766 134.637C89.5047 134.637 92.9555 133.291 95.529 130.598C98.1024 128.022 99.3891 124.098 99.3891 118.828V89.4936Z" fill="#111827"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg width="1024" height="220" viewBox="0 0 1024 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M268 167.731H314.961C342.309 167.731 359.582 154.103 359.582 133.123C359.582 117.522 352.745 106.404 332.234 101.563C347.707 95.8246 352.565 87.2173 352.565 75.203C352.565 55.6573 337.811 42.2085 310.463 42.2085H268V167.731ZM293.19 145.137V114.474H312.622C326.296 114.474 334.033 120.032 334.033 129.178C334.033 140.116 326.296 145.137 312.622 145.137H293.19ZM293.19 94.0314V64.6232H308.123C321.798 64.6232 327.016 70.5407 327.016 79.148C327.016 89.1898 321.798 94.0314 308.123 94.0314H293.19ZM379.054 205.208C401.005 205.208 412.7 192.477 420.617 173.649L462 74.4857H433.212L415.039 127.743C413.42 132.226 411.981 137.785 411.081 142.627C410.181 137.785 408.742 132.226 407.123 127.743L388.95 74.4857H359.622L396.687 163.427L395.427 167.372C392.729 175.621 386.791 181.539 377.435 181.539C373.476 181.539 370.058 181.18 366.459 180.283L362.861 203.415C367.719 204.85 374.196 205.208 379.054 205.208Z" fill="#111827"/>
<path d="M509.679 167.881H522.973V146.401H512.913C508.242 146.401 505.368 143.716 505.368 137.809V95.3848H525.129V74.7993H505.368V53.4979L481.115 56.0039V74.7993H468V95.3848H481.115V139.062C481.115 159.289 491.175 167.881 509.679 167.881ZM575.514 170.208C594.018 170.208 609.648 161.974 617.193 150.339L598.33 136.198C594.018 143.358 585.934 148.728 575.514 148.728C563.298 148.728 555.034 141.031 552.339 130.112H622.223C622.762 127.069 622.942 124.204 622.942 121.519C622.942 94.6688 603.539 72.4723 575.514 72.4723C547.488 72.4723 527.188 94.6688 527.188 121.519C527.188 148.37 546.59 170.208 575.514 170.208ZM552.339 112.032C555.034 100.934 563.657 93.2368 575.155 93.2368C587.012 93.2368 595.814 101.65 598.509 112.032H552.339ZM687.305 170.208C712.995 170.208 733.475 148.549 733.475 121.698C733.475 94.8478 712.995 72.8303 687.305 72.8303C671.316 72.8303 662.872 79.2744 657.842 89.1197V37.2085H633.589V167.881H657.123V152.666C662.153 163.227 670.597 170.208 687.305 170.208ZM657.123 121.698C657.123 107.199 667.363 94.6688 683.352 94.6688C698.802 94.6688 709.222 107.02 709.222 121.519C709.222 135.661 698.802 148.37 683.352 148.37C667.363 148.37 657.123 136.198 657.123 121.698ZM785.992 170.208C802.7 170.208 811.144 163.227 816.174 152.666V167.881H839.708V74.7993H815.635V89.4777C810.605 79.4534 802.161 72.8303 785.992 72.8303C760.302 72.8303 739.822 94.8478 739.822 121.698C739.822 148.549 760.302 170.208 785.992 170.208ZM764.075 121.519C764.075 107.02 774.495 94.6688 789.945 94.6688C805.934 94.6688 816.174 107.199 816.174 121.698C816.174 136.198 805.934 148.37 789.945 148.37C774.495 148.37 764.075 135.661 764.075 121.519ZM886.096 170.208C906.756 170.208 922.385 157.32 922.385 140.136C922.385 126.173 916.097 116.149 888.97 109.168C877.832 106.304 876.394 103.44 876.394 100.218C876.394 94.4898 881.425 91.9837 888.072 91.9837C894.36 91.9837 900.108 94.8478 904.6 102.008L922.206 88.5827C912.684 76.2314 902.624 72.4723 887.533 72.4723C865.615 72.4723 852.68 84.6446 852.68 100.934C852.68 112.748 856.453 124.025 881.425 129.933C896.156 133.334 898.492 136.019 898.492 140.852C898.492 146.401 893.821 150.518 885.557 150.518C877.652 150.518 870.466 146.759 863.639 139.062L847.291 153.919C857.531 164.838 870.286 170.208 886.096 170.208ZM976.572 170.208C995.076 170.208 1010.71 161.974 1018.25 150.339L999.388 136.198C995.076 143.358 986.992 148.728 976.572 148.728C964.356 148.728 956.092 141.031 953.397 130.112H1023.28C1023.82 127.069 1024 124.204 1024 121.519C1024 94.6688 1004.6 72.4723 976.572 72.4723C948.546 72.4723 928.246 94.6688 928.246 121.519C928.246 148.37 947.648 170.208 976.572 170.208ZM953.397 112.032C956.092 100.934 964.715 93.2368 976.213 93.2368C988.07 93.2368 996.873 101.65 999.567 112.032H953.397Z" fill="#111827"/>
<path d="M63.3735 209.286C41.0488 198.863 22.879 181.221 11.7918 159.201C0.704692 137.179 -2.65399 112.063 2.25697 87.8978C7.16794 63.733 20.0626 41.927 38.863 25.9936C57.6635 10.0604 81.2749 0.927572 105.892 0.0669197C130.51 -0.793732 154.7 6.66803 174.566 21.2495C194.43 35.8312 208.813 56.6835 215.395 80.4466C221.979 104.21 220.378 129.499 210.852 152.241C201.326 174.982 184.429 193.851 162.886 205.806L109.72 109.786L63.3735 209.286Z" fill="#111827"/>
<path d="M162.781 205.864C147.672 214.226 130.804 218.901 113.549 219.504C96.2953 220.108 79.1426 216.625 63.4872 209.338L109.72 109.785L162.781 205.864Z" fill="white"/>
<path d="M48.0338 64.5504H172.612V117.247C172.612 130.949 169.396 141.254 162.962 148.162C156.645 155.19 148.458 158.703 138.397 158.703C131.964 158.703 126.349 157.123 121.553 153.961C116.874 150.915 113.306 146.7 110.85 141.314C108.627 147.167 105.118 151.793 100.322 155.19C95.5258 158.703 89.8525 160.46 83.3019 160.46C72.3062 160.46 63.65 156.947 57.3333 149.92C51.1337 142.894 48.0338 132.764 48.0338 119.531V64.5504ZM149.627 89.4936H122.606V117.072C122.606 122.107 123.717 125.972 125.939 128.665C128.279 131.476 131.671 132.88 136.116 132.88C140.093 132.88 143.31 131.535 145.767 128.842C148.34 126.265 149.627 122.341 149.627 117.072V89.4936ZM99.4444 89.4936H71.0194V118.828C71.0194 124.098 72.2477 128.022 74.7042 130.598C77.1607 133.291 80.6699 134.637 85.2319 134.637C89.56 134.637 93.0108 133.291 95.5843 130.598C98.1577 128.022 99.4444 124.098 99.4444 118.828V89.4936Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg width="581" height="113" viewBox="0 0 581 113" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M151.397 66.7608C151.996 72.3621 157.091 81.9642 171.877 81.9642C184.764 81.9642 190.959 73.7624 190.959 65.7607C190.959 58.559 186.063 52.6577 176.373 50.6571L169.379 49.1569C166.682 48.6568 164.884 47.1565 164.884 44.7559C164.884 41.9552 167.681 39.8549 171.178 39.8549C176.772 39.8549 178.87 43.5556 179.27 46.4564L190.359 43.9558C189.76 38.6546 185.064 29.7527 171.078 29.7527C160.488 29.7527 152.696 37.0543 152.696 45.8561C152.696 52.7576 156.991 58.4591 166.482 60.5594L172.976 62.0598C176.772 62.8599 178.271 64.6605 178.271 66.8609C178.271 69.4615 176.173 71.762 171.777 71.762C165.983 71.762 163.085 68.1611 162.786 64.2602L151.397 66.7608Z" fill="white"/>
<path d="M233.421 80.4639H246.109C245.909 78.7635 245.609 75.3628 245.609 71.5618V31.2529H232.321V59.8592C232.321 65.5606 228.925 69.5614 223.031 69.5614C216.837 69.5614 214.039 65.1604 214.039 59.6592V31.2529H200.752V62.3599C200.752 73.0622 207.545 81.7642 219.434 81.7642C224.628 81.7642 230.325 79.7638 233.022 75.1627C233.022 77.1631 233.221 79.4636 233.421 80.4639Z" fill="white"/>
<path d="M273.076 99.4682V75.663C275.473 78.9636 280.469 81.6644 287.263 81.6644C301.149 81.6644 310.439 70.6617 310.439 55.7584C310.439 41.1553 302.148 30.1528 287.762 30.1528C280.37 30.1528 274.875 33.4534 272.677 37.2544V31.253H259.79V99.4682H273.076ZM297.352 55.8585C297.352 64.6606 291.958 69.7616 285.164 69.7616C278.372 69.7616 272.877 64.5605 272.877 55.8585C272.877 47.1566 278.372 42.0554 285.164 42.0554C291.958 42.0554 297.352 47.1566 297.352 55.8585Z" fill="white"/>
<path d="M317.964 67.0609C317.964 74.7627 324.357 81.8643 334.848 81.8643C342.139 81.8643 346.835 78.4634 349.332 74.5625C349.332 76.463 349.532 79.1635 349.832 80.4639H362.02C361.72 78.7635 361.422 75.2627 361.422 72.6622V48.4567C361.422 38.5545 355.627 29.7527 340.043 29.7527C326.855 29.7527 319.761 38.2544 318.963 45.9562L330.751 48.4567C331.151 44.1558 334.348 40.455 340.141 40.455C345.737 40.455 348.434 43.3556 348.434 46.8564C348.434 48.5568 347.536 49.9572 344.738 50.3572L332.65 52.1576C324.458 53.3579 317.964 58.2589 317.964 67.0609ZM337.644 71.962C333.349 71.962 331.25 69.1614 331.25 66.2608C331.25 62.4599 333.947 60.5594 337.345 60.0594L348.434 58.359V60.5594C348.434 69.2615 343.239 71.962 337.644 71.962Z" fill="white"/>
<path d="M387.703 80.4641V74.4627C390.299 78.6637 395.494 81.6644 402.288 81.6644C416.276 81.6644 425.467 70.5618 425.467 55.6585C425.467 41.0552 417.174 29.9528 402.788 29.9528C395.494 29.9528 390.1 33.1535 387.902 36.6541V8.04785H374.815V80.4641H387.703ZM412.178 55.7584C412.178 64.7605 406.784 69.7616 399.99 69.7616C393.297 69.7616 387.703 64.6606 387.703 55.7584C387.703 46.7564 393.297 41.8554 399.99 41.8554C406.784 41.8554 412.178 46.7564 412.178 55.7584Z" fill="white"/>
<path d="M432.99 67.0609C432.99 74.7627 439.383 81.8643 449.873 81.8643C457.165 81.8643 461.862 78.4634 464.358 74.5625C464.358 76.463 464.559 79.1635 464.858 80.4639H477.046C476.748 78.7635 476.448 75.2627 476.448 72.6622V48.4567C476.448 38.5545 470.653 29.7527 455.068 29.7527C441.881 29.7527 434.788 38.2544 433.989 45.9562L445.776 48.4567C446.177 44.1558 449.374 40.455 455.167 40.455C460.763 40.455 463.46 43.3556 463.46 46.8564C463.46 48.5568 462.561 49.9572 459.763 50.3572L447.676 52.1576C439.484 53.3579 432.99 58.2589 432.99 67.0609ZM452.671 71.962C448.375 71.962 446.276 69.1614 446.276 66.2608C446.276 62.4599 448.973 60.5594 452.371 60.0594L463.46 58.359V60.5594C463.46 69.2615 458.265 71.962 452.671 71.962Z" fill="white"/>
<path d="M485.645 66.7608C486.243 72.3621 491.339 81.9642 506.124 81.9642C519.012 81.9642 525.205 73.7624 525.205 65.7607C525.205 58.559 520.311 52.6577 510.62 50.6571L503.626 49.1569C500.929 48.6568 499.132 47.1565 499.132 44.7559C499.132 41.9552 501.928 39.8549 505.425 39.8549C511.021 39.8549 513.118 43.5556 513.519 46.4564L524.607 43.9558C524.007 38.6546 519.312 29.7527 505.326 29.7527C494.735 29.7527 486.944 37.0543 486.944 45.8561C486.944 52.7576 491.238 58.4591 500.73 60.5594L507.224 62.0598C511.021 62.8599 512.519 64.6605 512.519 66.8609C512.519 69.4615 510.421 71.762 506.025 71.762C500.23 71.762 497.334 68.1611 497.034 64.2602L485.645 66.7608Z" fill="white"/>
<path d="M545.385 50.2571C545.685 45.7562 549.482 40.5549 556.375 40.5549C563.967 40.5549 567.165 45.3561 567.365 50.2571H545.385ZM568.664 63.0601C567.065 67.4609 563.668 70.5617 557.474 70.5617C550.88 70.5617 545.385 65.8606 545.087 59.3593H580.252C580.252 59.159 580.451 57.1587 580.451 55.2582C580.451 39.4547 571.361 29.7527 556.175 29.7527C543.588 29.7527 531.998 39.9548 531.998 55.6584C531.998 72.262 543.886 81.9642 557.374 81.9642C569.462 81.9642 577.255 74.8626 579.753 66.3607L568.664 63.0601Z" fill="white"/>
<path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint0_linear)"/>
<path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint1_linear)" fill-opacity="0.2"/>
<path d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z" fill="#3ECF8E"/>
<defs>
<linearGradient id="paint0_linear" x1="53.9738" y1="54.974" x2="94.1635" y2="71.8295" gradientUnits="userSpaceOnUse">
<stop stop-color="#249361"/>
<stop offset="1" stop-color="#3ECF8E"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="36.1558" y1="30.578" x2="54.4844" y2="65.0806" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg width="581" height="113" viewBox="0 0 581 113" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M151.397 66.7608C151.996 72.3621 157.091 81.9642 171.877 81.9642C184.764 81.9642 190.959 73.7624 190.959 65.7607C190.959 58.559 186.063 52.6577 176.373 50.6571L169.379 49.1569C166.682 48.6568 164.884 47.1565 164.884 44.7559C164.884 41.9552 167.681 39.8549 171.178 39.8549C176.772 39.8549 178.87 43.5556 179.27 46.4564L190.359 43.9558C189.76 38.6546 185.064 29.7527 171.078 29.7527C160.488 29.7527 152.696 37.0543 152.696 45.8561C152.696 52.7576 156.991 58.4591 166.482 60.5594L172.976 62.0598C176.772 62.8599 178.271 64.6605 178.271 66.8609C178.271 69.4615 176.173 71.762 171.777 71.762C165.983 71.762 163.085 68.1611 162.786 64.2602L151.397 66.7608Z" fill="#1F1F1F"/>
<path d="M233.421 80.4639H246.109C245.909 78.7635 245.609 75.3628 245.609 71.5618V31.2529H232.321V59.8592C232.321 65.5606 228.925 69.5614 223.031 69.5614C216.837 69.5614 214.039 65.1604 214.039 59.6592V31.2529H200.752V62.3599C200.752 73.0622 207.545 81.7642 219.434 81.7642C224.628 81.7642 230.325 79.7638 233.022 75.1627C233.022 77.1631 233.221 79.4636 233.421 80.4639Z" fill="#1F1F1F"/>
<path d="M273.076 99.4682V75.663C275.473 78.9636 280.469 81.6644 287.263 81.6644C301.149 81.6644 310.439 70.6617 310.439 55.7584C310.439 41.1553 302.148 30.1528 287.762 30.1528C280.37 30.1528 274.875 33.4534 272.677 37.2544V31.253H259.79V99.4682H273.076ZM297.352 55.8585C297.352 64.6606 291.958 69.7616 285.164 69.7616C278.372 69.7616 272.877 64.5605 272.877 55.8585C272.877 47.1566 278.372 42.0554 285.164 42.0554C291.958 42.0554 297.352 47.1566 297.352 55.8585Z" fill="#1F1F1F"/>
<path d="M317.964 67.0609C317.964 74.7627 324.357 81.8643 334.848 81.8643C342.139 81.8643 346.835 78.4634 349.332 74.5625C349.332 76.463 349.532 79.1635 349.832 80.4639H362.02C361.72 78.7635 361.422 75.2627 361.422 72.6622V48.4567C361.422 38.5545 355.627 29.7527 340.043 29.7527C326.855 29.7527 319.761 38.2544 318.963 45.9562L330.751 48.4567C331.151 44.1558 334.348 40.455 340.141 40.455C345.737 40.455 348.434 43.3556 348.434 46.8564C348.434 48.5568 347.536 49.9572 344.738 50.3572L332.65 52.1576C324.458 53.3579 317.964 58.2589 317.964 67.0609ZM337.644 71.962C333.349 71.962 331.25 69.1614 331.25 66.2608C331.25 62.4599 333.947 60.5594 337.345 60.0594L348.434 58.359V60.5594C348.434 69.2615 343.239 71.962 337.644 71.962Z" fill="#1F1F1F"/>
<path d="M387.703 80.4641V74.4627C390.299 78.6637 395.494 81.6644 402.288 81.6644C416.276 81.6644 425.467 70.5618 425.467 55.6585C425.467 41.0552 417.174 29.9528 402.788 29.9528C395.494 29.9528 390.1 33.1535 387.902 36.6541V8.04785H374.815V80.4641H387.703ZM412.178 55.7584C412.178 64.7605 406.784 69.7616 399.99 69.7616C393.297 69.7616 387.703 64.6606 387.703 55.7584C387.703 46.7564 393.297 41.8554 399.99 41.8554C406.784 41.8554 412.178 46.7564 412.178 55.7584Z" fill="#1F1F1F"/>
<path d="M432.99 67.0609C432.99 74.7627 439.383 81.8643 449.873 81.8643C457.165 81.8643 461.862 78.4634 464.358 74.5625C464.358 76.463 464.559 79.1635 464.858 80.4639H477.046C476.748 78.7635 476.448 75.2627 476.448 72.6622V48.4567C476.448 38.5545 470.653 29.7527 455.068 29.7527C441.881 29.7527 434.788 38.2544 433.989 45.9562L445.776 48.4567C446.177 44.1558 449.374 40.455 455.167 40.455C460.763 40.455 463.46 43.3556 463.46 46.8564C463.46 48.5568 462.561 49.9572 459.763 50.3572L447.676 52.1576C439.484 53.3579 432.99 58.2589 432.99 67.0609ZM452.671 71.962C448.375 71.962 446.276 69.1614 446.276 66.2608C446.276 62.4599 448.973 60.5594 452.371 60.0594L463.46 58.359V60.5594C463.46 69.2615 458.265 71.962 452.671 71.962Z" fill="#1F1F1F"/>
<path d="M485.645 66.7608C486.243 72.3621 491.339 81.9642 506.124 81.9642C519.012 81.9642 525.205 73.7624 525.205 65.7607C525.205 58.559 520.311 52.6577 510.62 50.6571L503.626 49.1569C500.929 48.6568 499.132 47.1565 499.132 44.7559C499.132 41.9552 501.928 39.8549 505.425 39.8549C511.021 39.8549 513.118 43.5556 513.519 46.4564L524.607 43.9558C524.007 38.6546 519.312 29.7527 505.326 29.7527C494.735 29.7527 486.944 37.0543 486.944 45.8561C486.944 52.7576 491.238 58.4591 500.73 60.5594L507.224 62.0598C511.021 62.8599 512.519 64.6605 512.519 66.8609C512.519 69.4615 510.421 71.762 506.025 71.762C500.23 71.762 497.334 68.1611 497.034 64.2602L485.645 66.7608Z" fill="#1F1F1F"/>
<path d="M545.385 50.2571C545.685 45.7562 549.482 40.5549 556.375 40.5549C563.967 40.5549 567.165 45.3561 567.365 50.2571H545.385ZM568.664 63.0601C567.065 67.4609 563.668 70.5617 557.474 70.5617C550.88 70.5617 545.385 65.8606 545.087 59.3593H580.252C580.252 59.159 580.451 57.1587 580.451 55.2582C580.451 39.4547 571.361 29.7527 556.175 29.7527C543.588 29.7527 531.998 39.9548 531.998 55.6584C531.998 72.262 543.886 81.9642 557.374 81.9642C569.462 81.9642 577.255 74.8626 579.753 66.3607L568.664 63.0601Z" fill="#1F1F1F"/>
<path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint0_linear)"/>
<path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint1_linear)" fill-opacity="0.2"/>
<path d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z" fill="#3ECF8E"/>
<defs>
<linearGradient id="paint0_linear" x1="53.9738" y1="54.974" x2="94.1635" y2="71.8295" gradientUnits="userSpaceOnUse">
<stop stop-color="#249361"/>
<stop offset="1" stop-color="#3ECF8E"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="36.1558" y1="30.578" x2="54.4844" y2="65.0806" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

+20
View File
@@ -3,6 +3,7 @@
import re
import pytest
import requests
from postgrest import run
@@ -54,6 +55,25 @@ def test_openapi_in_big_schema(defaultenv):
assert response.status_code == 200
def test_stackoverflow_is_logged(defaultenv):
"Stack overflow errors should be logged with the Warp error message"
env = {
**defaultenv,
"PGRST_DB_SCHEMAS": "apflora",
"PGRST_DB_ANON_ROLE": "postgrest_test_anonymous",
}
with run(env=env, wait_max_seconds=30, no_startup_stdout=False) as postgrest:
with pytest.raises(requests.exceptions.ConnectionError):
postgrest.session.get("/")
output = postgrest.read_stdout(nlines=10)
output.extend(postgrest.read_stdout(nlines=10))
assert any("Warp server error: stack overflow" in line for line in output)
# See: https://github.com/PostgREST/postgrest/issues/3329
def test_should_not_fail_with_stack_overflow(defaultenv):
"requesting a non-existent relationship should not fail with stack overflow due to fuzzy search of candidates"
+18
View File
@@ -105,6 +105,24 @@ def test_flush_pool_no_interrupt(defaultenv):
t.join()
def test_termination_unix_signal_logging(defaultenv):
"Server logs when handling termination unix signals."
with run(env=defaultenv) as postgrest:
postgrest.process.send_signal(signal.SIGTERM)
lines = postgrest.read_stdout(nlines=1)
wait_until_exit(postgrest)
assert any("SIGTERM" in line for line in lines)
with run(env=defaultenv) as postgrest:
postgrest.process.send_signal(signal.SIGINT)
lines = postgrest.read_stdout(nlines=1)
wait_until_exit(postgrest)
assert any("SIGINT" in line for line in lines)
def test_random_port_bound(defaultenv):
"PostgREST should bind to a random port when PGRST_SERVER_PORT is 0."
+55
View File
@@ -0,0 +1,55 @@
module Main where
import qualified Hasql.Pool as P
import qualified Hasql.Pool.Config as P
import qualified Hasql.Transaction.Sessions as HT
import Data.Function (id)
import PostgREST.App (postgrest)
import qualified PostgREST.AppState as AppState
import PostgREST.Config (AppConfig (..))
import PostgREST.Config.Database (queryPgVersion)
import qualified PostgREST.Logger as Logger
import qualified PostgREST.Metrics as Metrics
import PostgREST.SchemaCache (querySchemaCache)
import qualified Observation.JwtCache
import ObsHelper
import Protolude hiding (toList, toS)
import Test.Hspec
main :: IO ()
main = do
pool <- P.acquire $ P.settings
[ P.size 3
, P.acquisitionTimeout 10
, P.agingTimeout 60
, P.idlenessTimeout 60
, P.staticConnectionSettings (toUtf8 $ configDbUri testCfg)
]
actualPgVersion <- either (panic . show) id <$> P.use pool (queryPgVersion False)
-- cached schema cache so most tests run fast
baseSchemaCache <- loadSCache pool testCfg
sockets <- AppState.initSockets testCfg
loggerState <- Logger.init
metricsState <- Metrics.init (configDbPoolSize testCfg)
let
initApp sCache st config = do
appState <- AppState.initWithPool sockets pool config loggerState metricsState (Metrics.observationMetrics metricsState)
AppState.putPgVersion appState actualPgVersion
AppState.putSchemaCache appState (Just sCache)
return (st, postgrest (configLogLevel config) appState (pure ()))
-- Run all test modules
hspec $ do
before (initApp baseSchemaCache metricsState testCfgJwtCache) $
describe "Observation.JwtCacheObs" Observation.JwtCache.spec
where
loadSCache pool conf =
either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ querySchemaCache conf)
+133
View File
@@ -0,0 +1,133 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
module ObsHelper where
import qualified Data.ByteString.Base64 as B64 (decodeLenient)
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as BL
import qualified Jose.Jwa as JWT
import qualified Jose.Jws as JWT
import qualified Jose.Jwt as JWT
import PostgREST.Config (AppConfig (..), JSPathExp (..),
LogLevel (..), OpenAPIMode (..), parseSecret)
import Data.List.NonEmpty (fromList)
import Data.String (String)
import Prometheus (Counter, getCounter)
import Test.Hspec.Expectations.Contrib (annotate)
import Network.HTTP.Types
import Protolude
import Test.Hspec
import Test.Hspec.Wai
baseCfg :: AppConfig
baseCfg = let secret = encodeUtf8 "reallyreallyreallyreallyverysafe" in
AppConfig {
configAppSettings = []
, configDbAggregates = False
, configDbAnonRole = Just "postgrest_test_anonymous"
, configDbChannel = mempty
, configDbChannelEnabled = True
, configDbExtraSearchPath = []
, configDbHoistedTxSettings = ["default_transaction_isolation","plan_filter.statement_cost_limit","statement_timeout"]
, configDbMaxRows = Nothing
, configDbPlanEnabled = False
, configDbPoolSize = 10
, configDbPoolAcquisitionTimeout = 10
, configDbPoolMaxLifetime = 1800
, configDbPoolMaxIdletime = 600
, configDbPoolAutomaticRecovery = True
, configDbPreRequest = Nothing
, configDbPreparedStatements = True
, configDbRootSpec = Nothing
, configDbSchemas = fromList ["test"]
, configDbConfig = False
, configDbPreConfig = Nothing
, configDbUri = "postgresql://"
, configFilePath = Nothing
, configJWKS = rightToMaybe $ parseSecret secret
, configJwtAudience = Nothing
, configJwtRoleClaimKey = [JSPKey "role"]
, configJwtSecret = Just secret
, configJwtSecretIsBase64 = False
, configJwtCacheMaxEntries = 10
, configLogLevel = LogCrit
, configLogQuery = False
, configOpenApiMode = OAFollowPriv
, configOpenApiSecurityActive = False
, configOpenApiServerProxyUri = Nothing
, configServerCorsAllowedOrigins = Nothing
, configServerHost = "localhost"
, configServerPort = 3000
, configServerTraceHeader = Nothing
, configServerUnixSocket = Nothing
, configServerUnixSocketMode = 432
, configDbTxAllowOverride = True
, configDbTxRollbackAll = True
, configAdminServerHost = "localhost"
, configAdminServerPort = Nothing
, configRoleSettings = mempty
, configRoleIsoLvl = mempty
, configInternalSCQuerySleep = Nothing
, configInternalSCLoadSleep = Nothing
, configInternalSCRelLoadSleep = Nothing
, configServerTimingEnabled = True
}
testCfg :: AppConfig
testCfg = baseCfg
testCfgJwtCache :: AppConfig
testCfgJwtCache =
baseCfg {
configJwtSecret = Just generateSecret
, configJWKS = rightToMaybe $ parseSecret generateSecret
, configJwtCacheMaxEntries = 2
}
authHeader :: BS.ByteString -> BS.ByteString -> Header
authHeader typ creds =
(hAuthorization, typ <> " " <> creds)
authHeaderJWT :: BS.ByteString -> Header
authHeaderJWT = authHeader "Bearer"
generateSecret :: ByteString
generateSecret = B64.decodeLenient "cmVhbGx5cmVhbGx5cmVhbGx5cmVhbGx5dmVyeXNhZmU="
generateJWT :: BL.ByteString -> ByteString
generateJWT claims =
either mempty JWT.unJwt $ JWT.hmacEncode JWT.HS256 generateSecret (BL.toStrict claims)
-- state check helpers
data StateCheck st m = forall a. StateCheck (st -> (String, m a)) (a -> a -> Expectation)
stateCheck :: (Show a, Eq a) => (c -> m a) -> (st -> (String, c)) -> (a -> a) -> StateCheck st m
stateCheck extractValue extractComponent expect = StateCheck (second extractValue . extractComponent) (flip shouldBe . expect)
expectField :: forall s st a c m. (KnownSymbol s, Show a, Eq a, HasField s st c) => (c -> m a) -> (a -> a) -> StateCheck st m
expectField extractValue = stateCheck extractValue ((symbolVal (Proxy @s),) . getField @s)
checkState :: (Traversable t) => t (StateCheck st (WaiSession st)) -> WaiSession st b -> WaiSession st ()
checkState checks act = getState >>= flip (`checkState'` checks) act
checkState' :: (Traversable t, MonadIO m) => st -> t (StateCheck st m) -> m b -> m ()
checkState' initialState checks act = do
expectations <- traverse (\(StateCheck g expect) -> let (msg, m) = g initialState in m >>= createExpectation msg m . expect) checks
void act
sequenceA_ expectations
where
createExpectation msg metrics expect = pure $ metrics >>= liftIO . annotate msg . expect
expectCounter :: forall s st m. (KnownSymbol s, HasField s st Counter, MonadIO m) => (Int -> Int) -> StateCheck st m
expectCounter = expectField @s intCounter
where
intCounter = ((round @Double @Int) <$>) . getCounter
@@ -1,29 +1,17 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Feature.Auth.JwtCacheSpec
where
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
module Observation.JwtCache where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec (Expectation, SpecWith, describe, it,
shouldBe)
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec.Wai
import Data.String (String)
import PostgREST.Metrics (MetricsState (..))
import Prometheus (getCounter)
import ObsHelper
import PostgREST.Metrics (MetricsState (..))
import Protolude
import SpecHelper
import Test.Hspec.Expectations.Contrib (annotate)
import Test.Hspec.Wai.JSON (json)
import Test.Hspec.Wai.JSON (json)
spec :: SpecWith (MetricsState, Application)
spec = describe "Server started with JWT and metrics enabled" $ do
@@ -36,7 +24,7 @@ spec = describe "Server started with JWT and metrics enabled" $ do
, hits (+ 0)
] $
request methodGet "/authors_only" [auth] ""
request methodGet "/authors_only" [auth] "" `shouldRespondWith` 200
it "Should have JWT in cache" $ do
let auth = genToken [json|{"exp": 9999999999, "role": "postgrest_test_author", "id": "jdoe2"}|]
@@ -143,25 +131,8 @@ spec = describe "Server started with JWT and metrics enabled" $ do
*> request methodGet "/authors_only" [jwt3] ""
where
counterToInt = second (fmap (round @Double @Int) . getCounter)
expectCounters = stateCheck . fmap (\(g, h) -> StateCheck (counterToInt . g) (flip shouldBe . h))
genToken = authHeaderJWT . generateJWT
requests = (,) (getF @"jwtCacheRequests")
hits = (,) (getF @"jwtCacheHits")
evictions = (,) (getF @"jwtCacheEvictions")
-- should be moved to helpers???
getF :: forall s r a. (KnownSymbol s, HasField s r a) => r -> (String, a)
getF r = (symbolVal (Proxy @s), getField @s r)
data StateCheck st = forall a. (Show a, Eq a) => StateCheck (st -> (String, WaiSession st a)) (a -> a -> Expectation)
stateCheck :: (Traversable t) => t (StateCheck st) -> WaiSession st a -> WaiSession st ()
stateCheck checks act = do
metrics <- getState
expectations <- traverse (\(StateCheck g expect) -> let (msg, m) = g metrics in m >>= createExpectation msg m . expect) checks
void act
sequenceA_ expectations
where
createExpectation msg metrics expect = pure $ metrics >>= liftIO . annotate msg . expect
requests = expectCounter @"jwtCacheRequests"
hits = expectCounter @"jwtCacheHits"
evictions = expectCounter @"jwtCacheEvictions"
expectCounters = checkState
+2
View File
@@ -0,0 +1,2 @@
-- Suppress NOTICE: ... messages
SET client_min_messages TO warning;
+8
View File
@@ -0,0 +1,8 @@
-- Loads all fixtures for the PostgREST observability tests
\set ON_ERROR_STOP on
\ir database.sql
\ir roles.sql
\ir schema.sql
\ir privileges.sql
@@ -0,0 +1,9 @@
-- Schema test objects
SET search_path = test, pg_catalog;
GRANT USAGE ON SCHEMA test TO postgrest_test_anonymous;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA test TO postgrest_test_anonymous;
REVOKE ALL PRIVILEGES ON TABLE authors_only FROM postgrest_test_anonymous;
GRANT USAGE ON SCHEMA test TO postgrest_test_author;
GRANT ALL ON TABLE authors_only TO postgrest_test_author;
+5
View File
@@ -0,0 +1,5 @@
DROP ROLE IF EXISTS postgrest_test_anonymous, postgrest_test_author;
CREATE ROLE postgrest_test_anonymous;
CREATE ROLE postgrest_test_author;
GRANT postgrest_test_anonymous, postgrest_test_author TO :PGUSER;
+21
View File
@@ -0,0 +1,21 @@
DROP SCHEMA IF EXISTS test;
CREATE SCHEMA test;
SET search_path = test, pg_catalog;
--
-- Name: authors_only; Type: TABLE; Schema: test; Owner: -
--
CREATE TABLE authors_only (
owner character varying NOT NULL,
secret character varying NOT NULL
);
--
-- Name: authors_only_pkey; Type: CONSTRAINT; Schema: test; Owner: -
--
ALTER TABLE ONLY authors_only
ADD CONSTRAINT authors_only_pkey PRIMARY KEY (secret);
+1 -1
View File
@@ -25,7 +25,7 @@ spec =
raceTest 10 $
get "/fakefake"
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fakefake' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.fakefake' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = []
}
+20 -2
View File
@@ -252,21 +252,39 @@ spec =
[json|[{"id": 7, "entities":null}, {"id": 8, "entities": {"id": 2}}, {"id": 9, "entities": {"id": 3}}]|]
{ matchStatus = 201 }
context "used with PATCH" $
context "used with PATCH" $ do
it "succeeds when using and/or params" $
request methodPatch "/grandchild_entities?or=(id.eq.1,id.eq.2)&select=id,name"
[("Prefer", "return=representation")]
[json|{ name : "updated grandchild entity"}|] `shouldRespondWith`
[json|[{ "id": 1, "name" : "updated grandchild entity"},{ "id": 2, "name" : "updated grandchild entity"}]|]
{ matchHeaders = [matchContentTypeJson] }
it "succeeds when the filtered column is modified" $
request methodPatch "/entities?select=id,name&or=(name.is.null,name.like.*test*)"
[("Prefer", "return=representation")]
[json|{ "name" : "updated entity" }|] `shouldRespondWith`
[json|[{ "id": 4, "name": "updated entity" }]|]
{ matchHeaders = [matchContentTypeJson] }
it "succeeds when the filtered column is not selected in the returned representation" $
request methodPatch "/entities?select=id&or=(name.is.null,name.like.*test*)"
[("Prefer", "return=representation")]
[json|{ "name" : "updated entity" }|] `shouldRespondWith`
[json|[{ "id": 4 }]|]
{ matchHeaders = [matchContentTypeJson] }
context "used with DELETE" $
context "used with DELETE" $ do
it "succeeds when using and/or params" $
request methodDelete "/grandchild_entities?or=(id.eq.1,id.eq.2)&select=id,name"
[("Prefer", "return=representation")]
""
`shouldRespondWith`
[json|[{ "id": 1, "name" : "grandchild entity 1" },{ "id": 2, "name" : "grandchild entity 2" }]|]
it "succeeds when the filtered column is not selected in the returned representation" $
request methodDelete "/entities?select=id&or=(name.is.null,name.like.*test*)"
[("Prefer", "return=representation")]
""
`shouldRespondWith`
[json|[{ "id": 4 }]|]
it "can query columns that begin with and/or reserved words" $
get "/grandchild_entities?or=(and_starting_col.eq.smth, or_starting_col.eq.smth)" `shouldRespondWith` 200
+1 -1
View File
@@ -116,7 +116,7 @@ spec =
it "fails with 404" $
request methodDelete "/foozle?id=eq.101" [] ""
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.foo'","message":"Could not find the table 'test.foozle' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.foozle' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = []
}
+29 -2
View File
@@ -42,10 +42,10 @@ pgErrorCodeMapping = do
it "works with SchemaCache error" $
get "/non_existent_table"
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.collision_test_table'","message":"Could not find the table 'test.non_existent_table' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.non_existent_table' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
, "Content-Length" <:> "182" ]
, "Content-Length" <:> "129" ]
}
it "works with Jwt error" $ do
@@ -75,3 +75,30 @@ pgErrorCodeMapping = do
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=123"
, "Content-Length" <:> "59" ]
}
context "show hint on PGRST205 table not found error" $ do
it "show hint when similarity score is at least 75%" $ do
get "/projectx" -- at least 75% similar to "projects"
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.projects'","message":"Could not find the table 'test.projectx' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
, "Content-Length" <:> "160" ]
}
get "/projecxx" -- at least 75% similar to "projects"
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.projects'","message":"Could not find the table 'test.projecxx' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
, "Content-Length" <:> "160" ]
}
it "don't show hint when similarity score is less than 75%" $
get "/projxxxx" -- less than 75% similar to "projects"
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.projxxxx' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
, "Content-Length" <:> "119" ]
}
+1 -1
View File
@@ -483,7 +483,7 @@ spec actualPgVersion = do
{"id": 204, "body": "yyy"},
{"id": 205, "body": "zzz"}]|]
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.garlic' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = []
}
+4 -4
View File
@@ -26,9 +26,9 @@ spec = do
it "causes a 404" $
get "/faketable"
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.private_table'","message":"Could not find the table 'test.faketable' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.faketable' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = ["Content-Length" <:> "166"]
, matchHeaders = ["Content-Length" <:> "120"]
}
describe "Filtering response" $ do
@@ -852,7 +852,7 @@ spec = do
-- the existence of first table, #3869
it "table not found error if first table does not exist" $
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.car_model_sales'","message":"Could not find the table 'test.car_model_sales_202101' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.car_model_sales_202101' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = [matchContentTypeJson]
}
@@ -870,7 +870,7 @@ spec = do
it "table not found error if first table does not exist" $
get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.car_model_sales'","message":"Could not find the table 'test.car_models_default' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.car_models_default' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = [matchContentTypeJson]
}
+4 -4
View File
@@ -18,9 +18,9 @@ spec = do
request methodPatch "/fake" []
[json| { "real": false } |]
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fake' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.fake' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = ["Content-Length" <:> "157"]
, matchHeaders = ["Content-Length" <:> "115"]
}
@@ -363,9 +363,9 @@ spec = do
{"id": 204, "body": "yyy"},
{"id": 205, "body": "zzz"}]|]
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |]
[json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'test.garlic' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = ["Content-Length" <:> "158"]
, matchHeaders = ["Content-Length" <:> "117"]
}
context "apply defaults on missing values" $ do
-4
View File
@@ -23,7 +23,6 @@ import qualified Feature.Auth.AsymmetricJwtSpec
import qualified Feature.Auth.AudienceJwtSecretSpec
import qualified Feature.Auth.AuthSpec
import qualified Feature.Auth.BinaryJwtSecretSpec
import qualified Feature.Auth.JwtCacheSpec
import qualified Feature.Auth.NoAnonSpec
import qualified Feature.Auth.NoJwtSecretSpec
import qualified Feature.ConcurrentSpec
@@ -275,9 +274,6 @@ main = do
before pgSafeUpdateApp $
describe "Feature.Query.PgSafeUpdateSpec.spec" Feature.Query.PgSafeUpdateSpec.spec
before (initApp baseSchemaCache metricsState testCfgJwtCache) $
describe "Feature.Auth.JwtCacheSpec" Feature.Auth.JwtCacheSpec.spec
where
loadSCache pool conf =
either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ querySchemaCache conf)
-9
View File
@@ -1,4 +1,3 @@
{-# LANGUAGE FlexibleContexts #-}
module SpecHelper where
import Control.Lens ((^?))
@@ -206,14 +205,6 @@ testCfgBinaryJWT =
, configJWKS = rightToMaybe $ parseSecret generateSecret
}
testCfgJwtCache :: AppConfig
testCfgJwtCache =
baseCfg {
configJwtSecret = Just generateSecret
, configJWKS = rightToMaybe $ parseSecret generateSecret
, configJwtCacheMaxEntries = 2
}
testCfgAudienceJWT :: AppConfig
testCfgAudienceJWT =
baseCfg {