Compare commits
63
Commits
feat/openapi
...
v9.0.1
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script builds PostgREST in a remote ARM server
|
||||||
|
|
||||||
|
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
|
||||||
|
[ -z "$2" ] && { echo "Missing 2nd argument: Build environment directory name"; exit 1; }
|
||||||
|
|
||||||
|
PGRST_GITHUB_COMMIT="$1"
|
||||||
|
SCRIPT_DIR="$2"
|
||||||
|
|
||||||
|
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
|
||||||
|
|
||||||
|
install_packages() {
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get upgrade -y
|
||||||
|
sudo apt-get install -y git build-essential curl libffi-dev libffi7 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5 llvm libnuma-dev zlib1g-dev libpq-dev jq gcc
|
||||||
|
sudo apt-get clean
|
||||||
|
}
|
||||||
|
|
||||||
|
install_ghcup() {
|
||||||
|
export BOOTSTRAP_HASKELL_NONINTERACTIVE=1
|
||||||
|
export BOOTSTRAP_HASKELL_MINIMAL=1
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
|
||||||
|
source ~/.ghcup/env
|
||||||
|
}
|
||||||
|
|
||||||
|
install_cabal() {
|
||||||
|
ghcup upgrade
|
||||||
|
ghcup install cabal 3.6.0.0
|
||||||
|
ghcup set cabal 3.6.0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
install_ghc() {
|
||||||
|
ghcup install ghc 8.10.7
|
||||||
|
ghcup set ghc 8.10.7
|
||||||
|
}
|
||||||
|
|
||||||
|
install_packages
|
||||||
|
|
||||||
|
# Add ghcup to the PATH for this session
|
||||||
|
[ -f ~/.ghcup/env ] && source ~/.ghcup/env
|
||||||
|
|
||||||
|
ghcup --version || install_ghcup
|
||||||
|
cabal --version || install_cabal
|
||||||
|
ghc --version || install_ghc
|
||||||
|
|
||||||
|
cd ~/$SCRIPT_DIR
|
||||||
|
|
||||||
|
# Clone the repository and build the project
|
||||||
|
git clone https://github.com/PostgREST/postgrest.git
|
||||||
|
cd postgrest
|
||||||
|
git checkout $PGRST_GITHUB_COMMIT
|
||||||
|
cabal v2-update && cabal v2-build
|
||||||
|
|
||||||
|
# Copy the built binary to the Dockerfile directory
|
||||||
|
PGRST_BIN=$(cabal exec which postgrest | tail -1)
|
||||||
|
cp $PGRST_BIN ~/$DOCKER_BUILD_DIR
|
||||||
|
|
||||||
|
# Move and compress the built binary
|
||||||
|
mkdir -p ~/$SCRIPT_DIR/result
|
||||||
|
mv $PGRST_BIN ~/$SCRIPT_DIR/result
|
||||||
|
cd ~/$SCRIPT_DIR
|
||||||
|
tar -cJf result.tar.xz result
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# PostgREST docker hub image
|
||||||
|
|
||||||
|
FROM ubuntu:focal AS postgrest
|
||||||
|
|
||||||
|
RUN apt-get update -y \
|
||||||
|
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY postgrest /usr/bin/postgrest
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
USER 1000
|
||||||
|
|
||||||
|
CMD postgrest
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script publishes the Docker ARM images to Docker Hub.
|
||||||
|
|
||||||
|
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
|
||||||
|
[ -z "$2" ] && { echo "Missing 2nd argument: Docker repo"; exit 1; }
|
||||||
|
[ -z "$3" ] && { echo "Missing 3rd argument: Docker username"; exit 1; }
|
||||||
|
[ -z "$4" ] && { echo "Missing 4th argument: Docker password"; exit 1; }
|
||||||
|
[ -z "$5" ] && { echo "Missing 5th argument: Build environment directory name"; exit 1; }
|
||||||
|
[ -z "$6" ] && { echo "Missing 6th argument: PostgREST version"; exit 1; }
|
||||||
|
|
||||||
|
PGRST_GITHUB_COMMIT="$1"
|
||||||
|
DOCKER_REPO="$2"
|
||||||
|
DOCKER_USER="$3"
|
||||||
|
DOCKER_PASS="$4"
|
||||||
|
SCRIPT_DIR="$5"
|
||||||
|
PGRST_VERSION="v$6"
|
||||||
|
IS_PRERELEASE="$7"
|
||||||
|
|
||||||
|
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
|
||||||
|
|
||||||
|
clean_env()
|
||||||
|
{
|
||||||
|
sudo docker logout
|
||||||
|
}
|
||||||
|
|
||||||
|
# Login to Docker
|
||||||
|
sudo docker logout
|
||||||
|
{ echo $DOCKER_PASS | sudo docker login -u $DOCKER_USER --password-stdin; } || { echo "Couldn't login to docker"; exit 1; }
|
||||||
|
|
||||||
|
trap clean_env sigint sigterm exit
|
||||||
|
|
||||||
|
# Move to the docker build environment
|
||||||
|
cd ~/$DOCKER_BUILD_DIR
|
||||||
|
|
||||||
|
# Push final images to Docker hub
|
||||||
|
# NOTE: This command publishes a separate ARM image because the builds cannot
|
||||||
|
# be added to the manifest if they are not in the registry beforehand.
|
||||||
|
# This image must be manually deleted from Docker Hub at the end of the process.
|
||||||
|
sudo docker buildx build --build-arg PGRST_GITHUB_COMMIT=$PGRST_GITHUB_COMMIT \
|
||||||
|
-t $DOCKER_REPO/postgrest:$PGRST_VERSION-arm \
|
||||||
|
--push .
|
||||||
|
|
||||||
|
# Add the arm images to the manifest
|
||||||
|
# NOTE: This assumes that there already is a `postgrest:<version>` image
|
||||||
|
# for the amd64 architecture pushed to Docker Hub
|
||||||
|
sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:$PGRST_VERSION $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
|
||||||
|
[ -z $IS_PRERELEASE ] && sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:latest $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
|
||||||
|
|
||||||
|
sudo docker logout
|
||||||
+171
-59
@@ -4,18 +4,20 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- rel-*
|
||||||
tags:
|
tags:
|
||||||
- v*
|
- v*
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- rel-*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Lint-Style:
|
Lint-Style:
|
||||||
name: Lint & check code style
|
name: Lint & check code style
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -35,39 +37,19 @@ jobs:
|
|||||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
tools: tests withTools
|
tools: tests
|
||||||
|
|
||||||
- name: Run coverage (IO tests and Spec tests against PostgreSQL 14)
|
- name: Run coverage (IO tests and Spec tests against PostgreSQL 14)
|
||||||
run: postgrest-coverage
|
run: postgrest-coverage
|
||||||
- name: Upload coverage to codecov
|
- name: Upload coverage to codecov
|
||||||
uses: codecov/codecov-action@v2.1.0
|
uses: codecov/codecov-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
files: ./coverage/codecov.json
|
files: ./coverage/codecov.json
|
||||||
|
|
||||||
- name: Run the spec tests against PostgreSQL 13
|
|
||||||
if: always()
|
|
||||||
run: postgrest-with-postgresql-13 postgrest-test-spec
|
|
||||||
- name: Run the spec tests against PostgreSQL 12
|
|
||||||
if: always()
|
|
||||||
run: postgrest-with-postgresql-12 postgrest-test-spec
|
|
||||||
- name: Run the spec tests against PostgreSQL 11
|
|
||||||
if: always()
|
|
||||||
run: postgrest-with-postgresql-11 postgrest-test-spec
|
|
||||||
- name: Run the spec tests against PostgreSQL 10
|
|
||||||
if: always()
|
|
||||||
run: postgrest-with-postgresql-10 postgrest-test-spec
|
|
||||||
- name: Run the spec tests against PostgreSQL 9.6
|
|
||||||
if: always()
|
|
||||||
run: postgrest-with-postgresql-9.6 postgrest-test-spec
|
|
||||||
|
|
||||||
- name: Run query cost tests against all PostgreSQL versions
|
|
||||||
if: always()
|
|
||||||
run: postgrest-with-all postgrest-test-querycost
|
|
||||||
|
|
||||||
- name: Run doctests
|
- name: Run doctests
|
||||||
if: always()
|
if: always()
|
||||||
run: nix-shell --run postgrest-test-doctests
|
run: nix-shell --run postgrest-test-doctests
|
||||||
@@ -77,11 +59,43 @@ jobs:
|
|||||||
run: postgrest-test-spec-idempotence
|
run: postgrest-test-spec-idempotence
|
||||||
|
|
||||||
|
|
||||||
|
Test-Pg-Nix:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pgVersion: [9.6, 10, 11, 12, 13, 14]
|
||||||
|
name: Test PG ${{ matrix.pgVersion }} (Nix)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
# Hack for enabling color output, see:
|
||||||
|
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||||
|
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup Nix Environment
|
||||||
|
uses: ./.github/actions/setup-nix
|
||||||
|
with:
|
||||||
|
tools: tests withTools
|
||||||
|
|
||||||
|
- name: Run spec tests
|
||||||
|
if: always()
|
||||||
|
run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-spec
|
||||||
|
|
||||||
|
- name: Run IO tests
|
||||||
|
if: always()
|
||||||
|
run: postgrest-with-postgresql-${{ matrix.pgVersion }} -f test/io/fixtures.sql postgrest-test-io
|
||||||
|
|
||||||
|
- name: Run query cost tests
|
||||||
|
if: always()
|
||||||
|
run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-querycost
|
||||||
|
|
||||||
|
|
||||||
Test-Memory-Nix:
|
Test-Memory-Nix:
|
||||||
name: Test memory (Nix)
|
name: Test memory (Nix)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -90,11 +104,11 @@ jobs:
|
|||||||
run: postgrest-test-memory
|
run: postgrest-test-memory
|
||||||
|
|
||||||
|
|
||||||
Build-Nix:
|
Build-Static-Nix:
|
||||||
name: Build Linux static (Nix)
|
name: Build Linux static (Nix)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -103,7 +117,7 @@ jobs:
|
|||||||
- name: Build static executable
|
- name: Build static executable
|
||||||
run: nix-build -A postgrestStatic
|
run: nix-build -A postgrestStatic
|
||||||
- name: Save built executable as artifact
|
- name: Save built executable as artifact
|
||||||
uses: actions/upload-artifact@v2.2.4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: postgrest-linux-static-x64
|
name: postgrest-linux-static-x64
|
||||||
path: result/bin/postgrest
|
path: result/bin/postgrest
|
||||||
@@ -112,7 +126,7 @@ jobs:
|
|||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
|
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
|
||||||
- name: Save built Docker image as artifact
|
- name: Save built Docker image as artifact
|
||||||
uses: actions/upload-artifact@v2.2.4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: postgrest-docker-x64
|
name: postgrest-docker-x64
|
||||||
path: postgrest-docker.tar.gz
|
path: postgrest-docker.tar.gz
|
||||||
@@ -155,8 +169,7 @@ jobs:
|
|||||||
~\AppData\Roaming\stack
|
~\AppData\Roaming\stack
|
||||||
~\AppData\Local\Programs\stack
|
~\AppData\Local\Programs\stack
|
||||||
.stack-work
|
.stack-work
|
||||||
deps: |
|
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
|
||||||
stack exec -- pacman -S mingw64/mingw-w64-x86_64-postgresql --noconfirm
|
|
||||||
# We'd need to make test/with_tmp_db run on Windows first
|
# We'd need to make test/with_tmp_db run on Windows first
|
||||||
# test: true
|
# test: true
|
||||||
artifact: postgrest-windows-x64
|
artifact: postgrest-windows-x64
|
||||||
@@ -164,9 +177,9 @@ jobs:
|
|||||||
name: Build ${{ matrix.name }} (Stack)
|
name: Build ${{ matrix.name }} (Stack)
|
||||||
runs-on: ${{ matrix.runs-on }}
|
runs-on: ${{ matrix.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Stack working files cache
|
- name: Stack working files cache
|
||||||
uses: actions/cache@v2.1.7
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ matrix.cache }}
|
path: ${{ matrix.cache }}
|
||||||
key: ${{ runner.os }}-${{ hashFiles('stack.yaml.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('stack.yaml.lock') }}
|
||||||
@@ -182,7 +195,7 @@ jobs:
|
|||||||
echo "Using PostgreSQL binaries at $postgresql_bin ..."
|
echo "Using PostgreSQL binaries at $postgresql_bin ..."
|
||||||
PATH="$postgresql_bin:$PATH" test/with_tmp_db stack test
|
PATH="$postgresql_bin:$PATH" test/with_tmp_db stack test
|
||||||
- name: Save built executable as artifact
|
- name: Save built executable as artifact
|
||||||
uses: actions/upload-artifact@v2.2.4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.artifact }}
|
name: ${{ matrix.artifact }}
|
||||||
path: |
|
path: |
|
||||||
@@ -190,24 +203,78 @@ jobs:
|
|||||||
result/postgrest.exe
|
result/postgrest.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
Get-FreeBSD-CirrusCI:
|
Get-FreeBSD-CirrusCI:
|
||||||
name: Get FreeBSD build from CirrusCI
|
name: Get FreeBSD build from CirrusCI
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Get FreeBSD executable from CirrusCI
|
- name: Get FreeBSD executable from CirrusCI
|
||||||
env:
|
env:
|
||||||
# GITHUB_SHA does weird things for pull request, so we roll our own:
|
# GITHUB_SHA does weird things for pull request, so we roll our own:
|
||||||
GITHUB_COMMIT: ${{github.event.pull_request.head.sha || github.sha}}
|
GITHUB_COMMIT: ${{github.event.pull_request.head.sha || github.sha}}
|
||||||
run: .github/get_cirrusci_freebsd
|
run: .github/get_cirrusci_freebsd
|
||||||
- name: Save executable as artifact
|
- name: Save executable as artifact
|
||||||
uses: actions/upload-artifact@v2.2.4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: postgrest-freebsd-x64
|
name: postgrest-freebsd-x64
|
||||||
path: postgrest
|
path: postgrest
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
Build-Cabal-Arm:
|
||||||
|
name: Build aarch64 (Cabal)
|
||||||
|
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/rel-') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
remotepath: ${{ steps.Remote-Dir.outputs.remotepath }}
|
||||||
|
env:
|
||||||
|
GITHUB_COMMIT: ${{ github.sha }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
- id: Remote-Dir
|
||||||
|
name: Unique directory name for the remote build
|
||||||
|
run: echo "::set-output name=remotepath::postgrest-build-$(uuidgen)"
|
||||||
|
- name: Copy script files to the remote server
|
||||||
|
uses: appleboy/scp-action@master
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_ARM_HOST }}
|
||||||
|
username: ubuntu
|
||||||
|
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||||
|
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||||
|
source: ".github/scripts/arm/*"
|
||||||
|
target: ${{ steps.Remote-Dir.outputs.remotepath }}
|
||||||
|
strip_components: 3
|
||||||
|
- name: Build ARM
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
|
env:
|
||||||
|
REMOTE_DIR: ${{ steps.Remote-Dir.outputs.remotepath }}
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_ARM_HOST }}
|
||||||
|
username: ubuntu
|
||||||
|
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||||
|
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||||
|
command_timeout: 120m
|
||||||
|
script_stop: true
|
||||||
|
envs: GITHUB_COMMIT,REMOTE_DIR
|
||||||
|
script: bash ~/$REMOTE_DIR/build.sh "$GITHUB_COMMIT" "$REMOTE_DIR"
|
||||||
|
- name: Download binaries from remote server
|
||||||
|
uses: nicklasfrahm/scp-action@main
|
||||||
|
with:
|
||||||
|
direction: download
|
||||||
|
host: ${{ secrets.SSH_ARM_HOST }}
|
||||||
|
username: ubuntu
|
||||||
|
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||||
|
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||||
|
source: "${{ steps.Remote-Dir.outputs.remotepath }}/result.tar.xz"
|
||||||
|
target: "result.tar.xz"
|
||||||
|
- name: Extract downloaded binaries
|
||||||
|
run: tar -xvf result.tar.xz && rm result.tar.xz
|
||||||
|
- name: Save aarch64 executable as artifact
|
||||||
|
uses: actions/upload-artifact@v2.3.1
|
||||||
|
with:
|
||||||
|
name: postgrest-ubuntu-aarch64
|
||||||
|
path: result/postgrest
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
Prepare-Release:
|
Prepare-Release:
|
||||||
name: Prepare release
|
name: Prepare release
|
||||||
@@ -216,15 +283,17 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- Lint-Style
|
- Lint-Style
|
||||||
- Test-Nix
|
- Test-Nix
|
||||||
|
- Test-Pg-Nix
|
||||||
- Test-Memory-Nix
|
- Test-Memory-Nix
|
||||||
- Build-Nix
|
- Build-Static-Nix
|
||||||
- Build-Stack
|
- Build-Stack
|
||||||
- Get-FreeBSD-CirrusCI
|
#- Get-FreeBSD-CirrusCI
|
||||||
|
- Build-Cabal-Arm
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.Identify-Version.outputs.version }}
|
version: ${{ steps.Identify-Version.outputs.version }}
|
||||||
isprerelease: ${{ steps.Identify-Version.outputs.isprerelease }}
|
isprerelease: ${{ steps.Identify-Version.outputs.isprerelease }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- id: Identify-Version
|
- id: Identify-Version
|
||||||
name: Identify the version to be released
|
name: Identify the version to be released
|
||||||
run: |
|
run: |
|
||||||
@@ -261,7 +330,7 @@ jobs:
|
|||||||
echo "Relevant extract from CHANGELOG.md:"
|
echo "Relevant extract from CHANGELOG.md:"
|
||||||
cat CHANGES.md
|
cat CHANGES.md
|
||||||
- name: Save CHANGES.md as artifact
|
- name: Save CHANGES.md as artifact
|
||||||
uses: actions/upload-artifact@v2.2.4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: release-changes
|
name: release-changes
|
||||||
path: CHANGES.md
|
path: CHANGES.md
|
||||||
@@ -277,9 +346,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
VERSION: ${{ needs.Prepare-Release.outputs.version }}
|
VERSION: ${{ needs.Prepare-Release.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v2.0.10
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: Create release bundle with archives for all builds
|
- name: Create release bundle with archives for all builds
|
||||||
@@ -299,14 +368,18 @@ jobs:
|
|||||||
tar cJvf "release-bundle/postgrest-v$VERSION-macos-x64.tar.xz" \
|
tar cJvf "release-bundle/postgrest-v$VERSION-macos-x64.tar.xz" \
|
||||||
-C artifacts/postgrest-macos-x64 postgrest
|
-C artifacts/postgrest-macos-x64 postgrest
|
||||||
|
|
||||||
tar cJvf "release-bundle/postgrest-v$VERSION-freebsd-x64.tar.xz" \
|
# TODO: Fix timeouts for FreeBSD builds in Cirrus
|
||||||
-C artifacts/postgrest-freebsd-x64 postgrest
|
#tar cJvf "release-bundle/postgrest-v$VERSION-freebsd-x64.tar.xz" \
|
||||||
|
# -C artifacts/postgrest-freebsd-x64 postgrest
|
||||||
|
|
||||||
|
tar cJvf "release-bundle/postgrest-v$VERSION-ubuntu-aarch64.tar.xz" \
|
||||||
|
-C artifacts/postgrest-ubuntu-aarch64 postgrest
|
||||||
|
|
||||||
zip "release-bundle/postgrest-v$VERSION-windows-x64.zip" \
|
zip "release-bundle/postgrest-v$VERSION-windows-x64.zip" \
|
||||||
artifacts/postgrest-windows-x64/postgrest.exe
|
artifacts/postgrest-windows-x64/postgrest.exe
|
||||||
|
|
||||||
- name: Save release bundle
|
- name: Save release bundle
|
||||||
uses: actions/upload-artifact@v2.2.4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: release-bundle
|
name: release-bundle
|
||||||
path: release-bundle
|
path: release-bundle
|
||||||
@@ -329,25 +402,29 @@ jobs:
|
|||||||
Release-Docker:
|
Release-Docker:
|
||||||
name: Release on Docker Hub
|
name: Release on Docker Hub
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: Prepare-Release
|
needs:
|
||||||
|
- Build-Cabal-Arm
|
||||||
|
- Prepare-Release
|
||||||
env:
|
env:
|
||||||
|
GITHUB_COMMIT: ${{ github.sha }}
|
||||||
DOCKER_REPO: postgrest
|
DOCKER_REPO: postgrest
|
||||||
DOCKER_USER: stevechavez
|
DOCKER_USER: stevechavez
|
||||||
|
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
||||||
VERSION: ${{ needs.Prepare-Release.outputs.version }}
|
VERSION: ${{ needs.Prepare-Release.outputs.version }}
|
||||||
ISPRERELEASE: ${{ needs.Prepare-Release.outputs.isprerelease }}
|
ISPRERELEASE: ${{ needs.Prepare-Release.outputs.isprerelease }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
tools: release
|
tools: release
|
||||||
- name: Download Docker image
|
- name: Download Docker image
|
||||||
uses: actions/download-artifact@v2.0.10
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: postgrest-docker-x64
|
name: postgrest-docker-x64
|
||||||
- name: Publish images on Docker Hub
|
- name: Publish images on Docker Hub
|
||||||
run: |
|
run: |
|
||||||
docker login -u "$DOCKER_USER" -p "${{ secrets.DOCKER_PASS }}"
|
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
|
||||||
docker load -i postgrest-docker.tar.gz
|
docker load -i postgrest-docker.tar.gz
|
||||||
|
|
||||||
docker tag postgrest:latest "$DOCKER_REPO/postgrest:v$VERSION"
|
docker tag postgrest:latest "$DOCKER_REPO/postgrest:v$VERSION"
|
||||||
@@ -361,13 +438,48 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "Skipping pushing to 'latest' tag for v$VERSION pre-release..."
|
echo "Skipping pushing to 'latest' tag for v$VERSION pre-release..."
|
||||||
fi
|
fi
|
||||||
- name: Update descriptions on Docker Hub
|
- name: Publish images for ARM builds on Docker Hub
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
env:
|
env:
|
||||||
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
|
||||||
run: |
|
with:
|
||||||
if [[ -z "$ISPRERELEASE" ]]; then
|
host: ${{ secrets.SSH_ARM_HOST }}
|
||||||
echo "Updating description on Docker Hub..."
|
username: ubuntu
|
||||||
postgrest-release-dockerhub-description
|
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||||
else
|
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||||
echo "Skipping updating description for pre-release..."
|
script_stop: true
|
||||||
fi
|
envs: GITHUB_COMMIT,DOCKER_REPO,DOCKER_USER,DOCKER_PASS,REMOTE_DIR,VERSION,ISPRERELEASE
|
||||||
|
script: bash ~/$REMOTE_DIR/docker-publish.sh "$GITHUB_COMMIT" "$DOCKER_REPO" "$DOCKER_USER" "$DOCKER_PASS" "$REMOTE_DIR" "$VERSION" "$ISPRERELEASE"
|
||||||
|
# TODO: Enable dockerhub description update again, once a solution for the permission problem is found:
|
||||||
|
# https://github.com/docker/hub-feedback/issues/1927
|
||||||
|
# - name: Update descriptions on Docker Hub
|
||||||
|
# env:
|
||||||
|
# DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
||||||
|
# run: |
|
||||||
|
# if [[ -z "$ISPRERELEASE" ]]; then
|
||||||
|
# echo "Updating description on Docker Hub..."
|
||||||
|
# postgrest-release-dockerhub-description
|
||||||
|
# else
|
||||||
|
# echo "Skipping updating description for pre-release..."
|
||||||
|
# fi
|
||||||
|
|
||||||
|
Clean-Arm-Server:
|
||||||
|
name: Remove copied files from server
|
||||||
|
needs:
|
||||||
|
- Build-Cabal-Arm
|
||||||
|
- Release-Docker
|
||||||
|
if: ${{ always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/rel-')) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
- name: Remove uploaded files from server
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_ARM_HOST }}
|
||||||
|
username: ubuntu
|
||||||
|
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||||
|
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||||
|
envs: REMOTE_DIR
|
||||||
|
script: rm -rf $REMOTE_DIR
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Loadtest
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Loadtest-Nix:
|
||||||
|
name: Loadtest (Nix)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Setup Nix Environment
|
||||||
|
uses: ./.github/actions/setup-nix
|
||||||
|
with:
|
||||||
|
tools: loadtest
|
||||||
|
- name: Run loadtest
|
||||||
|
run: |
|
||||||
|
postgrest-loadtest-against main
|
||||||
|
postgrest-loadtest-report > loadtest/loadtest.md
|
||||||
|
- name: Upload report
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: loadtest.md
|
||||||
|
path: loadtest/loadtest.md
|
||||||
|
if-no-files-found: error
|
||||||
@@ -21,3 +21,4 @@ __pycache__
|
|||||||
*.tix
|
*.tix
|
||||||
coverage
|
coverage
|
||||||
.hpc
|
.hpc
|
||||||
|
loadtest
|
||||||
|
|||||||
+17
-3
@@ -3,12 +3,26 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## Unreleased
|
## [9.0.1] - 2022-06-03
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- #2165, Fix json/jsonb columns should not have type in OpenAPI spec - @clrnd
|
||||||
|
- #2020, Execute deferred constraint triggers when using `Prefer: tx=rollback` - @wolfgangwalther
|
||||||
|
- #2077, Fix `is` not working with upper or mixed case values like `NULL, TrUe, FaLsE` - @steve-chavez
|
||||||
|
- #2024, Fix schema cache loading when views with XMLTABLE and DEFAULT are present - @wolfgangwalther
|
||||||
|
- #1724, Fix wrong CORS header Authentication -> Authorization - @wolfgangwalther
|
||||||
|
- #2120, Fix reading database configuration properly when `=` is present in value - @wolfgangwalther
|
||||||
|
- #2135, Remove trigger functions from schema cache and OpenAPI output, because they can't be called directly anyway. - @wolfgangwalther
|
||||||
|
- #2101, Remove aggregates, procedures and window functions from the schema cache and OpenAPI output. - @wolfgangwalther
|
||||||
|
- #2153, Fix --dump-schema running with a wrong PG version. - @wolfgangwalther
|
||||||
|
- #2042, Keep working when EMFILE(Too many open files) is reached. - @steve-chavez
|
||||||
|
- #2147, Ignore `Content-Type` headers for `GET` requests when calling RPCs. - @laurenceisla
|
||||||
|
+ Previously, `GET` without parameters, but with `Content-Type: text/plain` or `Content-Type: application/octet-stream` would fail with `404 Not Found`, even if a function without arguments was available.
|
||||||
|
- #2239, Fix misleading disambiguation error where the content of the `relationship` key looks like valid syntax - @laurenceisla
|
||||||
|
- #2294, Disable parallel GC for better performance on higher core CPUs - @steve-chavez
|
||||||
|
- #1076, Fix using CPU while idle - @steve-chavez
|
||||||
|
|
||||||
## [9.0.0] - 2021-11-25
|
## [9.0.0] - 2021-11-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
[](https://www.patreon.com/postgrest)
|
[](https://www.patreon.com/postgrest)
|
||||||
[](https://www.paypal.me/postgrest)
|
[](https://www.paypal.me/postgrest)
|
||||||
<a href="https://heroku.com/deploy?template=https://github.com/PostgREST/postgrest">
|
|
||||||
<img src="https://img.shields.io/badge/%E2%86%91_Deploy_to-Heroku-7056bf.svg" alt="Deploy">
|
|
||||||
</a>
|
|
||||||
[](https://gitter.im/begriffs/postgrest)
|
[](https://gitter.im/begriffs/postgrest)
|
||||||
[](http://postgrest.org)
|
[](http://postgrest.org)
|
||||||
[](https://hub.docker.com/r/postgrest/postgrest/)
|
[](https://hub.docker.com/r/postgrest/postgrest/)
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "PostgREST",
|
|
||||||
"description": "RESTful API for any PostgreSQL database.",
|
|
||||||
"logo": "https://avatars2.githubusercontent.com/u/15115011",
|
|
||||||
"repository": "https://github.com/PostgREST/postgrest",
|
|
||||||
"env": {
|
|
||||||
"BUILDPACK_URL": {
|
|
||||||
"description": "Heroku buildpack for deploying Haskell applications",
|
|
||||||
"value": "https://github.com/PostgREST/postgrest-heroku"
|
|
||||||
},
|
|
||||||
"POSTGREST_VER": {
|
|
||||||
"description": "Version of PostgREST to deploy",
|
|
||||||
"value": "8.0.0"
|
|
||||||
},
|
|
||||||
"DB_URI": {
|
|
||||||
"description": "Database connection string, e.g. postgres://user:pass@xxxxxxx.rds.amazonaws.com/mydb",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"DB_SCHEMA": {
|
|
||||||
"description": "The database schema to expose to REST clients. Tables, views and stored procedures in this schema will get API endpoints",
|
|
||||||
"required": true,
|
|
||||||
"value": "public"
|
|
||||||
},
|
|
||||||
"DB_ANON_ROLE": {
|
|
||||||
"description": "The database role to use when executing commands on behalf of unauthenticated clients",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"DB_POOL": {
|
|
||||||
"description": "Number of connections to keep open in PostgREST’s database pool",
|
|
||||||
"required": false,
|
|
||||||
"value": "10"
|
|
||||||
},
|
|
||||||
"SERVER_PROXY_URI": {
|
|
||||||
"description": "Overrides the base URL used within the OpenAPI self-documentation hosted at the API root path",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"JWT_SECRET": {
|
|
||||||
"description": "The secret used to decode JWT tokens clients provide for authentication",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"SECRET_IS_BASE64": {
|
|
||||||
"description": "When this is set to true, the value derived from jwt-secret will be treated as a base64 encoded secret",
|
|
||||||
"required": false,
|
|
||||||
"value": "false"
|
|
||||||
},
|
|
||||||
"JWT_AUD": {
|
|
||||||
"description": "The audience that should be validated if the JWT token contains an aud claim",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"MAX_ROWS": {
|
|
||||||
"description": "A hard limit to the number of rows PostgREST will fetch from a view, table, or stored procedure",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"PRE_REQUEST": {
|
|
||||||
"description": "A schema-qualified stored procedure name to call right after switching roles for a client request",
|
|
||||||
"required": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+5
-1
@@ -123,6 +123,10 @@ rec {
|
|||||||
docker =
|
docker =
|
||||||
pkgs.callPackage nix/tools/docker { postgrest = postgrestStatic; };
|
pkgs.callPackage nix/tools/docker { postgrest = postgrestStatic; };
|
||||||
|
|
||||||
|
# Load testing tools.
|
||||||
|
loadtest =
|
||||||
|
pkgs.callPackage nix/tools/loadtest.nix { inherit withTools; };
|
||||||
|
|
||||||
# Script for running memory tests.
|
# Script for running memory tests.
|
||||||
memory =
|
memory =
|
||||||
pkgs.callPackage nix/tools/memory.nix { inherit postgrestProfiled withTools; };
|
pkgs.callPackage nix/tools/memory.nix { inherit postgrestProfiled withTools; };
|
||||||
@@ -148,5 +152,5 @@ rec {
|
|||||||
};
|
};
|
||||||
|
|
||||||
withTools =
|
withTools =
|
||||||
pkgs.callPackage nix/tools/withTools.nix { inherit postgresqlVersions; };
|
pkgs.callPackage nix/tools/withTools.nix { inherit devCabalOptions postgresqlVersions postgrest; };
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ the PostgREST repo. Paths are resolved relative to the repo root:
|
|||||||
$ cd src
|
$ cd src
|
||||||
# Even though the current directory is ./src, the config path must still start
|
# Even though the current directory is ./src, the config path must still start
|
||||||
# from the repo root:
|
# from the repo root:
|
||||||
$ postgrest-run test/io-tests/configs/simple.conf
|
$ postgrest-run test/io/configs/simple.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{ name
|
{ name
|
||||||
, docs
|
, docs
|
||||||
, args ? [ ]
|
, args ? [ ]
|
||||||
, addCommandCompletion ? false
|
, positionalCompletion ? ""
|
||||||
, inRootDir ? false
|
, inRootDir ? false
|
||||||
, redirectTixFiles ? true
|
, redirectTixFiles ? true
|
||||||
, withEnv ? null
|
, withEnv ? null
|
||||||
@@ -22,11 +22,10 @@
|
|||||||
, withTmpDir ? false
|
, withTmpDir ? false
|
||||||
}: text:
|
}: text:
|
||||||
let
|
let
|
||||||
|
# square brackets are a pain to escape - if even possible. just don't use them...
|
||||||
|
escape = builtins.replaceStrings [ "\n" ] [ " \\n" ];
|
||||||
|
|
||||||
argsTemplate =
|
argsTemplate =
|
||||||
let
|
|
||||||
# square brackets are a pain to escape - if even possible. just don't use them...
|
|
||||||
escapedDocs = builtins.replaceStrings [ "\n" ] [ " \\n" ] docs;
|
|
||||||
in
|
|
||||||
writeTextFile {
|
writeTextFile {
|
||||||
inherit name;
|
inherit name;
|
||||||
destination = "/${name}.m4"; # destination is needed to have the proper basename for completion
|
destination = "/${name}.m4"; # destination is needed to have the proper basename for completion
|
||||||
@@ -37,7 +36,7 @@ let
|
|||||||
# stripping the /nix/store/... path for nicer display
|
# stripping the /nix/store/... path for nicer display
|
||||||
BASH_ARGV0="$(basename "$0")"
|
BASH_ARGV0="$(basename "$0")"
|
||||||
|
|
||||||
# ARG_HELP([${name}], [${escapedDocs}])
|
# ARG_HELP([${name}], [${escape docs}])
|
||||||
${lib.strings.concatMapStrings (arg: "# " + arg) args}
|
${lib.strings.concatMapStrings (arg: "# " + arg) args}
|
||||||
# ARG_POSITIONAL_DOUBLEDASH()
|
# ARG_POSITIONAL_DOUBLEDASH()
|
||||||
# ARG_DEFAULTS_POS()
|
# ARG_DEFAULTS_POS()
|
||||||
@@ -65,8 +64,8 @@ let
|
|||||||
${argbash}/bin/argbash --type completion --strip all ${argsTemplate}/${name}.m4 > $out
|
${argbash}/bin/argbash --type completion --strip all ${argsTemplate}/${name}.m4 > $out
|
||||||
''
|
''
|
||||||
|
|
||||||
+ lib.optionalString addCommandCompletion ''
|
+ lib.optionalString (positionalCompletion != "") ''
|
||||||
sed 's/COMPREPLY.*compgen -o bashdefault .*$/_command/' -i $out
|
sed 's#COMPREPLY.*compgen -o bashdefault .*$#${escape positionalCompletion}#' -i $out
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,54 @@ let
|
|||||||
# To get the sha256:
|
# To get the sha256:
|
||||||
# nix-prefetch-url --unpack https://hackage.haskell.org/package/protolude-0.3.0/protolude-0.3.0.tar.gz
|
# nix-prefetch-url --unpack https://hackage.haskell.org/package/protolude-0.3.0/protolude-0.3.0.tar.gz
|
||||||
|
|
||||||
|
# To temporarily pin unreleased versions from GitHub:
|
||||||
|
# <name> =
|
||||||
|
# prev.callCabal2nixWithOptions "<name>" (super.fetchFromGitHub {
|
||||||
|
# owner = "<owner>";
|
||||||
|
# repo = "<repo>";
|
||||||
|
# rev = "<commit>";
|
||||||
|
# sha256 = "<sha256>";
|
||||||
|
# }) "--subpath=<subpath>" {};
|
||||||
|
#
|
||||||
|
# To get the sha256:
|
||||||
|
# nix-prefetch-url --unpack https://github.com/<owner>/<repo>/archive/<commit>.tar.gz
|
||||||
|
|
||||||
|
protolude =
|
||||||
|
prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "protolude";
|
||||||
|
ver = "0.3.1";
|
||||||
|
sha256 = "0gf0mn1ycllr69kdq1p07qf7935s10jz0nnhynwqy3d6nmycxr5j";
|
||||||
|
}
|
||||||
|
{ };
|
||||||
|
|
||||||
|
wai-extra =
|
||||||
|
prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "wai-extra";
|
||||||
|
ver = "3.1.8";
|
||||||
|
sha256 = "1ha8sxc2ii7k7xs5nm06wfwqmf4f1p2acp4ya0jnx6yn6551qps4";
|
||||||
|
}
|
||||||
|
{ };
|
||||||
|
|
||||||
|
wai-logger =
|
||||||
|
prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "wai-logger";
|
||||||
|
ver = "2.3.7";
|
||||||
|
sha256 = "1d23fdbwbahr3y1vdyn57m1qhljy22pm5cpgb20dy6mlxzdb30xd";
|
||||||
|
}
|
||||||
|
{ };
|
||||||
|
|
||||||
|
warp =
|
||||||
|
lib.dontCheck (prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "warp";
|
||||||
|
ver = "3.3.19";
|
||||||
|
sha256 = "0y3jj4bhviss6ff9lwxki0zbdcl1rb398bk4s80zvfpnpy7p94cx";
|
||||||
|
}
|
||||||
|
{ });
|
||||||
|
|
||||||
hasql-dynamic-statements =
|
hasql-dynamic-statements =
|
||||||
lib.dontCheck (lib.unmarkBroken prev.hasql-dynamic-statements);
|
lib.dontCheck (lib.unmarkBroken prev.hasql-dynamic-statements);
|
||||||
|
|
||||||
|
|||||||
+149
-7
@@ -4,7 +4,10 @@
|
|||||||
, checkedShellScript
|
, checkedShellScript
|
||||||
, devCabalOptions
|
, devCabalOptions
|
||||||
, entr
|
, entr
|
||||||
|
, git
|
||||||
|
, gnugrep
|
||||||
, graphviz
|
, graphviz
|
||||||
|
, haskellPackages
|
||||||
, hsie
|
, hsie
|
||||||
, nix
|
, nix
|
||||||
, silver-searcher
|
, silver-searcher
|
||||||
@@ -29,7 +32,7 @@ let
|
|||||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||||
"ARG_LEFTOVERS([command arguments])"
|
"ARG_LEFTOVERS([command arguments])"
|
||||||
];
|
];
|
||||||
addCommandCompletion = true;
|
positionalCompletion = "_command";
|
||||||
redirectTixFiles = false; # will be done by sub-command
|
redirectTixFiles = false; # will be done by sub-command
|
||||||
inRootDir = true;
|
inRootDir = true;
|
||||||
}
|
}
|
||||||
@@ -64,23 +67,161 @@ let
|
|||||||
name = "postgrest-check";
|
name = "postgrest-check";
|
||||||
docs =
|
docs =
|
||||||
''
|
''
|
||||||
Run most checks that will also run on CI.
|
Run most checks that will also run on CI, but only against the
|
||||||
|
latest PostgreSQL version.
|
||||||
|
|
||||||
This currently excludes the memory tests, as those are particularly
|
This currently excludes the memory and spec-idempotence tests,
|
||||||
expensive.
|
as those are particularly expensive.
|
||||||
'';
|
'';
|
||||||
inRootDir = true;
|
inRootDir = true;
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${withTools}/bin/postgrest-with-all ${tests}/bin/postgrest-test-spec
|
${tests}/bin/postgrest-test-spec
|
||||||
${withTools}/bin/postgrest-with-all ${tests}/bin/postgrest-test-querycost
|
${tests}/bin/postgrest-test-querycost
|
||||||
${tests}/bin/postgrest-test-doctests
|
${tests}/bin/postgrest-test-doctests
|
||||||
${tests}/bin/postgrest-test-spec-idempotence
|
|
||||||
${tests}/bin/postgrest-test-io
|
${tests}/bin/postgrest-test-io
|
||||||
${style}/bin/postgrest-lint
|
${style}/bin/postgrest-lint
|
||||||
${style}/bin/postgrest-style-check
|
${style}/bin/postgrest-style-check
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
gitHooks =
|
||||||
|
let
|
||||||
|
name = "postgrest-git-hooks";
|
||||||
|
in
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
inherit name;
|
||||||
|
docs =
|
||||||
|
''
|
||||||
|
Enable or disable git pre-commit and pre-push hooks.
|
||||||
|
|
||||||
|
Basic is faster and will only run:
|
||||||
|
- pre-commit: postgrest-style
|
||||||
|
- pre-push: postgrest-lint
|
||||||
|
|
||||||
|
Full takes a lot more time and will run:
|
||||||
|
- pre-commit: postgrest-style && postgrest-lint
|
||||||
|
- pre-push: postgrest-check
|
||||||
|
|
||||||
|
Changes made by postgrest-style will be staged automatically.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
postgrest-git-hooks disable
|
||||||
|
postgrest-git-hooks enable basic
|
||||||
|
postgrest-git-hooks enable full
|
||||||
|
|
||||||
|
The "run" operation and "--hook" argument are only used internally.
|
||||||
|
'';
|
||||||
|
args =
|
||||||
|
[
|
||||||
|
"ARG_POSITIONAL_SINGLE([operation], [Operation])"
|
||||||
|
"ARG_TYPE_GROUP_SET([OPERATION], [OPERATION], [operation], [disable,enable,run])"
|
||||||
|
"ARG_POSITIONAL_SINGLE([mode], [Mode], [basic])"
|
||||||
|
"ARG_TYPE_GROUP_SET([MODE], [MODE], [mode], [basic,full])"
|
||||||
|
"ARG_OPTIONAL_SINGLE([hook], , [Hook], [pre-commit])"
|
||||||
|
"ARG_TYPE_GROUP_SET([HOOK], [HOOK], [hook], [pre-commit,pre-push])"
|
||||||
|
];
|
||||||
|
positionalCompletion =
|
||||||
|
''
|
||||||
|
if test "$prev" == "${name}"; then
|
||||||
|
COMPREPLY=( $(compgen -W "enable disable" -- "$cur") )
|
||||||
|
elif test "$prev" == "enable" || test "$prev" == "disable"; then
|
||||||
|
COMPREPLY=( $(compgen -W "basic full" -- "$cur") )
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
inRootDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
if [ run != "$_arg_operation" ]; then
|
||||||
|
# Remove all hooks first and ignore failures because the file might be missing.
|
||||||
|
# This assumes that we're only adding lines that include "postgrest-git-hooks"
|
||||||
|
# to the hook file.
|
||||||
|
sed -i -e '/postgrest-git-hooks/d' .git/hooks/pre-{commit,push} 2> /dev/null || true
|
||||||
|
|
||||||
|
if [ disabled != "$_arg_mode" ]; then
|
||||||
|
# The nix-shell && + nix-shell || pattern makes sure we can run the hook
|
||||||
|
# in a pure nix-shell, where nix-shell itself is not available, too.
|
||||||
|
|
||||||
|
# The $(nix-shell --run "command -v ...") pattern ensures we only need to enable
|
||||||
|
# the hooks once and still run the latest of our hook scripts, even when we
|
||||||
|
# update them in the repo.
|
||||||
|
|
||||||
|
echo 'command -v nix-shell > /dev/null || postgrest-git-hooks --hook=pre-commit run' "$_arg_mode" \
|
||||||
|
>> .git/hooks/pre-commit
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
echo 'command -v nix-shell > /dev/null && $(nix-shell --quiet -Q --run "command -v postgrest-git-hooks") --hook=pre-commit run' "$_arg_mode" \
|
||||||
|
>> .git/hooks/pre-commit
|
||||||
|
chmod +x .git/hooks/pre-commit
|
||||||
|
|
||||||
|
echo 'command -v nix-shell > /dev/null || postgrest-git-hooks --hook=pre-push run' "$_arg_mode" \
|
||||||
|
>> .git/hooks/pre-push
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
echo 'command -v nix-shell > /dev/null && $(nix-shell --quiet -Q --run "command -v postgrest-git-hooks") --hook=pre-push run' "$_arg_mode" \
|
||||||
|
>> .git/hooks/pre-push
|
||||||
|
chmod +x .git/hooks/pre-push
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# When run from a git hook, the GIT_ environment variables conflict with our withGit helper.
|
||||||
|
# The following unsets all GIT_ variables.
|
||||||
|
unset "''${!GIT_@}"
|
||||||
|
|
||||||
|
case "$_arg_mode" in
|
||||||
|
basic)
|
||||||
|
case "$_arg_hook" in
|
||||||
|
pre-commit)
|
||||||
|
# To be able to automatically add only changes from postgrest-style to the staging area,
|
||||||
|
# we need to run postgrest-style twice. Otherwise we'd risk merge conflicts when popping
|
||||||
|
# the stash afterwards.
|
||||||
|
${style}/bin/postgrest-style
|
||||||
|
|
||||||
|
stash="postgrest-git-hooks-$RANDOM"
|
||||||
|
${git}/bin/git stash push --include-untracked --keep-index -m "$stash"
|
||||||
|
if [ "$(git stash list --grep $stash)" ]; then
|
||||||
|
# Only create the stash pop trap, if we actually created a stash.
|
||||||
|
# Otherwise stash pop will cause havoc.
|
||||||
|
trap '${git}/bin/git stash pop $(git stash list --format=format:%gD --grep "$stash" -n1)' EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
${style}/bin/postgrest-style
|
||||||
|
${git}/bin/git add .
|
||||||
|
;;
|
||||||
|
pre-push)
|
||||||
|
# Create a clean working tree without any uncomitted changes.
|
||||||
|
${withTools.withGit} HEAD ${style}/bin/postgrest-lint
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
full)
|
||||||
|
case "$_arg_hook" in
|
||||||
|
pre-commit)
|
||||||
|
# To be able to automatically add only changes from postgrest-style to the staging area,
|
||||||
|
# we need to run postgrest-style twice. Otherwise we'd risk merge conflicts when popping
|
||||||
|
# the stash afterwards.
|
||||||
|
${style}/bin/postgrest-style
|
||||||
|
|
||||||
|
stash="postgrest-git-hooks-$RANDOM"
|
||||||
|
${git}/bin/git stash push --include-untracked --keep-index -m "$stash"
|
||||||
|
if [ "$(git stash list --grep $stash)" ]; then
|
||||||
|
# Only create the stash pop trap, if we actually created a stash.
|
||||||
|
# Otherwise stash pop will cause havoc.
|
||||||
|
trap '${git}/bin/git stash pop $(git stash list --format=format:%gD --grep "$stash" -n1)' EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
${style}/bin/postgrest-style
|
||||||
|
${git}/bin/git add .
|
||||||
|
|
||||||
|
${style}/bin/postgrest-lint
|
||||||
|
;;
|
||||||
|
pre-push)
|
||||||
|
# Create a clean working tree without any uncomitted changes.
|
||||||
|
${withTools.withGit} HEAD ${check}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
dumpMinimalImports =
|
dumpMinimalImports =
|
||||||
checkedShellScript
|
checkedShellScript
|
||||||
{
|
{
|
||||||
@@ -146,6 +287,7 @@ buildToolbox
|
|||||||
watch
|
watch
|
||||||
pushCachix
|
pushCachix
|
||||||
check
|
check
|
||||||
|
gitHooks
|
||||||
dumpMinimalImports
|
dumpMinimalImports
|
||||||
hsieMinimalImports
|
hsieMinimalImports
|
||||||
hsieGraphModules
|
hsieGraphModules
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
{ buildToolbox
|
||||||
|
, checkedShellScript
|
||||||
|
, jq
|
||||||
|
, python3Packages
|
||||||
|
, vegeta
|
||||||
|
, withTools
|
||||||
|
, writers
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
runner =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-loadtest-runner";
|
||||||
|
docs = "Run vegeta. Assume PostgREST to be running.";
|
||||||
|
args = [
|
||||||
|
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||||
|
"ARG_USE_ENV([PGRST_SERVER_UNIX_SOCKET], [], [Unix socket to connect to running PostgREST instance])"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
# ARG_USE_ENV only adds defaults or docs for environment variables
|
||||||
|
# We manually implement a required check here
|
||||||
|
# See also: https://github.com/matejak/argbash/issues/80
|
||||||
|
: "''${PGRST_SERVER_UNIX_SOCKET:?PGRST_SERVER_UNIX_SOCKET is required}"
|
||||||
|
|
||||||
|
${vegeta}/bin/vegeta -cpus 1 attack \
|
||||||
|
-unix-socket "$PGRST_SERVER_UNIX_SOCKET" \
|
||||||
|
-max-workers 1 \
|
||||||
|
-workers 1 \
|
||||||
|
-rate 0 \
|
||||||
|
-duration 60s \
|
||||||
|
"''${_arg_leftovers[@]}"
|
||||||
|
'';
|
||||||
|
|
||||||
|
loadtest =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-loadtest";
|
||||||
|
docs = "Run the vegeta loadtests with PostgREST.";
|
||||||
|
args = [
|
||||||
|
"ARG_OPTIONAL_SINGLE([output], [o], [Filename to dump json output to], [./loadtest/result.bin])"
|
||||||
|
"ARG_OPTIONAL_SINGLE([testdir], [t], [Directory to load tests and fixtures from], [./test/load])"
|
||||||
|
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||||
|
];
|
||||||
|
inRootDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
export PGRST_DB_CONFIG="false"
|
||||||
|
export PGRST_DB_POOL="1"
|
||||||
|
export PGRST_DB_TX_END="rollback-allow-override"
|
||||||
|
export PGRST_LOG_LEVEL="crit"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$_arg_output")"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2145
|
||||||
|
${withTools.withPg} --fixtures "$_arg_testdir"/fixtures.sql \
|
||||||
|
${withTools.withPgrst} \
|
||||||
|
sh -c "cd \"$_arg_testdir\" && ${runner} -targets targets.http \"''${_arg_leftovers[@]}\"" \
|
||||||
|
| tee "$_arg_output" \
|
||||||
|
| ${vegeta}/bin/vegeta report -type=text
|
||||||
|
'';
|
||||||
|
|
||||||
|
loadtestAgainst =
|
||||||
|
let
|
||||||
|
name = "postgrest-loadtest-against";
|
||||||
|
in
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
inherit name;
|
||||||
|
docs =
|
||||||
|
''
|
||||||
|
Run the vegeta loadtest twice:
|
||||||
|
- once on the <target> branch
|
||||||
|
- once in the current worktree
|
||||||
|
'';
|
||||||
|
args = [
|
||||||
|
"ARG_POSITIONAL_SINGLE([target], [Commit-ish reference to compare with])"
|
||||||
|
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||||
|
];
|
||||||
|
positionalCompletion =
|
||||||
|
''
|
||||||
|
if test "$prev" == "${name}"; then
|
||||||
|
__gitcomp_nl "$(__git_refs)"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
inRootDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Running loadtest on "$_arg_target"...
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Runs the test files from the current working tree
|
||||||
|
# to make sure both tests are run with the same files.
|
||||||
|
# Save the results in the current working tree, too,
|
||||||
|
# otherwise they'd be lost in the temporary working tree
|
||||||
|
# created by withTools.withGit.
|
||||||
|
${withTools.withGit} "$_arg_target" ${loadtest} --output "$PWD/loadtest/$_arg_target.bin" --testdir "$PWD/test/load" "''${_arg_leftovers[@]}"
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Done running on "$_arg_target".
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Running loadtest on HEAD...
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
${loadtest} --output "$PWD/loadtest/head.bin" --testdir "$PWD/test/load" "''${_arg_leftovers[@]}"
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Done running on HEAD.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
|
||||||
|
reporter =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-loadtest-reporter";
|
||||||
|
docs = "Create a named json report for a single result file.";
|
||||||
|
args = [
|
||||||
|
"ARG_POSITIONAL_SINGLE([file], [Filename of result to create report for])"
|
||||||
|
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||||
|
];
|
||||||
|
inRootDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
${vegeta}/bin/vegeta report -type=json "$_arg_file" \
|
||||||
|
| ${jq}/bin/jq --arg branch "$(basename "$_arg_file" .bin)" '. + {branch: $branch}'
|
||||||
|
'';
|
||||||
|
|
||||||
|
toMarkdown =
|
||||||
|
writers.writePython3 "postgrest-loadtest-to-markdown"
|
||||||
|
{
|
||||||
|
libraries = [ python3Packages.pandas python3Packages.tabulate ];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
import sys
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
pd.read_json(sys.stdin) \
|
||||||
|
.set_index('param') \
|
||||||
|
.drop(['branch', 'earliest', 'end', 'latest']) \
|
||||||
|
.convert_dtypes() \
|
||||||
|
.to_markdown(sys.stdout, floatfmt='.0f')
|
||||||
|
'';
|
||||||
|
|
||||||
|
|
||||||
|
report =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-loadtest-report";
|
||||||
|
docs = "Create a report of all loadtest reports as markdown.";
|
||||||
|
inRootDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
find loadtest -type f -iname '*.bin' -exec ${reporter} {} \; \
|
||||||
|
| ${jq}/bin/jq '[leaf_paths as $path | {param: $path | join("."), (.branch): getpath($path)}]' \
|
||||||
|
| ${jq}/bin/jq --slurp 'flatten | group_by(.param) | map(add)' \
|
||||||
|
| ${toMarkdown}
|
||||||
|
'';
|
||||||
|
|
||||||
|
in
|
||||||
|
buildToolbox {
|
||||||
|
name = "postgrest-loadtest";
|
||||||
|
tools = [ loadtest loadtestAgainst report ];
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ let
|
|||||||
withPath = [ postgrestProfiled curl ];
|
withPath = [ postgrestProfiled curl ];
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${withTools.latest} test/memory-tests.sh
|
${withTools.withPg} test/memory/memory-tests.sh
|
||||||
'';
|
'';
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ let
|
|||||||
''
|
''
|
||||||
${style}
|
${style}
|
||||||
|
|
||||||
|
trap "echo postgrest-style-check failed. Run postgrest-style to fix issues automatically." ERR
|
||||||
|
|
||||||
${git}/bin/git diff-index --exit-code HEAD -- '*.hs' '*.lhs' '*.nix'
|
${git}/bin/git diff-index --exit-code HEAD -- '*.hs' '*.lhs' '*.nix'
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|||||||
+14
-12
@@ -5,7 +5,7 @@
|
|||||||
, ghc
|
, ghc
|
||||||
, glibcLocales
|
, glibcLocales
|
||||||
, gnugrep
|
, gnugrep
|
||||||
, haskell
|
, haskellPackages
|
||||||
, hpc-codecov
|
, hpc-codecov
|
||||||
, jq
|
, jq
|
||||||
, postgrest
|
, postgrest
|
||||||
@@ -24,7 +24,7 @@ let
|
|||||||
withEnv = postgrest.env;
|
withEnv = postgrest.env;
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${withTools.latest} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
|
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
|
||||||
'';
|
'';
|
||||||
|
|
||||||
testQuerycost =
|
testQuerycost =
|
||||||
@@ -36,7 +36,7 @@ let
|
|||||||
withEnv = postgrest.env;
|
withEnv = postgrest.env;
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${withTools.latest} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:querycost
|
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:querycost
|
||||||
'';
|
'';
|
||||||
|
|
||||||
testDoctests =
|
testDoctests =
|
||||||
@@ -66,7 +66,7 @@ let
|
|||||||
withEnv = postgrest.env;
|
withEnv = postgrest.env;
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${withTools.latest} ${runtimeShell} -c " \
|
${withTools.withPg} ${runtimeShell} -c " \
|
||||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec && \
|
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec && \
|
||||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec"
|
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec"
|
||||||
'';
|
'';
|
||||||
@@ -92,8 +92,8 @@ let
|
|||||||
}
|
}
|
||||||
''
|
''
|
||||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions}
|
${cabal-install}/bin/cabal v2-build ${devCabalOptions}
|
||||||
${cabal-install}/bin/cabal v2-exec ${withTools.latest} \
|
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} -f test/io/fixtures.sql \
|
||||||
${ioTestPython}/bin/pytest -- -v test/io-tests "''${_arg_leftovers[@]}"
|
${ioTestPython}/bin/pytest -v test/io "''${_arg_leftovers[@]}"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
dumpSchema =
|
dumpSchema =
|
||||||
@@ -106,7 +106,7 @@ let
|
|||||||
withPath = [ jq ];
|
withPath = [ jq ];
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${withTools.latest} \
|
${withTools.withPg} \
|
||||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} --verbose=0 -- \
|
${cabal-install}/bin/cabal v2-run ${devCabalOptions} --verbose=0 -- \
|
||||||
postgrest --dump-schema \
|
postgrest --dump-schema \
|
||||||
| ${yq}/bin/yq -y .
|
| ${yq}/bin/yq -y .
|
||||||
@@ -116,7 +116,7 @@ let
|
|||||||
checkedShellScript
|
checkedShellScript
|
||||||
{
|
{
|
||||||
name = "postgrest-coverage";
|
name = "postgrest-coverage";
|
||||||
docs = "Run spec and io tests while collecting hpc coverage data.";
|
docs = "Run spec and io tests while collecting hpc coverage data. First runs weeder to detect dead code.";
|
||||||
args = [ "ARG_LEFTOVERS([hpc report arguments])" ];
|
args = [ "ARG_LEFTOVERS([hpc report arguments])" ];
|
||||||
inRootDir = true;
|
inRootDir = true;
|
||||||
redirectTixFiles = false;
|
redirectTixFiles = false;
|
||||||
@@ -133,16 +133,18 @@ let
|
|||||||
# build once before running all the tests
|
# build once before running all the tests
|
||||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec test:querycost
|
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec test:querycost
|
||||||
|
|
||||||
|
${haskellPackages.weeder}/bin/weeder --config=./test/weeder.dhall || echo Found dead code: Check file list above.
|
||||||
|
|
||||||
# collect all tests
|
# collect all tests
|
||||||
HPCTIXFILE="$tmpdir"/io.tix \
|
HPCTIXFILE="$tmpdir"/io.tix \
|
||||||
${withTools.latest} ${cabal-install}/bin/cabal v2-exec ${devCabalOptions} \
|
${withTools.withPg} -f test/io/fixtures.sql ${cabal-install}/bin/cabal v2-exec ${devCabalOptions} -- \
|
||||||
${ioTestPython}/bin/pytest -- -v test/io-tests
|
${ioTestPython}/bin/pytest -v test/io
|
||||||
|
|
||||||
HPCTIXFILE="$tmpdir"/spec.tix \
|
HPCTIXFILE="$tmpdir"/spec.tix \
|
||||||
${withTools.latest} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
|
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
|
||||||
|
|
||||||
HPCTIXFILE="$tmpdir"/querycost.tix \
|
HPCTIXFILE="$tmpdir"/querycost.tix \
|
||||||
${withTools.latest} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:querycost
|
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:querycost
|
||||||
|
|
||||||
# Note: No coverage for doctests, as doctests leverage GHCi and GHCi does not support hpc
|
# Note: No coverage for doctests, as doctests leverage GHCi and GHCi does not support hpc
|
||||||
|
|
||||||
|
|||||||
+158
-17
@@ -1,9 +1,14 @@
|
|||||||
{ bashCompletion
|
{ bashCompletion
|
||||||
, buildToolbox
|
, buildToolbox
|
||||||
|
, cabal-install
|
||||||
, checkedShellScript
|
, checkedShellScript
|
||||||
|
, curl
|
||||||
|
, devCabalOptions
|
||||||
|
, git
|
||||||
, lib
|
, lib
|
||||||
, postgresqlVersions
|
, postgresqlVersions
|
||||||
, writeTextFile
|
, postgrest
|
||||||
|
, writeText
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
withTmpDb =
|
withTmpDb =
|
||||||
@@ -14,7 +19,7 @@ let
|
|||||||
docs = "Run the given command in a temporary database with ${name}";
|
docs = "Run the given command in a temporary database with ${name}";
|
||||||
args =
|
args =
|
||||||
[
|
[
|
||||||
"ARG_OPTIONAL_SINGLE([fixtures], [f], [SQL file to load fixtures from], [test/fixtures/load.sql])"
|
"ARG_OPTIONAL_SINGLE([fixtures], [f], [SQL file to load fixtures from], [test/spec/fixtures/load.sql])"
|
||||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||||
"ARG_LEFTOVERS([command arguments])"
|
"ARG_LEFTOVERS([command arguments])"
|
||||||
"ARG_USE_ENV([PGUSER], [postgrest_test_authenticator], [Authenticator PG role])"
|
"ARG_USE_ENV([PGUSER], [postgrest_test_authenticator], [Authenticator PG role])"
|
||||||
@@ -22,7 +27,7 @@ let
|
|||||||
"ARG_USE_ENV([PGRST_DB_SCHEMAS], [test], [Schema to expose])"
|
"ARG_USE_ENV([PGRST_DB_SCHEMAS], [test], [Schema to expose])"
|
||||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [postgrest_test_anonymous], [Anonymous PG role])"
|
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [postgrest_test_anonymous], [Anonymous PG role])"
|
||||||
];
|
];
|
||||||
addCommandCompletion = true;
|
positionalCompletion = "_command";
|
||||||
inRootDir = true;
|
inRootDir = true;
|
||||||
redirectTixFiles = false;
|
redirectTixFiles = false;
|
||||||
withPath = [ postgresql ];
|
withPath = [ postgresql ];
|
||||||
@@ -31,7 +36,7 @@ let
|
|||||||
''
|
''
|
||||||
# avoid starting multiple layers of withTmpDb
|
# avoid starting multiple layers of withTmpDb
|
||||||
if test -v PGRST_DB_URI; then
|
if test -v PGRST_DB_URI; then
|
||||||
exec "$@"
|
exec "$_arg_command" "''${_arg_leftovers[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
setuplog="$tmpdir/setup.log"
|
setuplog="$tmpdir/setup.log"
|
||||||
@@ -78,27 +83,27 @@ let
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
# Helper script for running a command against all PostgreSQL versions.
|
# Helper script for running a command against all PostgreSQL versions.
|
||||||
withAll =
|
withPgAll =
|
||||||
let
|
let
|
||||||
runners =
|
runners =
|
||||||
builtins.map
|
builtins.map
|
||||||
(pg:
|
(version:
|
||||||
''
|
''
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
|
||||||
Running against ${pg.name}...
|
Running against ${version.name}...
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
trap 'echo "Failed on ${pg.name}"' exit
|
trap 'echo "Failed on ${version.name}"' exit
|
||||||
|
|
||||||
(${withTmpDb pg} "$_arg_command" "''${_arg_leftovers[@]}")
|
(${withTmpDb version} "$_arg_command" "''${_arg_leftovers[@]}")
|
||||||
|
|
||||||
trap "" exit
|
trap "" exit
|
||||||
|
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
|
||||||
Done running against ${pg.name}.
|
Done running against ${version.name}.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
'')
|
'')
|
||||||
@@ -113,21 +118,157 @@ let
|
|||||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||||
"ARG_LEFTOVERS([command arguments])"
|
"ARG_LEFTOVERS([command arguments])"
|
||||||
];
|
];
|
||||||
addCommandCompletion = true;
|
positionalCompletion = "_command";
|
||||||
inRootDir = true;
|
inRootDir = true;
|
||||||
}
|
}
|
||||||
(lib.concatStringsSep "\n\n" runners);
|
(lib.concatStringsSep "\n\n" runners);
|
||||||
|
|
||||||
# Create a `postgrest-with-postgresql-` for each PostgreSQL version
|
# Create a `postgrest-with-postgresql-` for each PostgreSQL version
|
||||||
withVersions = builtins.map withTmpDb postgresqlVersions;
|
withPgVersions = builtins.map withTmpDb postgresqlVersions;
|
||||||
|
|
||||||
|
withPg = builtins.head withPgVersions;
|
||||||
|
|
||||||
|
withGit =
|
||||||
|
let
|
||||||
|
name = "postgrest-with-git";
|
||||||
|
in
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
inherit name;
|
||||||
|
docs =
|
||||||
|
''
|
||||||
|
Create a new worktree of the postgrest repo in a temporary directory and
|
||||||
|
check out <commit>, then run <command> with arguments inside the temporary folder.
|
||||||
|
'';
|
||||||
|
args =
|
||||||
|
[
|
||||||
|
"ARG_POSITIONAL_SINGLE([commit], [Commit-ish reference to run command with])"
|
||||||
|
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||||
|
"ARG_LEFTOVERS([command arguments])"
|
||||||
|
];
|
||||||
|
positionalCompletion =
|
||||||
|
''
|
||||||
|
if test "$prev" == "${name}"; then
|
||||||
|
__gitcomp_nl "$(__git_refs)"
|
||||||
|
else
|
||||||
|
_command_offset 2
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
inRootDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
# not using withTmpDir here, because we don't want to keep the directory on error
|
||||||
|
tmpdir="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmpdir"' EXIT
|
||||||
|
|
||||||
|
${git}/bin/git worktree add -f "$tmpdir" "$_arg_commit" > /dev/null
|
||||||
|
|
||||||
|
cd "$tmpdir"
|
||||||
|
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||||
|
|
||||||
|
${git}/bin/git worktree remove -f "$tmpdir" > /dev/null
|
||||||
|
'';
|
||||||
|
|
||||||
|
legacyConfig =
|
||||||
|
writeText "legacy.conf"
|
||||||
|
''
|
||||||
|
# Using this config file to support older postgrest versions for `postgrest-loadtest-against`
|
||||||
|
db-uri="$(PGRST_DB_URI)"
|
||||||
|
db-schema="$(PGRST_DB_SCHEMAS)"
|
||||||
|
db-anon-role="$(PGRST_DB_ANON_ROLE)"
|
||||||
|
db-pool="$(PGRST_DB_POOL)"
|
||||||
|
server-unix-socket="$(PGRST_SERVER_UNIX_SOCKET)"
|
||||||
|
log-level="$(PGRST_LOG_LEVEL)"
|
||||||
|
'';
|
||||||
|
|
||||||
|
waitForPgrstPid =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-wait-for-pgrst-pid";
|
||||||
|
docs = "Wait for PostgREST to be running. Needs to be a separate command for timeout to work below.";
|
||||||
|
args = [
|
||||||
|
"ARG_USE_ENV([PGRST_SERVER_UNIX_SOCKET], [], [Unix socket to check for running PostgREST instance])"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
# ARG_USE_ENV only adds defaults or docs for environment variables
|
||||||
|
# We manually implement a required check here
|
||||||
|
# See also: https://github.com/matejak/argbash/issues/80
|
||||||
|
: "''${PGRST_SERVER_UNIX_SOCKET:?PGRST_SERVER_UNIX_SOCKET is required}"
|
||||||
|
|
||||||
|
until [ -S "$PGRST_SERVER_UNIX_SOCKET" ]
|
||||||
|
do
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
# return pid of postgrest process
|
||||||
|
lsof -t -c '/^postgrest$/' "$PGRST_SERVER_UNIX_SOCKET"
|
||||||
|
'';
|
||||||
|
|
||||||
|
waitForPgrstReady =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-wait-for-pgrst-ready";
|
||||||
|
docs = "Wait for PostgREST to be ready to serve requests. Needs to be a separate command for timeout to work below.";
|
||||||
|
args = [
|
||||||
|
"ARG_USE_ENV([PGRST_SERVER_UNIX_SOCKET], [], [Unix socket to check for running PostgREST instance])"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
# ARG_USE_ENV only adds defaults or docs for environment variables
|
||||||
|
# We manually implement a required check here
|
||||||
|
# See also: https://github.com/matejak/argbash/issues/80
|
||||||
|
: "''${PGRST_SERVER_UNIX_SOCKET:?PGRST_SERVER_UNIX_SOCKET is required}"
|
||||||
|
|
||||||
|
function check_status () {
|
||||||
|
${curl}/bin/curl -s -o /dev/null -w "%{http_code}" --unix-socket "$PGRST_SERVER_UNIX_SOCKET" http://localhost/
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ "$(check_status)" != "200" ]];
|
||||||
|
do sleep 0.1;
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
|
withPgrst =
|
||||||
|
checkedShellScript
|
||||||
|
{
|
||||||
|
name = "postgrest-with-pgrst";
|
||||||
|
docs = "Build and run PostgREST and run <command> with PGRST_SERVER_UNIX_SOCKET set.";
|
||||||
|
args =
|
||||||
|
[
|
||||||
|
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||||
|
"ARG_LEFTOVERS([command arguments])"
|
||||||
|
];
|
||||||
|
positionalCompletion = "_command";
|
||||||
|
inRootDir = true;
|
||||||
|
withEnv = postgrest.env;
|
||||||
|
withTmpDir = true;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
export PGRST_SERVER_UNIX_SOCKET="$tmpdir"/postgrest.socket
|
||||||
|
|
||||||
|
${cabal-install}/bin/cabal v2-build ${devCabalOptions} > "$tmpdir"/build.log 2>&1
|
||||||
|
${cabal-install}/bin/cabal v2-run ${devCabalOptions} --verbose=0 -- \
|
||||||
|
postgrest ${legacyConfig} > "$tmpdir"/run.log 2>&1 &
|
||||||
|
|
||||||
|
# to get the pid of the postgrest process, we need to jump through some hoops
|
||||||
|
# $! will return the pid of cabal - but killing this, will not propagate to postgrest
|
||||||
|
pid=$(timeout -s TERM 1 ${waitForPgrstPid})
|
||||||
|
cleanup() {
|
||||||
|
kill "$pid" || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
timeout -s TERM 5 ${waitForPgrstReady}
|
||||||
|
|
||||||
|
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||||
|
'';
|
||||||
|
|
||||||
in
|
in
|
||||||
buildToolbox
|
buildToolbox
|
||||||
{
|
{
|
||||||
name = "postgrest-with";
|
name = "postgrest-with";
|
||||||
tools = [ withAll ] ++ withVersions;
|
tools = [ withPgAll withGit withPgrst ] ++ withPgVersions;
|
||||||
extra = {
|
# make withTools available for other nix files
|
||||||
# make withTools.latest available for other nix files
|
extra = { inherit withGit withPg withPgAll withPgrst; };
|
||||||
latest = withTmpDb (builtins.head postgresqlVersions);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-75
@@ -1,8 +1,8 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 9.0.0
|
version: 9.0.1
|
||||||
synopsis: REST API for any Postgres database
|
synopsis: REST API for any Postgres database
|
||||||
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
||||||
for the tables and views, supporting all HTTP verbs that security
|
for tables, views, and functions, supporting all HTTP verbs that security
|
||||||
permits.
|
permits.
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
@@ -44,6 +44,7 @@ library
|
|||||||
PostgREST.Config.PgVersion
|
PostgREST.Config.PgVersion
|
||||||
PostgREST.Config.Proxy
|
PostgREST.Config.Proxy
|
||||||
PostgREST.ContentType
|
PostgREST.ContentType
|
||||||
|
PostgREST.Cors
|
||||||
PostgREST.DbStructure
|
PostgREST.DbStructure
|
||||||
PostgREST.DbStructure.Identifiers
|
PostgREST.DbStructure.Identifiers
|
||||||
PostgREST.DbStructure.Proc
|
PostgREST.DbStructure.Proc
|
||||||
@@ -51,6 +52,7 @@ library
|
|||||||
PostgREST.DbStructure.Table
|
PostgREST.DbStructure.Table
|
||||||
PostgREST.Error
|
PostgREST.Error
|
||||||
PostgREST.GucHeader
|
PostgREST.GucHeader
|
||||||
|
PostgREST.Logger
|
||||||
PostgREST.Middleware
|
PostgREST.Middleware
|
||||||
PostgREST.OpenAPI
|
PostgREST.OpenAPI
|
||||||
PostgREST.Query.QueryBuilder
|
PostgREST.Query.QueryBuilder
|
||||||
@@ -69,7 +71,6 @@ library
|
|||||||
, HTTP >= 4000.3.7 && < 4000.4
|
, HTTP >= 4000.3.7 && < 4000.4
|
||||||
, Ranged-sets >= 0.3 && < 0.5
|
, Ranged-sets >= 0.3 && < 0.5
|
||||||
, aeson >= 1.4.7 && < 1.6
|
, aeson >= 1.4.7 && < 1.6
|
||||||
, ansi-wl-pprint >= 0.6.7 && < 0.7
|
|
||||||
, auto-update >= 0.1.4 && < 0.2
|
, auto-update >= 0.1.4 && < 0.2
|
||||||
, base64-bytestring >= 1 && < 1.3
|
, base64-bytestring >= 1 && < 1.3
|
||||||
, bytestring >= 0.10.8 && < 0.11
|
, bytestring >= 0.10.8 && < 0.11
|
||||||
@@ -77,11 +78,9 @@ library
|
|||||||
, cassava >= 0.4.5 && < 0.6
|
, cassava >= 0.4.5 && < 0.6
|
||||||
, configurator-pg >= 0.2 && < 0.3
|
, configurator-pg >= 0.2 && < 0.3
|
||||||
, containers >= 0.5.7 && < 0.7
|
, containers >= 0.5.7 && < 0.7
|
||||||
, contravariant >= 1.4 && < 1.6
|
|
||||||
, contravariant-extras >= 0.3.3 && < 0.4
|
, contravariant-extras >= 0.3.3 && < 0.4
|
||||||
, cookie >= 0.4.2 && < 0.5
|
, cookie >= 0.4.2 && < 0.5
|
||||||
, either >= 4.4.1 && < 5.1
|
, either >= 4.4.1 && < 5.1
|
||||||
, fast-logger >= 2.4.5
|
|
||||||
, gitrev >= 1.2 && < 1.4
|
, gitrev >= 1.2 && < 1.4
|
||||||
, hasql >= 1.4 && < 1.5
|
, hasql >= 1.4 && < 1.5
|
||||||
, hasql-dynamic-statements == 0.3.1
|
, hasql-dynamic-statements == 0.3.1
|
||||||
@@ -99,7 +98,7 @@ library
|
|||||||
, network-uri >= 2.6.1 && < 2.8
|
, network-uri >= 2.6.1 && < 2.8
|
||||||
, optparse-applicative >= 0.13 && < 0.17
|
, optparse-applicative >= 0.13 && < 0.17
|
||||||
, parsec >= 3.1.11 && < 3.2
|
, parsec >= 3.1.11 && < 3.2
|
||||||
, protolude >= 0.3 && < 0.4
|
, protolude >= 0.3.1 && < 0.4
|
||||||
, regex-tdfa >= 1.2.2 && < 1.4
|
, regex-tdfa >= 1.2.2 && < 1.4
|
||||||
, retry >= 0.7.4 && < 0.10
|
, retry >= 0.7.4 && < 0.10
|
||||||
, scientific >= 0.3.4 && < 0.4
|
, scientific >= 0.3.4 && < 0.4
|
||||||
@@ -110,10 +109,8 @@ library
|
|||||||
, vector >= 0.11 && < 0.13
|
, vector >= 0.11 && < 0.13
|
||||||
, wai >= 3.2.1 && < 3.3
|
, wai >= 3.2.1 && < 3.3
|
||||||
, wai-cors >= 0.2.5 && < 0.3
|
, wai-cors >= 0.2.5 && < 0.3
|
||||||
, wai-extra >= 3.0.19 && < 3.2
|
, wai-extra >= 3.1.8 && < 3.2
|
||||||
, wai-logger >= 2.3.2
|
, warp >= 3.3.19 && < 3.4
|
||||||
, wai-middleware-static >= 0.8.1 && < 0.10
|
|
||||||
, warp >= 3.2.12 && < 3.4
|
|
||||||
-- -fno-spec-constr may help keep compile time memory use in check,
|
-- -fno-spec-constr may help keep compile time memory use in check,
|
||||||
-- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304
|
-- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304
|
||||||
-- -optP-Wno-nonportable-include-path
|
-- -optP-Wno-nonportable-include-path
|
||||||
@@ -123,7 +120,7 @@ library
|
|||||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||||
|
|
||||||
if flag(dev)
|
if flag(dev)
|
||||||
ghc-options: -O0
|
ghc-options: -O0 -fwrite-ide-info
|
||||||
if flag(hpc)
|
if flag(hpc)
|
||||||
ghc-options: -fhpc -hpcdir .hpc
|
ghc-options: -fhpc -hpcdir .hpc
|
||||||
else
|
else
|
||||||
@@ -146,13 +143,15 @@ executable postgrest
|
|||||||
build-depends: base >= 4.9 && < 4.16
|
build-depends: base >= 4.9 && < 4.16
|
||||||
, containers >= 0.5.7 && < 0.7
|
, containers >= 0.5.7 && < 0.7
|
||||||
, postgrest
|
, postgrest
|
||||||
, protolude >= 0.3 && < 0.4
|
, protolude >= 0.3.1 && < 0.4
|
||||||
ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I2"
|
ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I0 -qg"
|
||||||
-O2 -Werror -Wall -fwarn-identities
|
-O2 -Werror -Wall -fwarn-identities
|
||||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||||
|
|
||||||
if flag(dev)
|
if flag(dev)
|
||||||
ghc-options: -O0
|
ghc-options: -O0 -fwrite-ide-info
|
||||||
|
-- https://github.com/PostgREST/postgrest/issues/387
|
||||||
|
-with-rtsopts=-K1K
|
||||||
if flag(hpc)
|
if flag(hpc)
|
||||||
ghc-options: -fhpc -hpcdir .hpc
|
ghc-options: -fhpc -hpcdir .hpc
|
||||||
else
|
else
|
||||||
@@ -164,7 +163,7 @@ test-suite spec
|
|||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
QuasiQuotes
|
QuasiQuotes
|
||||||
NoImplicitPrelude
|
NoImplicitPrelude
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test/spec
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules: Feature.AndOrParamsSpec
|
other-modules: Feature.AndOrParamsSpec
|
||||||
Feature.AsymmetricJwtSpec
|
Feature.AsymmetricJwtSpec
|
||||||
@@ -211,10 +210,7 @@ test-suite spec
|
|||||||
, base64-bytestring >= 1 && < 1.3
|
, base64-bytestring >= 1 && < 1.3
|
||||||
, bytestring >= 0.10.8 && < 0.11
|
, bytestring >= 0.10.8 && < 0.11
|
||||||
, case-insensitive >= 1.2 && < 1.3
|
, case-insensitive >= 1.2 && < 1.3
|
||||||
, cassava >= 0.4.5 && < 0.6
|
|
||||||
, containers >= 0.5.7 && < 0.7
|
, containers >= 0.5.7 && < 0.7
|
||||||
, contravariant >= 1.4 && < 1.6
|
|
||||||
, hasql >= 1.4 && < 1.5
|
|
||||||
, hasql-pool >= 0.5 && < 0.6
|
, hasql-pool >= 0.5 && < 0.6
|
||||||
, hasql-transaction >= 1.0.1 && < 1.1
|
, hasql-transaction >= 1.0.1 && < 1.1
|
||||||
, heredoc >= 0.2 && < 0.3
|
, heredoc >= 0.2 && < 0.3
|
||||||
@@ -227,72 +223,67 @@ test-suite spec
|
|||||||
, monad-control >= 1.0.1 && < 1.1
|
, monad-control >= 1.0.1 && < 1.1
|
||||||
, postgrest
|
, postgrest
|
||||||
, process >= 1.4.2 && < 1.7
|
, process >= 1.4.2 && < 1.7
|
||||||
, protolude >= 0.3 && < 0.4
|
, protolude >= 0.3.1 && < 0.4
|
||||||
, regex-tdfa >= 1.2.2 && < 1.4
|
, regex-tdfa >= 1.2.2 && < 1.4
|
||||||
, text >= 1.2.2 && < 1.3
|
, text >= 1.2.2 && < 1.3
|
||||||
, time >= 1.6 && < 1.11
|
|
||||||
, transformers-base >= 0.4.4 && < 0.5
|
, transformers-base >= 0.4.4 && < 0.5
|
||||||
, wai >= 3.2.1 && < 3.3
|
, wai >= 3.2.1 && < 3.3
|
||||||
, wai-extra >= 3.0.19 && < 3.2
|
, wai-extra >= 3.0.19 && < 3.2
|
||||||
ghc-options: -O0 -Werror -Wall -fwarn-identities
|
ghc-options: -O0 -Werror -Wall -fwarn-identities
|
||||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||||
-fno-warn-missing-signatures
|
-fno-warn-missing-signatures
|
||||||
|
-fwrite-ide-info
|
||||||
|
-- https://github.com/PostgREST/postgrest/issues/387
|
||||||
|
-with-rtsopts=-K33K
|
||||||
|
|
||||||
test-suite querycost
|
test-suite querycost
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
QuasiQuotes
|
QuasiQuotes
|
||||||
NoImplicitPrelude
|
NoImplicitPrelude
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test/spec
|
||||||
main-is: QueryCost.hs
|
main-is: QueryCost.hs
|
||||||
other-modules: SpecHelper
|
other-modules: SpecHelper
|
||||||
build-depends: base >= 4.9 && < 4.16
|
build-depends: base >= 4.9 && < 4.16
|
||||||
, aeson >= 1.4.7 && < 1.6
|
, aeson >= 1.4.7 && < 1.6
|
||||||
, aeson-qq >= 0.8.1 && < 0.9
|
, base64-bytestring >= 1 && < 1.3
|
||||||
, async >= 2.1.1 && < 2.3
|
, bytestring >= 0.10.8 && < 0.11
|
||||||
, auto-update >= 0.1.4 && < 0.2
|
, case-insensitive >= 1.2 && < 1.3
|
||||||
, base64-bytestring >= 1 && < 1.3
|
, containers >= 0.5.7 && < 0.7
|
||||||
, bytestring >= 0.10.8 && < 0.11
|
, contravariant >= 1.4 && < 1.6
|
||||||
, case-insensitive >= 1.2 && < 1.3
|
, hasql >= 1.4 && < 1.5
|
||||||
, cassava >= 0.4.5 && < 0.6
|
, hasql-dynamic-statements == 0.3.1
|
||||||
, containers >= 0.5.7 && < 0.7
|
, hasql-pool >= 0.5 && < 0.6
|
||||||
, contravariant >= 1.4 && < 1.6
|
, hasql-transaction >= 1.0.1 && < 1.1
|
||||||
, hasql >= 1.4 && < 1.5
|
, heredoc >= 0.2 && < 0.3
|
||||||
, hasql-dynamic-statements == 0.3.1
|
, hspec >= 2.3 && < 2.9
|
||||||
, hasql-pool >= 0.5 && < 0.6
|
, hspec-wai >= 0.10 && < 0.12
|
||||||
, hasql-transaction >= 1.0.1 && < 1.1
|
, http-types >= 0.12.3 && < 0.13
|
||||||
, heredoc >= 0.2 && < 0.3
|
, lens >= 4.14 && < 5.1
|
||||||
, hspec >= 2.3 && < 2.9
|
, lens-aeson >= 1.0.1 && < 1.2
|
||||||
, hspec-wai >= 0.10 && < 0.12
|
, postgrest
|
||||||
, hspec-wai-json >= 0.10 && < 0.12
|
, process >= 1.4.2 && < 1.7
|
||||||
, http-types >= 0.12.3 && < 0.13
|
, protolude >= 0.3.1 && < 0.4
|
||||||
, lens >= 4.14 && < 5.1
|
, regex-tdfa >= 1.2.2 && < 1.4
|
||||||
, lens-aeson >= 1.0.1 && < 1.2
|
, wai-extra >= 3.0.19 && < 3.2
|
||||||
, monad-control >= 1.0.1 && < 1.1
|
ghc-options: -O0 -Werror -Wall -fwarn-identities
|
||||||
, postgrest
|
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||||
, process >= 1.4.2 && < 1.7
|
-fwrite-ide-info
|
||||||
, protolude >= 0.3 && < 0.4
|
-- https://github.com/PostgREST/postgrest/issues/387
|
||||||
, regex-tdfa >= 1.2.2 && < 1.4
|
-with-rtsopts=-K1K
|
||||||
, text >= 1.2.2 && < 1.3
|
|
||||||
, time >= 1.6 && < 1.11
|
|
||||||
, transformers-base >= 0.4.4 && < 0.5
|
|
||||||
, wai >= 3.2.1 && < 3.3
|
|
||||||
, wai-extra >= 3.0.19 && < 3.2
|
|
||||||
ghc-options: -O0 -Werror -Wall -fwarn-identities
|
|
||||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
|
||||||
|
|
||||||
test-suite doctests
|
test-suite doctests
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
NoImplicitPrelude
|
NoImplicitPrelude
|
||||||
hs-source-dirs: test/doctests
|
hs-source-dirs: test/doc
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
build-depends: base >= 4.9 && < 4.16
|
build-depends: base >= 4.9 && < 4.16
|
||||||
, doctest >= 0.8
|
, doctest >= 0.8
|
||||||
, postgrest
|
, postgrest
|
||||||
, pretty-simple
|
, pretty-simple
|
||||||
, protolude >= 0.3 && < 0.4
|
, protolude >= 0.3.1 && < 0.4
|
||||||
ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities
|
ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities
|
||||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ let
|
|||||||
[
|
[
|
||||||
postgrest.cabalTools
|
postgrest.cabalTools
|
||||||
postgrest.devTools
|
postgrest.devTools
|
||||||
|
postgrest.loadtest
|
||||||
postgrest.nixpkgsTools
|
postgrest.nixpkgsTools
|
||||||
postgrest.style
|
postgrest.style
|
||||||
postgrest.tests
|
postgrest.tests
|
||||||
@@ -37,6 +38,7 @@ lib.overrideDerivation postgrest.env (
|
|||||||
base.buildInputs ++ [
|
base.buildInputs ++ [
|
||||||
pkgs.cabal-install
|
pkgs.cabal-install
|
||||||
pkgs.cabal2nix
|
pkgs.cabal2nix
|
||||||
|
pkgs.git
|
||||||
pkgs.postgresql
|
pkgs.postgresql
|
||||||
postgrest.hsie.bin
|
postgrest.hsie.bin
|
||||||
]
|
]
|
||||||
@@ -45,6 +47,7 @@ lib.overrideDerivation postgrest.env (
|
|||||||
shellHook =
|
shellHook =
|
||||||
''
|
''
|
||||||
source ${pkgs.bashCompletion}/etc/profile.d/bash_completion.sh
|
source ${pkgs.bashCompletion}/etc/profile.d/bash_completion.sh
|
||||||
|
source ${pkgs.git}/share/git/contrib/completion/git-completion.bash
|
||||||
source ${postgrest.hsie.bashCompletion}
|
source ${postgrest.hsie.bashCompletion}
|
||||||
|
|
||||||
''
|
''
|
||||||
|
|||||||
@@ -42,8 +42,10 @@ import qualified Network.Wai.Handler.Warp as Warp
|
|||||||
|
|
||||||
import qualified PostgREST.AppState as AppState
|
import qualified PostgREST.AppState as AppState
|
||||||
import qualified PostgREST.Auth as Auth
|
import qualified PostgREST.Auth as Auth
|
||||||
|
import qualified PostgREST.Cors as Cors
|
||||||
import qualified PostgREST.DbStructure as DbStructure
|
import qualified PostgREST.DbStructure as DbStructure
|
||||||
import qualified PostgREST.Error as Error
|
import qualified PostgREST.Error as Error
|
||||||
|
import qualified PostgREST.Logger as Logger
|
||||||
import qualified PostgREST.Middleware as Middleware
|
import qualified PostgREST.Middleware as Middleware
|
||||||
import qualified PostgREST.OpenAPI as OpenAPI
|
import qualified PostgREST.OpenAPI as OpenAPI
|
||||||
import qualified PostgREST.Query.QueryBuilder as QueryBuilder
|
import qualified PostgREST.Query.QueryBuilder as QueryBuilder
|
||||||
@@ -137,8 +139,9 @@ serverSettings AppConfig{..} =
|
|||||||
|
|
||||||
-- | PostgREST application
|
-- | PostgREST application
|
||||||
postgrest :: LogLevel -> AppState.AppState -> IO () -> Wai.Application
|
postgrest :: LogLevel -> AppState.AppState -> IO () -> Wai.Application
|
||||||
postgrest logLev appState connWorker =
|
postgrest logLevel appState connWorker =
|
||||||
Middleware.pgrstMiddleware logLev $
|
Logger.middleware logLevel .
|
||||||
|
Cors.middleware $
|
||||||
\req respond -> do
|
\req respond -> do
|
||||||
time <- AppState.getTime appState
|
time <- AppState.getTime appState
|
||||||
conf <- AppState.getConfig appState
|
conf <- AppState.getConfig appState
|
||||||
@@ -466,7 +469,7 @@ handleOpenApi headersOnly tSchema (RequestContext conf@AppConfig{..} dbStructure
|
|||||||
OAFollowPriv ->
|
OAFollowPriv ->
|
||||||
OpenAPI.encode conf dbStructure
|
OpenAPI.encode conf dbStructure
|
||||||
<$> SQL.statement tSchema (DbStructure.accessibleTables ctxPgVersion configDbPreparedStatements)
|
<$> SQL.statement tSchema (DbStructure.accessibleTables ctxPgVersion configDbPreparedStatements)
|
||||||
<*> SQL.statement tSchema (DbStructure.accessibleProcs configDbPreparedStatements)
|
<*> SQL.statement tSchema (DbStructure.accessibleProcs ctxPgVersion configDbPreparedStatements)
|
||||||
<*> SQL.statement tSchema (DbStructure.schemaDescription configDbPreparedStatements)
|
<*> SQL.statement tSchema (DbStructure.schemaDescription configDbPreparedStatements)
|
||||||
OAIgnorePriv ->
|
OAIgnorePriv ->
|
||||||
OpenAPI.encode conf dbStructure
|
OpenAPI.encode conf dbStructure
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ main installSignalHandlers runAppWithSocket CLI{cliCommand, cliPath} = do
|
|||||||
dumpSchema :: AppState -> IO LBS.ByteString
|
dumpSchema :: AppState -> IO LBS.ByteString
|
||||||
dumpSchema appState = do
|
dumpSchema appState = do
|
||||||
AppConfig{..} <- AppState.getConfig appState
|
AppConfig{..} <- AppState.getConfig appState
|
||||||
actualPgVersion <- AppState.getPgVersion appState
|
|
||||||
result <-
|
result <-
|
||||||
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
||||||
SQL.use (AppState.getPool appState) $
|
SQL.use (AppState.getPool appState) $
|
||||||
@@ -61,7 +60,6 @@ dumpSchema appState = do
|
|||||||
queryDbStructure
|
queryDbStructure
|
||||||
(toList configDbSchemas)
|
(toList configDbSchemas)
|
||||||
configDbExtraSearchPath
|
configDbExtraSearchPath
|
||||||
actualPgVersion
|
|
||||||
configDbPreparedStatements
|
configDbPreparedStatements
|
||||||
SQL.release $ AppState.getPool appState
|
SQL.release $ AppState.getPool appState
|
||||||
case result of
|
case result of
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ toText conf =
|
|||||||
,("db-prepared-statements", T.toLower . show . configDbPreparedStatements)
|
,("db-prepared-statements", T.toLower . show . configDbPreparedStatements)
|
||||||
,("db-root-spec", q . maybe mempty dumpQi . configDbRootSpec)
|
,("db-root-spec", q . maybe mempty dumpQi . configDbRootSpec)
|
||||||
,("db-schemas", q . T.intercalate "," . toList . configDbSchemas)
|
,("db-schemas", q . T.intercalate "," . toList . configDbSchemas)
|
||||||
,("db-config", q . T.toLower . show . configDbConfig)
|
,("db-config", T.toLower . show . configDbConfig)
|
||||||
,("db-tx-end", q . showTxEnd)
|
,("db-tx-end", q . showTxEnd)
|
||||||
,("db-uri", q . configDbUri)
|
,("db-uri", q . configDbUri)
|
||||||
,("db-use-legacy-gucs", T.toLower . show . configDbUseLegacyGucs)
|
,("db-use-legacy-gucs", T.toLower . show . configDbUseLegacyGucs)
|
||||||
@@ -369,17 +369,17 @@ parser optPath env dbSettings =
|
|||||||
|
|
||||||
coerceInt :: (Read i, Integral i) => C.Value -> Maybe i
|
coerceInt :: (Read i, Integral i) => C.Value -> Maybe i
|
||||||
coerceInt (C.Number x) = rightToMaybe $ floatingOrInteger x
|
coerceInt (C.Number x) = rightToMaybe $ floatingOrInteger x
|
||||||
coerceInt (C.String x) = readMaybe $ toS x
|
coerceInt (C.String x) = readMaybe x
|
||||||
coerceInt _ = Nothing
|
coerceInt _ = Nothing
|
||||||
|
|
||||||
coerceBool :: C.Value -> Maybe Bool
|
coerceBool :: C.Value -> Maybe Bool
|
||||||
coerceBool (C.Bool b) = Just b
|
coerceBool (C.Bool b) = Just b
|
||||||
coerceBool (C.String s) =
|
coerceBool (C.String s) =
|
||||||
-- parse all kinds of text: True, true, TRUE, "true", ...
|
-- parse all kinds of text: True, true, TRUE, "true", ...
|
||||||
case readMaybe . toS $ T.toTitle $ T.filter isAlpha $ toS s of
|
case readMaybe $ T.toTitle $ T.filter isAlpha $ toS s of
|
||||||
Just b -> Just b
|
Just b -> Just b
|
||||||
-- numeric instead?
|
-- numeric instead?
|
||||||
Nothing -> (> 0) <$> (readMaybe $ toS s :: Maybe Integer)
|
Nothing -> (> 0) <$> (readMaybe s :: Maybe Integer)
|
||||||
coerceBool _ = Nothing
|
coerceBool _ = Nothing
|
||||||
|
|
||||||
splitOnCommas :: C.Value -> [Text]
|
splitOnCommas :: C.Value -> [Text]
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
module PostgREST.Config.Database
|
module PostgREST.Config.Database
|
||||||
( queryDbSettings
|
( pgVersionStatement
|
||||||
|
, queryDbSettings
|
||||||
, queryPgVersion
|
, queryPgVersion
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@@ -20,7 +21,10 @@ import Text.InterpolatedString.Perl6 (q)
|
|||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
queryPgVersion :: Session PgVersion
|
queryPgVersion :: Session PgVersion
|
||||||
queryPgVersion = statement mempty $ SQL.Statement sql HE.noParams versionRow False
|
queryPgVersion = statement mempty pgVersionStatement
|
||||||
|
|
||||||
|
pgVersionStatement :: SQL.Statement () PgVersion
|
||||||
|
pgVersionStatement = SQL.Statement sql HE.noParams versionRow False
|
||||||
where
|
where
|
||||||
sql = "SELECT current_setting('server_version_num')::integer, current_setting('server_version')"
|
sql = "SELECT current_setting('server_version_num')::integer, current_setting('server_version')"
|
||||||
versionRow = HD.singleRow $ PgVersion <$> column HD.int4 <*> column HD.text
|
versionRow = HD.singleRow $ PgVersion <$> column HD.int4 <*> column HD.text
|
||||||
@@ -36,19 +40,26 @@ dbSettingsStatement :: SQL.Statement () [(Text, Text)]
|
|||||||
dbSettingsStatement = SQL.Statement sql HE.noParams decodeSettings False
|
dbSettingsStatement = SQL.Statement sql HE.noParams decodeSettings False
|
||||||
where
|
where
|
||||||
sql = [q|
|
sql = [q|
|
||||||
with
|
WITH
|
||||||
role_setting as (
|
role_setting (database, setting) AS (
|
||||||
select setdatabase, unnest(setconfig) as setting from pg_catalog.pg_db_role_setting
|
SELECT setdatabase,
|
||||||
where setrole = current_user::regrole::oid
|
unnest(setconfig)
|
||||||
and setdatabase in (0, (select oid from pg_catalog.pg_database where datname = current_catalog))
|
FROM pg_catalog.pg_db_role_setting
|
||||||
|
WHERE setrole = CURRENT_USER::regrole::oid
|
||||||
|
AND setdatabase IN (0, (SELECT oid FROM pg_catalog.pg_database WHERE datname = CURRENT_CATALOG))
|
||||||
),
|
),
|
||||||
kv_settings as (
|
kv_settings (database, k, v) AS (
|
||||||
select setdatabase, split_part(setting, '=', 1) as k, split_part(setting, '=', 2) as value from role_setting
|
SELECT database,
|
||||||
where setting like 'pgrst.%'
|
substr(setting, 1, strpos(setting, '=') - 1),
|
||||||
|
substr(setting, strpos(setting, '=') + 1)
|
||||||
|
FROM role_setting
|
||||||
|
WHERE setting LIKE 'pgrst.%'
|
||||||
)
|
)
|
||||||
select distinct on (key) replace(k, 'pgrst.', '') as key, value
|
SELECT DISTINCT ON (key)
|
||||||
from kv_settings
|
replace(k, 'pgrst.', '') AS key,
|
||||||
order by key, setdatabase desc;
|
v AS value
|
||||||
|
FROM kv_settings
|
||||||
|
ORDER BY key, database DESC;
|
||||||
|]
|
|]
|
||||||
decodeSettings = HD.rowList $ (,) <$> column HD.text <*> column HD.text
|
decodeSettings = HD.rowList $ (,) <$> column HD.text <*> column HD.text
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{-|
|
||||||
|
Module : PostgREST.Cors
|
||||||
|
Description : Wai Middleware to set cors policy.
|
||||||
|
-}
|
||||||
|
module PostgREST.Cors (middleware) where
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Char8 as BS
|
||||||
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
import qualified Network.Wai as Wai
|
||||||
|
import qualified Network.Wai.Middleware.Cors as Wai
|
||||||
|
|
||||||
|
import Data.List (lookup)
|
||||||
|
|
||||||
|
import Protolude
|
||||||
|
|
||||||
|
middleware :: Wai.Middleware
|
||||||
|
middleware = Wai.cors corsPolicy
|
||||||
|
|
||||||
|
-- | CORS policy to be used in by Wai Cors middleware
|
||||||
|
corsPolicy :: Wai.Request -> Maybe Wai.CorsResourcePolicy
|
||||||
|
corsPolicy req = case lookup "origin" headers of
|
||||||
|
Just origin ->
|
||||||
|
Just Wai.CorsResourcePolicy
|
||||||
|
{ Wai.corsOrigins = Just ([origin], True)
|
||||||
|
, Wai.corsMethods = ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"]
|
||||||
|
, Wai.corsRequestHeaders = "Authorization" : accHeaders
|
||||||
|
, Wai.corsExposedHeaders = Just
|
||||||
|
[ "Content-Encoding", "Content-Location", "Content-Range", "Content-Type"
|
||||||
|
, "Date", "Location", "Server", "Transfer-Encoding", "Range-Unit"]
|
||||||
|
, Wai.corsMaxAge = Just $ 60*60*24
|
||||||
|
, Wai.corsVaryOrigin = False
|
||||||
|
, Wai.corsRequireOrigin = False
|
||||||
|
, Wai.corsIgnoreFailures = True
|
||||||
|
}
|
||||||
|
Nothing -> Nothing
|
||||||
|
where
|
||||||
|
headers = Wai.requestHeaders req
|
||||||
|
accHeaders = case lookup "access-control-request-headers" headers of
|
||||||
|
Just hdrs -> map (CI.mk . BS.strip) $ BS.split ',' hdrs
|
||||||
|
-- Impossible case, Middleware.Cors will not evaluate this when
|
||||||
|
-- the Access-Control-Request-Headers header is not set.
|
||||||
|
Nothing -> []
|
||||||
@@ -41,7 +41,9 @@ import Data.Set as S (fromList)
|
|||||||
import Data.Text (split)
|
import Data.Text (split)
|
||||||
import Text.InterpolatedString.Perl6 (q)
|
import Text.InterpolatedString.Perl6 (q)
|
||||||
|
|
||||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion100)
|
import PostgREST.Config.Database (pgVersionStatement)
|
||||||
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
|
||||||
|
pgVersion110)
|
||||||
import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..),
|
import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..),
|
||||||
Schema, TableName)
|
Schema, TableName)
|
||||||
import PostgREST.DbStructure.Proc (PgType (..),
|
import PostgREST.DbStructure.Proc (PgType (..),
|
||||||
@@ -83,15 +85,16 @@ type ViewColumn = Column
|
|||||||
-- | A SQL query that can be executed independently
|
-- | A SQL query that can be executed independently
|
||||||
type SqlQuery = ByteString
|
type SqlQuery = ByteString
|
||||||
|
|
||||||
queryDbStructure :: [Schema] -> [Schema] -> PgVersion -> Bool -> SQL.Transaction DbStructure
|
queryDbStructure :: [Schema] -> [Schema] -> Bool -> SQL.Transaction DbStructure
|
||||||
queryDbStructure schemas extraSearchPath pgVer prepared = do
|
queryDbStructure schemas extraSearchPath prepared = do
|
||||||
SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
|
SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
|
||||||
|
pgVer <- SQL.statement mempty pgVersionStatement
|
||||||
tabs <- SQL.statement mempty $ allTables pgVer prepared
|
tabs <- SQL.statement mempty $ allTables pgVer prepared
|
||||||
cols <- SQL.statement schemas $ allColumns tabs prepared
|
cols <- SQL.statement schemas $ allColumns tabs prepared
|
||||||
srcCols <- SQL.statement (schemas, extraSearchPath) $ pfkSourceColumns cols prepared
|
srcCols <- SQL.statement (schemas, extraSearchPath) $ pfkSourceColumns cols prepared
|
||||||
m2oRels <- SQL.statement mempty $ allM2ORels tabs cols prepared
|
m2oRels <- SQL.statement mempty $ allM2ORels tabs cols prepared
|
||||||
keys <- SQL.statement mempty $ allPrimaryKeys tabs prepared
|
keys <- SQL.statement mempty $ allPrimaryKeys tabs prepared
|
||||||
procs <- SQL.statement schemas $ allProcs prepared
|
procs <- SQL.statement schemas $ allProcs pgVer prepared
|
||||||
|
|
||||||
let rels = addO2MRels . addM2MRels $ addViewM2ORels srcCols m2oRels
|
let rels = addO2MRels . addM2MRels $ addViewM2ORels srcCols m2oRels
|
||||||
keys' = addViewPrimaryKeys srcCols keys
|
keys' = addViewPrimaryKeys srcCols keys
|
||||||
@@ -224,18 +227,18 @@ decodeProcs =
|
|||||||
| v == 's' = Stable
|
| v == 's' = Stable
|
||||||
| otherwise = Volatile -- only 'v' can happen here
|
| otherwise = Volatile -- only 'v' can happen here
|
||||||
|
|
||||||
allProcs :: Bool -> SQL.Statement [Schema] ProcsMap
|
allProcs :: PgVersion -> Bool -> SQL.Statement [Schema] ProcsMap
|
||||||
allProcs = SQL.Statement sql (arrayParam HE.text) decodeProcs
|
allProcs pgVer = SQL.Statement sql (arrayParam HE.text) decodeProcs
|
||||||
where
|
where
|
||||||
sql = procsSqlQuery <> " WHERE pn.nspname = ANY($1)"
|
sql = procsSqlQuery pgVer <> " AND pn.nspname = ANY($1)"
|
||||||
|
|
||||||
accessibleProcs :: Bool -> SQL.Statement Schema ProcsMap
|
accessibleProcs :: PgVersion -> Bool -> SQL.Statement Schema ProcsMap
|
||||||
accessibleProcs = SQL.Statement sql (param HE.text) decodeProcs
|
accessibleProcs pgVer = SQL.Statement sql (param HE.text) decodeProcs
|
||||||
where
|
where
|
||||||
sql = procsSqlQuery <> " WHERE pn.nspname = $1 AND has_function_privilege(p.oid, 'execute')"
|
sql = procsSqlQuery pgVer <> " AND pn.nspname = $1 AND has_function_privilege(p.oid, 'execute')"
|
||||||
|
|
||||||
procsSqlQuery :: SqlQuery
|
procsSqlQuery :: PgVersion -> SqlQuery
|
||||||
procsSqlQuery = [q|
|
procsSqlQuery pgVer = [q|
|
||||||
-- Recursively get the base types of domains
|
-- Recursively get the base types of domains
|
||||||
WITH
|
WITH
|
||||||
base_types AS (
|
base_types AS (
|
||||||
@@ -297,7 +300,8 @@ procsSqlQuery = [q|
|
|||||||
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
||||||
LEFT JOIN pg_class comp ON comp.oid = t.typrelid
|
LEFT JOIN pg_class comp ON comp.oid = t.typrelid
|
||||||
LEFT JOIN pg_catalog.pg_description as d ON d.objoid = p.oid
|
LEFT JOIN pg_catalog.pg_description as d ON d.objoid = p.oid
|
||||||
|]
|
WHERE t.oid <> 'pg_catalog.trigger'::regtype
|
||||||
|
|] <> (if pgVer >= pgVersion110 then "AND prokind = 'f'" else "AND NOT (proisagg OR proiswindow)")
|
||||||
|
|
||||||
schemaDescription :: Bool -> SQL.Statement Schema (Maybe Text)
|
schemaDescription :: Bool -> SQL.Statement Schema (Maybe Text)
|
||||||
schemaDescription =
|
schemaDescription =
|
||||||
@@ -792,7 +796,6 @@ pfkSourceColumns cols =
|
|||||||
replace(
|
replace(
|
||||||
replace(
|
replace(
|
||||||
replace(
|
replace(
|
||||||
replace(
|
|
||||||
regexp_replace(
|
regexp_replace(
|
||||||
replace(
|
replace(
|
||||||
replace(
|
replace(
|
||||||
@@ -803,6 +806,7 @@ pfkSourceColumns cols =
|
|||||||
replace(
|
replace(
|
||||||
replace(
|
replace(
|
||||||
replace(
|
replace(
|
||||||
|
replace(
|
||||||
replace(
|
replace(
|
||||||
view_definition::text,
|
view_definition::text,
|
||||||
-- This conversion to json is heavily optimized for performance.
|
-- This conversion to json is heavily optimized for performance.
|
||||||
@@ -814,9 +818,15 @@ pfkSourceColumns cols =
|
|||||||
-- -----------------------------------------------
|
-- -----------------------------------------------
|
||||||
-- pattern | replacement | flags
|
-- pattern | replacement | flags
|
||||||
-- -----------------------------------------------
|
-- -----------------------------------------------
|
||||||
|
-- `<>` in pg_node_tree is the same as `null` in JSON, but due to very poor performance of json_typeof
|
||||||
|
-- we need to make this an empty array here to prevent json_array_elements from throwing an error
|
||||||
|
-- when the targetList is null.
|
||||||
|
-- We'll need to put it first, to make the node protection below work for node lists that start with
|
||||||
|
-- null: `(<> ...`, too. This is the case for coldefexprs, when the first column does not have a default value.
|
||||||
|
'<>' , '()'
|
||||||
-- `,` is not part of the pg_node_tree format, but used in the regex.
|
-- `,` is not part of the pg_node_tree format, but used in the regex.
|
||||||
-- This removes all `,` that might be part of column names.
|
-- This removes all `,` that might be part of column names.
|
||||||
',' , ''
|
), ',' , ''
|
||||||
-- The same applies for `{` and `}`, although those are used a lot in pg_node_tree.
|
-- The same applies for `{` and `}`, although those are used a lot in pg_node_tree.
|
||||||
-- We remove the escaped ones, which might be part of column names again.
|
-- We remove the escaped ones, which might be part of column names again.
|
||||||
), E'\\{' , ''
|
), E'\\{' , ''
|
||||||
@@ -851,10 +861,6 @@ pfkSourceColumns cols =
|
|||||||
), ')' , ']'
|
), ')' , ']'
|
||||||
-- pg_node_tree has ` ` between list items, but JSON uses `,`
|
-- pg_node_tree has ` ` between list items, but JSON uses `,`
|
||||||
), ' ' , ','
|
), ' ' , ','
|
||||||
-- `<>` in pg_node_tree is the same as `null` in JSON, but due to very poor performance of json_typeof
|
|
||||||
-- we need to make this an empty array here to prevent json_array_elements from throwing an error
|
|
||||||
-- when the targetList is null.
|
|
||||||
), '<>' , '[]'
|
|
||||||
)::json as view_definition
|
)::json as view_definition
|
||||||
from views
|
from views
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -126,23 +126,22 @@ instance JSON.ToJSON ApiRequestError where
|
|||||||
compressedRel :: Relationship -> JSON.Value
|
compressedRel :: Relationship -> JSON.Value
|
||||||
compressedRel Relationship{..} =
|
compressedRel Relationship{..} =
|
||||||
let
|
let
|
||||||
fmtTbl Table{..} = tableSchema <> "." <> tableName
|
fmtEls els = "(" <> T.intercalate ", " els <> ")"
|
||||||
fmtEls els = "[" <> T.intercalate ", " els <> "]"
|
|
||||||
in
|
in
|
||||||
JSON.object $
|
JSON.object $
|
||||||
("embedding" .= (tableName relTable <> " with " <> tableName relForeignTable :: Text))
|
("embedding" .= (tableName relTable <> " with " <> tableName relForeignTable :: Text))
|
||||||
: case relCardinality of
|
: case relCardinality of
|
||||||
M2M Junction{..} -> [
|
M2M Junction{..} -> [
|
||||||
"cardinality" .= ("many-to-many" :: Text)
|
"cardinality" .= ("many-to-many" :: Text)
|
||||||
, "relationship" .= (fmtTbl junTable <> fmtEls [junConstraint1] <> fmtEls [junConstraint2])
|
, "relationship" .= (tableName junTable <> " using " <> junConstraint1 <> fmtEls (colName <$> junColumns1) <> " and " <> junConstraint2 <> fmtEls (colName <$> junColumns2))
|
||||||
]
|
]
|
||||||
M2O cons -> [
|
M2O cons -> [
|
||||||
"cardinality" .= ("many-to-one" :: Text)
|
"cardinality" .= ("many-to-one" :: Text)
|
||||||
, "relationship" .= (cons <> fmtEls (colName <$> relColumns) <> fmtEls (colName <$> relForeignColumns))
|
, "relationship" .= (cons <> " using " <> tableName relTable <> fmtEls (colName <$> relColumns) <> " and " <> tableName relForeignTable <> fmtEls (colName <$> relForeignColumns))
|
||||||
]
|
]
|
||||||
O2M cons -> [
|
O2M cons -> [
|
||||||
"cardinality" .= ("one-to-many" :: Text)
|
"cardinality" .= ("one-to-many" :: Text)
|
||||||
, "relationship" .= (cons <> fmtEls (colName <$> relColumns) <> fmtEls (colName <$> relForeignColumns))
|
, "relationship" .= (cons <> " using " <> tableName relTable <> fmtEls (colName <$> relColumns) <> " and " <> tableName relForeignTable <> fmtEls (colName <$> relForeignColumns))
|
||||||
]
|
]
|
||||||
|
|
||||||
relHint :: [Relationship] -> Text
|
relHint :: [Relationship] -> Text
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{-|
|
||||||
|
Module : PostgREST.Logger
|
||||||
|
Description : Wai Middleware to log requests to stdout.
|
||||||
|
-}
|
||||||
|
module PostgREST.Logger (middleware) where
|
||||||
|
|
||||||
|
import qualified Network.Wai as Wai
|
||||||
|
import qualified Network.Wai.Middleware.RequestLogger as Wai
|
||||||
|
|
||||||
|
import Network.HTTP.Types.Status (status400, status500)
|
||||||
|
import System.IO.Unsafe (unsafePerformIO)
|
||||||
|
|
||||||
|
import PostgREST.Config (LogLevel (..))
|
||||||
|
|
||||||
|
import Protolude
|
||||||
|
|
||||||
|
middleware :: LogLevel -> Wai.Middleware
|
||||||
|
middleware logLevel = case logLevel of
|
||||||
|
LogInfo -> requestLogger (const True)
|
||||||
|
LogWarn -> requestLogger (>= status400)
|
||||||
|
LogError -> requestLogger (>= status500)
|
||||||
|
LogCrit -> requestLogger (const False)
|
||||||
|
where
|
||||||
|
requestLogger filterStatus = unsafePerformIO $ Wai.mkRequestLogger Wai.defaultRequestLoggerSettings
|
||||||
|
{ Wai.outputFormat = Wai.ApacheWithSettings $
|
||||||
|
Wai.defaultApacheSettings
|
||||||
|
& Wai.setApacheRequestFilter (\_ res -> filterStatus $ Wai.responseStatus res)
|
||||||
|
}
|
||||||
+22
-103
@@ -2,47 +2,29 @@
|
|||||||
Module : PostgREST.Middleware
|
Module : PostgREST.Middleware
|
||||||
Description : Sets CORS policy. Also the PostgreSQL GUCs, role, search_path and pre-request function.
|
Description : Sets CORS policy. Also the PostgreSQL GUCs, role, search_path and pre-request function.
|
||||||
-}
|
-}
|
||||||
|
{-# LANGUAGE BlockArguments #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
module PostgREST.Middleware
|
module PostgREST.Middleware
|
||||||
( runPgLocals
|
( runPgLocals
|
||||||
, pgrstFormat
|
|
||||||
, pgrstMiddleware
|
|
||||||
, defaultCorsPolicy
|
|
||||||
, corsPolicy
|
|
||||||
, optionalRollback
|
, optionalRollback
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Aeson as JSON
|
import qualified Data.Aeson as JSON
|
||||||
import qualified Data.ByteString.Char8 as BS
|
import qualified Data.ByteString.Lazy.Char8 as LBS
|
||||||
import qualified Data.ByteString.Lazy.Char8 as LBS
|
import qualified Data.HashMap.Strict as M
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.Text as T
|
||||||
import qualified Data.HashMap.Strict as M
|
import qualified Data.Text.Encoding as T
|
||||||
import qualified Data.Text as T
|
import qualified Hasql.Decoders as HD
|
||||||
import qualified Data.Text.Encoding as T
|
import qualified Hasql.DynamicStatements.Snippet as SQL hiding (sql)
|
||||||
import qualified Hasql.Decoders as HD
|
import qualified Hasql.DynamicStatements.Statement as SQL
|
||||||
import qualified Hasql.DynamicStatements.Snippet as SQL hiding
|
import qualified Hasql.Transaction as SQL
|
||||||
(sql)
|
import qualified Network.Wai as Wai
|
||||||
import qualified Hasql.DynamicStatements.Statement as SQL
|
|
||||||
import qualified Hasql.Transaction as SQL
|
|
||||||
import qualified Network.Wai as Wai
|
|
||||||
import qualified Network.Wai.Logger as Wai
|
|
||||||
import qualified Network.Wai.Middleware.Cors as Wai
|
|
||||||
import qualified Network.Wai.Middleware.Gzip as Wai
|
|
||||||
import qualified Network.Wai.Middleware.RequestLogger as Wai
|
|
||||||
import qualified Network.Wai.Middleware.Static as Wai
|
|
||||||
|
|
||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
|
|
||||||
import Data.Function (id)
|
import Data.Scientific (FPFormat (..), formatScientific, isInteger)
|
||||||
import Data.List (lookup)
|
|
||||||
import Data.Scientific (FPFormat (..), formatScientific,
|
|
||||||
isInteger)
|
|
||||||
import Network.HTTP.Types.Status (Status, status400, status500,
|
|
||||||
statusCode)
|
|
||||||
import System.IO.Unsafe (unsafePerformIO)
|
|
||||||
import System.Log.FastLogger (toLogStr)
|
|
||||||
|
|
||||||
import PostgREST.Config (AppConfig (..), LogLevel (..))
|
import PostgREST.Config (AppConfig (..))
|
||||||
import PostgREST.Config.PgVersion (PgVersion (..), pgVersion140)
|
import PostgREST.Config.PgVersion (PgVersion (..), pgVersion140)
|
||||||
import PostgREST.Error (Error, errorResponseFor)
|
import PostgREST.Error (Error, errorResponseFor)
|
||||||
import PostgREST.GucHeader (addHeadersIfNotIncluded)
|
import PostgREST.GucHeader (addHeadersIfNotIncluded)
|
||||||
@@ -90,76 +72,12 @@ runPgLocals conf claims app req jsonDbS actualPgVersion = do
|
|||||||
_ -> mempty
|
_ -> mempty
|
||||||
usesLegacyGucs = configDbUseLegacyGucs conf && actualPgVersion < pgVersion140
|
usesLegacyGucs = configDbUseLegacyGucs conf && actualPgVersion < pgVersion140
|
||||||
|
|
||||||
-- | Log in apache format. Only requests that have a status greater than minStatus are logged.
|
unquoted :: JSON.Value -> Text
|
||||||
-- | There's no way to filter logs in the apache format on wai-extra: https://hackage.haskell.org/package/wai-extra-3.0.29.2/docs/Network-Wai-Middleware-RequestLogger.html#t:OutputFormat.
|
unquoted (JSON.String t) = t
|
||||||
-- | So here we copy wai-logger apacheLogStr function: https://github.com/kazu-yamamoto/logger/blob/a4f51b909a099c51af7a3f75cf16e19a06f9e257/wai-logger/Network/Wai/Logger/Apache.hs#L45
|
unquoted (JSON.Number n) =
|
||||||
-- | TODO: Add the ability to filter apache logs on wai-extra and remove this function.
|
toS $ formatScientific Fixed (if isInteger n then Just 0 else Nothing) n
|
||||||
pgrstFormat :: Status -> Wai.OutputFormatter
|
unquoted (JSON.Bool b) = show b
|
||||||
pgrstFormat minStatus date req status responseSize =
|
unquoted v = T.decodeUtf8 . LBS.toStrict $ JSON.encode v
|
||||||
if status < minStatus
|
|
||||||
then mempty
|
|
||||||
else toLogStr (getSourceFromSocket req)
|
|
||||||
<> " - - ["
|
|
||||||
<> toLogStr date
|
|
||||||
<> "] \""
|
|
||||||
<> toLogStr (Wai.requestMethod req)
|
|
||||||
<> " "
|
|
||||||
<> toLogStr (Wai.rawPathInfo req <> Wai.rawQueryString req)
|
|
||||||
<> " "
|
|
||||||
<> toLogStr (show (Wai.httpVersion req)::Text)
|
|
||||||
<> "\" "
|
|
||||||
<> toLogStr (show (statusCode status)::Text)
|
|
||||||
<> " "
|
|
||||||
<> toLogStr (maybe "-" show responseSize::Text)
|
|
||||||
<> " \""
|
|
||||||
<> toLogStr (fromMaybe mempty $ Wai.requestHeaderReferer req)
|
|
||||||
<> "\" \""
|
|
||||||
<> toLogStr (fromMaybe mempty $ Wai.requestHeaderUserAgent req)
|
|
||||||
<> "\"\n"
|
|
||||||
where
|
|
||||||
getSourceFromSocket = BS.pack . Wai.showSockAddr . Wai.remoteHost
|
|
||||||
|
|
||||||
pgrstMiddleware :: LogLevel -> Wai.Application -> Wai.Application
|
|
||||||
pgrstMiddleware logLevel =
|
|
||||||
logger
|
|
||||||
. Wai.cors corsPolicy
|
|
||||||
. Wai.staticPolicy (Wai.only [("favicon.ico", "static/favicon.ico")])
|
|
||||||
where
|
|
||||||
logger = case logLevel of
|
|
||||||
LogCrit -> id
|
|
||||||
LogError -> unsafePerformIO $ Wai.mkRequestLogger Wai.def { Wai.outputFormat = Wai.CustomOutputFormat $ pgrstFormat status500}
|
|
||||||
LogWarn -> unsafePerformIO $ Wai.mkRequestLogger Wai.def { Wai.outputFormat = Wai.CustomOutputFormat $ pgrstFormat status400}
|
|
||||||
LogInfo -> Wai.logStdout
|
|
||||||
|
|
||||||
defaultCorsPolicy :: Wai.CorsResourcePolicy
|
|
||||||
defaultCorsPolicy = Wai.CorsResourcePolicy Nothing
|
|
||||||
["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"] ["Authorization"] Nothing
|
|
||||||
(Just $ 60*60*24) False False True
|
|
||||||
|
|
||||||
-- | CORS policy to be used in by Wai Cors middleware
|
|
||||||
corsPolicy :: Wai.Request -> Maybe Wai.CorsResourcePolicy
|
|
||||||
corsPolicy req = case lookup "origin" headers of
|
|
||||||
Just origin -> Just defaultCorsPolicy {
|
|
||||||
Wai.corsOrigins = Just ([origin], True)
|
|
||||||
, Wai.corsRequestHeaders = "Authentication" : accHeaders
|
|
||||||
, Wai.corsExposedHeaders = Just [
|
|
||||||
"Content-Encoding", "Content-Location", "Content-Range", "Content-Type"
|
|
||||||
, "Date", "Location", "Server", "Transfer-Encoding", "Range-Unit"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Nothing -> Nothing
|
|
||||||
where
|
|
||||||
headers = Wai.requestHeaders req
|
|
||||||
accHeaders = case lookup "access-control-request-headers" headers of
|
|
||||||
Just hdrs -> map (CI.mk . BS.strip) $ BS.split ',' hdrs
|
|
||||||
Nothing -> []
|
|
||||||
|
|
||||||
unquoted :: JSON.Value -> Text
|
|
||||||
unquoted (JSON.String t) = t
|
|
||||||
unquoted (JSON.Number n) =
|
|
||||||
toS $ formatScientific Fixed (if isInteger n then Just 0 else Nothing) n
|
|
||||||
unquoted (JSON.Bool b) = show b
|
|
||||||
unquoted v = T.decodeUtf8 . LBS.toStrict $ JSON.encode v
|
|
||||||
|
|
||||||
-- | Set a transaction to eventually roll back if requested and set respective
|
-- | Set a transaction to eventually roll back if requested and set respective
|
||||||
-- headers on the response.
|
-- headers on the response.
|
||||||
@@ -170,8 +88,9 @@ optionalRollback
|
|||||||
-> ExceptT Error SQL.Transaction Wai.Response
|
-> ExceptT Error SQL.Transaction Wai.Response
|
||||||
optionalRollback AppConfig{..} ApiRequest{..} transaction = do
|
optionalRollback AppConfig{..} ApiRequest{..} transaction = do
|
||||||
resp <- catchError transaction $ return . errorResponseFor
|
resp <- catchError transaction $ return . errorResponseFor
|
||||||
when (shouldRollback || (configDbTxRollbackAll && not shouldCommit))
|
when (shouldRollback || (configDbTxRollbackAll && not shouldCommit)) $ lift do
|
||||||
(lift SQL.condemn)
|
SQL.sql "SET CONSTRAINTS ALL IMMEDIATE"
|
||||||
|
SQL.condemn
|
||||||
return $ Wai.mapResponseHeaders preferenceApplied resp
|
return $ Wai.mapResponseHeaders preferenceApplied resp
|
||||||
where
|
where
|
||||||
shouldCommit =
|
shouldCommit =
|
||||||
|
|||||||
+18
-16
@@ -55,24 +55,26 @@ encode conf dbStructure tables procs schemaDescription =
|
|||||||
makeMimeList :: [ContentType] -> MimeList
|
makeMimeList :: [ContentType] -> MimeList
|
||||||
makeMimeList cs = MimeList $ fmap (fromString . BS.unpack . toMime) cs
|
makeMimeList cs = MimeList $ fmap (fromString . BS.unpack . toMime) cs
|
||||||
|
|
||||||
toSwaggerType :: Text -> SwaggerType t
|
toSwaggerType :: Text -> Maybe (SwaggerType t)
|
||||||
toSwaggerType "character varying" = SwaggerString
|
toSwaggerType "character varying" = Just SwaggerString
|
||||||
toSwaggerType "character" = SwaggerString
|
toSwaggerType "character" = Just SwaggerString
|
||||||
toSwaggerType "text" = SwaggerString
|
toSwaggerType "text" = Just SwaggerString
|
||||||
toSwaggerType "boolean" = SwaggerBoolean
|
toSwaggerType "boolean" = Just SwaggerBoolean
|
||||||
toSwaggerType "smallint" = SwaggerInteger
|
toSwaggerType "smallint" = Just SwaggerInteger
|
||||||
toSwaggerType "integer" = SwaggerInteger
|
toSwaggerType "integer" = Just SwaggerInteger
|
||||||
toSwaggerType "bigint" = SwaggerInteger
|
toSwaggerType "bigint" = Just SwaggerInteger
|
||||||
toSwaggerType "numeric" = SwaggerNumber
|
toSwaggerType "numeric" = Just SwaggerNumber
|
||||||
toSwaggerType "real" = SwaggerNumber
|
toSwaggerType "real" = Just SwaggerNumber
|
||||||
toSwaggerType "double precision" = SwaggerNumber
|
toSwaggerType "double precision" = Just SwaggerNumber
|
||||||
toSwaggerType "ARRAY" = SwaggerArray
|
toSwaggerType "ARRAY" = Just SwaggerArray
|
||||||
toSwaggerType _ = SwaggerString
|
toSwaggerType "json" = Nothing
|
||||||
|
toSwaggerType "jsonb" = Nothing
|
||||||
|
toSwaggerType _ = Just SwaggerString
|
||||||
|
|
||||||
parseDefault :: Text -> Text -> Text
|
parseDefault :: Text -> Text -> Text
|
||||||
parseDefault colType colDefault =
|
parseDefault colType colDefault =
|
||||||
case toSwaggerType colType of
|
case toSwaggerType colType of
|
||||||
SwaggerString -> wrapInQuotations $ case T.stripSuffix ("::" <> colType) colDefault of
|
Just SwaggerString -> wrapInQuotations $ case T.stripSuffix ("::" <> colType) colDefault of
|
||||||
Just def -> T.dropAround (=='\'') def
|
Just def -> T.dropAround (=='\'') def
|
||||||
Nothing -> colDefault
|
Nothing -> colDefault
|
||||||
_ -> colDefault
|
_ -> colDefault
|
||||||
@@ -124,7 +126,7 @@ makeProperty rels pks c = (colName c, Inline s)
|
|||||||
& enum_ .~ e
|
& enum_ .~ e
|
||||||
& format ?~ colType c
|
& format ?~ colType c
|
||||||
& maxLength .~ (fromIntegral <$> colMaxLen c)
|
& maxLength .~ (fromIntegral <$> colMaxLen c)
|
||||||
& type_ ?~ toSwaggerType (colType c)
|
& type_ .~ toSwaggerType (colType c)
|
||||||
|
|
||||||
makeProcSchema :: ProcDescription -> Schema
|
makeProcSchema :: ProcDescription -> Schema
|
||||||
makeProcSchema pd =
|
makeProcSchema pd =
|
||||||
@@ -138,7 +140,7 @@ makeProcProperty :: ProcParam -> (Text, Referenced Schema)
|
|||||||
makeProcProperty (ProcParam n t _ _) = (n, Inline s)
|
makeProcProperty (ProcParam n t _ _) = (n, Inline s)
|
||||||
where
|
where
|
||||||
s = (mempty :: Schema)
|
s = (mempty :: Schema)
|
||||||
& type_ ?~ toSwaggerType t
|
& type_ .~ toSwaggerType t
|
||||||
& format ?~ t
|
& format ?~ t
|
||||||
|
|
||||||
makePreferParam :: [Text] -> Param
|
makePreferParam :: [Text] -> Param
|
||||||
|
|||||||
@@ -172,10 +172,10 @@ asJsonF returnsScalar
|
|||||||
| returnsScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar), '[]')::character varying"
|
| returnsScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar), '[]')::character varying"
|
||||||
| otherwise = "coalesce(json_agg(_postgrest_t), '[]')::character varying"
|
| otherwise = "coalesce(json_agg(_postgrest_t), '[]')::character varying"
|
||||||
|
|
||||||
asJsonSingleF :: Bool -> SqlFragment --TODO! unsafe when the query actually returns multiple rows, used only on inserting and returning single element
|
asJsonSingleF :: Bool -> SqlFragment
|
||||||
asJsonSingleF returnsScalar
|
asJsonSingleF returnsScalar
|
||||||
| returnsScalar = "coalesce(string_agg(to_json(_postgrest_t.pgrst_scalar)::text, ','), 'null')::character varying"
|
| returnsScalar = "coalesce((json_agg(_postgrest_t.pgrst_scalar)->0)::text, 'null')"
|
||||||
| otherwise = "coalesce(string_agg(to_json(_postgrest_t)::text, ','), '')::character varying"
|
| otherwise = "coalesce((json_agg(_postgrest_t)->0)::text, 'null')"
|
||||||
|
|
||||||
asBinaryF :: FieldName -> SqlFragment
|
asBinaryF :: FieldName -> SqlFragment
|
||||||
asBinaryF fieldName = "coalesce(string_agg(_postgrest_t." <> pgFmtIdent fieldName <> ", ''), '')"
|
asBinaryF fieldName = "coalesce(string_agg(_postgrest_t." <> pgFmtIdent fieldName <> ", ''), '')"
|
||||||
|
|||||||
@@ -364,9 +364,9 @@ userApiRequest conf@AppConfig{..} dbStructure req reqBody
|
|||||||
headerRange = rangeRequested hdrs
|
headerRange = rangeRequested hdrs
|
||||||
replaceLast x s = T.intercalate "." $ L.init (T.split (=='.') s) ++ [x]
|
replaceLast x s = T.intercalate "." $ L.init (T.split (=='.') s) ++ [x]
|
||||||
limitParams :: M.HashMap Text NonnegRange
|
limitParams :: M.HashMap Text NonnegRange
|
||||||
limitParams = M.fromList [(toS (replaceLast "limit" k), restrictRange (readMaybe . toS =<< v) allRange) | (k,v) <- qParams, isJust v, endingIn ["limit"] k]
|
limitParams = M.fromList [(toS (replaceLast "limit" k), restrictRange (readMaybe =<< v) allRange) | (k,v) <- qParams, isJust v, endingIn ["limit"] k]
|
||||||
offsetParams :: M.HashMap Text NonnegRange
|
offsetParams :: M.HashMap Text NonnegRange
|
||||||
offsetParams = M.fromList [(toS (replaceLast "limit" k), maybe allRange rangeGeq (readMaybe . toS =<< v)) | (k,v) <- qParams, isJust v, endingIn ["offset"] k]
|
offsetParams = M.fromList [(toS (replaceLast "limit" k), maybe allRange rangeGeq (readMaybe =<< v)) | (k,v) <- qParams, isJust v, endingIn ["offset"] k]
|
||||||
|
|
||||||
urlRange = M.unionWith f limitParams offsetParams
|
urlRange = M.unionWith f limitParams offsetParams
|
||||||
where
|
where
|
||||||
@@ -505,7 +505,7 @@ findProc qi argumentsKeys paramsAsSingleObject allProcs contentType isInvPost =
|
|||||||
then length params == 1 && (ppType <$> headMay params) `elem` [Just "json", Just "jsonb"]
|
then length params == 1 && (ppType <$> headMay params) `elem` [Just "json", Just "jsonb"]
|
||||||
-- If the function has no parameters, the arguments keys must be empty as well
|
-- If the function has no parameters, the arguments keys must be empty as well
|
||||||
else if null params
|
else if null params
|
||||||
then null argumentsKeys && contentType `notElem` [CTTextPlain, CTOctetStream]
|
then null argumentsKeys && not (isInvPost && contentType `elem` [CTTextPlain, CTOctetStream])
|
||||||
-- A function has optional and required parameters. Optional parameters have a default value and
|
-- A function has optional and required parameters. Optional parameters have a default value and
|
||||||
-- don't require arguments for the function to be executed, required parameters must have an argument present.
|
-- don't require arguments for the function to be executed, required parameters must have an argument present.
|
||||||
else case L.partition ppReq params of
|
else case L.partition ppReq params of
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ import PostgREST.Request.Types
|
|||||||
|
|
||||||
import qualified PostgREST.DbStructure.Relationship as Relationship
|
import qualified PostgREST.DbStructure.Relationship as Relationship
|
||||||
|
|
||||||
import Protolude hiding (from)
|
import Protolude hiding (from, isInfixOf)
|
||||||
|
|
||||||
-- | Builds the ReadRequest tree on a number of stages.
|
-- | Builds the ReadRequest tree on a number of stages.
|
||||||
-- | Adds filters, order, limits on its respective nodes.
|
-- | Adds filters, order, limits on its respective nodes.
|
||||||
|
|||||||
@@ -199,10 +199,10 @@ pOpExpr pSVal = try ( string "not" *> pDelimiter *> (OpExpr True <$> pOperation)
|
|||||||
<|> pFts
|
<|> pFts
|
||||||
<?> "operator (eq, gt, ...)"
|
<?> "operator (eq, gt, ...)"
|
||||||
|
|
||||||
pTriVal = try (string "null" $> TriNull)
|
pTriVal = try (ciString "null" $> TriNull)
|
||||||
<|> try (string "unknown" $> TriUnknown)
|
<|> try (ciString "unknown" $> TriUnknown)
|
||||||
<|> try (string "true" $> TriTrue)
|
<|> try (ciString "true" $> TriTrue)
|
||||||
<|> try (string "false" $> TriFalse)
|
<|> try (ciString "false" $> TriFalse)
|
||||||
<?> "null or trilean value (unknown, true, false)"
|
<?> "null or trilean value (unknown, true, false)"
|
||||||
|
|
||||||
pFts = do
|
pFts = do
|
||||||
@@ -213,6 +213,12 @@ pOpExpr pSVal = try ( string "not" *> pDelimiter *> (OpExpr True <$> pOperation)
|
|||||||
ops = M.filterWithKey (const . flip notElem ("in":"is":ftsOps)) operators
|
ops = M.filterWithKey (const . flip notElem ("in":"is":ftsOps)) operators
|
||||||
ftsOps = M.keys ftsOperators
|
ftsOps = M.keys ftsOperators
|
||||||
|
|
||||||
|
-- case insensitive char and string
|
||||||
|
ciChar :: Char -> GenParser Char state Char
|
||||||
|
ciChar c = char c <|> char (toUpper c)
|
||||||
|
ciString :: [Char] -> GenParser Char state [Char]
|
||||||
|
ciString = traverse ciChar
|
||||||
|
|
||||||
pSingleVal :: Parser SingleVal
|
pSingleVal :: Parser SingleVal
|
||||||
pSingleVal = toS <$> many anyChar
|
pSingleVal = toS <$> many anyChar
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ prettyVersion =
|
|||||||
|
|
||||||
|
|
||||||
-- | Version number used in docs.
|
-- | Version number used in docs.
|
||||||
|
-- Pre-release versions link to the latest docs
|
||||||
-- Uses only the two first components of the version. Example: 'v1.1'
|
-- Uses only the two first components of the version. Example: 'v1.1'
|
||||||
docsVersion :: Text
|
docsVersion :: Text
|
||||||
docsVersion =
|
docsVersion
|
||||||
"v" <> (T.intercalate "." . map show . take 2 $ versionBranch version)
|
| isPreRelease = "latest"
|
||||||
|
| otherwise = "v" <> (T.intercalate "." . map show . take 2 $ versionBranch version)
|
||||||
|
|
||||||
|
|
||||||
-- | Versions with four components (e.g., '1.1.1.1') are treated as pre-releases.
|
-- | Versions with four components (e.g., '1.1.1.1') are treated as pre-releases.
|
||||||
|
|||||||
@@ -152,11 +152,10 @@ connectionStatus appState =
|
|||||||
loadSchemaCache :: AppState -> IO SCacheStatus
|
loadSchemaCache :: AppState -> IO SCacheStatus
|
||||||
loadSchemaCache appState = do
|
loadSchemaCache appState = do
|
||||||
AppConfig{..} <- AppState.getConfig appState
|
AppConfig{..} <- AppState.getConfig appState
|
||||||
actualPgVersion <- AppState.getPgVersion appState
|
|
||||||
result <-
|
result <-
|
||||||
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
||||||
SQL.use (AppState.getPool appState) . transaction SQL.ReadCommitted SQL.Read $
|
SQL.use (AppState.getPool appState) . transaction SQL.ReadCommitted SQL.Read $
|
||||||
queryDbStructure (toList configDbSchemas) configDbExtraSearchPath actualPgVersion configDbPreparedStatements
|
queryDbStructure (toList configDbSchemas) configDbExtraSearchPath configDbPreparedStatements
|
||||||
case result of
|
case result of
|
||||||
Left e -> do
|
Left e -> do
|
||||||
let
|
let
|
||||||
|
|||||||
@@ -12,4 +12,8 @@ nix:
|
|||||||
extra-deps:
|
extra-deps:
|
||||||
- hasql-dynamic-statements-0.3.1@sha256:c3a2c89c4a8b3711368dbd33f0ccfe46a493faa7efc2c85d3e354c56a01dfc48,2673
|
- hasql-dynamic-statements-0.3.1@sha256:c3a2c89c4a8b3711368dbd33f0ccfe46a493faa7efc2c85d3e354c56a01dfc48,2673
|
||||||
- hasql-implicits-0.1.0.2@sha256:5d54e09cb779a209681b139fb3cc726bae75134557932156340cc0a56dd834a8,1361
|
- hasql-implicits-0.1.0.2@sha256:5d54e09cb779a209681b139fb3cc726bae75134557932156340cc0a56dd834a8,1361
|
||||||
|
- protolude-0.3.1@sha256:1cc9e5a5c26c33a43c52b554443dd9779fef13974eaa0beec7ca6d2551b400da,2647
|
||||||
- ptr-0.16.8.1@sha256:525219ec5f5da5c699725f7efcef91b00a7d44120fc019878b85c09440bf51d6,2686
|
- ptr-0.16.8.1@sha256:525219ec5f5da5c699725f7efcef91b00a7d44120fc019878b85c09440bf51d6,2686
|
||||||
|
- wai-extra-3.1.8@sha256:bf3dbe8f4c707b502b2a88262ed71c807220651597b76b56983f864af6197890,7280
|
||||||
|
- wai-logger-2.3.7@sha256:19a0dc5122e22d274776d80786fb9501956f5e75b8f82464bbdad5604d154d82,1671
|
||||||
|
- warp-3.3.19@sha256:c6a47029537d42844386170d732cdfe6d85b2f4279bbaefdd9b50caff6faeebb,10910
|
||||||
|
|||||||
+35
-7
@@ -5,29 +5,57 @@
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-dynamic-statements-0.3.1@sha256:c3a2c89c4a8b3711368dbd33f0ccfe46a493faa7efc2c85d3e354c56a01dfc48,2673
|
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
size: 641
|
|
||||||
sha256: b1b9a6a26ec765e5fe29f9a670a5c9ec7067ea00dee8491f0819284ff0201b6f
|
sha256: b1b9a6a26ec765e5fe29f9a670a5c9ec7067ea00dee8491f0819284ff0201b6f
|
||||||
|
size: 641
|
||||||
|
hackage: hasql-dynamic-statements-0.3.1@sha256:c3a2c89c4a8b3711368dbd33f0ccfe46a493faa7efc2c85d3e354c56a01dfc48,2673
|
||||||
original:
|
original:
|
||||||
hackage: hasql-dynamic-statements-0.3.1@sha256:c3a2c89c4a8b3711368dbd33f0ccfe46a493faa7efc2c85d3e354c56a01dfc48,2673
|
hackage: hasql-dynamic-statements-0.3.1@sha256:c3a2c89c4a8b3711368dbd33f0ccfe46a493faa7efc2c85d3e354c56a01dfc48,2673
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-implicits-0.1.0.2@sha256:5d54e09cb779a209681b139fb3cc726bae75134557932156340cc0a56dd834a8,1361
|
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
size: 310
|
|
||||||
sha256: 2f00d1467d0e226b966c2cd7bac433c8948e2f7bbdf8a44936029f66fc20b5f3
|
sha256: 2f00d1467d0e226b966c2cd7bac433c8948e2f7bbdf8a44936029f66fc20b5f3
|
||||||
|
size: 310
|
||||||
|
hackage: hasql-implicits-0.1.0.2@sha256:5d54e09cb779a209681b139fb3cc726bae75134557932156340cc0a56dd834a8,1361
|
||||||
original:
|
original:
|
||||||
hackage: hasql-implicits-0.1.0.2@sha256:5d54e09cb779a209681b139fb3cc726bae75134557932156340cc0a56dd834a8,1361
|
hackage: hasql-implicits-0.1.0.2@sha256:5d54e09cb779a209681b139fb3cc726bae75134557932156340cc0a56dd834a8,1361
|
||||||
- completed:
|
- completed:
|
||||||
hackage: ptr-0.16.8.1@sha256:525219ec5f5da5c699725f7efcef91b00a7d44120fc019878b85c09440bf51d6,2686
|
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
size: 1089
|
sha256: 6452a6ca8d395f7d810139779bb0fd16fc1dbb00f1862630bc08ef5a100430f9
|
||||||
|
size: 1645
|
||||||
|
hackage: protolude-0.3.1@sha256:1cc9e5a5c26c33a43c52b554443dd9779fef13974eaa0beec7ca6d2551b400da,2647
|
||||||
|
original:
|
||||||
|
hackage: protolude-0.3.1@sha256:1cc9e5a5c26c33a43c52b554443dd9779fef13974eaa0beec7ca6d2551b400da,2647
|
||||||
|
- completed:
|
||||||
|
pantry-tree:
|
||||||
sha256: d2b8440a738719ef8430ec38fe33b129e3940e4ccf2c016a727a1110a43656bb
|
sha256: d2b8440a738719ef8430ec38fe33b129e3940e4ccf2c016a727a1110a43656bb
|
||||||
|
size: 1089
|
||||||
|
hackage: ptr-0.16.8.1@sha256:525219ec5f5da5c699725f7efcef91b00a7d44120fc019878b85c09440bf51d6,2686
|
||||||
original:
|
original:
|
||||||
hackage: ptr-0.16.8.1@sha256:525219ec5f5da5c699725f7efcef91b00a7d44120fc019878b85c09440bf51d6,2686
|
hackage: ptr-0.16.8.1@sha256:525219ec5f5da5c699725f7efcef91b00a7d44120fc019878b85c09440bf51d6,2686
|
||||||
|
- completed:
|
||||||
|
pantry-tree:
|
||||||
|
sha256: a544ea95288d188e893322a8e6d68f2b1f844f772dbea1f26e5c0c1a74694f56
|
||||||
|
size: 4053
|
||||||
|
hackage: wai-extra-3.1.8@sha256:bf3dbe8f4c707b502b2a88262ed71c807220651597b76b56983f864af6197890,7280
|
||||||
|
original:
|
||||||
|
hackage: wai-extra-3.1.8@sha256:bf3dbe8f4c707b502b2a88262ed71c807220651597b76b56983f864af6197890,7280
|
||||||
|
- completed:
|
||||||
|
pantry-tree:
|
||||||
|
sha256: 52b5abf5c4c09bcfbc06e01f761a75c32cbd3e6ba23c8843981933fcc31ed53c
|
||||||
|
size: 474
|
||||||
|
hackage: wai-logger-2.3.7@sha256:19a0dc5122e22d274776d80786fb9501956f5e75b8f82464bbdad5604d154d82,1671
|
||||||
|
original:
|
||||||
|
hackage: wai-logger-2.3.7@sha256:19a0dc5122e22d274776d80786fb9501956f5e75b8f82464bbdad5604d154d82,1671
|
||||||
|
- completed:
|
||||||
|
pantry-tree:
|
||||||
|
sha256: 99ff839445ba2c9e29a294b45904e3f4575336c7d2b4504ce310d611661c761d
|
||||||
|
size: 3973
|
||||||
|
hackage: warp-3.3.19@sha256:c6a47029537d42844386170d732cdfe6d85b2f4279bbaefdd9b50caff6faeebb,10910
|
||||||
|
original:
|
||||||
|
hackage: warp-3.3.19@sha256:c6a47029537d42844386170d732cdfe6d85b2f4279bbaefdd9b50caff6faeebb,10910
|
||||||
snapshots:
|
snapshots:
|
||||||
- completed:
|
- completed:
|
||||||
|
sha256: 87842ecbaa8ca9cee59a7e6be52369dbed82ed075cb4e0d152614a627e8fd488
|
||||||
size: 586069
|
size: 586069
|
||||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/14.yaml
|
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/14.yaml
|
||||||
sha256: 87842ecbaa8ca9cee59a7e6be52369dbed82ed075cb4e0d152614a627e8fd488
|
|
||||||
original: lts-18.14
|
original: lts-18.14
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,74 +0,0 @@
|
|||||||
module Feature.CorsSpec where
|
|
||||||
|
|
||||||
-- {{{ Imports
|
|
||||||
import qualified Data.ByteString.Lazy as BL
|
|
||||||
|
|
||||||
import Network.Wai (Application)
|
|
||||||
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders))
|
|
||||||
|
|
||||||
import Network.HTTP.Types
|
|
||||||
import Test.Hspec
|
|
||||||
import Test.Hspec.Wai
|
|
||||||
|
|
||||||
import Protolude
|
|
||||||
import SpecHelper
|
|
||||||
-- }}}
|
|
||||||
|
|
||||||
spec :: SpecWith ((), Application)
|
|
||||||
spec =
|
|
||||||
describe "CORS" $ do
|
|
||||||
let preflightHeaders = [
|
|
||||||
("Accept", "*/*"),
|
|
||||||
("Origin", "http://example.com"),
|
|
||||||
("Access-Control-Request-Method", "POST"),
|
|
||||||
("Access-Control-Request-Headers", "Foo,Bar") ]
|
|
||||||
let normalCors = [
|
|
||||||
("Host", "localhost:3000"),
|
|
||||||
("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:32.0) Gecko/20100101 Firefox/32.0"),
|
|
||||||
("Origin", "http://localhost:8000"),
|
|
||||||
("Accept", "text/csv, */*; q=0.01"),
|
|
||||||
("Accept-Language", "en-US,en;q=0.5"),
|
|
||||||
("Accept-Encoding", "gzip, deflate"),
|
|
||||||
("Referer", "http://localhost:8000/"),
|
|
||||||
("Connection", "keep-alive") ]
|
|
||||||
|
|
||||||
describe "preflight request" $ do
|
|
||||||
it "replies naively and permissively to preflight request" $ do
|
|
||||||
r <- request methodOptions "/items" preflightHeaders ""
|
|
||||||
liftIO $ do
|
|
||||||
let respHeaders = simpleHeaders r
|
|
||||||
respHeaders `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Allow-Origin"
|
|
||||||
"http://example.com"
|
|
||||||
respHeaders `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Allow-Credentials"
|
|
||||||
"true"
|
|
||||||
respHeaders `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Allow-Methods"
|
|
||||||
"GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD"
|
|
||||||
respHeaders `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Allow-Headers"
|
|
||||||
"Authentication, Foo, Bar, Accept, Accept-Language, Content-Language"
|
|
||||||
respHeaders `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Max-Age"
|
|
||||||
"86400"
|
|
||||||
|
|
||||||
it "suppresses body in response" $ do
|
|
||||||
r <- request methodOptions "/" preflightHeaders ""
|
|
||||||
liftIO $ simpleBody r `shouldBe` ""
|
|
||||||
|
|
||||||
describe "regular request" $
|
|
||||||
it "exposes necesssary response headers" $ do
|
|
||||||
r <- request methodGet "/items" [("Origin", "http://example.com")] ""
|
|
||||||
liftIO $ simpleHeaders r `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Expose-Headers"
|
|
||||||
"Content-Encoding, Content-Location, Content-Range, Content-Type, \
|
|
||||||
\Date, Location, Server, Transfer-Encoding, Range-Unit"
|
|
||||||
|
|
||||||
describe "postflight request" $
|
|
||||||
it "allows INFO body through even with CORS request headers present" $ do
|
|
||||||
r <- request methodOptions "/items" normalCors ""
|
|
||||||
liftIO $ do
|
|
||||||
simpleHeaders r `shouldSatisfy` matchHeader
|
|
||||||
"Access-Control-Allow-Origin" "\\*"
|
|
||||||
simpleBody r `shouldSatisfy` BL.null
|
|
||||||
Vendored
-62
@@ -1,62 +0,0 @@
|
|||||||
\set AUTHENTICATOR current_user
|
|
||||||
DROP ROLE IF EXISTS postgrest_test_anonymous, postgrest_test_default_role, postgrest_test_author;
|
|
||||||
CREATE ROLE postgrest_test_anonymous;
|
|
||||||
CREATE ROLE postgrest_test_default_role;
|
|
||||||
CREATE ROLE postgrest_test_author;
|
|
||||||
|
|
||||||
GRANT postgrest_test_anonymous, postgrest_test_default_role, postgrest_test_author TO :USER;
|
|
||||||
|
|
||||||
-- reloadable config options for io tests
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_aud = 'https://example.org';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.db-config';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_secret = 'REALLYREALLYREALLYREALLYVERYSAFE';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_secret_is_base64 = 'true';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_tx_end = 'commit-allow-override';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_root_spec = 'root';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_prepared_statements = 'false';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_pre_request = 'test.custom_headers';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_max_rows = '1000';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_extra_search_path = 'public, extensions';
|
|
||||||
|
|
||||||
-- override with database specific setting
|
|
||||||
ALTER ROLE postgrest_test_authenticator IN DATABASE :DBNAME SET pgrst.jwt_secret = 'OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE';
|
|
||||||
ALTER ROLE postgrest_test_authenticator IN DATABASE :DBNAME SET pgrst.db_extra_search_path = 'public, extensions, private';
|
|
||||||
|
|
||||||
-- other database settings that should be ignored
|
|
||||||
DROP DATABASE IF EXISTS other;
|
|
||||||
CREATE DATABASE other;
|
|
||||||
ALTER ROLE postgrest_test_authenticator IN DATABASE other SET pgrst.db_max_rows = '1111';
|
|
||||||
|
|
||||||
-- non-reloadable configs for io tests
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.server_host = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.server_port = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.server_unix_socket = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.server_unix_socket_mode = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.log_level = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_anon_role = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_uri = 'postgresql://ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_channel_enabled = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_channel = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_pool = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_pool_timeout = 'ignored';
|
|
||||||
ALTER ROLE postgrest_test_authenticator SET pgrst.db_config = 'ignored';
|
|
||||||
|
|
||||||
-- other authenticator reloadable config options for io tests
|
|
||||||
CREATE ROLE other_authenticator LOGIN NOINHERIT;
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.other-db-config';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.jwt_role_claim_key = '."other"."role"';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_tx_end = 'rollback-allow-override';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_prepared_statements = 'false';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_pre_request = 'test.other_custom_headers';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other';
|
|
||||||
ALTER ROLE other_authenticator SET pgrst.openapi_mode = 'disabled';
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
db-pool = 1
|
|
||||||
server-unix-socket = "$(POSTGREST_TEST_SOCKET)"
|
|
||||||
jwt-secret = "reallyreallyreallyreallyverysafe"
|
|
||||||
db-config = false
|
|
||||||
@@ -1,5 +1,2 @@
|
|||||||
db-pool = 1
|
|
||||||
db-pool-timeout = 1
|
|
||||||
|
|
||||||
app.settings.external_api_secret = "0123456789abcdef"
|
app.settings.external_api_secret = "0123456789abcdef"
|
||||||
db-config = false
|
db-config = false
|
||||||
-2
@@ -1,5 +1,3 @@
|
|||||||
db-pool = 1
|
|
||||||
|
|
||||||
# Read secret from a file: /dev/stdin (alias for standard input)
|
# Read secret from a file: /dev/stdin (alias for standard input)
|
||||||
jwt-secret = "@/dev/stdin"
|
jwt-secret = "@/dev/stdin"
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = true
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
db-uri = "@/dev/stdin"
|
db-uri = "@/dev/stdin"
|
||||||
db-pool = 1
|
|
||||||
jwt-secret = "reallyreallyreallyreallyverysafe"
|
jwt-secret = "reallyreallyreallyreallyverysafe"
|
||||||
db-config = false
|
db-config = false
|
||||||
+1
-1
@@ -9,7 +9,7 @@ db-pre-request = "check_alias"
|
|||||||
db-prepared-statements = true
|
db-prepared-statements = true
|
||||||
db-root-spec = "open_alias"
|
db-root-spec = "open_alias"
|
||||||
db-schemas = "provided_through_alias"
|
db-schemas = "provided_through_alias"
|
||||||
db-config = "false"
|
db-config = false
|
||||||
db-tx-end = "commit"
|
db-tx-end = "commit"
|
||||||
db-uri = "required"
|
db-uri = "required"
|
||||||
db-use-legacy-gucs = true
|
db-use-legacy-gucs = true
|
||||||
+1
-1
@@ -9,7 +9,7 @@ db-pre-request = ""
|
|||||||
db-prepared-statements = false
|
db-prepared-statements = false
|
||||||
db-root-spec = ""
|
db-root-spec = ""
|
||||||
db-schemas = "required"
|
db-schemas = "required"
|
||||||
db-config = "false"
|
db-config = false
|
||||||
db-tx-end = "commit"
|
db-tx-end = "commit"
|
||||||
db-uri = "required"
|
db-uri = "required"
|
||||||
db-use-legacy-gucs = true
|
db-use-legacy-gucs = true
|
||||||
+1
-1
@@ -9,7 +9,7 @@ db-pre-request = ""
|
|||||||
db-prepared-statements = false
|
db-prepared-statements = false
|
||||||
db-root-spec = ""
|
db-root-spec = ""
|
||||||
db-schemas = "required"
|
db-schemas = "required"
|
||||||
db-config = "false"
|
db-config = false
|
||||||
db-tx-end = "commit"
|
db-tx-end = "commit"
|
||||||
db-uri = "required"
|
db-uri = "required"
|
||||||
db-use-legacy-gucs = true
|
db-use-legacy-gucs = true
|
||||||
+1
-1
@@ -9,7 +9,7 @@ db-pre-request = ""
|
|||||||
db-prepared-statements = true
|
db-prepared-statements = true
|
||||||
db-root-spec = ""
|
db-root-spec = ""
|
||||||
db-schemas = "required"
|
db-schemas = "required"
|
||||||
db-config = "false"
|
db-config = false
|
||||||
db-tx-end = "commit"
|
db-tx-end = "commit"
|
||||||
db-uri = "required"
|
db-uri = "required"
|
||||||
db-use-legacy-gucs = true
|
db-use-legacy-gucs = true
|
||||||
+1
-1
@@ -9,7 +9,7 @@ db-pre-request = "test.other_custom_headers"
|
|||||||
db-prepared-statements = false
|
db-prepared-statements = false
|
||||||
db-root-spec = "other_root"
|
db-root-spec = "other_root"
|
||||||
db-schemas = "test,other_tenant1,other_tenant2"
|
db-schemas = "test,other_tenant1,other_tenant2"
|
||||||
db-config = "true"
|
db-config = true
|
||||||
db-tx-end = "rollback-allow-override"
|
db-tx-end = "rollback-allow-override"
|
||||||
db-uri = "<REPLACED_WITH_DB_URI>"
|
db-uri = "<REPLACED_WITH_DB_URI>"
|
||||||
db-use-legacy-gucs = false
|
db-use-legacy-gucs = false
|
||||||
+3
-3
@@ -9,14 +9,14 @@ db-pre-request = "test.custom_headers"
|
|||||||
db-prepared-statements = false
|
db-prepared-statements = false
|
||||||
db-root-spec = "root"
|
db-root-spec = "root"
|
||||||
db-schemas = "test,tenant1,tenant2"
|
db-schemas = "test,tenant1,tenant2"
|
||||||
db-config = "true"
|
db-config = true
|
||||||
db-tx-end = "commit-allow-override"
|
db-tx-end = "commit-allow-override"
|
||||||
db-uri = "<REPLACED_WITH_DB_URI>"
|
db-uri = "<REPLACED_WITH_DB_URI>"
|
||||||
db-use-legacy-gucs = false
|
db-use-legacy-gucs = false
|
||||||
jwt-aud = "https://example.org"
|
jwt-aud = "https://example.org"
|
||||||
jwt-role-claim-key = ".\"a\".\"role\""
|
jwt-role-claim-key = ".\"a\".\"role\""
|
||||||
jwt-secret = "OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE"
|
jwt-secret = "OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE"
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = false
|
||||||
log-level = "info"
|
log-level = "info"
|
||||||
openapi-mode = "ignore-privileges"
|
openapi-mode = "ignore-privileges"
|
||||||
openapi-server-proxy-uri = "https://example.org/api"
|
openapi-server-proxy-uri = "https://example.org/api"
|
||||||
+1
-1
@@ -9,7 +9,7 @@ db-pre-request = "please_run_fast"
|
|||||||
db-prepared-statements = false
|
db-prepared-statements = false
|
||||||
db-root-spec = "openapi_v3"
|
db-root-spec = "openapi_v3"
|
||||||
db-schemas = "multi,tenant,setup"
|
db-schemas = "multi,tenant,setup"
|
||||||
db-config = "false"
|
db-config = false
|
||||||
db-tx-end = "rollback-allow-override"
|
db-tx-end = "rollback-allow-override"
|
||||||
db-uri = "tmp_db"
|
db-uri = "tmp_db"
|
||||||
db-use-legacy-gucs = false
|
db-use-legacy-gucs = false
|
||||||
@@ -9,7 +9,7 @@ db-pre-request = ""
|
|||||||
db-prepared-statements = true
|
db-prepared-statements = true
|
||||||
db-root-spec = ""
|
db-root-spec = ""
|
||||||
db-schemas = "required"
|
db-schemas = "required"
|
||||||
db-config = "true"
|
db-config = true
|
||||||
db-tx-end = "commit"
|
db-tx-end = "commit"
|
||||||
db-uri = "required"
|
db-uri = "required"
|
||||||
db-use-legacy-gucs = true
|
db-use-legacy-gucs = true
|
||||||
@@ -9,7 +9,7 @@ db-pre-request = "please_run_fast"
|
|||||||
db-prepared-statements = false
|
db-prepared-statements = false
|
||||||
db-root-spec = "openapi_v3"
|
db-root-spec = "openapi_v3"
|
||||||
db-schemas = "multi, tenant,setup"
|
db-schemas = "multi, tenant,setup"
|
||||||
db-config = "false"
|
db-config = false
|
||||||
db-tx-end = "rollback-allow-override"
|
db-tx-end = "rollback-allow-override"
|
||||||
db-uri = "tmp_db"
|
db-uri = "tmp_db"
|
||||||
db-use-legacy-gucs = false
|
db-use-legacy-gucs = false
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
db-pool = 1
|
|
||||||
jwt-role-claim-key = "$(ROLE_CLAIM_KEY)"
|
jwt-role-claim-key = "$(ROLE_CLAIM_KEY)"
|
||||||
jwt-secret = "reallyreallyreallyreallyverysafe"
|
jwt-secret = "reallyreallyreallyreallyverysafe"
|
||||||
db-config = false
|
db-config = false
|
||||||
-2
@@ -1,5 +1,3 @@
|
|||||||
db-pool = 1
|
|
||||||
|
|
||||||
# Read secret from a file: /dev/stdin (alias for standard input)
|
# Read secret from a file: /dev/stdin (alias for standard input)
|
||||||
jwt-secret = "@/dev/stdin"
|
jwt-secret = "@/dev/stdin"
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
-2
@@ -1,5 +1,3 @@
|
|||||||
db-pool = 1
|
|
||||||
|
|
||||||
jwt-secret = "$(JWT_SECRET_FILE)"
|
jwt-secret = "$(JWT_SECRET_FILE)"
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
db-config = false
|
db-config = false
|
||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
db-schemas = "test"
|
db-schemas = "public"
|
||||||
db-pool = 1
|
|
||||||
|
|
||||||
app.settings.name_var = "John"
|
app.settings.name_var = "John"
|
||||||
jwt-secret = "invalidinvalidinvalidinvalidinvalid"
|
jwt-secret = "invalidinvalidinvalidinvalidinvalid"
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
db-pool = 1
|
|
||||||
jwt-secret = "reallyreallyreallyreallyverysafe"
|
jwt-secret = "reallyreallyreallyreallyverysafe"
|
||||||
db-config = false
|
db-config = false
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
CREATE ROLE db_config_authenticator LOGIN NOINHERIT;
|
||||||
|
|
||||||
|
-- reloadable config options
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.jwt_aud = 'https://example.org';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.db-config';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.jwt_secret = 'REALLY=REALLY=REALLY=REALLY=VERY=SAFE';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.jwt_secret_is_base64 = 'false';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_tx_end = 'commit-allow-override';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_root_spec = 'root';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_prepared_statements = 'false';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_pre_request = 'test.custom_headers';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_max_rows = '1000';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_extra_search_path = 'public, extensions';
|
||||||
|
|
||||||
|
-- override with database specific setting
|
||||||
|
ALTER ROLE db_config_authenticator IN DATABASE :DBNAME SET pgrst.jwt_secret = 'OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE';
|
||||||
|
ALTER ROLE db_config_authenticator IN DATABASE :DBNAME SET pgrst.db_extra_search_path = 'public, extensions, private';
|
||||||
|
|
||||||
|
-- other database settings that should be ignored
|
||||||
|
CREATE DATABASE other;
|
||||||
|
ALTER ROLE db_config_authenticator IN DATABASE other SET pgrst.db_max_rows = '1111';
|
||||||
|
|
||||||
|
-- non-reloadable configs
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.server_host = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.server_port = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket_mode = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.log_level = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_anon_role = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_uri = 'postgresql://ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_channel_enabled = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_channel = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_pool = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_pool_timeout = 'ignored';
|
||||||
|
ALTER ROLE db_config_authenticator SET pgrst.db_config = 'ignored';
|
||||||
|
|
||||||
|
-- other authenticator reloadable config options
|
||||||
|
CREATE ROLE other_authenticator LOGIN NOINHERIT;
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.other-db-config';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.jwt_role_claim_key = '."other"."role"';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_tx_end = 'rollback-allow-override';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_prepared_statements = 'false';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_pre_request = 'test.other_custom_headers';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other';
|
||||||
|
ALTER ROLE other_authenticator SET pgrst.openapi_mode = 'disabled';
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
\ir db_config.sql
|
||||||
|
|
||||||
|
CREATE ROLE postgrest_test_anonymous;
|
||||||
|
CREATE ROLE postgrest_test_author;
|
||||||
|
|
||||||
|
GRANT postgrest_test_anonymous, postgrest_test_author TO :USER;
|
||||||
|
|
||||||
|
CREATE SCHEMA v1;
|
||||||
|
GRANT USAGE ON SCHEMA v1 TO postgrest_test_anonymous;
|
||||||
|
|
||||||
|
CREATE TABLE authors_only ();
|
||||||
|
GRANT SELECT ON authors_only TO postgrest_test_author;
|
||||||
|
|
||||||
|
CREATE TABLE projects AS SELECT FROM generate_series(1,5);
|
||||||
|
GRANT SELECT ON projects TO postgrest_test_anonymous;
|
||||||
|
|
||||||
|
create function get_guc_value(name text) returns text as $$
|
||||||
|
select nullif(current_setting(name), '')::text;
|
||||||
|
$$ language sql;
|
||||||
|
|
||||||
|
create function v1.get_guc_value(name text) returns text as $$
|
||||||
|
select nullif(current_setting(name), '')::text;
|
||||||
|
$$ language sql;
|
||||||
|
|
||||||
|
create function uses_prepared_statements() returns bool as $$
|
||||||
|
select count(name) > 0 from pg_catalog.pg_prepared_statements
|
||||||
|
$$ language sql;
|
||||||
|
|
||||||
|
create function change_max_rows_config(val int, notify bool default false) returns void as $_$
|
||||||
|
begin
|
||||||
|
execute format($$
|
||||||
|
alter role postgrest_test_authenticator set pgrst.db_max_rows = %L;
|
||||||
|
$$, val);
|
||||||
|
if notify then
|
||||||
|
perform pg_notify('pgrst', 'reload config');
|
||||||
|
end if;
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function reset_max_rows_config() returns void as $_$
|
||||||
|
begin
|
||||||
|
alter role postgrest_test_authenticator reset pgrst.db_max_rows;
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function change_db_schema_and_full_reload(schemas text) returns void as $_$
|
||||||
|
begin
|
||||||
|
execute format($$
|
||||||
|
alter role postgrest_test_authenticator set pgrst.db_schemas = %L;
|
||||||
|
$$, schemas);
|
||||||
|
perform pg_notify('pgrst', 'reload config');
|
||||||
|
perform pg_notify('pgrst', 'reload schema');
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function v1.reset_db_schema_config() returns void as $_$
|
||||||
|
begin
|
||||||
|
alter role postgrest_test_authenticator reset pgrst.db_schemas;
|
||||||
|
perform pg_notify('pgrst', 'reload config');
|
||||||
|
perform pg_notify('pgrst', 'reload schema');
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function invalid_role_claim_key_reload() returns void as $_$
|
||||||
|
begin
|
||||||
|
alter role postgrest_test_authenticator set pgrst.jwt_role_claim_key = 'test';
|
||||||
|
perform pg_notify('pgrst', 'reload config');
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function reset_invalid_role_claim_key() returns void as $_$
|
||||||
|
begin
|
||||||
|
alter role postgrest_test_authenticator reset pgrst.jwt_role_claim_key;
|
||||||
|
perform pg_notify('pgrst', 'reload config');
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function reload_pgrst_config() returns void as $_$
|
||||||
|
begin
|
||||||
|
perform pg_notify('pgrst', 'reload config');
|
||||||
|
end $_$ language plpgsql ;
|
||||||
|
|
||||||
|
create or replace function raise_bad_pt() returns void as $$
|
||||||
|
begin
|
||||||
|
raise sqlstate 'PT40A' using message = 'Wrong';
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
@@ -144,6 +144,7 @@ roleclaims:
|
|||||||
data:
|
data:
|
||||||
postgrest:
|
postgrest:
|
||||||
a_role: postgrest_test_author
|
a_role: postgrest_test_author
|
||||||
|
other: claims
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '.customObject.manyRoles[1]'
|
- key: '.customObject.manyRoles[1]'
|
||||||
data:
|
data:
|
||||||
@@ -151,21 +152,25 @@ roleclaims:
|
|||||||
manyRoles:
|
manyRoles:
|
||||||
- other
|
- other
|
||||||
- postgrest_test_author
|
- postgrest_test_author
|
||||||
|
other: {}
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '."https://www.example.com/roles"[0].value'
|
- key: '."https://www.example.com/roles"[0].value'
|
||||||
data:
|
data:
|
||||||
'https://www.example.com/roles':
|
'https://www.example.com/roles':
|
||||||
- value: postgrest_test_author
|
- value: postgrest_test_author
|
||||||
|
other: 666
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '.myDomain[3]'
|
- key: '.myDomain[3]'
|
||||||
data:
|
data:
|
||||||
myDomain:
|
myDomain:
|
||||||
- other
|
- other
|
||||||
- postgrest_test_author
|
- postgrest_test_author
|
||||||
|
other: 1.23
|
||||||
expected_status: 401
|
expected_status: 401
|
||||||
- key: '.myRole'
|
- key: '.myRole'
|
||||||
data:
|
data:
|
||||||
role: postgrest_test_author
|
role: postgrest_test_author
|
||||||
|
other: true
|
||||||
expected_status: 401
|
expected_status: 401
|
||||||
|
|
||||||
invalidroleclaimkeys:
|
invalidroleclaimkeys:
|
||||||
@@ -7,6 +7,7 @@ from itertools import repeat
|
|||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
@@ -77,7 +78,7 @@ class PostgrestProcess:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def dburi():
|
def dburi():
|
||||||
"Postgres database connection URI."
|
"Postgres database connection URI."
|
||||||
return os.getenv("PGRST_DB_URI").encode("utf-8")
|
return os.getenv("PGRST_DB_URI").encode()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -85,7 +86,7 @@ def defaultenv():
|
|||||||
"Default environment for PostgREST."
|
"Default environment for PostgREST."
|
||||||
return {
|
return {
|
||||||
"PGRST_DB_URI": os.environ["PGRST_DB_URI"],
|
"PGRST_DB_URI": os.environ["PGRST_DB_URI"],
|
||||||
"PGRST_DB_SCHEMAS": os.environ["PGRST_DB_SCHEMAS"],
|
"PGRST_DB_SCHEMAS": "public",
|
||||||
"PGRST_DB_ANON_ROLE": os.environ["PGRST_DB_ANON_ROLE"],
|
"PGRST_DB_ANON_ROLE": os.environ["PGRST_DB_ANON_ROLE"],
|
||||||
"PGRST_DB_CONFIG": "false",
|
"PGRST_DB_CONFIG": "false",
|
||||||
"PGRST_LOG_LEVEL": "info",
|
"PGRST_LOG_LEVEL": "info",
|
||||||
@@ -118,7 +119,7 @@ def cli(args, env=None, stdin=None):
|
|||||||
result = process.communicate(timeout=5)[0]
|
result = process.communicate(timeout=5)[0]
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
raise PostgrestError()
|
raise PostgrestError()
|
||||||
return result.decode("utf-8")
|
return result.decode()
|
||||||
finally:
|
finally:
|
||||||
process.kill()
|
process.kill()
|
||||||
process.wait()
|
process.wait()
|
||||||
@@ -138,6 +139,8 @@ def dumpconfig(configpath=None, env=None, stdin=None):
|
|||||||
def run(configpath=None, stdin=None, env=None, port=None):
|
def run(configpath=None, stdin=None, env=None, port=None):
|
||||||
"Run PostgREST and yield an endpoint that is ready for connections."
|
"Run PostgREST and yield an endpoint that is ready for connections."
|
||||||
env = env or {}
|
env = env or {}
|
||||||
|
env["PGRST_DB_POOL"] = "1"
|
||||||
|
env["PGRST_DB_POOL_TIMEOUT"] = "1"
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
if port:
|
if port:
|
||||||
@@ -156,17 +159,28 @@ def run(configpath=None, stdin=None, env=None, port=None):
|
|||||||
command.append(configpath)
|
command.append(configpath)
|
||||||
|
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
command, stdin=subprocess.PIPE, stderr=subprocess.PIPE, env=env
|
command,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
os.set_blocking(process.stdout.fileno(), False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
process.stdin.write(stdin or b"")
|
process.stdin.write(stdin or b"")
|
||||||
process.stdin.close()
|
process.stdin.close()
|
||||||
|
|
||||||
wait_until_ready(baseurl)
|
wait_until_ready(baseurl)
|
||||||
|
|
||||||
|
process.stdout.read()
|
||||||
|
|
||||||
yield PostgrestProcess(process=process, session=PostgrestSession(baseurl))
|
yield PostgrestProcess(process=process, session=PostgrestSession(baseurl))
|
||||||
finally:
|
finally:
|
||||||
|
remaining_output = process.stdout.read()
|
||||||
|
if remaining_output:
|
||||||
|
print(remaining_output.decode())
|
||||||
process.terminate()
|
process.terminate()
|
||||||
try:
|
try:
|
||||||
process.wait(timeout=1)
|
process.wait(timeout=1)
|
||||||
@@ -187,6 +201,7 @@ def wait_until_ready(url):
|
|||||||
"Wait for the given HTTP endpoint to return a status of 200."
|
"Wait for the given HTTP endpoint to return a status of 200."
|
||||||
session = requests_unixsocket.Session()
|
session = requests_unixsocket.Session()
|
||||||
|
|
||||||
|
response = None
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
try:
|
try:
|
||||||
response = session.get(url, timeout=1)
|
response = session.get(url, timeout=1)
|
||||||
@@ -197,7 +212,10 @@ def wait_until_ready(url):
|
|||||||
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
raise PostgrestTimedOut()
|
if response:
|
||||||
|
raise PostgrestTimedOut(f"{response.status_code}: {response.text}")
|
||||||
|
else:
|
||||||
|
raise PostgrestTimedOut()
|
||||||
|
|
||||||
|
|
||||||
def authheader(token):
|
def authheader(token):
|
||||||
@@ -273,7 +291,7 @@ def test_expected_config_from_environment():
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"role, expectedconfig",
|
"role, expectedconfig",
|
||||||
[
|
[
|
||||||
("postgrest_test_authenticator", "no-defaults-with-db.config"),
|
("db_config_authenticator", "no-defaults-with-db.config"),
|
||||||
("other_authenticator", "no-defaults-with-db-other-authenticator.config"),
|
("other_authenticator", "no-defaults-with-db-other-authenticator.config"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -299,23 +317,6 @@ def test_expected_config_from_db_settings(defaultenv, role, expectedconfig):
|
|||||||
assert dumpconfig(configpath=config, env=env) == expected
|
assert dumpconfig(configpath=config, env=env) == expected
|
||||||
|
|
||||||
|
|
||||||
def test_read_db_setting(defaultenv):
|
|
||||||
"""
|
|
||||||
Should be able to read db settings with current_setting.
|
|
||||||
|
|
||||||
See: https://github.com/PostgREST/postgrest/pull/1729#discussion_r572946461
|
|
||||||
"""
|
|
||||||
env = {
|
|
||||||
**defaultenv,
|
|
||||||
"PGRST_DB_CONFIG": "true",
|
|
||||||
}
|
|
||||||
with run(env=env) as postgrest:
|
|
||||||
uri = "/rpc/get_guc_value?name=pgrst.jwt_secret"
|
|
||||||
response = postgrest.session.get(uri)
|
|
||||||
|
|
||||||
assert response.text == '"OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE"'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
"config",
|
||||||
[conf for conf in CONFIGSDIR.iterdir() if conf.suffix == ".config"],
|
[conf for conf in CONFIGSDIR.iterdir() if conf.suffix == ".config"],
|
||||||
@@ -456,7 +457,7 @@ def test_iat_claim(defaultenv):
|
|||||||
response = postgrest.session.get("/authors_only", headers=headers)
|
response = postgrest.session.get("/authors_only", headers=headers)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def test_app_settings(defaultenv):
|
def test_app_settings(defaultenv):
|
||||||
@@ -473,7 +474,6 @@ def test_app_settings(defaultenv):
|
|||||||
uri = "/rpc/get_guc_value?name=app.settings.external_api_secret"
|
uri = "/rpc/get_guc_value?name=app.settings.external_api_secret"
|
||||||
response = postgrest.session.get(uri)
|
response = postgrest.session.get(uri)
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response.text == '"0123456789abcdef"'
|
assert response.text == '"0123456789abcdef"'
|
||||||
|
|
||||||
|
|
||||||
@@ -486,7 +486,6 @@ def test_app_settings_reload(tmp_path, defaultenv):
|
|||||||
|
|
||||||
with run(configfile, env=defaultenv) as postgrest:
|
with run(configfile, env=defaultenv) as postgrest:
|
||||||
response = postgrest.session.get(uri)
|
response = postgrest.session.get(uri)
|
||||||
assert response.status_code == 200
|
|
||||||
assert response.text == '"John"'
|
assert response.text == '"John"'
|
||||||
|
|
||||||
# change setting
|
# change setting
|
||||||
@@ -497,7 +496,6 @@ def test_app_settings_reload(tmp_path, defaultenv):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
response = postgrest.session.get(uri)
|
response = postgrest.session.get(uri)
|
||||||
assert response.status_code == 200
|
|
||||||
assert response.text == '"Jane"'
|
assert response.text == '"Jane"'
|
||||||
|
|
||||||
|
|
||||||
@@ -578,16 +576,15 @@ def test_db_schema_reload(tmp_path, defaultenv):
|
|||||||
configfile = tmp_path / "test.config"
|
configfile = tmp_path / "test.config"
|
||||||
configfile.write_text(config)
|
configfile.write_text(config)
|
||||||
|
|
||||||
headers = {"Accept-Profile": "v1"}
|
|
||||||
env = {key: value for key, value in defaultenv.items() if key != "PGRST_DB_SCHEMAS"}
|
env = {key: value for key, value in defaultenv.items() if key != "PGRST_DB_SCHEMAS"}
|
||||||
|
|
||||||
with run(configfile, env=env) as postgrest:
|
with run(configfile, env=env) as postgrest:
|
||||||
response = postgrest.session.get("/parents", headers=headers)
|
response = postgrest.session.get("/rpc/get_guc_value?name=search_path")
|
||||||
assert response.status_code == 404
|
assert response.text == '"public, public"'
|
||||||
|
|
||||||
# change setting
|
# change setting
|
||||||
configfile.write_text(
|
configfile.write_text(
|
||||||
config.replace('db-schemas = "test"', 'db-schemas = "test, v1"')
|
config.replace('db-schemas = "public"', 'db-schemas = "v1"')
|
||||||
)
|
)
|
||||||
|
|
||||||
# reload config
|
# reload config
|
||||||
@@ -598,36 +595,32 @@ def test_db_schema_reload(tmp_path, defaultenv):
|
|||||||
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
response = postgrest.session.get("/parents", headers=headers)
|
response = postgrest.session.get("/rpc/get_guc_value?name=search_path")
|
||||||
assert response.status_code == 200
|
assert response.text == '"v1, public"'
|
||||||
|
|
||||||
|
|
||||||
def test_db_schema_notify_reload(defaultenv):
|
def test_db_schema_notify_reload(defaultenv):
|
||||||
"DB schema and config should be reloaded when PostgREST is sent a NOTIFY"
|
"DB schema and config should be reloaded when PostgREST is sent a NOTIFY"
|
||||||
|
|
||||||
env = {
|
env = {**defaultenv, "PGRST_DB_CONFIG": "true", "PGRST_DB_CHANNEL_ENABLED": "true"}
|
||||||
**defaultenv,
|
|
||||||
"PGRST_DB_CONFIG": "true",
|
|
||||||
"PGRST_DB_CHANNEL_ENABLED": "true",
|
|
||||||
"PGRST_DB_SCHEMAS": "test",
|
|
||||||
}
|
|
||||||
|
|
||||||
with run(env=env) as postgrest:
|
with run(env=env) as postgrest:
|
||||||
response = postgrest.session.get("/parents")
|
response = postgrest.session.get("/rpc/get_guc_value?name=search_path")
|
||||||
assert response.status_code == 404
|
assert response.text == '"public, public"'
|
||||||
|
|
||||||
# change db-schemas config on the db and reload config and cache with notify
|
# change db-schemas config on the db and reload config and cache with notify
|
||||||
postgrest.session.post(
|
postgrest.session.post(
|
||||||
"/rpc/change_db_schema_and_full_reload", data={"schemas": "v1"}
|
"/rpc/change_db_schema_and_full_reload", data={"schemas": "v1"}
|
||||||
)
|
)
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.1)
|
||||||
|
|
||||||
response = postgrest.session.get("/parents?select=*,children(*)")
|
response = postgrest.session.get("/rpc/get_guc_value?name=search_path")
|
||||||
assert response.status_code == 200
|
assert response.text == '"v1, public"'
|
||||||
|
|
||||||
# reset db-schemas config on the db
|
# reset db-schemas config on the db
|
||||||
postgrest.session.post("/rpc/reset_db_schema_config")
|
response = postgrest.session.post("/rpc/reset_db_schema_config")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_max_rows_reload(defaultenv):
|
def test_max_rows_reload(defaultenv):
|
||||||
@@ -641,6 +634,7 @@ def test_max_rows_reload(defaultenv):
|
|||||||
|
|
||||||
with run(config, env=env) as postgrest:
|
with run(config, env=env) as postgrest:
|
||||||
response = postgrest.session.head("/projects")
|
response = postgrest.session.head("/projects")
|
||||||
|
assert response.status_code == 200
|
||||||
assert response.headers["Content-Range"] == "0-4/*"
|
assert response.headers["Content-Range"] == "0-4/*"
|
||||||
|
|
||||||
# change max-rows config on the db
|
# change max-rows config on the db
|
||||||
@@ -652,11 +646,12 @@ def test_max_rows_reload(defaultenv):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
response = postgrest.session.head("/projects")
|
response = postgrest.session.head("/projects")
|
||||||
|
assert response.status_code == 200
|
||||||
assert response.headers["Content-Range"] == "0-0/*"
|
assert response.headers["Content-Range"] == "0-0/*"
|
||||||
|
|
||||||
# reset max-rows config on the db
|
# reset max-rows config on the db
|
||||||
postgrest.session.post("/rpc/reset_max_rows_config")
|
response = postgrest.session.post("/rpc/reset_max_rows_config")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_max_rows_notify_reload(defaultenv):
|
def test_max_rows_notify_reload(defaultenv):
|
||||||
@@ -670,6 +665,7 @@ def test_max_rows_notify_reload(defaultenv):
|
|||||||
|
|
||||||
with run(env=env) as postgrest:
|
with run(env=env) as postgrest:
|
||||||
response = postgrest.session.head("/projects")
|
response = postgrest.session.head("/projects")
|
||||||
|
assert response.status_code == 200
|
||||||
assert response.headers["Content-Range"] == "0-4/*"
|
assert response.headers["Content-Range"] == "0-4/*"
|
||||||
|
|
||||||
# change max-rows config on the db and reload with notify
|
# change max-rows config on the db and reload with notify
|
||||||
@@ -680,11 +676,12 @@ def test_max_rows_notify_reload(defaultenv):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
response = postgrest.session.head("/projects")
|
response = postgrest.session.head("/projects")
|
||||||
|
assert response.status_code == 200
|
||||||
assert response.headers["Content-Range"] == "0-0/*"
|
assert response.headers["Content-Range"] == "0-0/*"
|
||||||
|
|
||||||
# reset max-rows config on the db
|
# reset max-rows config on the db
|
||||||
postgrest.session.post("/rpc/reset_max_rows_config")
|
response = postgrest.session.post("/rpc/reset_max_rows_config")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_role_claim_key_notify_reload(defaultenv):
|
def test_invalid_role_claim_key_notify_reload(defaultenv):
|
||||||
@@ -694,20 +691,23 @@ def test_invalid_role_claim_key_notify_reload(defaultenv):
|
|||||||
**defaultenv,
|
**defaultenv,
|
||||||
"PGRST_DB_CONFIG": "true",
|
"PGRST_DB_CONFIG": "true",
|
||||||
"PGRST_DB_CHANNEL_ENABLED": "true",
|
"PGRST_DB_CHANNEL_ENABLED": "true",
|
||||||
|
"PGRST_LOG_LEVEL": "crit",
|
||||||
}
|
}
|
||||||
|
|
||||||
with run(env=env) as postgrest:
|
with run(env=env) as postgrest:
|
||||||
postgrest.session.post("/rpc/invalid_role_claim_key_reload")
|
postgrest.session.post("/rpc/invalid_role_claim_key_reload")
|
||||||
|
|
||||||
# skips the first lines from stderr, the "Attempting to connect to database", "Connection successful", etc.
|
output = None
|
||||||
# this is a hack to avoid readline() from locking up the test
|
for _ in range(10):
|
||||||
for _ in range(6):
|
output = postgrest.process.stdout.readline()
|
||||||
postgrest.process.stderr.readline()
|
if output:
|
||||||
assert "failed to parse role-claim-key value" in str(
|
break
|
||||||
postgrest.process.stderr.readline()
|
time.sleep(0.1)
|
||||||
)
|
|
||||||
|
|
||||||
postgrest.session.post("/rpc/reset_invalid_role_claim_key")
|
assert "failed to parse role-claim-key value" in output.decode()
|
||||||
|
|
||||||
|
response = postgrest.session.post("/rpc/reset_invalid_role_claim_key")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_db_prepared_statements_enable(defaultenv):
|
def test_db_prepared_statements_enable(defaultenv):
|
||||||
@@ -729,3 +729,43 @@ def test_db_prepared_statements_disable(defaultenv):
|
|||||||
with run(env=env) as postgrest:
|
with run(env=env) as postgrest:
|
||||||
response = postgrest.session.post("/rpc/uses_prepared_statements")
|
response = postgrest.session.post("/rpc/uses_prepared_statements")
|
||||||
assert response.text == "false"
|
assert response.text == "false"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"level, has_output",
|
||||||
|
[
|
||||||
|
("info", [True, True, True]),
|
||||||
|
("warn", [False, True, True]),
|
||||||
|
("error", [False, False, True]),
|
||||||
|
("crit", [False, False, False]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_log_level(level, has_output, defaultenv):
|
||||||
|
"log_level should filter request logging"
|
||||||
|
|
||||||
|
env = {**defaultenv, "PGRST_LOG_LEVEL": level}
|
||||||
|
|
||||||
|
with run(env=env) as postgrest:
|
||||||
|
response = postgrest.session.get("/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
if has_output[0]:
|
||||||
|
assert re.match(
|
||||||
|
r'unknownSocket - - \[.+\] "GET / HTTP/1.1" 200 - "" "python-requests/.+"',
|
||||||
|
postgrest.process.stdout.readline().decode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = postgrest.session.get("/unknown")
|
||||||
|
assert response.status_code == 404
|
||||||
|
if has_output[1]:
|
||||||
|
assert re.match(
|
||||||
|
r'unknownSocket - - \[.+\] "GET /unknown HTTP/1.1" 404 - "" "python-requests/.+"',
|
||||||
|
postgrest.process.stdout.readline().decode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = postgrest.session.get("/rpc/raise_bad_pt")
|
||||||
|
assert response.status_code == 500
|
||||||
|
if has_output[2]:
|
||||||
|
assert re.match(
|
||||||
|
r'unknownSocket - - \[.+\] "GET /rpc/raise_bad_pt HTTP/1.1" 500 - "" "python-requests/.+"',
|
||||||
|
postgrest.process.stdout.readline().decode(),
|
||||||
|
)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
CREATE ROLE postgrest_test_anonymous;
|
||||||
|
GRANT postgrest_test_anonymous TO :USER;
|
||||||
|
CREATE SCHEMA test;
|
||||||
|
|
||||||
|
-- PUT+PATCH target needs one record and column to modify
|
||||||
|
CREATE TABLE test.actors (
|
||||||
|
PRIMARY KEY (actor),
|
||||||
|
actor INT,
|
||||||
|
name TEXT,
|
||||||
|
last_modified TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
INSERT INTO test.actors VALUES (1, 'John Doe');
|
||||||
|
|
||||||
|
-- POST target needs generated PK
|
||||||
|
CREATE TABLE test.films (
|
||||||
|
PRIMARY KEY (film),
|
||||||
|
film INT GENERATED BY DEFAULT AS IDENTITY,
|
||||||
|
title TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- DELETE target remains empty
|
||||||
|
CREATE TABLE test.roles (
|
||||||
|
actor INT REFERENCES test.actors,
|
||||||
|
film INT REFERENCES test.films,
|
||||||
|
character TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE FUNCTION test.call_me (name TEXT) RETURNS TEXT
|
||||||
|
STABLE LANGUAGE SQL AS $$
|
||||||
|
SELECT 'Hello ' || name || ', how are you?';
|
||||||
|
$$;
|
||||||
|
|
||||||
|
GRANT USAGE ON SCHEMA test TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA test TO postgrest_test_anonymous;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"last_modified": "now"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"title": "Workers Leaving The Lumière Factory In Lyon"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"actor": 1,
|
||||||
|
"name": "John Doe",
|
||||||
|
"last_modified": "now"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name": "John"
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
GET http://postgrest/
|
||||||
|
Prefer: tx=commit
|
||||||
|
|
||||||
|
HEAD http://postgrest/actors?actor=eq.1
|
||||||
|
Prefer: tx=commit
|
||||||
|
|
||||||
|
GET http://postgrest/actors?select=*,roles(*,films(*))
|
||||||
|
Prefer: tx=commit
|
||||||
|
|
||||||
|
POST http://postgrest/films?columns=title
|
||||||
|
Prefer: tx=rollback
|
||||||
|
@post.json
|
||||||
|
|
||||||
|
PUT http://postgrest/actors?actor=eq.1&columns=name
|
||||||
|
Prefer: tx=rollback
|
||||||
|
@put.json
|
||||||
|
|
||||||
|
PATCH http://postgrest/actors?actor=eq.1
|
||||||
|
Prefer: tx=rollback
|
||||||
|
@patch.json
|
||||||
|
|
||||||
|
DELETE http://postgrest/roles
|
||||||
|
Prefer: tx=rollback
|
||||||
|
|
||||||
|
GET http://postgrest/rpc/call_me?name=John
|
||||||
|
|
||||||
|
POST http://postgrest/rpc/call_me
|
||||||
|
@rpc.json
|
||||||
|
|
||||||
|
OPTIONS http://postgrest/actors
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user