diff --git a/.gitbook.yml b/.gitbook.yml index 7dc6716..d20102c 100644 --- a/.gitbook.yml +++ b/.gitbook.yml @@ -1,6 +1,6 @@ -root: ./docs/ - -structure: - readme: ./base.md - summary: ./SUMMARY.md - +root: ./docs/ + +structure: + readme: ./base.md + summary: ./SUMMARY.md + diff --git a/.github/workflows/check-generate.sh b/.github/workflows/check-generate.sh index 25bd596..ba44bad 100755 --- a/.github/workflows/check-generate.sh +++ b/.github/workflows/check-generate.sh @@ -1,11 +1,11 @@ -#!/bin/bash - -set -euo pipefail - -go generate ./... - -if [[ $(git --no-pager diff) ]] ; then - echo "you need to run "go generate ./..." and commit the changes" - git --no-pager diff - exit 1 -fi +#!/bin/bash + +set -euo pipefail + +go generate ./... + +if [[ $(git --no-pager diff) ]] ; then + echo "you need to run "go generate ./..." and commit the changes" + git --no-pager diff + exit 1 +fi diff --git a/.github/workflows/check-init b/.github/workflows/check-init index 6cba299..24679c0 100644 --- a/.github/workflows/check-init +++ b/.github/workflows/check-init @@ -1,21 +1,21 @@ -#!/bin/bash - -set -euo pipefail - -fastgql_dir=$(pwd) -cd $(mktemp -d) -go mod init inittest -printf '// +build tools\npackage tools\nimport _ "github.com/roneli/fastgql"' | gofmt > tools.go -go mod tidy -go mod edit -replace=github.com/roneli/fastgql="$fastgql_dir" -go mod tidy - -if ! go run github.com/roneli/fastgql init ; then - echo "gqlgen init failed" - exit 125 -fi - -if ! go run github.com/roneli/fastgql generate -c gqlgen.yml ; then - echo "gqlgen generate failed" - exit 125 +#!/bin/bash + +set -euo pipefail + +fastgql_dir=$(pwd) +cd $(mktemp -d) +go mod init inittest +printf '// +build tools\npackage tools\nimport _ "github.com/roneli/fastgql"' | gofmt > tools.go +go mod tidy +go mod edit -replace=github.com/roneli/fastgql="$fastgql_dir" +go mod tidy + +if ! go run github.com/roneli/fastgql init ; then + echo "gqlgen init failed" + exit 125 +fi + +if ! go run github.com/roneli/fastgql generate -c gqlgen.yml ; then + echo "gqlgen generate failed" + exit 125 fi \ No newline at end of file diff --git a/.github/workflows/data/.env b/.github/workflows/data/.env index 102d544..a87ec48 100644 --- a/.github/workflows/data/.env +++ b/.github/workflows/data/.env @@ -1,5 +1,5 @@ -POSTGRES_PASSWORD= -POSTGRES_USER=postgres -POSTGRES_HOST=postgres -POSTGRES_DB=postgres -POSTGRES_HOST_AUTH_METHOD=trust +POSTGRES_PASSWORD= +POSTGRES_USER=postgres +POSTGRES_HOST=postgres +POSTGRES_DB=postgres +POSTGRES_HOST_AUTH_METHOD=trust diff --git a/.github/workflows/data/docker-compose.yml b/.github/workflows/data/docker-compose.yml index 10b8c90..8b7e73f 100644 --- a/.github/workflows/data/docker-compose.yml +++ b/.github/workflows/data/docker-compose.yml @@ -1,11 +1,11 @@ -version: '3.1' -services: - db: - image: postgres - restart: always - env_file: - - .env - volumes: - - ./:/docker-entrypoint-initdb.d - ports: +version: '3.1' +services: + db: + image: postgres + restart: always + env_file: + - .env + volumes: + - ./:/docker-entrypoint-initdb.d + ports: - 5432:5432 \ No newline at end of file diff --git a/.github/workflows/data/init.sql b/.github/workflows/data/init.sql index 1384dc1..1f93f47 100644 --- a/.github/workflows/data/init.sql +++ b/.github/workflows/data/init.sql @@ -1,78 +1,78 @@ --- create schema for example/interface/schema.graphql - -CREATE TABLE animals ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL, - type TEXT NOT NULL, - breed TEXT NOT NULL, - color TEXT NOT NULL -); - -CREATE TABLE "user" ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE post ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL, - user_id INTEGER NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE category ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE posts_to_categories ( - post_id INTEGER NOT NULL, - category_id INTEGER NOT NULL, - PRIMARY KEY (post_id, category_id) -); - --- initialize base data -INSERT INTO "user" (name) VALUES ('Alice'); -INSERT INTO "user" (name) VALUES ('Bob'); -INSERT INTO "user" (name) VALUES ('Charlie'); -INSERT INTO "user" (name) VALUES ('David'); -INSERT INTO "user" (name) VALUES ('Eve'); - -INSERT INTO category (name) VALUES ('News'); -INSERT INTO category (name) VALUES ('Technology'); -INSERT INTO category (name) VALUES ('Science'); -INSERT INTO category (name) VALUES ('Sports'); -INSERT INTO category (name) VALUES ('Entertainment'); - -INSERT INTO post (name, user_id) VALUES ('Hello World', 1); -INSERT INTO post (name, user_id) VALUES ('GraphQL is awesome', 2); -INSERT INTO post (name, user_id) VALUES ('Postgres is cool', 3); -INSERT INTO post (name, user_id) VALUES ('Deno is interesting', 4); -INSERT INTO post (name, user_id) VALUES ('Node.js is fast', 5); - --- some posts are in multiple categories -INSERT INTO posts_to_categories (post_id, category_id) VALUES (1, 1); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (2, 2); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (3, 3); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (4, 4); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (5, 5); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (1, 2); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (2, 3); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (3, 4); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (4, 5); -INSERT INTO posts_to_categories (post_id, category_id) VALUES (5, 1); - - --- insert some animals -INSERT INTO animals (name, type, breed, color) VALUES ('Fido', 'dog', 'labrador', 'black'); -INSERT INTO animals (name, type, breed, color) VALUES ('Whiskers', 'cat', 'siamese', 'white'); -INSERT INTO animals (name, type, breed, color) VALUES ('Spot', 'dog', 'dalmatian', 'white'); -INSERT INTO animals (name, type, breed, color) VALUES ('Fluffy', 'cat', 'persian', 'grey'); -INSERT INTO animals (name, type, breed, color) VALUES ('Rover', 'dog', 'bulldog', 'brown'); -INSERT INTO animals (name, type, breed, color) VALUES ('Mittens', 'cat', 'maine coon', 'black'); - +-- create schema for example/interface/schema.graphql + +CREATE TABLE animals ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + breed TEXT NOT NULL, + color TEXT NOT NULL +); + +CREATE TABLE "user" ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE post ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + user_id INTEGER NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE category ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE posts_to_categories ( + post_id INTEGER NOT NULL, + category_id INTEGER NOT NULL, + PRIMARY KEY (post_id, category_id) +); + +-- initialize base data +INSERT INTO "user" (name) VALUES ('Alice'); +INSERT INTO "user" (name) VALUES ('Bob'); +INSERT INTO "user" (name) VALUES ('Charlie'); +INSERT INTO "user" (name) VALUES ('David'); +INSERT INTO "user" (name) VALUES ('Eve'); + +INSERT INTO category (name) VALUES ('News'); +INSERT INTO category (name) VALUES ('Technology'); +INSERT INTO category (name) VALUES ('Science'); +INSERT INTO category (name) VALUES ('Sports'); +INSERT INTO category (name) VALUES ('Entertainment'); + +INSERT INTO post (name, user_id) VALUES ('Hello World', 1); +INSERT INTO post (name, user_id) VALUES ('GraphQL is awesome', 2); +INSERT INTO post (name, user_id) VALUES ('Postgres is cool', 3); +INSERT INTO post (name, user_id) VALUES ('Deno is interesting', 4); +INSERT INTO post (name, user_id) VALUES ('Node.js is fast', 5); + +-- some posts are in multiple categories +INSERT INTO posts_to_categories (post_id, category_id) VALUES (1, 1); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (2, 2); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (3, 3); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (4, 4); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (5, 5); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (1, 2); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (2, 3); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (3, 4); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (4, 5); +INSERT INTO posts_to_categories (post_id, category_id) VALUES (5, 1); + + +-- insert some animals +INSERT INTO animals (name, type, breed, color) VALUES ('Fido', 'dog', 'labrador', 'black'); +INSERT INTO animals (name, type, breed, color) VALUES ('Whiskers', 'cat', 'siamese', 'white'); +INSERT INTO animals (name, type, breed, color) VALUES ('Spot', 'dog', 'dalmatian', 'white'); +INSERT INTO animals (name, type, breed, color) VALUES ('Fluffy', 'cat', 'persian', 'grey'); +INSERT INTO animals (name, type, breed, color) VALUES ('Rover', 'dog', 'bulldog', 'brown'); +INSERT INTO animals (name, type, breed, color) VALUES ('Mittens', 'cat', 'maine coon', 'black'); + diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3c43826..4b930db 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,39 +1,39 @@ -name: deploy Github pages - -on: - # Trigger the workflow every time you push to the `main` branch - # Using a different branch name? Replace `main` with your branch’s name - push: - branches: [ master ] - # Allows you to run this workflow manually from the Actions tab on GitHub. - workflow_dispatch: - -# Allow this job to clone the repo and create a page deployment -permissions: - contents: read - pages: write - id-token: write - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout your repository using git - uses: actions/checkout@v3 - - name: Install, build, and upload your site - uses: withastro/action@v1 - with: - path: ./docs # The root location of your Astro project inside the repository. (optional) - # node-version: 18 # The specific version of Node that should be used to build your site. Defaults to 18. (optional) - package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional) - - deploy: - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment +name: deploy Github pages + +on: + # Trigger the workflow every time you push to the `main` branch + # Using a different branch name? Replace `main` with your branch’s name + push: + branches: [ master ] + # Allows you to run this workflow manually from the Actions tab on GitHub. + workflow_dispatch: + +# Allow this job to clone the repo and create a page deployment +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout your repository using git + uses: actions/checkout@v3 + - name: Install, build, and upload your site + uses: withastro/action@v1 + with: + path: ./docs # The root location of your Astro project inside the repository. (optional) + # node-version: 18 # The specific version of Node that should be used to build your site. Defaults to 18. (optional) + package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional) + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment uses: actions/deploy-pages@v1 \ No newline at end of file diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index b99d152..98c292d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -1,22 +1,22 @@ -name: integration -on: [ pull_request ] -# When a new revision is pushed to a PR, cancel all in-progress CI runs for that -# PR. See https://docs.github.com/en/actions/using-jobs/using-concurrency -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true -jobs: - init: - strategy: - matrix: - go: [ "1.18", "1.20" ] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go }} - - run: | - chmod +x .github/workflows/check-init - .github/workflows/check-init - +name: integration +on: [ pull_request ] +# When a new revision is pushed to a PR, cancel all in-progress CI runs for that +# PR. See https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +jobs: + init: + strategy: + matrix: + go: [ "1.18", "1.20" ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go }} + - run: | + chmod +x .github/workflows/check-init + .github/workflows/check-init + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 69e077c..252f6dc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,13 +1,13 @@ -name: golangci-lint -on: [ pull_request ] - -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: golangci-lint - uses: golangci/golangci-lint-action@v2 - with: +name: golangci-lint +on: [ pull_request ] + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: version: v1.55.2 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c681ba8..d9c9795 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,38 +1,38 @@ -name: tests -on: - push: - branches: - - master - pull_request: - branches: [ master ] - -jobs: - unitests: - runs-on: 'ubuntu-latest' - services: - postgres: - image: postgres:latest - env: - POSTGRES_USER: postgres - POSTGRES_DB: postgres - POSTGRES_PASSWORD: "" - POSTGRES_HOST_AUTH_METHOD: trust - ports: - - 5432:5432 - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - - run: | - sudo apt-get update - sudo apt-get install --yes --no-install-recommends postgresql-client - - name: Set up Go 1.x - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - name: Check out code into the Go module directory - uses: actions/checkout@v4 - # run the sql script init.sql in workflows/data - - run: | - psql -h localhost -U postgres -f .github/workflows/data/init.sql - - run: go mod download - - name: Test +name: tests +on: + push: + branches: + - master + pull_request: + branches: [ master ] + +jobs: + unitests: + runs-on: 'ubuntu-latest' + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: postgres + POSTGRES_DB: postgres + POSTGRES_PASSWORD: "" + POSTGRES_HOST_AUTH_METHOD: trust + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - run: | + sudo apt-get update + sudo apt-get install --yes --no-install-recommends postgresql-client + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + - name: Check out code into the Go module directory + uses: actions/checkout@v4 + # run the sql script init.sql in workflows/data + - run: | + psql -h localhost -U postgres -f .github/workflows/data/init.sql + - run: go mod download + - name: Test run: go test -v ./... \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6d969d4..b2c4d03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -.idea/ - - -docs/node_modules/* -docs/resources/* -docs/public/* +.idea/ + + +docs/node_modules/* +docs/resources/* +docs/public/* docs/themes/docsy/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 9a3e242..ed87124 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "docs/themes/docsy"] - path = docs/themes/docsy - url = https://github.com/google/docsy +[submodule "docs/themes/docsy"] + path = docs/themes/docsy + url = https://github.com/google/docsy diff --git a/.golangci.yml b/.golangci.yml index f0f3095..b5c68c0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,35 +1,35 @@ -run: - tests: true - skip-dirs: - - bin - - docs - - example - -linters-settings: - errcheck: - ignore: fmt:.*,[rR]ead|[wW]rite|[cC]lose,io:Copy - -linters: - disable-all: true - enable: - - bodyclose - - dupl - - errcheck - - gocritic - - gofmt - - goimports - - gosimple - - govet - - ineffassign - - misspell - - nakedret - - prealloc - - unconvert - - unused - -issues: - exclude-rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: +run: + tests: true + skip-dirs: + - bin + - docs + - example + +linters-settings: + errcheck: + ignore: fmt:.*,[rR]ead|[wW]rite|[cC]lose,io:Copy + +linters: + disable-all: true + enable: + - bodyclose + - dupl + - errcheck + - gocritic + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - prealloc + - unconvert + - unused + +issues: + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: - dupl \ No newline at end of file diff --git a/LICENSE b/LICENSE index f30af5b..ada150e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2021 Ron - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2021 Ron + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5c33c4e..3a70b02 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,49 @@ -![fastgql](docs/src/assets/logo_dark.svg) - - -
- - - - - -
- -## What is fastGQL? - -[*fastGQL*](https://github.com/roneli/fastgql) is a Go library that -extends [gqlgen](https://github.com/99designs/gqlgen) to create a blazing-fast GraphQL server that gives you instant, -realtime GraphQL APIs over Postgres. - -- **fastgql is based on a Schema first approach** — You get to Define your API using the - GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). -- **fastgql prioritizes extendability** — You can modify resolvers, add your own custom operators and even create your - own database query builder. -- **fastgql enables codegen** — We generate even more of the boring CRUD bits, so you can focus on building your app - even faster! - -## Getting Started - -- To install fastgql run the command `go get github.com/roneli/fastgql` in your project directory.
-- You could initialize a new project using the recommended folder structure by running this - command `go run github.com/roneli/fastgql init`. - -You could find a more comprehensive guide on [gqlgen](https://github.com/99designs/gqlgen) to help you get -started [here](https://gqlgen.com/getting-started/). -We also have a couple of [examples](https://github.com/roneli/fastgql/tree/master/example) that show how fastgql -generates the full API seamlessly. - -## Reporting Issues - -If you think you've found a bug, or something isn't behaving the way you think it should, please raise -an [issue](https://github.com/roneli/fastgql/issues) on GitHub. - -## Contributing - -Feel free to open Pull-Request for small fixes and changes. For bigger changes and new builders please open -an [issue](https://github.com/roneli/fastgql/issues) first to prevent double work and discuss relevant stuff. - -## Roadmap 🚧 -- Integration testing, and generation testing -- configurable database connections, augmenters and better schema control -- Support multiple database (mongodb, cockroachDB, neo4j) +![fastgql](docs/src/assets/logo_dark.svg) + + +
+ + + + + +
+ +## What is fastGQL? + +[*fastGQL*](https://github.com/roneli/fastgql) is a Go library that +extends [gqlgen](https://github.com/99designs/gqlgen) to create a blazing-fast GraphQL server that gives you instant, +realtime GraphQL APIs over Postgres. + +- **fastgql is based on a Schema first approach** — You get to Define your API using the + GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). +- **fastgql prioritizes extendability** — You can modify resolvers, add your own custom operators and even create your + own database query builder. +- **fastgql enables codegen** — We generate even more of the boring CRUD bits, so you can focus on building your app + even faster! + +## Getting Started + +- To install fastgql run the command `go get github.com/roneli/fastgql` in your project directory.
+- You could initialize a new project using the recommended folder structure by running this + command `go run github.com/roneli/fastgql init`. + +You could find a more comprehensive guide on [gqlgen](https://github.com/99designs/gqlgen) to help you get +started [here](https://gqlgen.com/getting-started/). +We also have a couple of [examples](https://github.com/roneli/fastgql/tree/master/example) that show how fastgql +generates the full API seamlessly. + +## Reporting Issues + +If you think you've found a bug, or something isn't behaving the way you think it should, please raise +an [issue](https://github.com/roneli/fastgql/issues) on GitHub. + +## Contributing + +Feel free to open Pull-Request for small fixes and changes. For bigger changes and new builders please open +an [issue](https://github.com/roneli/fastgql/issues) first to prevent double work and discuss relevant stuff. + +## Roadmap 🚧 +- Integration testing, and generation testing +- configurable database connections, augmenters and better schema control +- Support multiple database (mongodb, cockroachDB, neo4j) diff --git a/docs/.gitignore b/docs/.gitignore index 6240da8..a1465b1 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,21 +1,21 @@ -# build output -dist/ -# generated types -.astro/ - -# dependencies -node_modules/ - -# logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - - -# environment variables -.env -.env.production - -# macOS-specific files -.DS_Store +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/docs/README.md b/docs/README.md index b51abaa..2101aa8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,54 +1,54 @@ -# Starlight Starter Kit: Basics - -[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) - -``` -npm create astro@latest -- --template starlight -``` - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) -[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) - -> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! - -## 🚀 Project Structure - -Inside of your Astro + Starlight project, you'll see the following folders and files: - -``` -. -├── public/ -├── src/ -│ ├── assets/ -│ ├── content/ -│ │ ├── docs/ -│ │ └── config.ts -│ └── env.d.ts -├── astro.config.mjs -├── package.json -└── tsconfig.json -``` - -Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. - -Images can be added to `src/assets/` and embedded in Markdown with a relative link. - -Static assets, like favicons, can be placed in the `public/` directory. - -## 🧞 Commands - -All commands are run from the root of the project, from a terminal: - -| Command | Action | -| :------------------------ | :----------------------------------------------- | -| `npm install` | Installs dependencies | -| `npm run dev` | Starts local dev server at `localhost:4321` | -| `npm run build` | Build your production site to `./dist/` | -| `npm run preview` | Preview your build locally, before deploying | -| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | -| `npm run astro -- --help` | Get help using the Astro CLI | - -## 👀 Want to learn more? - -Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). +# Starlight Starter Kit: Basics + +[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) + +``` +npm create astro@latest -- --template starlight +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro + Starlight project, you'll see the following folders and files: + +``` +. +├── public/ +├── src/ +│ ├── assets/ +│ ├── content/ +│ │ ├── docs/ +│ │ └── config.ts +│ └── env.d.ts +├── astro.config.mjs +├── package.json +└── tsconfig.json +``` + +Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. + +Images can be added to `src/assets/` and embedded in Markdown with a relative link. + +Static assets, like favicons, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts index 9df91b6..e2fc9fe 100644 --- a/docs/src/content/config.ts +++ b/docs/src/content/config.ts @@ -1,7 +1,7 @@ -import { defineCollection } from 'astro:content'; -import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; - -export const collections = { - docs: defineCollection({ schema: docsSchema() }), - i18n: defineCollection({ type: 'data', schema: i18nSchema() }), -}; +import { defineCollection } from 'astro:content'; +import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; + +export const collections = { + docs: defineCollection({ schema: docsSchema() }), + i18n: defineCollection({ type: 'data', schema: i18nSchema() }), +}; diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 10ed33d..2627932 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -1,36 +1,36 @@ ---- -title: Welcome to FastGQL.io -description: Get started building your docs site with Starlight. -template: splash -hero: - tagline: Congrats on setting up a new Starlight project! - image: - file: ../../assets/logo_dark.svg - actions: - - text: Get Started - link: /fastgql/start/setup/ - icon: right-arrow - variant: primary - - text: Read the Starlight docs - link: https://starlight.astro.build - icon: external ---- - -import { Card, CardGrid } from '@astrojs/starlight/components'; - -## Next steps - - - - Edit `src/content/docs/index.mdx` to see this page change. - - - Add Markdown or MDX files to `src/content/docs` to create new pages. - - - Edit your `sidebar` and other config in `astro.config.mjs`. - - - Learn more in [the Starlight Docs](https://starlight.astro.build/). - - +--- +title: Welcome to FastGQL.io +description: Get started building your docs site with Starlight. +template: splash +hero: + tagline: Congrats on setting up a new Starlight project! + image: + file: ../../assets/logo_dark.svg + actions: + - text: Get Started + link: /fastgql/start/setup/ + icon: right-arrow + variant: primary + - text: Read the Starlight docs + link: https://starlight.astro.build + icon: external +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; + +## Next steps + + + + Edit `src/content/docs/index.mdx` to see this page change. + + + Add Markdown or MDX files to `src/content/docs` to create new pages. + + + Edit your `sidebar` and other config in `astro.config.mjs`. + + + Learn more in [the Starlight Docs](https://starlight.astro.build/). + + diff --git a/docs/src/content/docs/mutations/base.md b/docs/src/content/docs/mutations/base.md index 5a24364..866f619 100644 --- a/docs/src/content/docs/mutations/base.md +++ b/docs/src/content/docs/mutations/base.md @@ -1,23 +1,23 @@ ---- -title: Mutations -description: GraphQL mutations ---- - -## Introduction - -GraphQL mutations are used to modify data on the server such as updating, creating or deleting. - -FastGQL auto generates mutations using the [#generatemutations](../schema/directives.md#generatemutations "mention") directive in your schema. - -## Types of mutations - -The following types of mutations are possible: - -* [insert.md](insert.mdx "mention") -* [update.md](update.mdx "mention") -* [delete.md](delete.mdx "mention") - - - - - +--- +title: Mutations +description: GraphQL mutations +--- + +## Introduction + +GraphQL mutations are used to modify data on the server such as updating, creating or deleting. + +FastGQL auto generates mutations using the [#generatemutations](../schema/directives.md#generatemutations "mention") directive in your schema. + +## Types of mutations + +The following types of mutations are possible: + +* [insert.md](insert.mdx "mention") +* [update.md](update.mdx "mention") +* [delete.md](delete.mdx "mention") + + + + + diff --git a/docs/src/content/docs/mutations/delete.mdx b/docs/src/content/docs/mutations/delete.mdx index 8d70f9d..ea23ac9 100644 --- a/docs/src/content/docs/mutations/delete.mdx +++ b/docs/src/content/docs/mutations/delete.mdx @@ -1,89 +1,89 @@ ---- -title: Delete -description: Delete mutations ---- - -import { Tabs, TabItem } from '@astrojs/starlight/components'; - -FastGQL auto generates `delete` delete mutations if the [#generatemutations](../schema/directives.md#generatemutations "mention") is set. -The following code will be added to the GraphQL schema. - -```graphql - -mutation { - # createPosts allows to insert one or more Posts - deletePosts(cascade: Boolean, filter: PostFilterInput): PostsPayload - # ... more mutations -} - -""" -Autogenerated Posts Filter Input -""" -input PostFilterInput { - id: IntComparator - name: StringComparator - categories: CategoryFilterInput - user: UserFilterInput - """ - Logical AND of FilterInput - """ - AND: [PostFilterInput] - """ - Logical OR of FilterInput - """ - OR: [PostFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: PostFilterInput -} - -""" -Autogenerated payload object -""" -type PostsPayload { - """ - rows affection by mutation - """ - rows_affected: Int! - posts: [Post] -} -``` - -The [#generatemutations](../schema/directives.md#generatemutations "mention") directive adds the above into our schema. - -* **filter** argument passed to `deletePosts`mutation allows to filter what posts we want to delete. -* The **cascade** argument defines if we want to cascade our objects on deletion (postgres only) - -## Delete Objects - -**Example:** Delete a post object and return the deleted posts in the response: - - - -```graphql -mutation { - deletePosts { - rows_affected - posts { - name - id - } - } -} -``` - - -```graphql -mutation { - deletePosts(filter: {id: {eq: 1}) { - rows_affected - posts { - name - id - } - } -} -``` - - +--- +title: Delete +description: Delete mutations +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +FastGQL auto generates `delete` delete mutations if the [#generatemutations](../schema/directives.md#generatemutations "mention") is set. +The following code will be added to the GraphQL schema. + +```graphql + +mutation { + # createPosts allows to insert one or more Posts + deletePosts(cascade: Boolean, filter: PostFilterInput): PostsPayload + # ... more mutations +} + +""" +Autogenerated Posts Filter Input +""" +input PostFilterInput { + id: IntComparator + name: StringComparator + categories: CategoryFilterInput + user: UserFilterInput + """ + Logical AND of FilterInput + """ + AND: [PostFilterInput] + """ + Logical OR of FilterInput + """ + OR: [PostFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: PostFilterInput +} + +""" +Autogenerated payload object +""" +type PostsPayload { + """ + rows affection by mutation + """ + rows_affected: Int! + posts: [Post] +} +``` + +The [#generatemutations](../schema/directives.md#generatemutations "mention") directive adds the above into our schema. + +* **filter** argument passed to `deletePosts`mutation allows to filter what posts we want to delete. +* The **cascade** argument defines if we want to cascade our objects on deletion (postgres only) + +## Delete Objects + +**Example:** Delete a post object and return the deleted posts in the response: + + + +```graphql +mutation { + deletePosts { + rows_affected + posts { + name + id + } + } +} +``` + + +```graphql +mutation { + deletePosts(filter: {id: {eq: 1}) { + rows_affected + posts { + name + id + } + } +} +``` + + diff --git a/docs/src/content/docs/mutations/insert.mdx b/docs/src/content/docs/mutations/insert.mdx index 135bd75..5bf2a48 100644 --- a/docs/src/content/docs/mutations/insert.mdx +++ b/docs/src/content/docs/mutations/insert.mdx @@ -1,95 +1,95 @@ ---- -title: Insert -description: Insert mutations ---- - -import { Tabs, TabItem } from '@astrojs/starlight/components'; - -FastGQL auto generates `create` insert mutations if the [#generatemutations](../schema/directives.md#generatemutations "mention")is set. The following code will be added to the GraphQL schema. - -```graphql -// Some code - -mutation { - # createPosts allows to insert one or more Posts - createPosts(inputs: [CreatePostInput!]!): PostsPayload - # ... more mutations -} - -""" -AutoGenerated input for Post -""" -input CreatePostInput { - id: Int! - name: String -} - -""" -Autogenerated payload object -""" -type PostsPayload { - """ - rows affection by mutation - """ - rows_affected: Int! - posts: [Post] -} -``` - -The [#generatemutations](../schema/directives.md#generatemutations "mention") directive adds the above into our schema. - -* `inputs` argument passed to `createPosts` mutation is required and you can pass 1 or more objects. -* `PostsPayload` is the response object returned from the mutation, returning the amounts of rows\_affected i.e. the amount of objects inserted and access to all our posts info - -## Insert Objects - -**Example:** Insert a new `post object` and return the inserted post object in the response: - - - - -```graphql -mutation { - createPosts(inputs: {name: "fastGQL", id: 111}) { - rows_affected - posts { - name - id - } - } -``` - - - -```graphql -mutation { - createPosts(inputs: [{name: "fastGQL", id: 1}, {name: "fastGQL2", id: 2}]) { - rows_affected - posts { - name - id - } - } -} -``` - - - -```graphql -mutation { - createPosts(inputs: [{name: "fastGQL", id: 1}, {name: "fastGQL2", id: 2}]) { - rows_affected - posts { - name - id - categories { - id - name - } - } - } -} -``` - - - +--- +title: Insert +description: Insert mutations +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +FastGQL auto generates `create` insert mutations if the [#generatemutations](../schema/directives.md#generatemutations "mention")is set. The following code will be added to the GraphQL schema. + +```graphql +// Some code + +mutation { + # createPosts allows to insert one or more Posts + createPosts(inputs: [CreatePostInput!]!): PostsPayload + # ... more mutations +} + +""" +AutoGenerated input for Post +""" +input CreatePostInput { + id: Int! + name: String +} + +""" +Autogenerated payload object +""" +type PostsPayload { + """ + rows affection by mutation + """ + rows_affected: Int! + posts: [Post] +} +``` + +The [#generatemutations](../schema/directives.md#generatemutations "mention") directive adds the above into our schema. + +* `inputs` argument passed to `createPosts` mutation is required and you can pass 1 or more objects. +* `PostsPayload` is the response object returned from the mutation, returning the amounts of rows\_affected i.e. the amount of objects inserted and access to all our posts info + +## Insert Objects + +**Example:** Insert a new `post object` and return the inserted post object in the response: + + + + +```graphql +mutation { + createPosts(inputs: {name: "fastGQL", id: 111}) { + rows_affected + posts { + name + id + } + } +``` + + + +```graphql +mutation { + createPosts(inputs: [{name: "fastGQL", id: 1}, {name: "fastGQL2", id: 2}]) { + rows_affected + posts { + name + id + } + } +} +``` + + + +```graphql +mutation { + createPosts(inputs: [{name: "fastGQL", id: 1}, {name: "fastGQL2", id: 2}]) { + rows_affected + posts { + name + id + categories { + id + name + } + } + } +} +``` + + + diff --git a/docs/src/content/docs/mutations/update.mdx b/docs/src/content/docs/mutations/update.mdx index cbf64b3..2ccbf51 100644 --- a/docs/src/content/docs/mutations/update.mdx +++ b/docs/src/content/docs/mutations/update.mdx @@ -1,83 +1,83 @@ ---- -title: Update -description: Update mutations ---- - -import { Tabs, TabItem } from '@astrojs/starlight/components'; - -FastGQL auto generates `update` update mutations if the [#generatemutations](../schema/directives.md#generatemutations "mention")is set. The following code will be added to the GraphQL schema. - -```graphql - -mutation { - # updatePosts allows to update posts with a filter - updatePosts(input: UpdatePostInput!, filter: PostFilterInput): PostsPayload - # ... more mutations -} - -""" -Autogenerated Posts Filter Input -""" -input PostFilterInput { - id: IntComparator - name: StringComparator - categories: CategoryFilterInput - user: UserFilterInput - """ - Logical AND of FilterInput - """ - AND: [PostFilterInput] - """ - Logical OR of FilterInput - """ - OR: [PostFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: PostFilterInput -} - -""" -AutoGenerated update input for Post -""" -input UpdatePostInput { - id: Int - name: String -} - -""" -Autogenerated payload object -""" -type PostsPayload { - """ - rows affection by mutation - """ - rows_affected: Int! - posts: [Post] -} -``` - -The [#generatemutations](../schema/directives.md#generatemutations "mention") directive adds the above into our schema. - -* `inputs` argument passed to `createPosts` mutation is required and you can pass 1 or more objects. -* `PostsPayload` is the response object returned from the mutation, returning the amounts of rows\_affected i.e. the amount of objects inserted and access to all our posts info - -## Update Objects - -**Example:** update posts and return the updated posts object in the response: - - - -```graphql -mutation { - updatePosts(input: {name: "newPost"}, filter: {id: {eq: 1}}) { - rows_affected - posts { - name - id - } - } -} -``` - - +--- +title: Update +description: Update mutations +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +FastGQL auto generates `update` update mutations if the [#generatemutations](../schema/directives.md#generatemutations "mention")is set. The following code will be added to the GraphQL schema. + +```graphql + +mutation { + # updatePosts allows to update posts with a filter + updatePosts(input: UpdatePostInput!, filter: PostFilterInput): PostsPayload + # ... more mutations +} + +""" +Autogenerated Posts Filter Input +""" +input PostFilterInput { + id: IntComparator + name: StringComparator + categories: CategoryFilterInput + user: UserFilterInput + """ + Logical AND of FilterInput + """ + AND: [PostFilterInput] + """ + Logical OR of FilterInput + """ + OR: [PostFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: PostFilterInput +} + +""" +AutoGenerated update input for Post +""" +input UpdatePostInput { + id: Int + name: String +} + +""" +Autogenerated payload object +""" +type PostsPayload { + """ + rows affection by mutation + """ + rows_affected: Int! + posts: [Post] +} +``` + +The [#generatemutations](../schema/directives.md#generatemutations "mention") directive adds the above into our schema. + +* `inputs` argument passed to `createPosts` mutation is required and you can pass 1 or more objects. +* `PostsPayload` is the response object returned from the mutation, returning the amounts of rows\_affected i.e. the amount of objects inserted and access to all our posts info + +## Update Objects + +**Example:** update posts and return the updated posts object in the response: + + + +```graphql +mutation { + updatePosts(input: {name: "newPost"}, filter: {id: {eq: 1}}) { + rows_affected + posts { + name + id + } + } +} +``` + + diff --git a/docs/src/content/docs/queries/aggregation.md b/docs/src/content/docs/queries/aggregation.md index fd20933..1531e9f 100644 --- a/docs/src/content/docs/queries/aggregation.md +++ b/docs/src/content/docs/queries/aggregation.md @@ -1,28 +1,28 @@ ---- -title: Aggregation -description: Execute aggregation queries ---- - -FastGQL auto extends the schema using the `@generate` directive to add aggregation queries to the extended types. Aggregation results can be accessed using the `_[fieldName]Aggregate` field in the GraphQL query. - -## Count Aggregate - -```graphql -query { - _postsAggregate { - count - } -} -``` - -## Aggregate Filter - -Similar to [filter ](filtering.mdx)queries we can filter our aggregate queries to returned different results. The following example will count all posts that have a category with the `id == 1` . - -```graphql -query { - _postsAggregate(filter: {categories: {id:{eq: 1}}}) { - count - } -} -``` +--- +title: Aggregation +description: Execute aggregation queries +--- + +FastGQL auto extends the schema using the `@generate` directive to add aggregation queries to the extended types. Aggregation results can be accessed using the `_[fieldName]Aggregate` field in the GraphQL query. + +## Count Aggregate + +```graphql +query { + _postsAggregate { + count + } +} +``` + +## Aggregate Filter + +Similar to [filter ](filtering.mdx)queries we can filter our aggregate queries to returned different results. The following example will count all posts that have a category with the `id == 1` . + +```graphql +query { + _postsAggregate(filter: {categories: {id:{eq: 1}}}) { + count + } +} +``` diff --git a/docs/src/content/docs/queries/filtering.mdx b/docs/src/content/docs/queries/filtering.mdx index 8fa896a..9fbb6fb 100644 --- a/docs/src/content/docs/queries/filtering.mdx +++ b/docs/src/content/docs/queries/filtering.mdx @@ -1,137 +1,137 @@ ---- -title: Filtering -description: Filter queries with FastGQL ---- - -import { Tabs, TabItem } from '@astrojs/starlight/components'; - -# Filtering - -FastGQL extends the GraphQL schema adding `filter` input for objects and adding `filter` arguments to the queries and mutations. We can use the `filter` argument in our queries to filter results based on the field's values. FastGQL allows to add multiple filters with `AND`, `OR`, `NOT` and even nested object filters. - -## Simple Operators - -fetch posts with where `id == 3` - -```graphql -query { - posts(limit: 3, filter: { id: { eq: 3 } }) { - id - } -} -``` - -## Logical filters - -#### AND filter - -By default, a logical `AND` operation is preformed on all the conditions mentioned in the filter clause. If we want to use the same field key, than we need to use the `AND` operator in the GraphQL query. - - - - ```graphql - query { - # expected filter: name = "article 1" AND id = 1 - posts(filter: {name: {eq: "article 1"}, id: {eq: 1}}) { - id - name - } - } - ``` - - - - - - ```graphql - query { - posts( - # expected filter: name = "article 1" AND name like "%article%" AND id = 1 - filter: { - AND: [ - {name: {eq: "article 1"}} - {name: {like: "%article%"}} - {id: {eq: 1}} - ] - } - ) { - id - name - } - } - ``` - - - -#### OR filter - -To perform a logical `OR` operation, we have to use the `OR` operator in the GraphQL query. - -If we would like to fetch information of all post's name that start with the prefix `a` or `b` the following query will return the desired result: - -```graphql -query { - posts( - filter: { OR: [{ name: { prefix: "a" } }, { name: { suffix: "b" } }] } - ) { - id - name - } -} -``` - -#### NOT filter - -To perform a logical `NOT`operation, we have to use the `NOT`operator in the GraphQL query. - -if we would like to fetch information of all post's name that **don't** start with "a" or ends with "b". the following query will return the desired result: - -```graphql -query { - posts( - filter: { - NOT: { OR: [{ name: { prefix: "a" } }, { name: { suffix: "b" } }] } - } - ) { - id - name - } -} -``` - -#### Nested filters - -We can also perform complex nested logical operation, such as `OR` inside an `AND` operator. The following query will result the following filter: - -```sql -(name like 'b%' OR name like 'a%') AND name like '%c%' -``` - -```graphql -query { - posts( - filter: { - AND: [ - { OR: [{ name: { prefix: "a" } }, { name: { suffix: "b" } }] } - { name: { like: "%c%" } } - ] - } - ) { - id - name - } -} -``` - -## Object Filters - -Most objects in our schema usually have fields that aren't `scalars`, and we would like to filter by thier fields. In our example each post has one or more categories we would want to filter posts by some category name. The following GraphQL query will filter for us those posts that have a category name called "Security" - -```graphql -query ObjectFilterExample { - posts(filter: {categories: {name: {eq: "Security"}}}) { - id - name - } -} -``` +--- +title: Filtering +description: Filter queries with FastGQL +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Filtering + +FastGQL extends the GraphQL schema adding `filter` input for objects and adding `filter` arguments to the queries and mutations. We can use the `filter` argument in our queries to filter results based on the field's values. FastGQL allows to add multiple filters with `AND`, `OR`, `NOT` and even nested object filters. + +## Simple Operators + +fetch posts with where `id == 3` + +```graphql +query { + posts(limit: 3, filter: { id: { eq: 3 } }) { + id + } +} +``` + +## Logical filters + +#### AND filter + +By default, a logical `AND` operation is preformed on all the conditions mentioned in the filter clause. If we want to use the same field key, than we need to use the `AND` operator in the GraphQL query. + + + + ```graphql + query { + # expected filter: name = "article 1" AND id = 1 + posts(filter: {name: {eq: "article 1"}, id: {eq: 1}}) { + id + name + } + } + ``` + + + + + + ```graphql + query { + posts( + # expected filter: name = "article 1" AND name like "%article%" AND id = 1 + filter: { + AND: [ + {name: {eq: "article 1"}} + {name: {like: "%article%"}} + {id: {eq: 1}} + ] + } + ) { + id + name + } + } + ``` + + + +#### OR filter + +To perform a logical `OR` operation, we have to use the `OR` operator in the GraphQL query. + +If we would like to fetch information of all post's name that start with the prefix `a` or `b` the following query will return the desired result: + +```graphql +query { + posts( + filter: { OR: [{ name: { prefix: "a" } }, { name: { suffix: "b" } }] } + ) { + id + name + } +} +``` + +#### NOT filter + +To perform a logical `NOT`operation, we have to use the `NOT`operator in the GraphQL query. + +if we would like to fetch information of all post's name that **don't** start with "a" or ends with "b". the following query will return the desired result: + +```graphql +query { + posts( + filter: { + NOT: { OR: [{ name: { prefix: "a" } }, { name: { suffix: "b" } }] } + } + ) { + id + name + } +} +``` + +#### Nested filters + +We can also perform complex nested logical operation, such as `OR` inside an `AND` operator. The following query will result the following filter: + +```sql +(name like 'b%' OR name like 'a%') AND name like '%c%' +``` + +```graphql +query { + posts( + filter: { + AND: [ + { OR: [{ name: { prefix: "a" } }, { name: { suffix: "b" } }] } + { name: { like: "%c%" } } + ] + } + ) { + id + name + } +} +``` + +## Object Filters + +Most objects in our schema usually have fields that aren't `scalars`, and we would like to filter by thier fields. In our example each post has one or more categories we would want to filter posts by some category name. The following GraphQL query will filter for us those posts that have a category name called "Security" + +```graphql +query ObjectFilterExample { + posts(filter: {categories: {name: {eq: "Security"}}}) { + id + name + } +} +``` diff --git a/docs/src/content/docs/queries/ordering.md b/docs/src/content/docs/queries/ordering.md index 1f7e3d7..9b7162e 100644 --- a/docs/src/content/docs/queries/ordering.md +++ b/docs/src/content/docs/queries/ordering.md @@ -1,43 +1,43 @@ ---- -title: Ordering -description: Sort query results ---- - -We can sort query results using the `orderBy` argument that is added when extending the schema with fastGQL. The value of the sort argument should be an array containing the name of fields to sort the result by. - -{% hint style="info" %} -Ordering is ALPHA so it API might change in future versions -{% endhint %} - -## Simple Sort - -```graphql -query { - posts(orderBy: {name: ASC}) { - name - } -} -``` - -## Multiple fields - -```graphql -query { - posts(orderBy: [{name: ASC}, {id: DESC}]) { - name - } -} -``` - -## Sorting Nested Queries - -```graphql -query { - posts(orderBy: {name: DESC}) { - name - categories(orderBy: {name: ASC_NULL_FIRST}) { - name - } - } -} -``` +--- +title: Ordering +description: Sort query results +--- + +We can sort query results using the `orderBy` argument that is added when extending the schema with fastGQL. The value of the sort argument should be an array containing the name of fields to sort the result by. + +{% hint style="info" %} +Ordering is ALPHA so it API might change in future versions +{% endhint %} + +## Simple Sort + +```graphql +query { + posts(orderBy: {name: ASC}) { + name + } +} +``` + +## Multiple fields + +```graphql +query { + posts(orderBy: [{name: ASC}, {id: DESC}]) { + name + } +} +``` + +## Sorting Nested Queries + +```graphql +query { + posts(orderBy: {name: DESC}) { + name + categories(orderBy: {name: ASC_NULL_FIRST}) { + name + } + } +} +``` diff --git a/docs/src/content/docs/queries/pagination.md b/docs/src/content/docs/queries/pagination.md index a85c362..6113205 100644 --- a/docs/src/content/docs/queries/pagination.md +++ b/docs/src/content/docs/queries/pagination.md @@ -1,50 +1,50 @@ ---- -title: Pagination -description: Paginate your data with ease ---- - -The operators `limit` and `offset` are used for pagination of objects in our queries. `limit` specifies the number of rows to return from the result set, and `offset` determines the number of objects skip. - -The following are examples of different pagination queries: - -### Limit Results - -```graphql -query { - posts(limit: 5) { - name - id - } -} -``` - -Fetch the first 5 posts from a list of posts. - -### Offset and Limit Results - -```graphql -query { - posts(limit: 5, offset: 3) { - name - id - } -} -``` - -Fetch the first 5 posts from a list of posts, skip first 3. - -### Offset and Limit Results in nested queries - -```graphql -query { - posts(limit: 5, offset: 3) { - name - id - categories(limit: 1) { - name - } - } -} -``` - -Fetch the first 5 posts from list of posts, skip first 3, and fetch only the first category of each post. +--- +title: Pagination +description: Paginate your data with ease +--- + +The operators `limit` and `offset` are used for pagination of objects in our queries. `limit` specifies the number of rows to return from the result set, and `offset` determines the number of objects skip. + +The following are examples of different pagination queries: + +### Limit Results + +```graphql +query { + posts(limit: 5) { + name + id + } +} +``` + +Fetch the first 5 posts from a list of posts. + +### Offset and Limit Results + +```graphql +query { + posts(limit: 5, offset: 3) { + name + id + } +} +``` + +Fetch the first 5 posts from a list of posts, skip first 3. + +### Offset and Limit Results in nested queries + +```graphql +query { + posts(limit: 5, offset: 3) { + name + id + categories(limit: 1) { + name + } + } +} +``` + +Fetch the first 5 posts from list of posts, skip first 3, and fetch only the first category of each post. diff --git a/docs/src/content/docs/queries/queries.md b/docs/src/content/docs/queries/queries.md index 034ddef..7c8695c 100644 --- a/docs/src/content/docs/queries/queries.md +++ b/docs/src/content/docs/queries/queries.md @@ -1,60 +1,60 @@ ---- -title: Queries -description: Executing queries on a generated FastGQL Server ---- - -## Introduction - -FastGQL allows to easily execute queries against a remote data source, i.e. postgres and convert the GraphQL AST into a valid SQL query and return queried data. - -FastGQL auto-generates query filters, aggregation, pagination and ordering from your schema definition using the [#generate](../schema/directives.md#generate "mention") and [#generatefilterinput](../schema/directives.md#generatefilterinput "mention") directives. - -## Queries - -### Fetch list of objects - -```graphql -query { - users { - name - } -} -``` - -Fetch all available users. - -### Fetch nested objects - -```graphql -query { - users { - name - posts { - name - } - } -} -``` - -**fetch all users and their posts.** - -### Fetch nested object recursively - -```graphql -query { - users { - name - posts { - name - categories { - name - } - user { - name - } - } - } -} -``` - -fetch all users and thier posts, for each post we fetch it's categories and the user who posted it. +--- +title: Queries +description: Executing queries on a generated FastGQL Server +--- + +## Introduction + +FastGQL allows to easily execute queries against a remote data source, i.e. postgres and convert the GraphQL AST into a valid SQL query and return queried data. + +FastGQL auto-generates query filters, aggregation, pagination and ordering from your schema definition using the [#generate](../schema/directives.md#generate "mention") and [#generatefilterinput](../schema/directives.md#generatefilterinput "mention") directives. + +## Queries + +### Fetch list of objects + +```graphql +query { + users { + name + } +} +``` + +Fetch all available users. + +### Fetch nested objects + +```graphql +query { + users { + name + posts { + name + } + } +} +``` + +**fetch all users and their posts.** + +### Fetch nested object recursively + +```graphql +query { + users { + name + posts { + name + categories { + name + } + user { + name + } + } + } +} +``` + +fetch all users and thier posts, for each post we fetch it's categories and the user who posted it. diff --git a/docs/src/content/docs/reference/example.md b/docs/src/content/docs/reference/example.md index 0224f09..1a6303d 100644 --- a/docs/src/content/docs/reference/example.md +++ b/docs/src/content/docs/reference/example.md @@ -1,11 +1,11 @@ ---- -title: Example Reference -description: A reference page in my new Starlight docs site. ---- - -Reference pages are ideal for outlining how things work in terse and clear terms. -Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what you're documenting. - -## Further reading - -- Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework +--- +title: Example Reference +description: A reference page in my new Starlight docs site. +--- + +Reference pages are ideal for outlining how things work in terse and clear terms. +Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what you're documenting. + +## Further reading + +- Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework diff --git a/docs/src/content/docs/schema/directives.md b/docs/src/content/docs/schema/directives.md index 66c14f7..95f55d5 100644 --- a/docs/src/content/docs/schema/directives.md +++ b/docs/src/content/docs/schema/directives.md @@ -1,147 +1,147 @@ ---- -title: Directives -description: fastGQL supported directives ---- - -In this section we will go over all of the custom _fastGQL_ directives, what they do and how to use them. These directives are added automatically when we augment and generate the server code. You can also use the following command `go run github.com/roneli/fastgql init` or copy the [fastgql\_schema\_fragment.md](fastgql\_schema\_fragment.md "mention"). - -## Augmentation directives - -These directives extend the original schema functionality adding filters, pagination, aggregation, ordering etc' and are commonly used to tell _fastgql_ what parts of the schema we want to augment. - -We currently have four augmentation directives: - -* [#generatefilterinput](directives.md#generatefilterinput "mention") -* [#generate](directives.md#generate "mention") -* [#skipgenerate](directives.md#skipgenerate "mention") -* [#generatemutations](directives.md#generatemutations "mention") - - - -### @generateFilterInput - -The `@generateFilterInput` tells the augmenter on which `OBJECT | INTERFACE` to generate filter inputs, this is required so _fastGQL_ knows what objects to build filters for giving you more control over which filters are created. - -```graphql -# Generate filter input on an object -directive @generateFilterInput(name: String!, description: String) - on OBJECT | INTERFACE -``` - -**Example**: - -In this example, we can are creating a filter input for the category type, when arguments will be generated for categories in `Query` the CategoryFilterInput will be used for the `filter` argument. - -```graphql -type Category @generateFilterInput(name: "CategoryFilterInput"){ - id: Int! - name: String -} - -type Query @generate { - categories: [Category] -} -``` - -This also works for objects that contain fields of objects, see [Object filters](../../../queries/filter/#object-filters) - -### @generate - -The `@generate` tells the augmenter on which `OBJECT` to generate arguments i.e. filter, pagination and ordering, aggregation. By default, all arguments are created, and are recursive, this means that arguments are generated from the top level `OBJECT` until exhausted meaning no new OBJECT fields are found in a certain level. - -```graphql -# Generate arguments for a given field or all object fields -directive @generate(filter: Boolean = True, - pagination: Boolean = True, ordering: Boolean = True, - aggregate: Boolean = True, recursive: Boolean = True) on OBJECT -``` - -**Example**: The following example adds arguments to all fields in Query and does the same for each field type. - -```graphql -type Query @generate { - posts: [Post] - users: [User] - categories: [Category] @skipGenerate -} -``` - -### @skipGenerate - -The `@skipGenerate` tells the codegen whether we want to skip creating the resolver code or not. - -```graphql -# Tells fastgql to skip resolver generation on field -directive @skipGenerate(resolver:Boolean = True) on FIELD_DEFINITION -``` - -**Example:** - -```graphql - -# Generate augmentation on posts and users, but skip categories -type Query @generate { - posts: [Post] - users: [User] - categories: [Category] @skipGenerate -} -``` - -### @generateMutations - -The `@generateMutations` tells the augmenter on which `OBJECT` to generate mutations on. -There are 3 possible mutations, create, update and delete, by default all of them are set to true. - -```graphql -# Generate filter input on an object -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT -``` - -## Builder directives - -Builder directives are used by builders to build queries based on the given GraphQL query requested. - -We have two builder directives: - -* [#table](directives.md#table "mention") -* [#relation](directives.md#relation "mention") - -### @table - -The `@table` directive is used to define the name of the table of the graphql `OBJECT` in the database. - -By default, the table name is the name of type, we use this directive when the table name is different from the object typename. - -If our table resides in a schema that isn't the one set as the default we need to add the schema argument. - -```graphql -# Used if Object/Interface type name is different then the actual table name or if the table resides in a schema other than default path. -directive @table(name: String!, dialect: String, schema: String) on OBJECT | INTERFACE -``` - -**Example:** - -```graphql - -type User @table(name: "users", dialect: "postgres", schema: "app"){ - id: Int - name: String -} -``` - -### @relation - -The `@relation` directive tells the builder about relations in your database. - -```graphql -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", - manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION -``` - -#### Table Relationships - -There are three major types of database relationships: - -* `ONE_TO_ONE` -* `ONE_TO_MANY` -* `MANY_TO_MANY` +--- +title: Directives +description: fastGQL supported directives +--- + +In this section we will go over all of the custom _fastGQL_ directives, what they do and how to use them. These directives are added automatically when we augment and generate the server code. You can also use the following command `go run github.com/roneli/fastgql init` or copy the [fastgql\_schema\_fragment.md](fastgql\_schema\_fragment.md "mention"). + +## Augmentation directives + +These directives extend the original schema functionality adding filters, pagination, aggregation, ordering etc' and are commonly used to tell _fastgql_ what parts of the schema we want to augment. + +We currently have four augmentation directives: + +* [#generatefilterinput](directives.md#generatefilterinput "mention") +* [#generate](directives.md#generate "mention") +* [#skipgenerate](directives.md#skipgenerate "mention") +* [#generatemutations](directives.md#generatemutations "mention") + + + +### @generateFilterInput + +The `@generateFilterInput` tells the augmenter on which `OBJECT | INTERFACE` to generate filter inputs, this is required so _fastGQL_ knows what objects to build filters for giving you more control over which filters are created. + +```graphql +# Generate filter input on an object +directive @generateFilterInput(name: String!, description: String) + on OBJECT | INTERFACE +``` + +**Example**: + +In this example, we can are creating a filter input for the category type, when arguments will be generated for categories in `Query` the CategoryFilterInput will be used for the `filter` argument. + +```graphql +type Category @generateFilterInput(name: "CategoryFilterInput"){ + id: Int! + name: String +} + +type Query @generate { + categories: [Category] +} +``` + +This also works for objects that contain fields of objects, see [Object filters](../../../queries/filter/#object-filters) + +### @generate + +The `@generate` tells the augmenter on which `OBJECT` to generate arguments i.e. filter, pagination and ordering, aggregation. By default, all arguments are created, and are recursive, this means that arguments are generated from the top level `OBJECT` until exhausted meaning no new OBJECT fields are found in a certain level. + +```graphql +# Generate arguments for a given field or all object fields +directive @generate(filter: Boolean = True, + pagination: Boolean = True, ordering: Boolean = True, + aggregate: Boolean = True, recursive: Boolean = True) on OBJECT +``` + +**Example**: The following example adds arguments to all fields in Query and does the same for each field type. + +```graphql +type Query @generate { + posts: [Post] + users: [User] + categories: [Category] @skipGenerate +} +``` + +### @skipGenerate + +The `@skipGenerate` tells the codegen whether we want to skip creating the resolver code or not. + +```graphql +# Tells fastgql to skip resolver generation on field +directive @skipGenerate(resolver:Boolean = True) on FIELD_DEFINITION +``` + +**Example:** + +```graphql + +# Generate augmentation on posts and users, but skip categories +type Query @generate { + posts: [Post] + users: [User] + categories: [Category] @skipGenerate +} +``` + +### @generateMutations + +The `@generateMutations` tells the augmenter on which `OBJECT` to generate mutations on. +There are 3 possible mutations, create, update and delete, by default all of them are set to true. + +```graphql +# Generate filter input on an object +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT +``` + +## Builder directives + +Builder directives are used by builders to build queries based on the given GraphQL query requested. + +We have two builder directives: + +* [#table](directives.md#table "mention") +* [#relation](directives.md#relation "mention") + +### @table + +The `@table` directive is used to define the name of the table of the graphql `OBJECT` in the database. + +By default, the table name is the name of type, we use this directive when the table name is different from the object typename. + +If our table resides in a schema that isn't the one set as the default we need to add the schema argument. + +```graphql +# Used if Object/Interface type name is different then the actual table name or if the table resides in a schema other than default path. +directive @table(name: String!, dialect: String, schema: String) on OBJECT | INTERFACE +``` + +**Example:** + +```graphql + +type User @table(name: "users", dialect: "postgres", schema: "app"){ + id: Int + name: String +} +``` + +### @relation + +The `@relation` directive tells the builder about relations in your database. + +```graphql +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", + manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION +``` + +#### Table Relationships + +There are three major types of database relationships: + +* `ONE_TO_ONE` +* `ONE_TO_MANY` +* `MANY_TO_MANY` diff --git a/docs/src/content/docs/schema/fastgql_schema_fragment.md b/docs/src/content/docs/schema/fastgql_schema_fragment.md index c55f10a..fcdc8f8 100644 --- a/docs/src/content/docs/schema/fastgql_schema_fragment.md +++ b/docs/src/content/docs/schema/fastgql_schema_fragment.md @@ -1,127 +1,127 @@ ---- -title: FastGQL Schema Fragment -description: fastGQL schema fragment ---- -# FastGQL Schema Fragment - -While editing your schema, you might find it useful to include this GraphQL schema -fragment. It sets up the definitions of the directives, etc. -(like @generate) that you’ll use in your schema. -If your editor is GraphQL aware, it may give you errors if you don’t have -this available. - -```graphql - -# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed -# i.e , "postgres", "" -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE - -# Relation directive defines relations cross tables and dialects -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION - -# used to skip generate on a certain field, this should be used if we use recursive = True in @generate -directive @skipGenerate(resolver: Boolean = True) on FIELD_DEFINITION - -# Generate filter input on an object -directive @generateFilterInput(name: String!, description: String) repeatable on OBJECT | INTERFACE - -# Generate arguments for a given field or all object fields -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, - recursive: Boolean = True, wrapper:Boolean = False) repeatable on OBJECT - -# Generate mutations object -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT - -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} - -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST -} - -type _AggregateResult { - count: Int! -} - -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} - -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} - -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} - -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} - -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} - -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} - - -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} - -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean -} - +--- +title: FastGQL Schema Fragment +description: fastGQL schema fragment +--- +# FastGQL Schema Fragment + +While editing your schema, you might find it useful to include this GraphQL schema +fragment. It sets up the definitions of the directives, etc. +(like @generate) that you’ll use in your schema. +If your editor is GraphQL aware, it may give you errors if you don’t have +this available. + +```graphql + +# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed +# i.e , "postgres", "" +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE + +# Relation directive defines relations cross tables and dialects +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION + +# used to skip generate on a certain field, this should be used if we use recursive = True in @generate +directive @skipGenerate(resolver: Boolean = True) on FIELD_DEFINITION + +# Generate filter input on an object +directive @generateFilterInput(name: String!, description: String) repeatable on OBJECT | INTERFACE + +# Generate arguments for a given field or all object fields +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, + recursive: Boolean = True, wrapper:Boolean = False) repeatable on OBJECT + +# Generate mutations object +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT + +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} + +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST +} + +type _AggregateResult { + count: Int! +} + +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} + +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} + +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} + +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} + +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} + +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} + + +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} + +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean +} + ``` \ No newline at end of file diff --git a/docs/src/content/docs/start/intro.mdx b/docs/src/content/docs/start/intro.mdx index 20d2533..b0ce688 100644 --- a/docs/src/content/docs/start/intro.mdx +++ b/docs/src/content/docs/start/intro.mdx @@ -1,34 +1,34 @@ ---- -title: Intro -description: fastGQL is a Go library that extends gqlgen to create a blazing-fast GraphQL server that gives you instant, realtime GraphQL APIs over Postgres. ---- -## What is fastGQL? - -[_fastGQL_](https://github.com/roneli/fastgql) is a Go library that extends [gqlgen](https://github.com/99designs/gqlgen) to create a blazing-fast GraphQL server that gives you instant, realtime GraphQL APIs over Postgres. - -* **fastgql is based on a Schema first approach** — You get to Define your API using the GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). -* **fastgql prioritizes extendability** — You can modify resolvers, add your own custom operators and even create your own database query builder. -* **fastgql enables codegen** — We generate even more of the boring CRUD bits, so you can focus on building your app even faster! - -## Getting Started - -* To install fastgql run the command `go get github.com/roneli/fastgql` in your project directory.\ - -* You could initialize a new project using the recommended folder structure by running this command `go run github.com/roneli/fastgql init`. - -You could find a more comprehensive guide on [gqlgen](https://github.com/99designs/gqlgen) to help you get started [here](https://gqlgen.com/getting-started/). We also have a couple of [examples](https://github.com/roneli/fastgql/tree/master/example) that show how fastgql generates the full API seamlessly. - -## Reporting Issues - -If you think you've found a bug, or something isn't behaving the way you think it should, please raise an [issue](https://github.com/roneli/fastgql/issues) on GitHub. - -## Contributing - -Feel free to open Pull-Request for small fixes and changes. For bigger changes and new builders please open an [issue](https://github.com/roneli/fastgql/issues) first to prevent double work and discuss relevant stuff. - -## Roadmap 🚧 - -* More tests -* configurable database connections -* Support multiple database (mongodb, cockroachDB, neo4j) -* full CRUD creation +--- +title: Intro +description: fastGQL is a Go library that extends gqlgen to create a blazing-fast GraphQL server that gives you instant, realtime GraphQL APIs over Postgres. +--- +## What is fastGQL? + +[_fastGQL_](https://github.com/roneli/fastgql) is a Go library that extends [gqlgen](https://github.com/99designs/gqlgen) to create a blazing-fast GraphQL server that gives you instant, realtime GraphQL APIs over Postgres. + +* **fastgql is based on a Schema first approach** — You get to Define your API using the GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). +* **fastgql prioritizes extendability** — You can modify resolvers, add your own custom operators and even create your own database query builder. +* **fastgql enables codegen** — We generate even more of the boring CRUD bits, so you can focus on building your app even faster! + +## Getting Started + +* To install fastgql run the command `go get github.com/roneli/fastgql` in your project directory.\ + +* You could initialize a new project using the recommended folder structure by running this command `go run github.com/roneli/fastgql init`. + +You could find a more comprehensive guide on [gqlgen](https://github.com/99designs/gqlgen) to help you get started [here](https://gqlgen.com/getting-started/). We also have a couple of [examples](https://github.com/roneli/fastgql/tree/master/example) that show how fastgql generates the full API seamlessly. + +## Reporting Issues + +If you think you've found a bug, or something isn't behaving the way you think it should, please raise an [issue](https://github.com/roneli/fastgql/issues) on GitHub. + +## Contributing + +Feel free to open Pull-Request for small fixes and changes. For bigger changes and new builders please open an [issue](https://github.com/roneli/fastgql/issues) first to prevent double work and discuss relevant stuff. + +## Roadmap 🚧 + +* More tests +* configurable database connections +* Support multiple database (mongodb, cockroachDB, neo4j) +* full CRUD creation diff --git a/docs/src/content/docs/start/setup.md b/docs/src/content/docs/start/setup.md index b828087..ff2ac61 100644 --- a/docs/src/content/docs/start/setup.md +++ b/docs/src/content/docs/start/setup.md @@ -1,161 +1,161 @@ ---- -title: Setup FastGQL -description: How to setup fastGQL ---- - -This tutorial will take you through the process of building a GraphQL server with fastgql that can: - -* automatically query, filter, order and pagination users, posts & categories from a postgres database. - -You can find the finished code for this tutorial [here](https://github.com/roneli/fastgql/tree/master/example). - -If you are familiar with [gqlgen](https://gqlgen.com), the setup is nearly identical, with a little work in your Schema you won't need to define any resolvers! - -## Setup Project - -Create a directory for your project, and initialise it as a Go Module: - -```bash -$ mkdir fastgql-example -$ cd fastgql-example -$ go mod init github.com/[username]/fastgql-example -$ go get github.com/roneli/fastgql -``` - -#### Add github.com/roneli/fastgql to your project’s tools.go - -```bash -printf '// +build tools\npackage tools\nimport _ "github.com/roneli/fastgql"' | gofmt > tools.go -go mod tidy -``` - -## Building the server - -### Create the project skeleton - -```bash -$ go run github.com/roneli/fastgql init -$ go mod tidy -``` - -This will create our suggested package layout. You can modify these paths in gqlgen.yml if you need to. - -``` -├── go.mod -├── go.sum -├── gqlgen.yml - The gqlgen config file, knobs for controlling the generated code. -├── graph -│ ├── generated - A package that only contains the generated runtime -│ │ └── generated.go -│ ├── model - A package for all your graph models, generated or otherwise -│ │ └── models_gen.go -│ ├── resolver.go - The root graph resolver type. This file wont get regenerated -| ├── fastgql.graphql - fastgql schema fragemnt, adding all directives, inputs etc' required for schema augment -│ ├── schema.graphql - Some schema. You can split the schema into as many graphql files as you like -│ └── schema.fastgql.go - the resolver implementation for schema.graphql -└── server.go - The entry point to your app. Customize it however you see fit -``` - -### Define your schema - -gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). By default this goes into a file called `schema.graphql` but you can break it up into as many different files as you want. - -The schema that was generated for us was: - -{% code overflow="wrap" %} -```graphql -type User @generateFilterInput(name: "UserFilterInput") @tableName(name: "user"){ - id: Int! - name: String! - posts: [Post] @relation(relationType: ONE_TO_MANY, baseTable: "user", refTable: "posts", fields: ["id"], references: ["user_id"]) -} - -type Post @generateFilterInput(name: "PostFilterInput") { - id: Int! - name: String - categories: [Category] @relation(relationType: MANY_TO_MANY, baseTable: "posts", refTable: "categories", fields: ["id"], references: ["id"] - manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) - user: User @relation(relationType: ONE_TO_ONE, baseTable: "posts", refTable: "user", fields: ["user_id"], references: ["id"]) -} - - -type Category @generateFilterInput(name: "CategoryFilterInput"){ - id: Int! - name: String -} - -type Query @generate { - posts: [Post] - users: [User] - categories: [Category] @skipGenerate -} -``` -{% endcode %} - -### Implement the resolvers - -`fastgql generate` compares the schema file (`schema.graphql`) with the models `graph/model/*` and wherever it can it will bind directly to the model. It generates resolvers just like gqlgen, but also implements some resolvers to directly work with the database. - -If we take a look in `graph/schema.fastgql.go` you will see all the resolvers that fastgql autogenerated for example: - -{% code overflow="wrap" %} -```go -func (r *queryResolver) Posts(ctx context.Context, limit *int, offset *int, orderBy *model.PostOrdering, filter *model.PostFilterInput) ([]*model.Post, error) { - var data []*model.Post - if err := r.Executor.Scan(ctx, "postgres", &data); err != nil { - return nil, err - } - return data, nil -} -``` -{% endcode %} - -We just need to start a postgres server and insert a schema, you can try the example's [compose file](https://github.com/roneli/fastgql/tree/master/example/docker-compose.yml) and execute the [schema.sql](https://github.com/roneli/fastgql/blob/master/example/graph/schema.graphql): - -Finally, we just need to define our postgres connection str that defined in server.go. We can override with `PG_CONN_STR` env variable. - -If we used the example's docker compose we can use this DSN: `PG_CONN_STR=postgresql://localhost/postgres?user=postgres&password=password` - -We now have a working server, to start it: - -```bash -go run server.go -``` - -then open http://localhost:8080 in a browser. here are some queries to try: - -```graphql -query { - posts(limit: 2) { - name - categories { - name - } - } -} -query filterPostsByUser { - posts(limit: 10, filter: {user:{name: {eq: "fastgql"}}}, orderBy: {name: ASC}) { - name - categories { - name - } - user { - name - } - } -} -``` - -## Finishing touches - -At the top of our `server.go`, between `package` and `import`, add the following line: - -```go -//go:generate go run github.com/roneli/fastgql generate -c gqlgen.yml -``` - -This magic comment tells `go generate` what command to run when we want to regenerate our code. To run go generate recursively over your entire project, use this command: - -```go -go generate ./... -``` +--- +title: Setup FastGQL +description: How to setup fastGQL +--- + +This tutorial will take you through the process of building a GraphQL server with fastgql that can: + +* automatically query, filter, order and pagination users, posts & categories from a postgres database. + +You can find the finished code for this tutorial [here](https://github.com/roneli/fastgql/tree/master/example). + +If you are familiar with [gqlgen](https://gqlgen.com), the setup is nearly identical, with a little work in your Schema you won't need to define any resolvers! + +## Setup Project + +Create a directory for your project, and initialise it as a Go Module: + +```bash +$ mkdir fastgql-example +$ cd fastgql-example +$ go mod init github.com/[username]/fastgql-example +$ go get github.com/roneli/fastgql +``` + +#### Add github.com/roneli/fastgql to your project’s tools.go + +```bash +printf '// +build tools\npackage tools\nimport _ "github.com/roneli/fastgql"' | gofmt > tools.go +go mod tidy +``` + +## Building the server + +### Create the project skeleton + +```bash +$ go run github.com/roneli/fastgql init +$ go mod tidy +``` + +This will create our suggested package layout. You can modify these paths in gqlgen.yml if you need to. + +``` +├── go.mod +├── go.sum +├── gqlgen.yml - The gqlgen config file, knobs for controlling the generated code. +├── graph +│ ├── generated - A package that only contains the generated runtime +│ │ └── generated.go +│ ├── model - A package for all your graph models, generated or otherwise +│ │ └── models_gen.go +│ ├── resolver.go - The root graph resolver type. This file wont get regenerated +| ├── fastgql.graphql - fastgql schema fragemnt, adding all directives, inputs etc' required for schema augment +│ ├── schema.graphql - Some schema. You can split the schema into as many graphql files as you like +│ └── schema.fastgql.go - the resolver implementation for schema.graphql +└── server.go - The entry point to your app. Customize it however you see fit +``` + +### Define your schema + +gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). By default this goes into a file called `schema.graphql` but you can break it up into as many different files as you want. + +The schema that was generated for us was: + +{% code overflow="wrap" %} +```graphql +type User @generateFilterInput(name: "UserFilterInput") @tableName(name: "user"){ + id: Int! + name: String! + posts: [Post] @relation(relationType: ONE_TO_MANY, baseTable: "user", refTable: "posts", fields: ["id"], references: ["user_id"]) +} + +type Post @generateFilterInput(name: "PostFilterInput") { + id: Int! + name: String + categories: [Category] @relation(relationType: MANY_TO_MANY, baseTable: "posts", refTable: "categories", fields: ["id"], references: ["id"] + manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) + user: User @relation(relationType: ONE_TO_ONE, baseTable: "posts", refTable: "user", fields: ["user_id"], references: ["id"]) +} + + +type Category @generateFilterInput(name: "CategoryFilterInput"){ + id: Int! + name: String +} + +type Query @generate { + posts: [Post] + users: [User] + categories: [Category] @skipGenerate +} +``` +{% endcode %} + +### Implement the resolvers + +`fastgql generate` compares the schema file (`schema.graphql`) with the models `graph/model/*` and wherever it can it will bind directly to the model. It generates resolvers just like gqlgen, but also implements some resolvers to directly work with the database. + +If we take a look in `graph/schema.fastgql.go` you will see all the resolvers that fastgql autogenerated for example: + +{% code overflow="wrap" %} +```go +func (r *queryResolver) Posts(ctx context.Context, limit *int, offset *int, orderBy *model.PostOrdering, filter *model.PostFilterInput) ([]*model.Post, error) { + var data []*model.Post + if err := r.Executor.Scan(ctx, "postgres", &data); err != nil { + return nil, err + } + return data, nil +} +``` +{% endcode %} + +We just need to start a postgres server and insert a schema, you can try the example's [compose file](https://github.com/roneli/fastgql/tree/master/example/docker-compose.yml) and execute the [schema.sql](https://github.com/roneli/fastgql/blob/master/example/graph/schema.graphql): + +Finally, we just need to define our postgres connection str that defined in server.go. We can override with `PG_CONN_STR` env variable. + +If we used the example's docker compose we can use this DSN: `PG_CONN_STR=postgresql://localhost/postgres?user=postgres&password=password` + +We now have a working server, to start it: + +```bash +go run server.go +``` + +then open http://localhost:8080 in a browser. here are some queries to try: + +```graphql +query { + posts(limit: 2) { + name + categories { + name + } + } +} +query filterPostsByUser { + posts(limit: 10, filter: {user:{name: {eq: "fastgql"}}}, orderBy: {name: ASC}) { + name + categories { + name + } + user { + name + } + } +} +``` + +## Finishing touches + +At the top of our `server.go`, between `package` and `import`, add the following line: + +```go +//go:generate go run github.com/roneli/fastgql generate -c gqlgen.yml +``` + +This magic comment tells `go generate` what command to run when we want to regenerate our code. To run go generate recursively over your entire project, use this command: + +```go +go generate ./... +``` diff --git a/docs/src/env.d.ts b/docs/src/env.d.ts index acef35f..633d9f0 100644 --- a/docs/src/env.d.ts +++ b/docs/src/env.d.ts @@ -1,2 +1,2 @@ -/// -/// +/// +/// diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 77da9dd..ebbe6d1 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -1,3 +1,3 @@ -{ - "extends": "astro/tsconfigs/strict" +{ + "extends": "astro/tsconfigs/strict" } \ No newline at end of file diff --git a/examples/init/gqlgen.yml b/examples/init/gqlgen.yml index 5ad3c01..b0fb20a 100644 --- a/examples/init/gqlgen.yml +++ b/examples/init/gqlgen.yml @@ -1,47 +1,47 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - graph/*.graphql -# Where should the generated servergen code go? -exec: - filename: graph/generated/generated.go - package: generated -# Uncomment to enable federation -# federation: -# filename: graph/generated/federation.go -# package: generated -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: graph - package: graph -# Optional: turn on use `gqlgen:"fieldName"` tags in your models -# struct_tag: json -# Optional: turn on to use []Thing instead of []*Thing -# omit_slice_element_pointers: false -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -autobind: - - "github.com/roneli/fastgql/examples/init/graph/model" -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - Id: - model: - - github.com/99designs/gqlgen/graphql.Id - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - graph/*.graphql +# Where should the generated servergen code go? +exec: + filename: graph/generated/generated.go + package: generated +# Uncomment to enable federation +# federation: +# filename: graph/generated/federation.go +# package: generated +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: graph + package: graph +# Optional: turn on use `gqlgen:"fieldName"` tags in your models +# struct_tag: json +# Optional: turn on to use []Thing instead of []*Thing +# omit_slice_element_pointers: false +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +autobind: + - "github.com/roneli/fastgql/examples/init/graph/model" +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + Id: + model: + - github.com/99designs/gqlgen/graphql.Id + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 diff --git a/examples/init/graph/fastgql.graphql b/examples/init/graph/fastgql.graphql index 132edc9..eee509c 100644 --- a/examples/init/graph/fastgql.graphql +++ b/examples/init/graph/fastgql.graphql @@ -1,123 +1,123 @@ -# ================== schema generation fastgql directives ================== - -# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field -# @generateResolver can only be defined on Query and Mutation fields. -# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments -# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error -# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, -# this will modify the object itself and add arguments to the object fields. -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION - -# Generate mutations for an object -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT - -# Generate filter input on an object -directive @generateFilterInput(description: String) repeatable on OBJECT - -# ================== Directives supported by fastgql for Querying ================== - -# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed -# i.e , "postgres", "" -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE - -# Relation directive defines relations cross tables and dialects -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION - -# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION - -# ================== Default Filter input types supported by fastgql ================== - -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} - -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} - -type _AggregateResult { - count: Int! -} - -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} - -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} - -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} - -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} - -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} - -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} - - -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} - -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean +# ================== schema generation fastgql directives ================== + +# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field +# @generateResolver can only be defined on Query and Mutation fields. +# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments +# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error +# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, +# this will modify the object itself and add arguments to the object fields. +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION + +# Generate mutations for an object +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT + +# Generate filter input on an object +directive @generateFilterInput(description: String) repeatable on OBJECT + +# ================== Directives supported by fastgql for Querying ================== + +# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed +# i.e , "postgres", "" +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE + +# Relation directive defines relations cross tables and dialects +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION + +# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION + +# ================== Default Filter input types supported by fastgql ================== + +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} + +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} + +type _AggregateResult { + count: Int! +} + +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} + +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} + +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} + +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} + +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} + +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} + + +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} + +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean } \ No newline at end of file diff --git a/examples/init/graph/schema.graphql b/examples/init/graph/schema.graphql index 68974b2..0f7aec6 100644 --- a/examples/init/graph/schema.graphql +++ b/examples/init/graph/schema.graphql @@ -1,26 +1,26 @@ -type User @table(name: "user"){ - id: Int! - name: String! - posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) -} - -type Post @generateFilterInput { - id: Int! - name: String - categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], - manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) - user_id: Int - user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) -} - - -type Category @generateFilterInput{ - id: Int! - name: String -} - -type Query { - posts: [Post] @generate - users: [User] @generate - categories: [Category] @generate +type User @table(name: "user"){ + id: Int! + name: String! + posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) +} + +type Post @generateFilterInput { + id: Int! + name: String + categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], + manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) + user_id: Int + user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) +} + + +type Category @generateFilterInput{ + id: Int! + name: String +} + +type Query { + posts: [Post] @generate + users: [User] @generate + categories: [Category] @generate } \ No newline at end of file diff --git a/examples/interface/gqlgen.yml b/examples/interface/gqlgen.yml index ed99b91..e5594de 100644 --- a/examples/interface/gqlgen.yml +++ b/examples/interface/gqlgen.yml @@ -1,50 +1,50 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - graph/*.graphql -# Where should the generated servergen code go? -exec: - filename: graph/generated/generated.go - package: generated -# Uncomment to enable federation -# federation: -# filename: graph/generated/federation.go -# package: generated -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: graph - package: graph -# Optional: turn on use `gqlgen:"fieldName"` tags in your models -# struct_tag: json -# Optional: turn on to use []Thing instead of []*Thing -# omit_slice_element_pointers: false -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -autobind: - - "github.com/roneli/fastgql/examples/interface/graph/model" -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - Id: - model: - - github.com/99designs/gqlgen/graphql.Id - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - - +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - graph/*.graphql +# Where should the generated servergen code go? +exec: + filename: graph/generated/generated.go + package: generated +# Uncomment to enable federation +# federation: +# filename: graph/generated/federation.go +# package: generated +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: graph + package: graph +# Optional: turn on use `gqlgen:"fieldName"` tags in your models +# struct_tag: json +# Optional: turn on to use []Thing instead of []*Thing +# omit_slice_element_pointers: false +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +autobind: + - "github.com/roneli/fastgql/examples/interface/graph/model" +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + Id: + model: + - github.com/99designs/gqlgen/graphql.Id + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + + omit_interface_checks : true \ No newline at end of file diff --git a/examples/interface/graph/.graphqlconfig b/examples/interface/graph/.graphqlconfig index 98f702d..76817e6 100644 --- a/examples/interface/graph/.graphqlconfig +++ b/examples/interface/graph/.graphqlconfig @@ -1,16 +1,16 @@ -{ - "name": "GraphQL Schema", - "includes": ["*"], - "excludes": ["augmented_schema.graphql"], - "extensions": { - "endpoints": { - "Default GraphQL Endpoint": { - "url": "http://localhost:8080/graphql", - "headers": { - "user-agent": "JS GraphQL" - }, - "introspect": false - } - } - } +{ + "name": "GraphQL Schema", + "includes": ["*"], + "excludes": ["augmented_schema.graphql"], + "extensions": { + "endpoints": { + "Default GraphQL Endpoint": { + "url": "http://localhost:8080/graphql", + "headers": { + "user-agent": "JS GraphQL" + }, + "introspect": false + } + } + } } \ No newline at end of file diff --git a/examples/interface/graph/fastgql.graphql b/examples/interface/graph/fastgql.graphql index b46c0cc..dfbf006 100644 --- a/examples/interface/graph/fastgql.graphql +++ b/examples/interface/graph/fastgql.graphql @@ -1,92 +1,92 @@ -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION -directive @generateFilterInput(description: String) on OBJECT | INTERFACE -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT -directive @isInterfaceFilter on INPUT_FIELD_DEFINITION -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE -directive @typename(name: String!) on INTERFACE -scalar Map - -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean -} -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} -type _AggregateResult { - count: Int! -} -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION +directive @generateFilterInput(description: String) on OBJECT | INTERFACE +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT +directive @isInterfaceFilter on INPUT_FIELD_DEFINITION +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE +directive @typename(name: String!) on INTERFACE +scalar Map + +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean +} +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} +type _AggregateResult { + count: Int! +} +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} diff --git a/examples/interface/graph/schema.graphql b/examples/interface/graph/schema.graphql index cae441f..836c833 100644 --- a/examples/interface/graph/schema.graphql +++ b/examples/interface/graph/schema.graphql @@ -1,39 +1,39 @@ -interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { - id: Int! - name: String! - type: String! -} -type Cat implements Animal { - id: Int! - name: String! - type: String! - color: String! -} -type Category @generateFilterInput @table(name: "categories") { - id: Int! - name: String -} -type Dog implements Animal { - id: Int! - name: String! - type: String! - breed: String! -} -type Post @generateFilterInput @table(name: "posts") { - id: Int! - name: String - categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) - user_id: Int - user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) -} -type Query { - posts: [Post] @generate - users: [User] @generate - categories: [Category] @generate - animals: [Animal] @generate -} -type User @table(name: "user") @generateFilterInput { - id: Int! - name: String! - posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) -} +interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { + id: Int! + name: String! + type: String! +} +type Cat implements Animal { + id: Int! + name: String! + type: String! + color: String! +} +type Category @generateFilterInput @table(name: "categories") { + id: Int! + name: String +} +type Dog implements Animal { + id: Int! + name: String! + type: String! + breed: String! +} +type Post @generateFilterInput @table(name: "posts") { + id: Int! + name: String + categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) + user_id: Int + user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) +} +type Query { + posts: [Post] @generate + users: [User] @generate + categories: [Category] @generate + animals: [Animal] @generate +} +type User @table(name: "user") @generateFilterInput { + id: Int! + name: String! + posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) +} diff --git a/examples/mutations/gqlgen.yml b/examples/mutations/gqlgen.yml index ed99b91..e5594de 100644 --- a/examples/mutations/gqlgen.yml +++ b/examples/mutations/gqlgen.yml @@ -1,50 +1,50 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - graph/*.graphql -# Where should the generated servergen code go? -exec: - filename: graph/generated/generated.go - package: generated -# Uncomment to enable federation -# federation: -# filename: graph/generated/federation.go -# package: generated -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: graph - package: graph -# Optional: turn on use `gqlgen:"fieldName"` tags in your models -# struct_tag: json -# Optional: turn on to use []Thing instead of []*Thing -# omit_slice_element_pointers: false -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -autobind: - - "github.com/roneli/fastgql/examples/interface/graph/model" -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - Id: - model: - - github.com/99designs/gqlgen/graphql.Id - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - - +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - graph/*.graphql +# Where should the generated servergen code go? +exec: + filename: graph/generated/generated.go + package: generated +# Uncomment to enable federation +# federation: +# filename: graph/generated/federation.go +# package: generated +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: graph + package: graph +# Optional: turn on use `gqlgen:"fieldName"` tags in your models +# struct_tag: json +# Optional: turn on to use []Thing instead of []*Thing +# omit_slice_element_pointers: false +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +autobind: + - "github.com/roneli/fastgql/examples/interface/graph/model" +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + Id: + model: + - github.com/99designs/gqlgen/graphql.Id + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + + omit_interface_checks : true \ No newline at end of file diff --git a/examples/mutations/graph/.graphqlconfig b/examples/mutations/graph/.graphqlconfig index 98f702d..76817e6 100644 --- a/examples/mutations/graph/.graphqlconfig +++ b/examples/mutations/graph/.graphqlconfig @@ -1,16 +1,16 @@ -{ - "name": "GraphQL Schema", - "includes": ["*"], - "excludes": ["augmented_schema.graphql"], - "extensions": { - "endpoints": { - "Default GraphQL Endpoint": { - "url": "http://localhost:8080/graphql", - "headers": { - "user-agent": "JS GraphQL" - }, - "introspect": false - } - } - } +{ + "name": "GraphQL Schema", + "includes": ["*"], + "excludes": ["augmented_schema.graphql"], + "extensions": { + "endpoints": { + "Default GraphQL Endpoint": { + "url": "http://localhost:8080/graphql", + "headers": { + "user-agent": "JS GraphQL" + }, + "introspect": false + } + } + } } \ No newline at end of file diff --git a/examples/mutations/graph/fastgql.graphql b/examples/mutations/graph/fastgql.graphql index 6143c25..8303af7 100644 --- a/examples/mutations/graph/fastgql.graphql +++ b/examples/mutations/graph/fastgql.graphql @@ -1,91 +1,91 @@ -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION -directive @generateFilterInput(description: String) on OBJECT | INTERFACE -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT -directive @isInterfaceFilter on INPUT_FIELD_DEFINITION -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE -directive @typename(name: String!) on INTERFACE -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean -} -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} -scalar Map -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} -type _AggregateResult { - count: Int! -} -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION +directive @generateFilterInput(description: String) on OBJECT | INTERFACE +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT +directive @isInterfaceFilter on INPUT_FIELD_DEFINITION +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE +directive @typename(name: String!) on INTERFACE +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean +} +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} +scalar Map +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} +type _AggregateResult { + count: Int! +} +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} diff --git a/examples/mutations/graph/schema.graphql b/examples/mutations/graph/schema.graphql index 53d92df..08585f4 100644 --- a/examples/mutations/graph/schema.graphql +++ b/examples/mutations/graph/schema.graphql @@ -1,21 +1,21 @@ -type Category @generateFilterInput @table(name: "categories") { - id: Int! - name: String -} -type Post @generateFilterInput @table(name: "posts") @generateMutations { - id: Int! - name: String - categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) - user_id: Int - user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) -} -type Query { - posts: [Post] @generate - users: [User] @generate - categories: [Category] @generate -} -type User @table(name: "user") @generateFilterInput { - id: Int! - name: String! - posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) -} +type Category @generateFilterInput @table(name: "categories") { + id: Int! + name: String +} +type Post @generateFilterInput @table(name: "posts") @generateMutations { + id: Int! + name: String + categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) + user_id: Int + user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) +} +type Query { + posts: [Post] @generate + users: [User] @generate + categories: [Category] @generate +} +type User @table(name: "user") @generateFilterInput { + id: Int! + name: String! + posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) +} diff --git a/examples/simple/gqlgen.yml b/examples/simple/gqlgen.yml index 7725bc6..b667b07 100644 --- a/examples/simple/gqlgen.yml +++ b/examples/simple/gqlgen.yml @@ -1,50 +1,50 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - "./graph/*.graphql" - -# Where should the generated servergen code go? -exec: - filename: graph/generated/generated.go - package: generated - -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model - -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: ./graph - package: graph - -# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models -# struct_tag: json - -# Optional: turn on to use []Thing instead of []*Thing -omit_slice_element_pointers: true - -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true - -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -#autobind: - -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - ID: - model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - "./graph/*.graphql" + +# Where should the generated servergen code go? +exec: + filename: graph/generated/generated.go + package: generated + +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: ./graph + package: graph + +# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +omit_slice_element_pointers: true + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +#autobind: + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 \ No newline at end of file diff --git a/examples/simple/graph/.graphqlconfig b/examples/simple/graph/.graphqlconfig index 98f702d..76817e6 100644 --- a/examples/simple/graph/.graphqlconfig +++ b/examples/simple/graph/.graphqlconfig @@ -1,16 +1,16 @@ -{ - "name": "GraphQL Schema", - "includes": ["*"], - "excludes": ["augmented_schema.graphql"], - "extensions": { - "endpoints": { - "Default GraphQL Endpoint": { - "url": "http://localhost:8080/graphql", - "headers": { - "user-agent": "JS GraphQL" - }, - "introspect": false - } - } - } +{ + "name": "GraphQL Schema", + "includes": ["*"], + "excludes": ["augmented_schema.graphql"], + "extensions": { + "endpoints": { + "Default GraphQL Endpoint": { + "url": "http://localhost:8080/graphql", + "headers": { + "user-agent": "JS GraphQL" + }, + "introspect": false + } + } + } } \ No newline at end of file diff --git a/examples/simple/graph/fastgql.graphql b/examples/simple/graph/fastgql.graphql index 424c9b1..b5283da 100644 --- a/examples/simple/graph/fastgql.graphql +++ b/examples/simple/graph/fastgql.graphql @@ -1,89 +1,89 @@ -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True) on FIELD_DEFINITION -directive @generateFilterInput(description: String) on OBJECT -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE -scalar Map -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean -} -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} -type _AggregateResult { - count: Int! -} -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True) on FIELD_DEFINITION +directive @generateFilterInput(description: String) on OBJECT +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE +scalar Map +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean +} +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} +type _AggregateResult { + count: Int! +} +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} diff --git a/examples/simple/graph/fastgql_schema.graphql b/examples/simple/graph/fastgql_schema.graphql index 7172962..921db2d 100644 --- a/examples/simple/graph/fastgql_schema.graphql +++ b/examples/simple/graph/fastgql_schema.graphql @@ -1,61 +1,61 @@ -input UserFilterInput { - name: StringComparator - age: IntComparator - someInnerValue: UserFilterInput - someInnerValueList: UserFilterInput - """ - Logical AND of FilterInput - """ - AND: [UserFilterInput] - """ - Logical OR of FilterInput - """ - OR: [UserFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: UserFilterInput -} -""" -max aggregator for User -""" -type UserMin { - """ - Compute the maxiumum for name - """ - name: String! - """ - Compute the maxiumum for age - """ - age: Int! -} -""" -Ordering for User -""" -input UserOrdering { - """ - Order User by name - """ - name: _OrderingTypes - """ - Order User by age - """ - age: _OrderingTypes -} -""" -Aggregate User -""" -type UsersAggregate { - """ - Count results - """ - count: Int! - """ - Computes the maximum of the non-null input values. - """ - max: UserMin - """ - Computes the minimum of the non-null input values. - """ - min: UserMin -} +input UserFilterInput { + name: StringComparator + age: IntComparator + someInnerValue: UserFilterInput + someInnerValueList: UserFilterInput + """ + Logical AND of FilterInput + """ + AND: [UserFilterInput] + """ + Logical OR of FilterInput + """ + OR: [UserFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: UserFilterInput +} +""" +max aggregator for User +""" +type UserMin { + """ + Compute the maxiumum for name + """ + name: String! + """ + Compute the maxiumum for age + """ + age: Int! +} +""" +Ordering for User +""" +input UserOrdering { + """ + Order User by name + """ + name: _OrderingTypes + """ + Order User by age + """ + age: _OrderingTypes +} +""" +Aggregate User +""" +type UsersAggregate { + """ + Count results + """ + count: Int! + """ + Computes the maximum of the non-null input values. + """ + max: UserMin + """ + Computes the minimum of the non-null input values. + """ + min: UserMin +} diff --git a/examples/simple/graph/schema.graphql b/examples/simple/graph/schema.graphql index 40192f9..e72ba4e 100644 --- a/examples/simple/graph/schema.graphql +++ b/examples/simple/graph/schema.graphql @@ -1,49 +1,49 @@ -type Person { - name: String -} -type Query { - person: Person! - user( - """ - Limit - """ - limit: Int = 100, - """ - Offset - """ - offset: Int = 0, - """ - Ordering for User - """ - orderBy: [UserOrdering], - """ - Filter user - """ - filter: UserFilterInput): [User] @generate - """ - user Aggregate - """ - _userAggregate: UsersAggregate! -} -type User @generateFilterInput { - name: String - age: Int - someInnerValue: User - someInnerValueList( - """ - Limit - """ - limit: Int = 100, - """ - Offset - """ - offset: Int = 0, - """ - Ordering for User - """ - orderBy: [UserOrdering], - """ - Filter someInnerValueList - """ - filter: UserFilterInput): [User] -} +type Person { + name: String +} +type Query { + person: Person! + user( + """ + Limit + """ + limit: Int = 100, + """ + Offset + """ + offset: Int = 0, + """ + Ordering for User + """ + orderBy: [UserOrdering], + """ + Filter user + """ + filter: UserFilterInput): [User] @generate + """ + user Aggregate + """ + _userAggregate: UsersAggregate! +} +type User @generateFilterInput { + name: String + age: Int + someInnerValue: User + someInnerValueList( + """ + Limit + """ + limit: Int = 100, + """ + Offset + """ + offset: Int = 0, + """ + Ordering for User + """ + orderBy: [UserOrdering], + """ + Filter someInnerValueList + """ + filter: UserFilterInput): [User] +} diff --git a/go.sum b/go.sum index 3e8c01c..a03a851 100644 --- a/go.sum +++ b/go.sum @@ -1,193 +1,193 @@ -github.com/99designs/gqlgen v0.17.41 h1:C1/zYMhGVP5TWNCNpmZ9Mb6CqT1Vr5SHEWoTOEJ3v3I= -github.com/99designs/gqlgen v0.17.41/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE= -github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= -github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= -github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/doug-martin/goqu/v9 v9.10.0 h1:ggTSAwshc5nubbFN7Q8Or1/Xzv+x8YTLCyv6CpBb9DM= -github.com/doug-martin/goqu/v9 v9.10.0/go.mod h1:zx5/YoiHux3wn7477GnI3PXzKyKpLKu32Teo9U4yCFE= -github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU= -github.com/georgysavva/scany/v2 v2.0.0/go.mod h1:sigOdh+0qb/+aOs3TVhehVT10p8qJL7K/Zhyz8vWo38= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= -github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw= -github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= -github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= -github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4= -github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= -github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/vektah/gqlparser/v2 v2.5.10 h1:6zSM4azXC9u4Nxy5YmdmGu4uKamfwsdKTwp5zsEealU= -github.com/vektah/gqlparser/v2 v2.5.10/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= -github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.13.0 h1:67DgFFjYOCMWdtTEmKFpV3ffWlFnh+CYZ8ZS/tXWUfY= -go.mongodb.org/mongo-driver v1.13.0/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/99designs/gqlgen v0.17.41 h1:C1/zYMhGVP5TWNCNpmZ9Mb6CqT1Vr5SHEWoTOEJ3v3I= +github.com/99designs/gqlgen v0.17.41/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE= +github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= +github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/doug-martin/goqu/v9 v9.10.0 h1:ggTSAwshc5nubbFN7Q8Or1/Xzv+x8YTLCyv6CpBb9DM= +github.com/doug-martin/goqu/v9 v9.10.0/go.mod h1:zx5/YoiHux3wn7477GnI3PXzKyKpLKu32Teo9U4yCFE= +github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU= +github.com/georgysavva/scany/v2 v2.0.0/go.mod h1:sigOdh+0qb/+aOs3TVhehVT10p8qJL7K/Zhyz8vWo38= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= +github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw= +github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= +github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= +github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4= +github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= +github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/vektah/gqlparser/v2 v2.5.10 h1:6zSM4azXC9u4Nxy5YmdmGu4uKamfwsdKTwp5zsEealU= +github.com/vektah/gqlparser/v2 v2.5.10/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.13.0 h1:67DgFFjYOCMWdtTEmKFpV3ffWlFnh+CYZ8ZS/tXWUfY= +go.mongodb.org/mongo-driver v1.13.0/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/execution/builders/directives.go b/pkg/execution/builders/directives.go index 0e523e3..45427f6 100644 --- a/pkg/execution/builders/directives.go +++ b/pkg/execution/builders/directives.go @@ -3,70 +3,14 @@ package builders import ( "fmt" - "github.com/roneli/fastgql/pkg/schema" - - "github.com/spf13/cast" - "github.com/vektah/gqlparser/v2/ast" ) -type TableDirective struct { - // Name of the table/collection - Name string - // Schema name table resides in, can be omitted - Schema string - // Dialect name the table resides in - Dialect string -} - type DialectDirective struct { Dialect string ParentDialect string } -type RelationType string - -type RelationDirective struct { - RelType RelationType - BaseTable string - ReferenceTable string - Fields []string - References []string - ManyToManyTable string - ManyToManyReferences []string - ManyToManyFields []string -} - -func GetTableDirective(def *ast.Definition) (*TableDirective, error) { - d := def.Directives.ForName("table") - if d == nil { - return nil, fmt.Errorf("failed to get table directive for %s", def.Name) - } - return &TableDirective{ - Name: GetArgumentValue(d.Arguments, "name"), - Schema: GetArgumentValue(d.Arguments, "schema"), - Dialect: GetArgumentValue(d.Arguments, "dialect"), - }, nil -} - -func GetRelationDirective(field *ast.FieldDefinition) *RelationDirective { - d := field.Directives.ForName("relation") - if d == nil { - return nil - } - relType := d.Arguments.ForName("type").Value.Raw - return &RelationDirective{ - RelType: RelationType(relType), - Fields: cast.ToStringSlice(schema.GetDirectiveValue(d, "fields")), - References: cast.ToStringSlice(schema.GetDirectiveValue(d, "references")), - BaseTable: cast.ToString(schema.GetDirectiveValue(d, "baseTable")), - ReferenceTable: cast.ToString(schema.GetDirectiveValue(d, "refTable")), - ManyToManyTable: cast.ToString(schema.GetDirectiveValue(d, "manyToManyTable")), - ManyToManyFields: cast.ToStringSlice(schema.GetDirectiveValue(d, "manyToManyFields")), - ManyToManyReferences: cast.ToStringSlice(schema.GetDirectiveValue(d, "manyToManyReferences")), - } -} - func GetDialectDirective(schema *ast.Schema, field Field) (*DialectDirective, error) { typeName := field.GetTypeName() objType, ok := schema.Types[typeName] @@ -90,11 +34,3 @@ func GetDialectDirective(schema *ast.Schema, field Field) (*DialectDirective, er ParentDialect: parentDialect, }, nil } - -func GetArgumentValue(args ast.ArgumentList, name string) string { - arg := args.ForName(name) - if arg == nil { - return "" - } - return arg.Value.Raw -} diff --git a/pkg/execution/builders/field.go b/pkg/execution/builders/field.go index 728e739..c9fb29e 100644 --- a/pkg/execution/builders/field.go +++ b/pkg/execution/builders/field.go @@ -5,6 +5,8 @@ import ( "fmt" "strings" + "github.com/roneli/fastgql/pkg/schema" + "github.com/iancoleman/strcase" "github.com/99designs/gqlgen/graphql" @@ -85,21 +87,17 @@ func (f Field) GetTypeName() string { } // Relation directive on field, if it exists -func (f Field) Relation() *RelationDirective { - return GetRelationDirective(f.Definition) +func (f Field) Relation() *schema.RelationDirective { + return schema.GetRelationDirective(f.Definition) } // Table directive on field, if it exists -func (f Field) Table() *TableDirective { - d := f.TypeDefinition.Directives.ForName("table") - if d == nil { +func (f Field) Table() *schema.TableDirective { + t, err := schema.GetTableDirective(f.TypeDefinition) + if err != nil { return nil } - return &TableDirective{ - Name: GetArgumentValue(d.Arguments, "name"), - Schema: GetArgumentValue(d.Arguments, "schema"), - Dialect: GetArgumentValue(d.Arguments, "dialect"), - } + return t } func GetFilterInput(s *ast.Schema, f *ast.Definition) *ast.Definition { diff --git a/pkg/execution/builders/filters.go b/pkg/execution/builders/filters.go index a969034..879a4ce 100644 --- a/pkg/execution/builders/filters.go +++ b/pkg/execution/builders/filters.go @@ -4,6 +4,8 @@ import ( "context" "reflect" + "github.com/roneli/fastgql/pkg/schema" + "github.com/vektah/gqlparser/v2/ast" "github.com/iancoleman/strcase" @@ -20,7 +22,7 @@ type FilterFieldContext struct { func AddRelationFilters(ctx context.Context, s *ast.Schema, obj interface{}) context.Context { // TODO: Use collect field field := CollectFields(ctx, s) - relation := GetRelationDirective(field.ObjectDefinition.Fields.ForName(field.Name)) + relation := schema.GetRelationDirective(field.ObjectDefinition.Fields.ForName(field.Name)) filters := make(map[string]interface{}, len(relation.Fields)) for i := 0; i < len(relation.Fields); i++ { fieldValue := getFieldValue(obj, strcase.ToLowerCamel(relation.Fields[i])) diff --git a/pkg/execution/builders/sql/builder.go b/pkg/execution/builders/sql/builder.go index 4f264df..80ee417 100644 --- a/pkg/execution/builders/sql/builder.go +++ b/pkg/execution/builders/sql/builder.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/roneli/fastgql/pkg/schema" + "github.com/doug-martin/goqu/v9" "github.com/doug-martin/goqu/v9/exp" "github.com/iancoleman/strcase" @@ -432,11 +434,11 @@ func (b Builder) buildFilterExp(table tableHelper, astDefinition *ast.Definition ffd := astDefinition.Fields.ForName(k) // Create a Builder - d := ffd.Directives.ForName("relation") - if d == nil { - return nil, fmt.Errorf("missing directive sqlRelation") + rel := schema.GetRelationDirective(ffd) + if rel == nil { + return nil, fmt.Errorf("missing directive relation") } - fq, err := b.buildFilterQuery(table, b.Schema.Types[ffd.Type.Name()], parseRelationDirective(d), kv) + fq, err := b.buildFilterQuery(table, b.Schema.Types[ffd.Type.Name()], *rel, kv) if err != nil { return nil, err } @@ -464,24 +466,24 @@ func (b Builder) buildRelation(parentQuery *queryHelper, rf builders.Field) erro if err != nil { return errors.Wrap(err, "failed building relation") } - rel := parseRelationDirective(rf.Definition.Directives.ForName("relation")) - switch rel.relType { - case OneToOne: + rel := schema.GetRelationDirective(rf.Definition) + switch rel.RelType { + case schema.OneToOne: parentQuery.SelectDataset = parentQuery.SelectDataset.LeftJoin(goqu.Lateral(relationQuery.SelectJson(rf.Name).As(relationQuery.alias). - Where(buildCrossCondition(parentQuery.alias, rel.fields, relationQuery.alias, rel.references))), + Where(buildCrossCondition(parentQuery.alias, rel.Fields, relationQuery.alias, rel.References))), goqu.On(goqu.L("true")), ) parentQuery.selects = append(parentQuery.selects, column{name: rf.Name, alias: "", table: relationQuery.alias}) - case OneToMany: + case schema.OneToMany: parentQuery.SelectDataset = parentQuery.SelectDataset.LeftJoin( goqu.Lateral(relationQuery.SelectJsonAgg(rf.Name).As(relationQuery.alias). - Where(buildCrossCondition(parentQuery.alias, rel.fields, relationQuery.alias, rel.references))), + Where(buildCrossCondition(parentQuery.alias, rel.Fields, relationQuery.alias, rel.References))), goqu.On(goqu.L("true")), ) parentQuery.selects = append(parentQuery.selects, column{name: rf.Name, alias: "", table: relationQuery.alias}) - case ManyToMany: + case schema.ManyToMany: m2mTableAlias := b.TableNameGenerator.Generate(6) - m2mTable := goqu.T(rel.manyToManyTable).Schema(tableDef.schema).As(m2mTableAlias) + m2mTable := goqu.T(rel.ManyToManyTable).Schema(tableDef.schema).As(m2mTableAlias) m2mQuery := queryHelper{ SelectDataset: goqu.From(m2mTable), table: m2mTable, @@ -490,11 +492,11 @@ func (b Builder) buildRelation(parentQuery *queryHelper, rf builders.Field) erro } // Join m2mBuilder with the relBuilder m2mQuery.SelectDataset = m2mQuery.LeftJoin( - goqu.Lateral(relationQuery.SelectRow(false).Where(buildCrossCondition(relationQuery.alias, rel.references, m2mTableAlias, rel.manyToManyReferences))).As(relationQuery.alias), + goqu.Lateral(relationQuery.SelectRow(false).Where(buildCrossCondition(relationQuery.alias, rel.References, m2mTableAlias, rel.ManyToManyReferences))).As(relationQuery.alias), goqu.On(goqu.L("true"))) // Add cross condition from parent Builder (current Builder instance) - m2mQuery.SelectDataset = m2mQuery.Where(buildCrossCondition(parentQuery.alias, rel.fields, m2mTableAlias, rel.manyToManyFields)).As(relationQuery.alias) + m2mQuery.SelectDataset = m2mQuery.Where(buildCrossCondition(parentQuery.alias, rel.Fields, m2mTableAlias, rel.ManyToManyFields)).As(relationQuery.alias) // Finally, aggregate relation query and join the m2m tableDefinition with the main query aggTableName := b.TableNameGenerator.Generate(6) @@ -513,51 +515,51 @@ func (b Builder) buildRelationAggregate(parentQuery *queryHelper, rf builders.Fi return errors.Wrap(err, "failed building relation") } originalDef := rf.ObjectDefinition.Fields.ForName(strings.Split(rf.Name, "Aggregate")[0][1:]) - rel := parseRelationDirective(originalDef.Directives.ForName("relation")) + rel := schema.GetRelationDirective(originalDef) name := b.CaseConverter(rf.Name) // TODO: finish this - switch rel.relType { - case OneToMany, OneToOne: + switch rel.RelType { + case schema.OneToMany, schema.OneToOne: parentQuery.SelectDataset = parentQuery.SelectDataset.LeftJoin( goqu.Lateral(goqu.Select(goqu.Func("jsonb_agg", aggQuery.table.Col(name)).As(name)).From(aggQuery.SelectJson(name).As(aggQuery.alias). - Where(buildCrossCondition(parentQuery.alias, rel.fields, aggQuery.alias, rel.references)))).As(aggQuery.alias), + Where(buildCrossCondition(parentQuery.alias, rel.Fields, aggQuery.alias, rel.References)))).As(aggQuery.alias), goqu.On(goqu.L("true")), ) parentQuery.selects = append(parentQuery.selects, column{name: name, alias: "", table: aggQuery.alias}) - case ManyToMany: + case schema.ManyToMany: m2mTableName := b.TableNameGenerator.Generate(6) - jExps := buildJoinCondition(parentQuery.alias, rel.fields, m2mTableName, rel.manyToManyFields) - jExps = append(jExps, buildJoinCondition(m2mTableName, rel.manyToManyReferences, aggQuery.alias, rel.references)...) - aggQuery.SelectDataset = aggQuery.InnerJoin(goqu.T(rel.manyToManyTable).As(m2mTableName), goqu.On(jExps...)) + jExps := buildJoinCondition(parentQuery.alias, rel.Fields, m2mTableName, rel.ManyToManyFields) + jExps = append(jExps, buildJoinCondition(m2mTableName, rel.ManyToManyReferences, aggQuery.alias, rel.References)...) + aggQuery.SelectDataset = aggQuery.InnerJoin(goqu.T(rel.ManyToManyTable).As(m2mTableName), goqu.On(jExps...)) parentQuery.SelectDataset = parentQuery.CrossJoin(goqu.Lateral(goqu.Select(goqu.Func("jsonb_agg", aggQuery.table.Col(name)).As(name)).From(aggQuery.SelectJson(name).As(aggQuery.alias))).As(aggQuery.alias)) parentQuery.selects = append(parentQuery.selects, column{name: b.CaseConverter(name), alias: "", table: aggQuery.alias}) } return nil } -func (b Builder) buildFilterQuery(parentTable tableHelper, rf *ast.Definition, rel relation, filters map[string]any) (*queryHelper, error) { +func (b Builder) buildFilterQuery(parentTable tableHelper, rf *ast.Definition, rel schema.RelationDirective, filters map[string]any) (*queryHelper, error) { tableAlias := b.TableNameGenerator.Generate(6) - td, err := builders.GetTableDirective(rf) + td, err := schema.GetTableDirective(rf) if err != nil { return nil, fmt.Errorf("missing @table directive to create filter query for %s: %w", rf.Name, err) } table := goqu.T(td.Name).Schema(td.Schema).As(tableAlias) fq := &queryHelper{goqu.From(table), table, tableAlias, nil} - switch rel.relType { - case ManyToMany: + switch rel.RelType { + case schema.ManyToMany: m2mTableName := b.TableNameGenerator.Generate(6) - jExps := buildJoinCondition(parentTable.alias, rel.fields, m2mTableName, rel.manyToManyFields) - jExps = append(jExps, buildJoinCondition(m2mTableName, rel.manyToManyReferences, fq.alias, rel.references)...) - fq.SelectDataset = fq.InnerJoin(goqu.T(rel.manyToManyTable).Schema(td.Schema).As(m2mTableName), goqu.On(jExps...)) - case OneToOne: + jExps := buildJoinCondition(parentTable.alias, rel.Fields, m2mTableName, rel.ManyToManyFields) + jExps = append(jExps, buildJoinCondition(m2mTableName, rel.ManyToManyReferences, fq.alias, rel.References)...) + fq.SelectDataset = fq.InnerJoin(goqu.T(rel.ManyToManyTable).Schema(td.Schema).As(m2mTableName), goqu.On(jExps...)) + case schema.OneToOne: relationTableName := b.TableNameGenerator.Generate(6) - jExps := buildJoinCondition(parentTable.alias, rel.fields, fq.alias, rel.references) - jExps = append(jExps, buildJoinCondition(parentTable.alias, rel.fields, relationTableName, rel.references)...) + jExps := buildJoinCondition(parentTable.alias, rel.Fields, fq.alias, rel.References) + jExps = append(jExps, buildJoinCondition(parentTable.alias, rel.Fields, relationTableName, rel.References)...) fq.SelectDataset = fq.InnerJoin(goqu.T(td.Name).Schema(td.Schema).As(relationTableName), goqu.On(jExps...)) - case OneToMany: + case schema.OneToMany: fq.SelectDataset = fq.InnerJoin(parentTable.table.Aliased().(exp.Aliaseable).As(b.TableNameGenerator.Generate(6)), - goqu.On(buildJoinCondition(parentTable.alias, rel.fields, fq.alias, rel.references)...)) + goqu.On(buildJoinCondition(parentTable.alias, rel.Fields, fq.alias, rel.References)...)) default: panic("unknown relation type") } diff --git a/pkg/execution/builders/sql/directives.go b/pkg/execution/builders/sql/directives.go index 25df569..66c79ed 100644 --- a/pkg/execution/builders/sql/directives.go +++ b/pkg/execution/builders/sql/directives.go @@ -4,32 +4,12 @@ import ( "fmt" "strings" - "github.com/roneli/fastgql/pkg/schema" - "github.com/doug-martin/goqu/v9" "github.com/doug-martin/goqu/v9/exp" "github.com/jinzhu/inflection" - "github.com/spf13/cast" "github.com/vektah/gqlparser/v2/ast" ) -type RelationType string - -const ( - OneToMany RelationType = "ONE_TO_MANY" - OneToOne RelationType = "ONE_TO_ONE" - ManyToMany RelationType = "MANY_TO_MANY" -) - -type relation struct { - relType RelationType - fields []string - references []string - manyToManyTable string - manyToManyReferences []string - manyToManyFields []string -} - type tableDefinition struct { name string schema string @@ -51,20 +31,6 @@ func (t tableDefinition) String() string { return fmt.Sprintf(`"%s"`, t.name) } -// parseRelationDirective parses the sqlRelation directive to connect graphQL Resources with SQL relations, this directive -// is also important for creating relational filters. -func parseRelationDirective(d *ast.Directive) relation { - relType := d.Arguments.ForName("type").Value.Raw - return relation{ - relType: RelationType(relType), - fields: cast.ToStringSlice(schema.GetDirectiveValue(d, "fields")), - references: cast.ToStringSlice(schema.GetDirectiveValue(d, "references")), - manyToManyTable: cast.ToString(schema.GetDirectiveValue(d, "manyToManyTable")), - manyToManyFields: cast.ToStringSlice(schema.GetDirectiveValue(d, "manyToManyFields")), - manyToManyReferences: cast.ToStringSlice(schema.GetDirectiveValue(d, "manyToManyReferences")), - } -} - // getTableNameFromField returns the field's type tableDefinition name in the database, if no directive is defined, type name is presumed // as the tableDefinition's name func getTableNameFromField(schema *ast.Schema, f *ast.FieldDefinition) tableDefinition { diff --git a/pkg/execution/builders/sql/testdata/.graphqlconfig b/pkg/execution/builders/sql/testdata/.graphqlconfig index 4db3115..9de4644 100644 --- a/pkg/execution/builders/sql/testdata/.graphqlconfig +++ b/pkg/execution/builders/sql/testdata/.graphqlconfig @@ -1,15 +1,15 @@ -{ - "name": "GraphQL Schema", - "includes": ["schema_simple.graphql"], - "extensions": { - "endpoints": { - "Default GraphQL Endpoint": { - "url": "http://localhost:8080/graphql", - "headers": { - "user-agent": "JS GraphQL" - }, - "introspect": false - } - } - } +{ + "name": "GraphQL Schema", + "includes": ["schema_simple.graphql"], + "extensions": { + "endpoints": { + "Default GraphQL Endpoint": { + "url": "http://localhost:8080/graphql", + "headers": { + "user-agent": "JS GraphQL" + }, + "introspect": false + } + } + } } \ No newline at end of file diff --git a/pkg/execution/builders/sql/testdata/gqlgen.yml b/pkg/execution/builders/sql/testdata/gqlgen.yml index 4dd39f8..54186b3 100644 --- a/pkg/execution/builders/sql/testdata/gqlgen.yml +++ b/pkg/execution/builders/sql/testdata/gqlgen.yml @@ -1,50 +1,50 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - "./*.graphql" - -# Where should the generated servergen code go? -exec: - filename: generated.go - package: generated - -# Where should any generated models go? -model: - filename: models_gen.go - package: generated - -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: ./graph - package: generated - -# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models -# struct_tag: json - -# Optional: turn on to use []Thing instead of []*Thing -omit_slice_element_pointers: true - -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true - -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -#autobind: - -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - ID: - model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - "./*.graphql" + +# Where should the generated servergen code go? +exec: + filename: generated.go + package: generated + +# Where should any generated models go? +model: + filename: models_gen.go + package: generated + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: ./graph + package: generated + +# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +omit_slice_element_pointers: true + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +#autobind: + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 \ No newline at end of file diff --git a/pkg/execution/builders/sql/testdata/schema_simple.graphql b/pkg/execution/builders/sql/testdata/schema_simple.graphql index edda3ef..eff953c 100644 --- a/pkg/execution/builders/sql/testdata/schema_simple.graphql +++ b/pkg/execution/builders/sql/testdata/schema_simple.graphql @@ -1,175 +1,175 @@ -type User @generateFilterInput @table(name: "users", schema: "app"){ - id: Int! - name: String! - posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) - someOtherName: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) -} - -type Post @generateFilterInput @table(name: "posts") @generateMutations(create: true, delete: true, update: true) { - id: Int! - name: String - categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"] - manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) - user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) -} - - -type Category @table(name:"categories", schema:"") @generateFilterInput { - id: Int! - name: String -} - - -interface Animal @table(name: "animals", schema: "app") @typename(name: "type") { - id: ID! - name: String! - type: String! -} -type Cat implements Animal { - id: ID! - name: String! - type: String! - color: String! -} - -type Dog implements Animal { - id: ID! - name: String! - type: String! - breed: String! -} - -type Query { - posts: [Post] @generate - users: [User] @generate - categories: [Category] @generate - animals: [Animal] @generate -} - -# ================== schema generation fastgql directives ================== - -# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field -# @generateResolver can only be defined on Query and Mutation fields. -# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments -# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error -# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, -# this will modify the object itself and add arguments to the object fields. -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION - -# Generate mutations for an object -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT - -# Generate filter input on an object -directive @generateFilterInput(description: String) repeatable on OBJECT - -# ================== Directives supported by fastgql for Querying ================== - -# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed -# i.e , "postgres", "" -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE - -# Relation directive defines relations cross tables and dialects -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION - -# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION - -directive @typename(name: String!) on INTERFACE - -# =================== Default Scalar types supported by fastgql =================== -scalar Map -# ================== Default Filter input types supported by fastgql ================== - -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} - -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} - -type _AggregateResult { - count: Int! -} - -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} - -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} - -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} - -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} - -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} - -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} - - -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} - -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean +type User @generateFilterInput @table(name: "users", schema: "app"){ + id: Int! + name: String! + posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) + someOtherName: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) +} + +type Post @generateFilterInput @table(name: "posts") @generateMutations(create: true, delete: true, update: true) { + id: Int! + name: String + categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"] + manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) + user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) +} + + +type Category @table(name:"categories", schema:"") @generateFilterInput { + id: Int! + name: String +} + + +interface Animal @table(name: "animals", schema: "app") @typename(name: "type") { + id: ID! + name: String! + type: String! +} +type Cat implements Animal { + id: ID! + name: String! + type: String! + color: String! +} + +type Dog implements Animal { + id: ID! + name: String! + type: String! + breed: String! +} + +type Query { + posts: [Post] @generate + users: [User] @generate + categories: [Category] @generate + animals: [Animal] @generate +} + +# ================== schema generation fastgql directives ================== + +# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field +# @generateResolver can only be defined on Query and Mutation fields. +# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments +# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error +# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, +# this will modify the object itself and add arguments to the object fields. +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION + +# Generate mutations for an object +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT + +# Generate filter input on an object +directive @generateFilterInput(description: String) repeatable on OBJECT + +# ================== Directives supported by fastgql for Querying ================== + +# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed +# i.e , "postgres", "" +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE + +# Relation directive defines relations cross tables and dialects +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION + +# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION + +directive @typename(name: String!) on INTERFACE + +# =================== Default Scalar types supported by fastgql =================== +scalar Map +# ================== Default Filter input types supported by fastgql ================== + +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} + +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} + +type _AggregateResult { + count: Int! +} + +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} + +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} + +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} + +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} + +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} + +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} + + +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} + +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean } \ No newline at end of file diff --git a/pkg/execution/test/gqlgen.yml b/pkg/execution/test/gqlgen.yml index 7725bc6..b667b07 100644 --- a/pkg/execution/test/gqlgen.yml +++ b/pkg/execution/test/gqlgen.yml @@ -1,50 +1,50 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - "./graph/*.graphql" - -# Where should the generated servergen code go? -exec: - filename: graph/generated/generated.go - package: generated - -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model - -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: ./graph - package: graph - -# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models -# struct_tag: json - -# Optional: turn on to use []Thing instead of []*Thing -omit_slice_element_pointers: true - -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true - -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -#autobind: - -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - ID: - model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - "./graph/*.graphql" + +# Where should the generated servergen code go? +exec: + filename: graph/generated/generated.go + package: generated + +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: ./graph + package: graph + +# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +omit_slice_element_pointers: true + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +#autobind: + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 \ No newline at end of file diff --git a/pkg/execution/test/graph/.graphqlconfig b/pkg/execution/test/graph/.graphqlconfig index 98f702d..76817e6 100644 --- a/pkg/execution/test/graph/.graphqlconfig +++ b/pkg/execution/test/graph/.graphqlconfig @@ -1,16 +1,16 @@ -{ - "name": "GraphQL Schema", - "includes": ["*"], - "excludes": ["augmented_schema.graphql"], - "extensions": { - "endpoints": { - "Default GraphQL Endpoint": { - "url": "http://localhost:8080/graphql", - "headers": { - "user-agent": "JS GraphQL" - }, - "introspect": false - } - } - } +{ + "name": "GraphQL Schema", + "includes": ["*"], + "excludes": ["augmented_schema.graphql"], + "extensions": { + "endpoints": { + "Default GraphQL Endpoint": { + "url": "http://localhost:8080/graphql", + "headers": { + "user-agent": "JS GraphQL" + }, + "introspect": false + } + } + } } \ No newline at end of file diff --git a/pkg/execution/test/graph/common.graphql b/pkg/execution/test/graph/common.graphql index 470c302..053a1c9 100644 --- a/pkg/execution/test/graph/common.graphql +++ b/pkg/execution/test/graph/common.graphql @@ -1,131 +1,131 @@ -# ================== schema generation fastgql directives ================== - -# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field -# @generateResolver can only be defined on Query and Mutation fields. -# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments -# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error -# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, -# this will modify the object itself and add arguments to the object fields. -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION - -# Generate mutations for an object -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT - -# Generate filter input on an object -directive @generateFilterInput(description: String) repeatable on OBJECT | INTERFACE - -directive @isInterfaceFilter on INPUT_FIELD_DEFINITION - -# ================== Directives supported by fastgql for Querying ================== - -# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed -# i.e , "postgres", "" -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE - -# Relation directive defines relations cross tables and dialects -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION - -# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION - -# Typename is the field name that will be used to resolve the type of the interface, -# default model is the default model that will be used to resolve the interface if none is found. -directive @typename(name: String!) on INTERFACE - -# =================== Default Scalar types supported by fastgql =================== -scalar Map -# ================== Default Filter input types supported by fastgql ================== - -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} - -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} - -type _AggregateResult { - count: Int! -} - -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} - -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} - -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} - -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} - -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} - -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} - - -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} - -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean +# ================== schema generation fastgql directives ================== + +# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field +# @generateResolver can only be defined on Query and Mutation fields. +# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments +# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error +# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, +# this will modify the object itself and add arguments to the object fields. +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION + +# Generate mutations for an object +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT + +# Generate filter input on an object +directive @generateFilterInput(description: String) repeatable on OBJECT | INTERFACE + +directive @isInterfaceFilter on INPUT_FIELD_DEFINITION + +# ================== Directives supported by fastgql for Querying ================== + +# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed +# i.e , "postgres", "" +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE + +# Relation directive defines relations cross tables and dialects +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION + +# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION + +# Typename is the field name that will be used to resolve the type of the interface, +# default model is the default model that will be used to resolve the interface if none is found. +directive @typename(name: String!) on INTERFACE + +# =================== Default Scalar types supported by fastgql =================== +scalar Map +# ================== Default Filter input types supported by fastgql ================== + +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} + +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} + +type _AggregateResult { + count: Int! +} + +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} + +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} + +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} + +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} + +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} + +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} + + +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} + +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean } \ No newline at end of file diff --git a/pkg/execution/test/graph/schema.graphql b/pkg/execution/test/graph/schema.graphql index 2d127a0..63d9c80 100644 --- a/pkg/execution/test/graph/schema.graphql +++ b/pkg/execution/test/graph/schema.graphql @@ -1,39 +1,39 @@ -interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { - id: Int! - name: String! - type: String! -} -type Cat implements Animal { - id: Int! - name: String! - type: String! - color: String! -} -type Category @generateFilterInput @table(name: "category") { - id: Int! - name: String -} -type Dog implements Animal { - id: Int! - name: String! - type: String! - breed: String! -} -type Post @generateFilterInput @table(name: "post") @generateMutations { - id: Int! - name: String - categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) - user_id: Int - user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) -} -type Query { - posts: [Post] @generate - users: [User] @generate - categories: [Category] @generate - animals: [Animal] @generate -} -type User @table(name: "user") @generateFilterInput { - id: Int! - name: String! - posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) -} +interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { + id: Int! + name: String! + type: String! +} +type Cat implements Animal { + id: Int! + name: String! + type: String! + color: String! +} +type Category @generateFilterInput @table(name: "category") { + id: Int! + name: String +} +type Dog implements Animal { + id: Int! + name: String! + type: String! + breed: String! +} +type Post @generateFilterInput @table(name: "post") @generateMutations { + id: Int! + name: String + categories: [Category] @relation(type: MANY_TO_MANY, fields: ["id"], references: ["id"], manyToManyTable: "posts_to_categories", manyToManyFields: ["post_id"], manyToManyReferences: ["category_id"]) + user_id: Int + user: User @relation(type: ONE_TO_ONE, fields: ["user_id"], references: ["id"]) +} +type Query { + posts: [Post] @generate + users: [User] @generate + categories: [Category] @generate + animals: [Animal] @generate +} +type User @table(name: "user") @generateFilterInput { + id: Int! + name: String! + posts: [Post] @relation(type: ONE_TO_MANY, fields: ["id"], references: ["user_id"]) +} diff --git a/pkg/schema/fastgql.go b/pkg/schema/fastgql.go index ec73b6e..09bfeac 100644 --- a/pkg/schema/fastgql.go +++ b/pkg/schema/fastgql.go @@ -5,13 +5,14 @@ import ( _ "embed" "errors" "fmt" - "github.com/spf13/cast" "go/types" "io/fs" "os" "strings" "text/template" + "github.com/spf13/cast" + "github.com/99designs/gqlgen/codegen" "github.com/99designs/gqlgen/codegen/templates" @@ -163,7 +164,7 @@ func (f *FastGqlPlugin) CreateAugmented(schema *ast.Schema, augmenters ...Augmen } } // Format augmented schema to *.graphql files - return FormatSchema(f.rootDirectory, schema), nil + return formatSchema(f.rootDirectory, schema), nil } type fastGQLResolver struct { diff --git a/pkg/schema/fastgql.graphql b/pkg/schema/fastgql.graphql index 470c302..053a1c9 100644 --- a/pkg/schema/fastgql.graphql +++ b/pkg/schema/fastgql.graphql @@ -1,131 +1,131 @@ -# ================== schema generation fastgql directives ================== - -# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field -# @generateResolver can only be defined on Query and Mutation fields. -# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments -# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error -# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, -# this will modify the object itself and add arguments to the object fields. -directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION - -# Generate mutations for an object -directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT - -# Generate filter input on an object -directive @generateFilterInput(description: String) repeatable on OBJECT | INTERFACE - -directive @isInterfaceFilter on INPUT_FIELD_DEFINITION - -# ================== Directives supported by fastgql for Querying ================== - -# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed -# i.e , "postgres", "" -directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE - -# Relation directive defines relations cross tables and dialects -directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION - -# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually -directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION - -# Typename is the field name that will be used to resolve the type of the interface, -# default model is the default model that will be used to resolve the interface if none is found. -directive @typename(name: String!) on INTERFACE - -# =================== Default Scalar types supported by fastgql =================== -scalar Map -# ================== Default Filter input types supported by fastgql ================== - -enum _relationType { - ONE_TO_ONE - ONE_TO_MANY - MANY_TO_MANY -} - -enum _OrderingTypes { - ASC - DESC - ASC_NULL_FIRST - DESC_NULL_FIRST - ASC_NULL_LAST - DESC_NULL_LAST -} - -type _AggregateResult { - count: Int! -} - -input StringComparator { - eq: String - neq: String - contains: [String] - notContains: [String] - like: String - ilike: String - suffix: String - prefix: String - isNull: Boolean -} - -input StringListComparator { - eq: [String] - neq: [String] - contains: [String] - containedBy: [String] - overlap: [String] - isNull: Boolean -} - -input IntComparator { - eq: Int - neq: Int - gt: Int - gte: Int - lt: Int - lte: Int - isNull: Boolean -} - -input IntListComparator { - eq: [Int] - neq: [Int] - contains: [Int] - contained: [Int] - overlap: [Int] - isNull: Boolean -} - -input FloatComparator { - eq: Float - neq: Float - gt: Float - gte: Float - lt: Float - lte: Float - isNull: Boolean -} - -input FloatListComparator { - eq: [Float] - neq: [Float] - contains: [Float] - contained: [Float] - overlap: [Float] - isNull: Boolean -} - - -input BooleanComparator { - eq: Boolean - neq: Boolean - isNull: Boolean -} - -input BooleanListComparator { - eq: [Boolean] - neq: [Boolean] - contains: [Boolean] - contained: [Boolean] - overlap: [Boolean] - isNull: Boolean +# ================== schema generation fastgql directives ================== + +# Generate Resolver directive tells fastgql to generate an automatic resolver for a given field +# @generateResolver can only be defined on Query and Mutation fields. +# adding pagination, ordering, aggregate, filter to false will disable the generation of the corresponding arguments +# for filter to work @generateFilterInput must be defined on the object, if its missing you will get an error +# recursive will generate pagination, filtering, ordering and aggregate for all the relations of the object, +# this will modify the object itself and add arguments to the object fields. +directive @generate(filter: Boolean = True, pagination: Boolean = True, ordering: Boolean = True, aggregate: Boolean = True, recursive: Boolean = True, filterTypeName: String) on FIELD_DEFINITION + +# Generate mutations for an object +directive @generateMutations(create: Boolean = True, delete: Boolean = True, update: Boolean = True) on OBJECT + +# Generate filter input on an object +directive @generateFilterInput(description: String) repeatable on OBJECT | INTERFACE + +directive @isInterfaceFilter on INPUT_FIELD_DEFINITION + +# ================== Directives supported by fastgql for Querying ================== + +# Table directive is defined on OBJECTS, if no table directive is defined defaults are assumed +# i.e , "postgres", "" +directive @table(name: String!, dialect: String! = "postgres", schema: String = "") on OBJECT | INTERFACE + +# Relation directive defines relations cross tables and dialects +directive @relation(type: _relationType!, fields: [String!]!, references: [String!]!, manyToManyTable: String = "", manyToManyFields: [String] = [], manyToManyReferences: [String] = []) on FIELD_DEFINITION + +# This will make the field skipped in select, this is useful for fields that are not columns in the database, and you want to resolve it manually +directive @fastgqlField(skipSelect: Boolean = True) on FIELD_DEFINITION + +# Typename is the field name that will be used to resolve the type of the interface, +# default model is the default model that will be used to resolve the interface if none is found. +directive @typename(name: String!) on INTERFACE + +# =================== Default Scalar types supported by fastgql =================== +scalar Map +# ================== Default Filter input types supported by fastgql ================== + +enum _relationType { + ONE_TO_ONE + ONE_TO_MANY + MANY_TO_MANY +} + +enum _OrderingTypes { + ASC + DESC + ASC_NULL_FIRST + DESC_NULL_FIRST + ASC_NULL_LAST + DESC_NULL_LAST +} + +type _AggregateResult { + count: Int! +} + +input StringComparator { + eq: String + neq: String + contains: [String] + notContains: [String] + like: String + ilike: String + suffix: String + prefix: String + isNull: Boolean +} + +input StringListComparator { + eq: [String] + neq: [String] + contains: [String] + containedBy: [String] + overlap: [String] + isNull: Boolean +} + +input IntComparator { + eq: Int + neq: Int + gt: Int + gte: Int + lt: Int + lte: Int + isNull: Boolean +} + +input IntListComparator { + eq: [Int] + neq: [Int] + contains: [Int] + contained: [Int] + overlap: [Int] + isNull: Boolean +} + +input FloatComparator { + eq: Float + neq: Float + gt: Float + gte: Float + lt: Float + lte: Float + isNull: Boolean +} + +input FloatListComparator { + eq: [Float] + neq: [Float] + contains: [Float] + contained: [Float] + overlap: [Float] + isNull: Boolean +} + + +input BooleanComparator { + eq: Boolean + neq: Boolean + isNull: Boolean +} + +input BooleanListComparator { + eq: [Boolean] + neq: [Boolean] + contains: [Boolean] + contained: [Boolean] + overlap: [Boolean] + isNull: Boolean } \ No newline at end of file diff --git a/pkg/schema/fastgql.tpl b/pkg/schema/fastgql.tpl index 939dd28..8430ac1 100644 --- a/pkg/schema/fastgql.tpl +++ b/pkg/schema/fastgql.tpl @@ -1,43 +1,43 @@ -{{- reserveImport "github.com/georgysavva/scany/v2/pgxscan" -}} -{{- reserveImport "github.com/jackc/pgx/v5" }} -{{- reserveImport "github.com/roneli/fastgql/pkg/execution/builders/sql" -}} - -{{- if or (hasPrefix .Field.Name "create") (hasPrefix .Field.Name "delete") (hasPrefix .Field.Name "update") -}} -var data {{.Field.TypeReference.GO | deref}} -q, args, err := sql.BuildQuery(ctx, sql.NewBuilder(r.Cfg)) -if err != nil { - return nil, err -} -if err := sql.ExecuteQuery(ctx, r.Executor, func(rows pgx.Rows) error { - return pgxscan.ScanOne(&data, rows) -}, q, args...); err != nil { - return nil, err -} -return &data, nil -{{- else if eq .Field.TypeReference.Definition.Kind "INTERFACE" -}} -scanner := execution.NewTypeNameScanner[{{.FieldType | ref}}](map[string]reflect.Type{ -{{- range $key, $value := .Implementors }} - {{$key|quote}}: reflect.TypeOf({{$value.Type | deref}}{}), -{{- end -}} -}, {{.ImplementorsTypeName|quote}}) -q, args, err := sql.BuildQuery(ctx, sql.NewBuilder(r.Cfg)) -if err != nil { - return nil, err -} -return sql.Collect[{{.FieldType | ref}}](ctx, r.Executor, func(row pgx.CollectableRow) ({{.FieldType | ref}}, error) { - return scanner.ScanRow(row) -}, q, args...) -{{- else -}} -var data {{.Field.TypeReference.GO | ref}} -q, args, err := sql.BuildQuery(ctx, sql.NewBuilder(r.Cfg)) -if err != nil { - return nil, err -} -if err := sql.ExecuteQuery(ctx, r.Executor, func(rows pgx.Rows) error { - return pgxscan.ScanAll(&data, rows) -}, q, args...); err != nil { - return nil, err -} -return data, nil -{{- end -}} - +{{- reserveImport "github.com/georgysavva/scany/v2/pgxscan" -}} +{{- reserveImport "github.com/jackc/pgx/v5" }} +{{- reserveImport "github.com/roneli/fastgql/pkg/execution/builders/sql" -}} + +{{- if or (hasPrefix .Field.Name "create") (hasPrefix .Field.Name "delete") (hasPrefix .Field.Name "update") -}} +var data {{.Field.TypeReference.GO | deref}} +q, args, err := sql.BuildQuery(ctx, sql.NewBuilder(r.Cfg)) +if err != nil { + return nil, err +} +if err := sql.ExecuteQuery(ctx, r.Executor, func(rows pgx.Rows) error { + return pgxscan.ScanOne(&data, rows) +}, q, args...); err != nil { + return nil, err +} +return &data, nil +{{- else if eq .Field.TypeReference.Definition.Kind "INTERFACE" -}} +scanner := execution.NewTypeNameScanner[{{.FieldType | ref}}](map[string]reflect.Type{ +{{- range $key, $value := .Implementors }} + {{$key|quote}}: reflect.TypeOf({{$value.Type | deref}}{}), +{{- end -}} +}, {{.ImplementorsTypeName|quote}}) +q, args, err := sql.BuildQuery(ctx, sql.NewBuilder(r.Cfg)) +if err != nil { + return nil, err +} +return sql.Collect[{{.FieldType | ref}}](ctx, r.Executor, func(row pgx.CollectableRow) ({{.FieldType | ref}}, error) { + return scanner.ScanRow(row) +}, q, args...) +{{- else -}} +var data {{.Field.TypeReference.GO | ref}} +q, args, err := sql.BuildQuery(ctx, sql.NewBuilder(r.Cfg)) +if err != nil { + return nil, err +} +if err := sql.ExecuteQuery(ctx, r.Executor, func(rows pgx.Rows) error { + return pgxscan.ScanAll(&data, rows) +}, q, args...); err != nil { + return nil, err +} +return data, nil +{{- end -}} + diff --git a/pkg/schema/formatter.go b/pkg/schema/formatter.go index cd8afbd..b32f551 100644 --- a/pkg/schema/formatter.go +++ b/pkg/schema/formatter.go @@ -13,10 +13,10 @@ import ( // Save all fastgql augmented schema into fastgql_schema.graphql file. const defaultFastGqlSchema = "fastgql_schema.graphql" -// FormatSchema into multiple sources, the original format schema from gqlparser lib saves all in one file, +// formatSchema into multiple sources, the original format schema from gqlparser lib saves all in one file, // in this case after augmentation we want to keep all original files and structure and all added definitions to put in // fastgql_schema.graphql file. -func FormatSchema(resolverPackageDir string, schema *ast.Schema) []*ast.Source { +func formatSchema(resolverPackageDir string, schema *ast.Schema) []*ast.Source { if schema == nil { return nil } diff --git a/pkg/schema/gql.go b/pkg/schema/gql.go index 0092f76..e933c9d 100644 --- a/pkg/schema/gql.go +++ b/pkg/schema/gql.go @@ -25,7 +25,7 @@ func GetType(a *ast.Type) *ast.Type { return a } -func GetDirectiveValue(d *ast.Directive, name string) interface{} { +func GetDirectiveValue(d *ast.Directive, name string) any { if d == nil { return nil } diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index cab98d3..c009eb7 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -1,5 +1,12 @@ package schema +import ( + "fmt" + + "github.com/spf13/cast" + "github.com/vektah/gqlparser/v2/ast" +) + type ArgName string const ( @@ -14,3 +21,69 @@ const ( tableDirectiveName = "table" relationDirectiveName = "relation" ) + +type TableDirective struct { + // Name of the table/collection + Name string + // Schema name table resides in, can be omitted + Schema string + // Dialect name the table resides in + Dialect string +} + +type RelationType string + +const ( + OneToMany RelationType = "ONE_TO_MANY" + OneToOne RelationType = "ONE_TO_ONE" + ManyToMany RelationType = "MANY_TO_MANY" +) + +type RelationDirective struct { + RelType RelationType + BaseTable string + ReferenceTable string + Fields []string + References []string + ManyToManyTable string + ManyToManyReferences []string + ManyToManyFields []string +} + +func GetTableDirective(def *ast.Definition) (*TableDirective, error) { + d := def.Directives.ForName("table") + if d == nil { + return nil, fmt.Errorf("failed to get table directive for %s", def.Name) + } + return &TableDirective{ + Name: getArgumentValue(d.Arguments, "name"), + Schema: getArgumentValue(d.Arguments, "schema"), + Dialect: getArgumentValue(d.Arguments, "dialect"), + }, nil +} + +func GetRelationDirective(field *ast.FieldDefinition) *RelationDirective { + d := field.Directives.ForName(relationDirectiveName) + if d == nil { + return nil + } + relType := d.Arguments.ForName("type").Value.Raw + return &RelationDirective{ + RelType: RelationType(relType), + Fields: cast.ToStringSlice(GetDirectiveValue(d, "fields")), + References: cast.ToStringSlice(GetDirectiveValue(d, "references")), + BaseTable: cast.ToString(GetDirectiveValue(d, "baseTable")), + ReferenceTable: cast.ToString(GetDirectiveValue(d, "refTable")), + ManyToManyTable: cast.ToString(GetDirectiveValue(d, "manyToManyTable")), + ManyToManyFields: cast.ToStringSlice(GetDirectiveValue(d, "manyToManyFields")), + ManyToManyReferences: cast.ToStringSlice(GetDirectiveValue(d, "manyToManyReferences")), + } +} + +func getArgumentValue(args ast.ArgumentList, name string) string { + arg := args.ForName(name) + if arg == nil { + return "" + } + return arg.Value.Raw +} diff --git a/pkg/schema/server.gotpl b/pkg/schema/server.gotpl index 4f61316..a1089fe 100644 --- a/pkg/schema/server.gotpl +++ b/pkg/schema/server.gotpl @@ -1,43 +1,43 @@ -{{ reserveImport "context" }} -{{ reserveImport "log" }} -{{ reserveImport "net/http" }} -{{ reserveImport "os" }} - -{{ reserveImport "github.com/99designs/gqlgen/graphql/playground" }} -{{ reserveImport "github.com/99designs/gqlgen/graphql/handler" }} -{{ reserveImport "github.com/jackc/pgx/v5/pgxpool" }} -{{ reserveImport "github.com/roneli/fastgql/pkg/execution/builders" }} -{{ reserveImport "github.com/roneli/fastgql/pkg/log/adapters" }} - -const defaultPort = "8080" - -const defaultPGConnection = "postgresql://localhost/postgres?user=postgres" - -func main() { - port := os.Getenv("PORT") - if port == "" { - port = defaultPort - } - pgConnectionString := os.Getenv("PG_CONN_STR") - if pgConnectionString == "" { - pgConnectionString = defaultPGConnection - } - - pool, err := pgxpool.New(context.Background(), pgConnectionString) - if err != nil { - panic(err) - } - defer pool.Close() - resolver := &{{ lookupImport .ResolverPackageName}}.Resolver{Executor: pool} - executableSchema := {{ lookupImport .ExecPackageName }}.NewExecutableSchema({{ lookupImport .ExecPackageName}}.Config{Resolvers:resolver}) - // Add logger to config for building trace logging - cfg := &builders.Config{Schema: executableSchema.Schema(), Logger: nil} - resolver.Cfg = cfg - resolver.Executor = pool - - srv := handler.NewDefaultServer(executableSchema) - http.Handle("/", playground.Handler("GraphQL playground", "/query")) - http.Handle("/query", srv) - log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) - log.Fatal(http.ListenAndServe(":" + port, nil)) +{{ reserveImport "context" }} +{{ reserveImport "log" }} +{{ reserveImport "net/http" }} +{{ reserveImport "os" }} + +{{ reserveImport "github.com/99designs/gqlgen/graphql/playground" }} +{{ reserveImport "github.com/99designs/gqlgen/graphql/handler" }} +{{ reserveImport "github.com/jackc/pgx/v5/pgxpool" }} +{{ reserveImport "github.com/roneli/fastgql/pkg/execution/builders" }} +{{ reserveImport "github.com/roneli/fastgql/pkg/log/adapters" }} + +const defaultPort = "8080" + +const defaultPGConnection = "postgresql://localhost/postgres?user=postgres" + +func main() { + port := os.Getenv("PORT") + if port == "" { + port = defaultPort + } + pgConnectionString := os.Getenv("PG_CONN_STR") + if pgConnectionString == "" { + pgConnectionString = defaultPGConnection + } + + pool, err := pgxpool.New(context.Background(), pgConnectionString) + if err != nil { + panic(err) + } + defer pool.Close() + resolver := &{{ lookupImport .ResolverPackageName}}.Resolver{Executor: pool} + executableSchema := {{ lookupImport .ExecPackageName }}.NewExecutableSchema({{ lookupImport .ExecPackageName}}.Config{Resolvers:resolver}) + // Add logger to config for building trace logging + cfg := &builders.Config{Schema: executableSchema.Schema(), Logger: nil} + resolver.Cfg = cfg + resolver.Executor = pool + + srv := handler.NewDefaultServer(executableSchema) + http.Handle("/", playground.Handler("GraphQL playground", "/query")) + http.Handle("/query", srv) + log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) + log.Fatal(http.ListenAndServe(":" + port, nil)) } \ No newline at end of file diff --git a/pkg/schema/test/no_fastgql.graphql b/pkg/schema/test/no_fastgql.graphql index 711bfdb..d9d5ea0 100644 --- a/pkg/schema/test/no_fastgql.graphql +++ b/pkg/schema/test/no_fastgql.graphql @@ -1,8 +1,8 @@ - -type Person { - name: String! -} - -type Query { - person: Person + +type Person { + name: String! +} + +type Query { + person: Person } \ No newline at end of file diff --git a/pkg/schema/test/no_fastgql_gqlgen.yml b/pkg/schema/test/no_fastgql_gqlgen.yml index 4d3dcdc..a0b5002 100644 --- a/pkg/schema/test/no_fastgql_gqlgen.yml +++ b/pkg/schema/test/no_fastgql_gqlgen.yml @@ -1,50 +1,50 @@ -# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - "test/no_fastgql.graphql" - -# Where should the generated servergen code go? -exec: - filename: graph/generated/generated.go - package: generated - -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model - -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: ./graph - package: graph - -# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models -# struct_tag: json - -# Optional: turn on to use []Thing instead of []*Thing -omit_slice_element_pointers: true - -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true - -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -#autobind: - -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - ID: - model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - "test/no_fastgql.graphql" + +# Where should the generated servergen code go? +exec: + filename: graph/generated/generated.go + package: generated + +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: ./graph + package: graph + +# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +omit_slice_element_pointers: true + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +#autobind: + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 \ No newline at end of file diff --git a/pkg/schema/testdata/.graphqlconfig b/pkg/schema/testdata/.graphqlconfig index 98f702d..76817e6 100644 --- a/pkg/schema/testdata/.graphqlconfig +++ b/pkg/schema/testdata/.graphqlconfig @@ -1,16 +1,16 @@ -{ - "name": "GraphQL Schema", - "includes": ["*"], - "excludes": ["augmented_schema.graphql"], - "extensions": { - "endpoints": { - "Default GraphQL Endpoint": { - "url": "http://localhost:8080/graphql", - "headers": { - "user-agent": "JS GraphQL" - }, - "introspect": false - } - } - } +{ + "name": "GraphQL Schema", + "includes": ["*"], + "excludes": ["augmented_schema.graphql"], + "extensions": { + "endpoints": { + "Default GraphQL Endpoint": { + "url": "http://localhost:8080/graphql", + "headers": { + "user-agent": "JS GraphQL" + }, + "introspect": false + } + } + } } \ No newline at end of file diff --git a/pkg/schema/testdata/aggregation_expected.graphql b/pkg/schema/testdata/aggregation_expected.graphql index 7546c43..ea7e71e 100644 --- a/pkg/schema/testdata/aggregation_expected.graphql +++ b/pkg/schema/testdata/aggregation_expected.graphql @@ -1,19 +1,19 @@ -type Object @generateFilterInput { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} -type Query { - scalar: String - generateField: [ObjectWithRecursive] @generate(ordering: true) - dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] - """ - generateField Aggregate - """ - _generateFieldAggregate(groupBy: [ObjectWithRecursiveGroupBy!]): [ObjectWithRecursivesAggregate!]! @generate(filter: true) -} +type Object @generateFilterInput { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} +type Query { + scalar: String + generateField: [ObjectWithRecursive] @generate(ordering: true) + dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] + """ + generateField Aggregate + """ + _generateFieldAggregate(groupBy: [ObjectWithRecursiveGroupBy!]): [ObjectWithRecursivesAggregate!]! @generate(filter: true) +} diff --git a/pkg/schema/testdata/aggregation_fastgql_expected.graphql b/pkg/schema/testdata/aggregation_fastgql_expected.graphql index a2e43b4..3930763 100644 --- a/pkg/schema/testdata/aggregation_fastgql_expected.graphql +++ b/pkg/schema/testdata/aggregation_fastgql_expected.graphql @@ -1,47 +1,47 @@ -""" -Group by ObjectWithRecursive -""" -enum ObjectWithRecursiveGroupBy { - """ - Group by id - """ - ID - """ - Group by name - """ - NAME -} -""" -max aggregator for ObjectWithRecursive -""" -type ObjectWithRecursiveMin { - """ - Compute the maxiumum for id - """ - id: ID! - """ - Compute the maxiumum for name - """ - name: String! -} -""" -Aggregate ObjectWithRecursive -""" -type ObjectWithRecursivesAggregate { - """ - Group - """ - group: Map - """ - Count results - """ - count: Int! - """ - Computes the maximum of the non-null input values. - """ - max: ObjectWithRecursiveMin - """ - Computes the minimum of the non-null input values. - """ - min: ObjectWithRecursiveMin -} +""" +Group by ObjectWithRecursive +""" +enum ObjectWithRecursiveGroupBy { + """ + Group by id + """ + ID + """ + Group by name + """ + NAME +} +""" +max aggregator for ObjectWithRecursive +""" +type ObjectWithRecursiveMin { + """ + Compute the maxiumum for id + """ + id: ID! + """ + Compute the maxiumum for name + """ + name: String! +} +""" +Aggregate ObjectWithRecursive +""" +type ObjectWithRecursivesAggregate { + """ + Group + """ + group: Map + """ + Count results + """ + count: Int! + """ + Computes the maximum of the non-null input values. + """ + max: ObjectWithRecursiveMin + """ + Computes the minimum of the non-null input values. + """ + min: ObjectWithRecursiveMin +} diff --git a/pkg/schema/testdata/base.graphql b/pkg/schema/testdata/base.graphql index 0db9cb8..e7f23e2 100644 --- a/pkg/schema/testdata/base.graphql +++ b/pkg/schema/testdata/base.graphql @@ -1,15 +1,15 @@ -type Object @generateFilterInput { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} -type Query { - scalar: String - generateField: [ObjectWithRecursive] @generate(ordering: true) - dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] +type Object @generateFilterInput { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} +type Query { + scalar: String + generateField: [ObjectWithRecursive] @generate(ordering: true) + dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/base_filter_only_expected.graphql b/pkg/schema/testdata/base_filter_only_expected.graphql index 008d400..fb71ddf 100644 --- a/pkg/schema/testdata/base_filter_only_expected.graphql +++ b/pkg/schema/testdata/base_filter_only_expected.graphql @@ -1,19 +1,19 @@ -type Object @generateFilterInput { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} -type Query { - scalar: String - generateField: [ObjectWithRecursive] @generate(ordering: true) - dontGenerate( - """ - Filter dontGenerate - """ - filter: ObjectFilterInput): [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] -} +type Object @generateFilterInput { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} +type Query { + scalar: String + generateField: [ObjectWithRecursive] @generate(ordering: true) + dontGenerate( + """ + Filter dontGenerate + """ + filter: ObjectFilterInput): [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] +} diff --git a/pkg/schema/testdata/base_filter_only_fastgql_expected.graphql b/pkg/schema/testdata/base_filter_only_fastgql_expected.graphql index 6e3d897..49745cb 100644 --- a/pkg/schema/testdata/base_filter_only_fastgql_expected.graphql +++ b/pkg/schema/testdata/base_filter_only_fastgql_expected.graphql @@ -1,15 +1,15 @@ -input ObjectFilterInput { - name: StringComparator - """ - Logical AND of FilterInput - """ - AND: [ObjectFilterInput] - """ - Logical OR of FilterInput - """ - OR: [ObjectFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: ObjectFilterInput +input ObjectFilterInput { + name: StringComparator + """ + Logical AND of FilterInput + """ + AND: [ObjectFilterInput] + """ + Logical OR of FilterInput + """ + OR: [ObjectFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: ObjectFilterInput } \ No newline at end of file diff --git a/pkg/schema/testdata/filter_interface.graphql b/pkg/schema/testdata/filter_interface.graphql index 682e933..d33c763 100644 --- a/pkg/schema/testdata/filter_interface.graphql +++ b/pkg/schema/testdata/filter_interface.graphql @@ -1,22 +1,22 @@ -interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { - id: Int! - name: String! - type: String! -} -type Cat implements Animal { - id: Int! - name: String! - type: String! - color: String! -} - -type Dog implements Animal { - id: Int! - name: String! - type: String! - breed: String! -} - -type Query { - animals: [Animal] @generate -} +interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { + id: Int! + name: String! + type: String! +} +type Cat implements Animal { + id: Int! + name: String! + type: String! + color: String! +} + +type Dog implements Animal { + id: Int! + name: String! + type: String! + breed: String! +} + +type Query { + animals: [Animal] @generate +} diff --git a/pkg/schema/testdata/filter_interface_expected.graphql b/pkg/schema/testdata/filter_interface_expected.graphql index 8ed88c8..192d988 100644 --- a/pkg/schema/testdata/filter_interface_expected.graphql +++ b/pkg/schema/testdata/filter_interface_expected.graphql @@ -1,27 +1,27 @@ -interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { - id: Int! - name: String! - type: String! -} -type Cat implements Animal { - id: Int! - name: String! - type: String! - color: String! -} - -type Dog implements Animal { - id: Int! - name: String! - type: String! - breed: String! -} - -type Query { - animals( - """ - Filter animals - """ - filter: AnimalFilterInput - ): [Animal] @generate -} +interface Animal @table(name: "animals") @typename(name: "type") @generateFilterInput { + id: Int! + name: String! + type: String! +} +type Cat implements Animal { + id: Int! + name: String! + type: String! + color: String! +} + +type Dog implements Animal { + id: Int! + name: String! + type: String! + breed: String! +} + +type Query { + animals( + """ + Filter animals + """ + filter: AnimalFilterInput + ): [Animal] @generate +} diff --git a/pkg/schema/testdata/filter_interface_fastgql_expected.graphql b/pkg/schema/testdata/filter_interface_fastgql_expected.graphql index 2a8cfd4..4589cd1 100644 --- a/pkg/schema/testdata/filter_interface_fastgql_expected.graphql +++ b/pkg/schema/testdata/filter_interface_fastgql_expected.graphql @@ -1,57 +1,57 @@ -input AnimalFilterInput { - id: IntComparator - name: StringComparator - type: StringComparator - cat: CatFilterInput @isInterfaceFilter - dog: DogFilterInput @isInterfaceFilter - """ - Logical AND of FilterInput - """ - AND: [AnimalFilterInput] - """ - Logical OR of FilterInput - """ - OR: [AnimalFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: AnimalFilterInput -} - -input CatFilterInput { - id: IntComparator - name: StringComparator - type: StringComparator - color: StringComparator - """ - Logical AND of FilterInput - """ - AND: [CatFilterInput] - """ - Logical OR of FilterInput - """ - OR: [CatFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: CatFilterInput -} - -input DogFilterInput { - id: IntComparator - name: StringComparator - type: StringComparator - breed: StringComparator - """ - Logical AND of FilterInput - """ - AND: [DogFilterInput] - """ - Logical OR of FilterInput - """ - OR: [DogFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: DogFilterInput +input AnimalFilterInput { + id: IntComparator + name: StringComparator + type: StringComparator + cat: CatFilterInput @isInterfaceFilter + dog: DogFilterInput @isInterfaceFilter + """ + Logical AND of FilterInput + """ + AND: [AnimalFilterInput] + """ + Logical OR of FilterInput + """ + OR: [AnimalFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: AnimalFilterInput +} + +input CatFilterInput { + id: IntComparator + name: StringComparator + type: StringComparator + color: StringComparator + """ + Logical AND of FilterInput + """ + AND: [CatFilterInput] + """ + Logical OR of FilterInput + """ + OR: [CatFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: CatFilterInput +} + +input DogFilterInput { + id: IntComparator + name: StringComparator + type: StringComparator + breed: StringComparator + """ + Logical AND of FilterInput + """ + AND: [DogFilterInput] + """ + Logical OR of FilterInput + """ + OR: [DogFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: DogFilterInput } \ No newline at end of file diff --git a/pkg/schema/testdata/mutations.graphql b/pkg/schema/testdata/mutations.graphql index a691ea4..6840068 100644 --- a/pkg/schema/testdata/mutations.graphql +++ b/pkg/schema/testdata/mutations.graphql @@ -1,15 +1,15 @@ -type Object @generateFilterInput @generateMutations { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} -type Query { - scalar: String - generateField: [ObjectWithRecursive] @generate(ordering: true) - dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] +type Object @generateFilterInput @generateMutations { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} +type Query { + scalar: String + generateField: [ObjectWithRecursive] @generate(ordering: true) + dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/mutations_expected.graphql b/pkg/schema/testdata/mutations_expected.graphql index a691ea4..6840068 100644 --- a/pkg/schema/testdata/mutations_expected.graphql +++ b/pkg/schema/testdata/mutations_expected.graphql @@ -1,15 +1,15 @@ -type Object @generateFilterInput @generateMutations { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} -type Query { - scalar: String - generateField: [ObjectWithRecursive] @generate(ordering: true) - dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] +type Object @generateFilterInput @generateMutations { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} +type Query { + scalar: String + generateField: [ObjectWithRecursive] @generate(ordering: true) + dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/mutations_fastgql_expected.graphql b/pkg/schema/testdata/mutations_fastgql_expected.graphql index a29a880..9b5e5c6 100644 --- a/pkg/schema/testdata/mutations_fastgql_expected.graphql +++ b/pkg/schema/testdata/mutations_fastgql_expected.graphql @@ -1,45 +1,45 @@ -""" -Graphql Mutations -""" -type Mutation { - """ - AutoGenerated input for Object - """ - createObjects(inputs: [CreateObjectInput!]!): ObjectsPayload - """ - AutoGenerated input for Object - """ - deleteObjects( - """ - cascade on delete - """ - cascade: Boolean): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") - """ - AutoGenerated input for Object - """ - updateObjects(input: UpdateObjectInput!): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") -} -""" -Autogenerated payload object -""" -type ObjectsPayload { - """ - rows affection by mutation - """ - rows_affected: Int! - objects: [Object] -} -""" -AutoGenerated input for Object -""" -input CreateObjectInput { - id: ID! - name: String! -} -""" -AutoGenerated update input for Object -""" -input UpdateObjectInput { - id: ID - name: String +""" +Graphql Mutations +""" +type Mutation { + """ + AutoGenerated input for Object + """ + createObjects(inputs: [CreateObjectInput!]!): ObjectsPayload + """ + AutoGenerated input for Object + """ + deleteObjects( + """ + cascade on delete + """ + cascade: Boolean): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") + """ + AutoGenerated input for Object + """ + updateObjects(input: UpdateObjectInput!): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") +} +""" +Autogenerated payload object +""" +type ObjectsPayload { + """ + rows affection by mutation + """ + rows_affected: Int! + objects: [Object] +} +""" +AutoGenerated input for Object +""" +input CreateObjectInput { + id: ID! + name: String! +} +""" +AutoGenerated update input for Object +""" +input UpdateObjectInput { + id: ID + name: String } \ No newline at end of file diff --git a/pkg/schema/testdata/mutations_fastgql_filter_expected.graphql b/pkg/schema/testdata/mutations_fastgql_filter_expected.graphql index 07d4488..6f5963b 100644 --- a/pkg/schema/testdata/mutations_fastgql_filter_expected.graphql +++ b/pkg/schema/testdata/mutations_fastgql_filter_expected.graphql @@ -1,68 +1,68 @@ -""" -Graphql Mutations -""" -type Mutation { - """ - AutoGenerated input for Object - """ - createObjects(inputs: [CreateObjectInput!]!): ObjectsPayload - """ - AutoGenerated input for Object - """ - deleteObjects( - """ - cascade on delete - """ - cascade: Boolean, - """ - Filter deleteObjects - """ - filter: ObjectFilterInput): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") - """ - AutoGenerated input for Object - """ - updateObjects(input: UpdateObjectInput!, - """ - Filter updateObjects - """ - filter: ObjectFilterInput): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") -} -input ObjectFilterInput { - name: StringComparator - """ - Logical AND of FilterInput - """ - AND: [ObjectFilterInput] - """ - Logical OR of FilterInput - """ - OR: [ObjectFilterInput] - """ - Logical NOT of FilterInput - """ - NOT: ObjectFilterInput -} -""" -Autogenerated payload object -""" -type ObjectsPayload { - """ - rows affection by mutation - """ - rows_affected: Int! - objects: [Object] -} -""" -AutoGenerated input for Object -""" -input CreateObjectInput { - id: ID! - name: String! -} -""" -AutoGenerated update input for Object -""" -input UpdateObjectInput { - id: ID - name: String +""" +Graphql Mutations +""" +type Mutation { + """ + AutoGenerated input for Object + """ + createObjects(inputs: [CreateObjectInput!]!): ObjectsPayload + """ + AutoGenerated input for Object + """ + deleteObjects( + """ + cascade on delete + """ + cascade: Boolean, + """ + Filter deleteObjects + """ + filter: ObjectFilterInput): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") + """ + AutoGenerated input for Object + """ + updateObjects(input: UpdateObjectInput!, + """ + Filter updateObjects + """ + filter: ObjectFilterInput): ObjectsPayload @generate(filter: true, filterTypeName: "ObjectFilterInput") +} +input ObjectFilterInput { + name: StringComparator + """ + Logical AND of FilterInput + """ + AND: [ObjectFilterInput] + """ + Logical OR of FilterInput + """ + OR: [ObjectFilterInput] + """ + Logical NOT of FilterInput + """ + NOT: ObjectFilterInput +} +""" +Autogenerated payload object +""" +type ObjectsPayload { + """ + rows affection by mutation + """ + rows_affected: Int! + objects: [Object] +} +""" +AutoGenerated input for Object +""" +input CreateObjectInput { + id: ID! + name: String! +} +""" +AutoGenerated update input for Object +""" +input UpdateObjectInput { + id: ID + name: String } \ No newline at end of file diff --git a/pkg/schema/testdata/mutations_filter_expected.graphql b/pkg/schema/testdata/mutations_filter_expected.graphql index 08e2fcf..895a5e3 100644 --- a/pkg/schema/testdata/mutations_filter_expected.graphql +++ b/pkg/schema/testdata/mutations_filter_expected.graphql @@ -1,19 +1,19 @@ -type Object @generateFilterInput @generateMutations { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} -type Query { - scalar: String - generateField: [ObjectWithRecursive] @generate(ordering: true) - dontGenerate( - """ - Filter dontGenerate - """ - filter: ObjectFilterInput): [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] +type Object @generateFilterInput @generateMutations { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} +type Query { + scalar: String + generateField: [ObjectWithRecursive] @generate(ordering: true) + dontGenerate( + """ + Filter dontGenerate + """ + filter: ObjectFilterInput): [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/ordering_augmentation_test.graphql b/pkg/schema/testdata/ordering_augmentation_test.graphql index 52c610e..5a097e2 100644 --- a/pkg/schema/testdata/ordering_augmentation_test.graphql +++ b/pkg/schema/testdata/ordering_augmentation_test.graphql @@ -1,18 +1,18 @@ -type Object { - id: ID! - name: String! -} - -type ObjectWithRecursive { - id: ID! - name: String! - children: [ObjectWithRecursive] -} - - -type Query { - scalar: String - paginated: [ObjectWithRecursive] @generate(ordering: true) - nonPaginated: [Object] @generate(ordering: False) - listNoDirective: [Object] +type Object { + id: ID! + name: String! +} + +type ObjectWithRecursive { + id: ID! + name: String! + children: [ObjectWithRecursive] +} + + +type Query { + scalar: String + paginated: [ObjectWithRecursive] @generate(ordering: true) + nonPaginated: [Object] @generate(ordering: False) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/ordering_expected.graphql b/pkg/schema/testdata/ordering_expected.graphql index 4f8eb43..c314994 100644 --- a/pkg/schema/testdata/ordering_expected.graphql +++ b/pkg/schema/testdata/ordering_expected.graphql @@ -1,23 +1,23 @@ -type Object @generateFilterInput { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children( - """ - Ordering for ObjectWithRecursive - """ - orderBy: [ObjectWithRecursiveOrdering]): [ObjectWithRecursive] -} -type Query { - scalar: String - generateField( - """ - Ordering for ObjectWithRecursive - """ - orderBy: [ObjectWithRecursiveOrdering]): [ObjectWithRecursive] @generate(ordering: true) - dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] +type Object @generateFilterInput { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children( + """ + Ordering for ObjectWithRecursive + """ + orderBy: [ObjectWithRecursiveOrdering]): [ObjectWithRecursive] +} +type Query { + scalar: String + generateField( + """ + Ordering for ObjectWithRecursive + """ + orderBy: [ObjectWithRecursiveOrdering]): [ObjectWithRecursive] @generate(ordering: true) + dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/ordering_fastgql_expected.graphql b/pkg/schema/testdata/ordering_fastgql_expected.graphql index 0fd9f2e..4baadfa 100644 --- a/pkg/schema/testdata/ordering_fastgql_expected.graphql +++ b/pkg/schema/testdata/ordering_fastgql_expected.graphql @@ -1,13 +1,13 @@ -""" -Ordering for ObjectWithRecursive -""" -input ObjectWithRecursiveOrdering { - """ - Order ObjectWithRecursive by id - """ - id: _OrderingTypes - """ - Order ObjectWithRecursive by name - """ - name: _OrderingTypes +""" +Ordering for ObjectWithRecursive +""" +input ObjectWithRecursiveOrdering { + """ + Order ObjectWithRecursive by id + """ + id: _OrderingTypes + """ + Order ObjectWithRecursive by name + """ + name: _OrderingTypes } \ No newline at end of file diff --git a/pkg/schema/testdata/pagination_expected.graphql b/pkg/schema/testdata/pagination_expected.graphql index c548875..2709bb8 100644 --- a/pkg/schema/testdata/pagination_expected.graphql +++ b/pkg/schema/testdata/pagination_expected.graphql @@ -1,31 +1,31 @@ -type Object @generateFilterInput { - id: ID! - name: String! -} -type ObjectWithRecursive { - id: ID! - name: String! - children( - """ - Limit - """ - limit: Int = 100, - """ - Offset - """ - offset: Int = 0): [ObjectWithRecursive] -} -type Query { - scalar: String - generateField( - """ - Limit - """ - limit: Int = 100, - """ - Offset - """ - offset: Int = 0): [ObjectWithRecursive] @generate(ordering: true) - dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) - listNoDirective: [Object] +type Object @generateFilterInput { + id: ID! + name: String! +} +type ObjectWithRecursive { + id: ID! + name: String! + children( + """ + Limit + """ + limit: Int = 100, + """ + Offset + """ + offset: Int = 0): [ObjectWithRecursive] +} +type Query { + scalar: String + generateField( + """ + Limit + """ + limit: Int = 100, + """ + Offset + """ + offset: Int = 0): [ObjectWithRecursive] @generate(ordering: true) + dontGenerate: [Object] @generate(ordering: False, aggregate: false, pagination: false) + listNoDirective: [Object] } \ No newline at end of file diff --git a/pkg/schema/testdata/pagination_test.graphql b/pkg/schema/testdata/pagination_test.graphql index 58248bf..3213109 100644 --- a/pkg/schema/testdata/pagination_test.graphql +++ b/pkg/schema/testdata/pagination_test.graphql @@ -1,18 +1,18 @@ -type Object { - id: ID! - name: String! -} - -type ObjectWithRecursivePagination { - id: ID! - name: String! - children: [ObjectWithRecursivePagination] -} - - -type Query { - scalar: String - paginated: [ObjectWithRecursivePagination] @generate(pagination: True) - nonPaginated: [Object] @generate(pagination: False) - listNoDirective: [Object] +type Object { + id: ID! + name: String! +} + +type ObjectWithRecursivePagination { + id: ID! + name: String! + children: [ObjectWithRecursivePagination] +} + + +type Query { + scalar: String + paginated: [ObjectWithRecursivePagination] @generate(pagination: True) + nonPaginated: [Object] @generate(pagination: False) + listNoDirective: [Object] } \ No newline at end of file