diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..232dda4 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +# we will put our integration testing in this path +INTEGRATION_TEST_PATH?=./integration_tests + +# set of env variables that you need for testing +POSTGRES_URI=postgresql://postgres:test@localhost:5432/postgres + +# this command will start a docker components that we set in docker-compose.yml +docker.start.components: + docker-compose -f docker-compose-test.yaml up -d --remove-orphans postgres; + +# shutting down docker components +docker.stop: + docker-compose -f docker-compose-test.yaml down -v + +# this command will trigger integration test +# INTEGRATION_TEST_SUITE_PATH is used for run specific test in Golang, if it's not specified +# it will run all tests under ./integration_tests directory +test.integration: + $(MAKE) docker.start.components + - go test -tags=integration $(INTEGRATION_TEST_PATH) -count=1 -run=$(INTEGRATION_TEST_SUITE_PATH) --integration --postgres-uri=$(POSTGRES_URI) + $(MAKE) docker.stop + +# this command will trigger integration test with verbose mode +test.integration.debug: + $(MAKE) docker.start.components + - go test -tags=integration $(INTEGRATION_TEST_PATH) -count=1 -v -run=$(INTEGRATION_TEST_SUITE_PATH) --integration --postgres-uri=$(POSTGRES_URI) + $(MAKE) docker.stop diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml new file mode 100644 index 0000000..2c6e233 --- /dev/null +++ b/docker-compose-test.yaml @@ -0,0 +1,11 @@ +services: + postgres: + image: "postgres:15-alpine" + restart: always + command: [ "postgres", "-c", "log_statement=all" ] + environment: + POSTGRES_PASSWORD: test + ports: + - "5432:5432" + volumes: + - ./integration_tests/init.sql:/docker-entrypoint-initdb.d/db.sql \ No newline at end of file diff --git a/go.mod b/go.mod index af9dad1..c6b5d26 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.19 require ( github.com/99designs/gqlgen v0.17.13 - github.com/KnightHacks/knighthacks_shared v0.0.0-20221121010043-cf2a1397df2e + github.com/KnightHacks/knighthacks_shared v0.0.0-20221122100922-bb5380fde900 github.com/gin-gonic/gin v1.8.1 - github.com/jackc/pgx/v4 v4.16.1 + github.com/jackc/pgx/v5 v5.1.1 github.com/vektah/gqlparser/v2 v2.4.7 ) @@ -24,14 +24,9 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.12.1 // indirect - github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.0 // indirect github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.11.0 // indirect - github.com/jackc/puddle v1.2.1 // indirect + github.com/jackc/puddle/v2 v2.1.2 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -39,18 +34,19 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ugorji/go/codec v1.2.7 // indirect github.com/urfave/cli/v2 v2.11.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.2.0 // indirect golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect - golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 2514356..e44d14f 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,8 @@ github.com/99designs/gqlgen v0.17.13 h1:ETUEqvRg5Zvr1lXtpoRdj026fzVay0ZlJPwI33qXLIw= github.com/99designs/gqlgen v0.17.13/go.mod h1:w1brbeOdqVyNJI553BGwtwdVcYu1LKeYE1opLWN9RgQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/KnightHacks/knighthacks_shared v0.0.0-20221120025925-541d81e10069 h1:yvEO67PhIBcBO/Yn3Mh2iUPSgVC0az/T3ls7IRg3ZRk= -github.com/KnightHacks/knighthacks_shared v0.0.0-20221120025925-541d81e10069/go.mod h1:1PTL6VJL44UN/BdfFyxT8GsAa46W2+9C5dDRnfqKJYA= -github.com/KnightHacks/knighthacks_shared v0.0.0-20221121010043-cf2a1397df2e h1:2s45geYfGUlA0P+OQC9BB05WBKCaooB+TStnGXC6u/c= -github.com/KnightHacks/knighthacks_shared v0.0.0-20221121010043-cf2a1397df2e/go.mod h1:1PTL6VJL44UN/BdfFyxT8GsAa46W2+9C5dDRnfqKJYA= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/KnightHacks/knighthacks_shared v0.0.0-20221122100922-bb5380fde900 h1:u7Kk62/6IS5f5ROwx+YvEqqZlaVZoBckk/nXHeKqRFE= +github.com/KnightHacks/knighthacks_shared v0.0.0-20221122100922-bb5380fde900/go.mod h1:CoTeIUwu66fDN5b6Ib6aioECv5Cib7KVPEdrY4jL0g4= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= @@ -14,14 +10,9 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= 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/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -32,8 +23,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -42,11 +31,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc= github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -61,89 +47,34 @@ github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMe github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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 v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= -github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 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/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= -github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= -github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= -github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= -github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/pgx/v5 v5.1.1 h1:pZD79K1SYv8wc2HmCQA6VdmRQi7/OtCfv9bM3WAXUYA= +github.com/jackc/pgx/v5 v5.1.1/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk= +github.com/jackc/puddle/v2 v2.1.2 h1:0f7vaaXINONKTsxYDn4otOAiJanX/BMeAtY//BXqzlg= +github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels= 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/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -157,40 +88,24 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -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/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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= @@ -203,62 +118,38 @@ github.com/vektah/gqlparser/v2 v2.4.7/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rty github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20190813141303-74dc4d7220e7/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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 h1:N9Vc/rorQUDes6B9CNdIxAn5jODGj2wzfrei2x4wNj4= -golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY= golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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= @@ -266,32 +157,24 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME= -golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= 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= @@ -309,7 +192,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -319,4 +201,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/graph/entity.resolvers.go b/graph/entity.resolvers.go index 6e6bacc..bc7282f 100644 --- a/graph/entity.resolvers.go +++ b/graph/entity.resolvers.go @@ -5,12 +5,16 @@ package graph import ( "context" - "github.com/KnightHacks/knighthacks_shared/models" "github.com/KnightHacks/knighthacks_users/graph/generated" "github.com/KnightHacks/knighthacks_users/graph/model" ) +// FindHackathonApplicationByID is the resolver for the findHackathonApplicationByID field. +func (r *entityResolver) FindHackathonApplicationByID(ctx context.Context, id string) (*model.HackathonApplication, error) { + return &model.HackathonApplication{ID: id}, nil +} + // FindUserByID is the resolver for the findUserByID field. func (r *entityResolver) FindUserByID(ctx context.Context, id string) (*model.User, error) { user, err := r.Resolver.Repository.GetUserByID(ctx, id) diff --git a/graph/generated/federation.go b/graph/generated/federation.go index 55a2b64..e4d672d 100644 --- a/graph/generated/federation.go +++ b/graph/generated/federation.go @@ -80,6 +80,26 @@ func (ec *executionContext) __resolve_entities(ctx context.Context, representati }() switch typeName { + case "HackathonApplication": + resolverName, err := entityResolverNameForHackathonApplication(ctx, rep) + if err != nil { + return fmt.Errorf(`finding resolver for Entity "HackathonApplication": %w`, err) + } + switch resolverName { + + case "findHackathonApplicationByID": + id0, err := ec.unmarshalNID2string(ctx, rep["id"]) + if err != nil { + return fmt.Errorf(`unmarshalling param 0 for findHackathonApplicationByID(): %w`, err) + } + entity, err := ec.resolvers.Entity().FindHackathonApplicationByID(ctx, id0) + if err != nil { + return fmt.Errorf(`resolving Entity "HackathonApplication": %w`, err) + } + + list[idx[i]] = entity + return nil + } case "User": resolverName, err := entityResolverNameForUser(ctx, rep) if err != nil { @@ -185,6 +205,23 @@ func (ec *executionContext) __resolve_entities(ctx context.Context, representati } } +func entityResolverNameForHackathonApplication(ctx context.Context, rep map[string]interface{}) (string, error) { + for { + var ( + m map[string]interface{} + val interface{} + ok bool + ) + _ = val + m = rep + if _, ok = m["id"]; !ok { + break + } + return "findHackathonApplicationByID", nil + } + return "", fmt.Errorf("%w for HackathonApplication", ErrTypeNotFound) +} + func entityResolverNameForUser(ctx context.Context, rep map[string]interface{}) (string, error) { for { var ( diff --git a/graph/generated/generated.go b/graph/generated/generated.go index c000278..aaa0152 100644 --- a/graph/generated/generated.go +++ b/graph/generated/generated.go @@ -40,6 +40,7 @@ type Config struct { type ResolverRoot interface { Entity() EntityResolver + HackathonApplication() HackathonApplicationResolver Mutation() MutationResolver Query() QueryResolver User() UserResolver @@ -64,10 +65,16 @@ type ComplexityRoot struct { } Entity struct { + FindHackathonApplicationByID func(childComplexity int, id string) int FindUserByID func(childComplexity int, id string) int FindUserByOAuthUIDAndOAuthProvider func(childComplexity int, oAuthUID string, oAuthProvider models.Provider) int } + HackathonApplication struct { + ID func(childComplexity int) int + User func(childComplexity int) int + } + LoginPayload struct { AccessToken func(childComplexity int) int AccountExists func(childComplexity int) int @@ -164,9 +171,13 @@ type ComplexityRoot struct { } type EntityResolver interface { + FindHackathonApplicationByID(ctx context.Context, id string) (*model.HackathonApplication, error) FindUserByID(ctx context.Context, id string) (*model.User, error) FindUserByOAuthUIDAndOAuthProvider(ctx context.Context, oAuthUID string, oAuthProvider models.Provider) (*model.User, error) } +type HackathonApplicationResolver interface { + User(ctx context.Context, obj *model.HackathonApplication) (*model.User, error) +} type MutationResolver interface { Register(ctx context.Context, provider models.Provider, encryptedOauthAccessToken string, input model.NewUser) (*model.RegistrationPayload, error) UpdateUser(ctx context.Context, id string, input model.UpdatedUser) (*model.User, error) @@ -250,6 +261,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EducationInfo.Name(childComplexity), true + case "Entity.findHackathonApplicationByID": + if e.complexity.Entity.FindHackathonApplicationByID == nil { + break + } + + args, err := ec.field_Entity_findHackathonApplicationByID_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Entity.FindHackathonApplicationByID(childComplexity, args["id"].(string)), true + case "Entity.findUserByID": if e.complexity.Entity.FindUserByID == nil { break @@ -274,6 +297,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Entity.FindUserByOAuthUIDAndOAuthProvider(childComplexity, args["oAuthUID"].(string), args["oAuthProvider"].(models.Provider)), true + case "HackathonApplication.id": + if e.complexity.HackathonApplication.ID == nil { + break + } + + return e.complexity.HackathonApplication.ID(childComplexity), true + + case "HackathonApplication.user": + if e.complexity.HackathonApplication.User == nil { + break + } + + return e.complexity.HackathonApplication.User(childComplexity), true + case "LoginPayload.accessToken": if e.complexity.LoginPayload.AccessToken == nil { break @@ -1052,6 +1089,11 @@ type RegistrationPayload { refreshToken: String! } +extend type HackathonApplication @key(fields: "id") { + id: ID! + user: User! @goField(forceResolver: true) +} + type Query { """ The code supplied must be the code given to the frontend by the oauth flow @@ -1092,11 +1134,12 @@ type Mutation { `, BuiltIn: true}, {Name: "../../federation/entity.graphql", Input: ` # a union of all types that use the @key directive -union _Entity = User +union _Entity = HackathonApplication | User # fake type to build resolver interfaces for users to implement type Entity { - findUserByID(id: ID!,): User! + findHackathonApplicationByID(id: ID!,): HackathonApplication! + findUserByID(id: ID!,): User! findUserByOAuthUIDAndOAuthProvider(oAuthUID: String!,oAuthProvider: Provider!,): User! } @@ -1147,6 +1190,21 @@ func (ec *executionContext) dir_pagination_args(ctx context.Context, rawArgs map return args, nil } +func (ec *executionContext) field_Entity_findHackathonApplicationByID_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + func (ec *executionContext) field_Entity_findUserByID_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1743,6 +1801,67 @@ func (ec *executionContext) fieldContext_EducationInfo_level(ctx context.Context return fc, nil } +func (ec *executionContext) _Entity_findHackathonApplicationByID(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Entity_findHackathonApplicationByID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Entity().FindHackathonApplicationByID(rctx, fc.Args["id"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.HackathonApplication) + fc.Result = res + return ec.marshalNHackathonApplication2ᚖgithubᚗcomᚋKnightHacksᚋknighthacks_usersᚋgraphᚋmodelᚐHackathonApplication(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Entity_findHackathonApplicationByID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Entity", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_HackathonApplication_id(ctx, field) + case "user": + return ec.fieldContext_HackathonApplication_user(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type HackathonApplication", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Entity_findHackathonApplicationByID_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Entity_findUserByID(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Entity_findUserByID(ctx, field) if err != nil { @@ -1929,6 +2048,132 @@ func (ec *executionContext) fieldContext_Entity_findUserByOAuthUIDAndOAuthProvid return fc, nil } +func (ec *executionContext) _HackathonApplication_id(ctx context.Context, field graphql.CollectedField, obj *model.HackathonApplication) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_HackathonApplication_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_HackathonApplication_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "HackathonApplication", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _HackathonApplication_user(ctx context.Context, field graphql.CollectedField, obj *model.HackathonApplication) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_HackathonApplication_user(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.HackathonApplication().User(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.User) + fc.Result = res + return ec.marshalNUser2ᚖgithubᚗcomᚋKnightHacksᚋknighthacks_usersᚋgraphᚋmodelᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_HackathonApplication_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "HackathonApplication", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "firstName": + return ec.fieldContext_User_firstName(ctx, field) + case "lastName": + return ec.fieldContext_User_lastName(ctx, field) + case "fullName": + return ec.fieldContext_User_fullName(ctx, field) + case "email": + return ec.fieldContext_User_email(ctx, field) + case "phoneNumber": + return ec.fieldContext_User_phoneNumber(ctx, field) + case "pronouns": + return ec.fieldContext_User_pronouns(ctx, field) + case "age": + return ec.fieldContext_User_age(ctx, field) + case "role": + return ec.fieldContext_User_role(ctx, field) + case "gender": + return ec.fieldContext_User_gender(ctx, field) + case "race": + return ec.fieldContext_User_race(ctx, field) + case "oAuth": + return ec.fieldContext_User_oAuth(ctx, field) + case "mailingAddress": + return ec.fieldContext_User_mailingAddress(ctx, field) + case "mlh": + return ec.fieldContext_User_mlh(ctx, field) + case "shirtSize": + return ec.fieldContext_User_shirtSize(ctx, field) + case "yearsOfExperience": + return ec.fieldContext_User_yearsOfExperience(ctx, field) + case "educationInfo": + return ec.fieldContext_User_educationInfo(ctx, field) + case "apiKey": + return ec.fieldContext_User_apiKey(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _LoginPayload_accountExists(ctx context.Context, field graphql.CollectedField, obj *model.LoginPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_LoginPayload_accountExists(ctx, field) if err != nil { @@ -7941,6 +8186,13 @@ func (ec *executionContext) __Entity(ctx context.Context, sel ast.SelectionSet, switch obj := (obj).(type) { case nil: return graphql.Null + case model.HackathonApplication: + return ec._HackathonApplication(ctx, sel, &obj) + case *model.HackathonApplication: + if obj == nil { + return graphql.Null + } + return ec._HackathonApplication(ctx, sel, obj) case model.User: return ec._User(ctx, sel, &obj) case *model.User: @@ -8057,6 +8309,29 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Entity") + case "findHackathonApplicationByID": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Entity_findHackathonApplicationByID(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) case "findUserByID": field := field @@ -8114,6 +8389,54 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g return out } +var hackathonApplicationImplementors = []string{"HackathonApplication", "_Entity"} + +func (ec *executionContext) _HackathonApplication(ctx context.Context, sel ast.SelectionSet, obj *model.HackathonApplication) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, hackathonApplicationImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("HackathonApplication") + case "id": + + out.Values[i] = ec._HackathonApplication_id(ctx, field, obj) + + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "user": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._HackathonApplication_user(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var loginPayloadImplementors = []string{"LoginPayload"} func (ec *executionContext) _LoginPayload(ctx context.Context, sel ast.SelectionSet, obj *model.LoginPayload) graphql.Marshaler { @@ -9341,6 +9664,20 @@ func (ec *executionContext) unmarshalNEducationInfoInput2ᚖgithubᚗcomᚋKnigh return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNHackathonApplication2githubᚗcomᚋKnightHacksᚋknighthacks_usersᚋgraphᚋmodelᚐHackathonApplication(ctx context.Context, sel ast.SelectionSet, v model.HackathonApplication) graphql.Marshaler { + return ec._HackathonApplication(ctx, sel, &v) +} + +func (ec *executionContext) marshalNHackathonApplication2ᚖgithubᚗcomᚋKnightHacksᚋknighthacks_usersᚋgraphᚋmodelᚐHackathonApplication(ctx context.Context, sel ast.SelectionSet, v *model.HackathonApplication) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._HackathonApplication(ctx, sel, v) +} + func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalID(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/graph/model/models_custom.go b/graph/model/models_custom.go new file mode 100644 index 0000000..67be71e --- /dev/null +++ b/graph/model/models_custom.go @@ -0,0 +1,8 @@ +package model + +import "encoding/json" + +func (u User) String() string { + marshal, _ := json.Marshal(u) + return string(marshal) +} diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index 0de55b5..451586e 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -41,6 +41,13 @@ type EducationInfoUpdate struct { Level *LevelOfStudy `json:"level"` } +type HackathonApplication struct { + ID string `json:"id"` + User *User `json:"user"` +} + +func (HackathonApplication) IsEntity() {} + type LoginPayload struct { // If false then you must register immediately following this. Else, you are logged in and have access to your own user. AccountExists bool `json:"accountExists"` diff --git a/graph/schema.graphqls b/graph/schema.graphqls index 3a52ace..27763fb 100644 --- a/graph/schema.graphqls +++ b/graph/schema.graphqls @@ -233,6 +233,11 @@ type RegistrationPayload { refreshToken: String! } +extend type HackathonApplication @key(fields: "id") { + id: ID! + user: User! @goField(forceResolver: true) +} + type Query { """ The code supplied must be the code given to the frontend by the oauth flow diff --git a/graph/schema.resolvers.go b/graph/schema.resolvers.go index 7fb2e44..31e3ae0 100644 --- a/graph/schema.resolvers.go +++ b/graph/schema.resolvers.go @@ -5,10 +5,10 @@ package graph import ( "context" - "crypto/rand" "encoding/base64" "errors" "fmt" + "math/rand" "net/http" "net/url" @@ -21,6 +21,11 @@ import ( "github.com/KnightHacks/knighthacks_users/repository" ) +// User is the resolver for the user field. +func (r *hackathonApplicationResolver) User(ctx context.Context, obj *model.HackathonApplication) (*model.User, error) { + return r.Repository.GetUserByID(ctx, obj.ID) +} + // Register is the resolver for the register field. func (r *mutationResolver) Register(ctx context.Context, provider models.Provider, encryptedOauthAccessToken string, input model.NewUser) (*model.RegistrationPayload, error) { // Decode the encrypted OAuth AccessToken from base64 @@ -88,7 +93,7 @@ func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, err // AddAPIKey is the resolver for the addAPIKey field. func (r *mutationResolver) AddAPIKey(ctx context.Context, userID string) (*model.APIKey, error) { - return r.Repository.AddAPIKey(ctx, userID) + return r.Repository.AddAPIKey(ctx, userID, GenerateAPIKey(100)) } // DeleteAPIKey is the resolver for the deleteAPIKey field. @@ -264,7 +269,12 @@ func (r *userResolver) Mlh(ctx context.Context, obj *model.User) (*model.MLHTerm // APIKey is the resolver for the apiKey field. func (r *userResolver) APIKey(ctx context.Context, obj *model.User) (*model.APIKey, error) { - return r.Repository.GetAPIKey(ctx, obj) + return r.Repository.GetAPIKey(ctx, obj.ID) +} + +// HackathonApplication returns generated.HackathonApplicationResolver implementation. +func (r *Resolver) HackathonApplication() generated.HackathonApplicationResolver { + return &hackathonApplicationResolver{r} } // Mutation returns generated.MutationResolver implementation. @@ -276,6 +286,17 @@ func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } // User returns generated.UserResolver implementation. func (r *Resolver) User() generated.UserResolver { return &userResolver{r} } +type hackathonApplicationResolver struct{ *Resolver } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } type userResolver struct{ *Resolver } + +var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func GenerateAPIKey(length int) string { + b := make([]rune, length) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} diff --git a/integration_tests/database_repository_test.go b/integration_tests/database_repository_test.go new file mode 100644 index 0000000..5a977b0 --- /dev/null +++ b/integration_tests/database_repository_test.go @@ -0,0 +1,1097 @@ +package integration_tests + +import ( + "context" + "flag" + "fmt" + shared_db_utils "github.com/KnightHacks/knighthacks_shared/database" + "github.com/KnightHacks/knighthacks_shared/models" + "github.com/KnightHacks/knighthacks_shared/utils" + model "github.com/KnightHacks/knighthacks_users/graph/model" + "github.com/KnightHacks/knighthacks_users/repository/database" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + "log" + "os" + "reflect" + "testing" + "time" +) + +var integrationTest = flag.Bool("integration", false, "whether to run integration tests") +var databaseUri = flag.String("postgres-uri", "postgresql://postgres:test@localhost:5432/postgres", "postgres uri for running integration tests") + +var databaseRepository *database.DatabaseRepository + +type Test[A any, T any] struct { + name string + args A + want T + wantErr bool +} + +func TestMain(t *testing.M) { + flag.Parse() + // check if integration testing is disabled + if *integrationTest == false { + return + } + + // connect to database + var err error + pool, err := shared_db_utils.ConnectWithRetries(*databaseUri) + fmt.Printf("connecting to database, pool=%v, err=%v\n", pool, err) + if err != nil { + log.Fatalf("unable to connect to database err=%v\n", err) + } + + databaseRepository, err = database.NewDatabaseRepository(context.Background(), pool) + if err != nil { + log.Fatalf("unable to initialize database repository err=%v\n", err) + } + os.Exit(t.Run()) +} + +func TestDatabaseRepository_AddAPIKey(t *testing.T) { + type args struct { + ctx context.Context + id string + key string + } + tests := []Test[args, *model.APIKey]{ + { + name: "add APIKey to Joe Bob", + args: args{ + ctx: context.Background(), + id: "1", + key: "12345abcdef", + }, + want: &model.APIKey{ + Key: "12345abcdef", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + apiKey, err := databaseRepository.AddAPIKey(tt.args.ctx, tt.args.id, tt.args.key) + if (err != nil) != tt.wantErr { + t.Errorf("AddAPIKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(apiKey.Key, tt.want.Key) { + t.Errorf("AddAPIKey() apiKey = %v, want %v", apiKey, tt.want) + } + }) + } +} + +func TestDatabaseRepository_CreateUser(t *testing.T) { + type args struct { + ctx context.Context + oAuth *model.OAuth + input *model.NewUser + } + tests := []Test[args, *model.User]{ + { + name: "create thomas", + args: args{ + ctx: context.Background(), + oAuth: &model.OAuth{ + Provider: models.ProviderGithub, + UID: "100", + }, + input: &model.NewUser{ + FirstName: "Thomas", + LastName: "Bob", + Email: "thomas.bob@example.com", + PhoneNumber: "100-203-9112", + Pronouns: &model.PronounsInput{ + Subjective: "He", + Objective: "Him", + }, + Age: utils.Ptr(21), + MailingAddress: &model.MailingAddressInput{ + Country: "United States", + State: "Florida", + City: "Orlando", + PostalCode: "32765", + AddressLines: []string{ + "1234 Joe Mama Row", + }, + }, + Mlh: &model.MLHTermsInput{ + SendMessages: true, + CodeOfConduct: true, + ShareInfo: true, + }, + ShirtSize: model.ShirtSizeM, + YearsOfExperience: utils.Ptr(3.5), + EducationInfo: &model.EducationInfoInput{ + Name: "University of Central Florida", + GraduationDate: time.Date(2026, 12, 20, 0, 0, 0, 0, time.UTC), + Major: "Bachelors of Science", + Level: utils.Ptr(model.LevelOfStudyFreshman), + }, + Gender: utils.Ptr("male"), + Race: []model.Race{model.RaceCaucasian, model.RaceAfricanAmerican}, + }, + }, + want: &model.User{ + //ID: "", don't check for this + FirstName: "Thomas", + LastName: "Bob", + Email: "thomas.bob@example.com", + PhoneNumber: "100-203-9112", + Pronouns: &model.Pronouns{ + Subjective: "He", + Objective: "Him", + }, + Age: utils.Ptr(21), + MailingAddress: &model.MailingAddress{ + Country: "United States", + State: "Florida", + City: "Orlando", + PostalCode: "32765", + AddressLines: []string{ + "1234 Joe Mama Row", + }, + }, + Role: models.RoleNormal, + Gender: utils.Ptr("male"), + Race: []model.Race{model.RaceCaucasian, model.RaceAfricanAmerican}, + OAuth: &model.OAuth{ + Provider: models.ProviderGithub, + UID: "100", + }, + Mlh: &model.MLHTerms{ + SendMessages: true, + CodeOfConduct: true, + ShareInfo: true, + }, + ShirtSize: model.ShirtSizeM, + YearsOfExperience: utils.Ptr(3.5), + EducationInfo: &model.EducationInfo{ + Name: "University of Central Florida", + GraduationDate: time.Date(2026, 12, 20, 0, 0, 0, 0, time.UTC), + Major: "Bachelors of Science", + Level: utils.Ptr(model.LevelOfStudyFreshman), + }, + APIKey: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + user, err := databaseRepository.CreateUser(tt.args.ctx, tt.args.oAuth, tt.args.input) + if (err != nil) != tt.wantErr { + t.Errorf("CreateUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(user, tt.want) { + t.Errorf("CreateUser() user = %v, want %v", user, tt.want) + } + }) + } +} + +func TestDatabaseRepository_DeleteAPIKey(t *testing.T) { + type args struct { + ctx context.Context + id string + } + tests := []Test[args, any]{ + { + name: "delete Joe Biron's APIKey", + args: args{ + ctx: context.Background(), + id: "2", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.DeleteAPIKey(tt.args.ctx, tt.args.id); (err != nil) != tt.wantErr { + t.Errorf("DeleteAPIKey() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_DeleteUser(t *testing.T) { + type args struct { + ctx context.Context + id string + } + tests := []Test[args, bool]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.DeleteUser(tt.args.ctx, tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("DeleteUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("DeleteUser() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetAPIKey(t *testing.T) { + type args struct { + ctx context.Context + userId string + } + tests := []Test[args, *model.APIKey]{ + { + name: "get Joe Bob's APIKey", + args: args{ + ctx: context.Background(), + userId: "1", + }, + want: &model.APIKey{ + Key: "12345abcdef", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotApiKey, err := databaseRepository.GetAPIKey(tt.args.ctx, tt.args.userId) + if (err != nil) != tt.wantErr { + t.Errorf("GetAPIKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotApiKey.Key, tt.want.Key) { + t.Errorf("GetAPIKey() gotApiKey = %v, want %v", gotApiKey, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetById(t *testing.T) { + type args struct { + id int + } + type want struct { + pronouns model.Pronouns + exists bool + } + tests := []Test[args, want]{ + { + name: "get he/him", + args: args{id: 1}, + want: want{ + pronouns: model.Pronouns{ + Subjective: "he", + Objective: "him", + }, + exists: true, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pronouns, got1 := databaseRepository.GetById(tt.args.id) + if !reflect.DeepEqual(pronouns, tt.want.pronouns) { + t.Errorf("GetById() pronouns = %v, want %v", pronouns, tt.want) + } + if got1 != tt.want.exists { + t.Errorf("GetById() got1 = %v, want %v", got1, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetByPronouns(t *testing.T) { + type args struct { + pronouns model.Pronouns + } + type want struct { + id int + exists bool + } + tests := []Test[args, want]{ + { + name: "get he him by pronouns", + args: args{ + pronouns: model.Pronouns{ + Subjective: "he", + Objective: "him", + }, + }, + want: want{ + id: 1, + exists: true, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + id, exists := databaseRepository.GetByPronouns(tt.args.pronouns) + if id != tt.want.id { + t.Errorf("GetByPronouns() userId = %v, want %v", id, tt.want) + } + if exists != tt.want.exists { + t.Errorf("GetByPronouns() exists = %v, want %v", exists, tt.want.exists) + } + }) + } +} + +func TestDatabaseRepository_GetOAuth(t *testing.T) { + type args struct { + ctx context.Context + userId string + } + tests := []Test[args, *model.OAuth]{ + { + name: "get joe bob's oauth", + args: args{ + ctx: context.Background(), + userId: "1", + }, + want: &model.OAuth{ + Provider: models.ProviderGithub, + UID: "1", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.GetOAuth(tt.args.ctx, tt.args.userId) + if (err != nil) != tt.wantErr { + t.Errorf("GetOAuth() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetOAuth() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetOrCreatePronoun(t *testing.T) { + type args struct { + ctx context.Context + queryable shared_db_utils.Queryable + pronouns model.Pronouns + input *model.NewUser + } + tests := []Test[args, *int]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + got, err := databaseRepository.GetOrCreatePronoun(tt.args.ctx, tt.args.queryable, tt.args.pronouns, tt.args.input) + if (err != nil) != tt.wantErr { + t.Errorf("GetOrCreatePronoun() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetOrCreatePronoun() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetUserByID(t *testing.T) { + type args struct { + ctx context.Context + id string + } + tests := []Test[args, *model.User]{ + { + name: "get Joe Bob by id", + args: args{ + ctx: context.Background(), + id: "1", + }, + want: &model.User{ + ID: "1", + FirstName: "Joe", + LastName: "Bob", + Email: "joe.bob@example.com", + PhoneNumber: "100-200-3000", + Pronouns: &model.Pronouns{ + Subjective: "he", + Objective: "him", + }, + Age: utils.Ptr(22), + Role: models.RoleNormal, + Gender: utils.Ptr("MALE"), + Race: []model.Race{"CAUCASIAN"}, + OAuth: nil, + MailingAddress: nil, + Mlh: nil, + ShirtSize: model.ShirtSizeL, + YearsOfExperience: utils.Ptr(3.5), + EducationInfo: nil, + APIKey: nil, + }, + wantErr: false, + }, + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.GetUserByID(tt.args.ctx, tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("GetUserByID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetUserByID() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetUserByOAuthUID(t *testing.T) { + type args struct { + ctx context.Context + oAuthUID string + provider models.Provider + } + tests := []Test[args, *model.User]{ + { + name: "get Joe Bob by oauth id", + args: args{ + ctx: context.Background(), + oAuthUID: "1", + provider: models.ProviderGithub, + }, + want: &model.User{ + ID: "1", + FirstName: "Joe", + LastName: "Bob", + Email: "joe.bob@example.com", + PhoneNumber: "100-200-3000", + Pronouns: &model.Pronouns{ + Subjective: "he", + Objective: "him", + }, + Age: utils.Ptr(22), + Role: models.RoleNormal, + Gender: utils.Ptr("MALE"), + Race: []model.Race{"CAUCASIAN"}, + OAuth: nil, + MailingAddress: nil, + Mlh: nil, + ShirtSize: model.ShirtSizeL, + YearsOfExperience: utils.Ptr(3.5), + EducationInfo: nil, + APIKey: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.GetUserByOAuthUID(tt.args.ctx, tt.args.oAuthUID, tt.args.provider) + if (err != nil) != tt.wantErr { + t.Errorf("GetUserByOAuthUID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetUserByOAuthUID() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetUserMLHTerms(t *testing.T) { + type args struct { + ctx context.Context + userId string + } + tests := []Test[args, *model.MLHTerms]{ + { + name: "", + args: args{ + ctx: context.Background(), + userId: "1", + }, + want: &model.MLHTerms{ + SendMessages: true, + CodeOfConduct: true, + ShareInfo: true, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + got, err := databaseRepository.GetUserMLHTerms(tt.args.ctx, tt.args.userId) + if (err != nil) != tt.wantErr { + t.Errorf("GetUserMLHTerms() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetUserMLHTerms() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetUserMailingAddress(t *testing.T) { + type args struct { + ctx context.Context + userId string + } + tests := []Test[args, *model.MailingAddress]{ + { + name: "get mailing address for Joe Bob", + args: args{ + ctx: context.Background(), + userId: "1", + }, + want: &model.MailingAddress{ + Country: "United States", + State: "Florida", + City: "Orlando", + PostalCode: "32765", + AddressLines: []string{ + "1000 Abc Rd", + "APT 69", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.GetUserMailingAddress(tt.args.ctx, tt.args.userId) + if (err != nil) != tt.wantErr { + t.Errorf("GetUserMailingAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetUserMailingAddress() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_GetUsers(t *testing.T) { + type args struct { + ctx context.Context + first int + after string + } + type want struct { + users []*model.User + total int + } + tests := []Test[args, want]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + users, total, err := databaseRepository.GetUsers(tt.args.ctx, tt.args.first, tt.args.after) + if (err != nil) != tt.wantErr { + t.Errorf("GetUsers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(users, tt.want) { + t.Errorf("GetUsers() users = %v, want %v", users, tt.want) + } + if total != tt.want.total { + t.Errorf("GetUsers() total = %v, want %v", total, tt.want.total) + } + }) + } +} + +func TestDatabaseRepository_InsertEducationInfo(t *testing.T) { + type args struct { + ctx context.Context + queryable shared_db_utils.Queryable + userId int + input *model.EducationInfoInput + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.InsertEducationInfo(tt.args.ctx, tt.args.queryable, tt.args.userId, tt.args.input); (err != nil) != tt.wantErr { + t.Errorf("InsertEducationInfo() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_InsertMLHTerms(t *testing.T) { + type args struct { + ctx context.Context + queryable shared_db_utils.Queryable + userId int + input *model.MLHTermsInput + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.InsertMLHTerms(tt.args.ctx, tt.args.queryable, tt.args.userId, tt.args.input); (err != nil) != tt.wantErr { + t.Errorf("InsertMLHTerms() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_InsertMailingAddress(t *testing.T) { + type args struct { + ctx context.Context + queryable shared_db_utils.Queryable + userId int + input *model.MailingAddressInput + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.InsertMailingAddress(tt.args.ctx, tt.args.queryable, tt.args.userId, tt.args.input); (err != nil) != tt.wantErr { + t.Errorf("InsertMailingAddress() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_InsertUser(t *testing.T) { + type args struct { + ctx context.Context + queryable shared_db_utils.Queryable + input *model.NewUser + pronounIdPtr *int + oAuth *model.OAuth + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.InsertUser(tt.args.ctx, tt.args.queryable, tt.args.input, tt.args.pronounIdPtr, tt.args.oAuth) + if (err != nil) != tt.wantErr { + t.Errorf("InsertUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("InsertUser() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_SearchUser(t *testing.T) { + type args struct { + ctx context.Context + name string + } + tests := []Test[args, []*model.User]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + users, err := databaseRepository.SearchUser(tt.args.ctx, tt.args.name) + if (err != nil) != tt.wantErr { + t.Errorf("SearchUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(users, tt.want) { + t.Errorf("SearchUser() users = %v, want %v", users, tt.want) + } + }) + } +} + +func TestDatabaseRepository_Set(t *testing.T) { + type args struct { + id int + pronouns model.Pronouns + } + tests := []struct { + name string + args args + }{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + databaseRepository.Set(tt.args.id, tt.args.pronouns) + }) + } +} + +func TestDatabaseRepository_UpdateAge(t *testing.T) { + type args struct { + ctx context.Context + id string + age *int + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.UpdateAge(tt.args.ctx, tt.args.id, tt.args.age, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateAge() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateEducationInfo(t *testing.T) { + + type args struct { + ctx context.Context + id string + input *model.EducationInfoUpdate + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateEducationInfo(tt.args.ctx, tt.args.id, tt.args.input, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateEducationInfo() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateEmail(t *testing.T) { + + type args struct { + ctx context.Context + id string + email *string + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateEmail(tt.args.ctx, tt.args.id, tt.args.email, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateEmail() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateFirstName(t *testing.T) { + type args struct { + ctx context.Context + id string + first *string + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateFirstName(tt.args.ctx, tt.args.id, tt.args.first, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateFirstName() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateGender(t *testing.T) { + type args struct { + ctx context.Context + id string + gender *string + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.UpdateGender(tt.args.ctx, tt.args.id, tt.args.gender, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateGender() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateLastName(t *testing.T) { + type args struct { + ctx context.Context + id string + last *string + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateLastName(tt.args.ctx, tt.args.id, tt.args.last, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateLastName() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateMLHTerms(t *testing.T) { + type args struct { + ctx context.Context + id string + input *model.MLHTermsUpdate + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateMLHTerms(tt.args.ctx, tt.args.id, tt.args.input, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateMLHTerms() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateMailingAddress(t *testing.T) { + type args struct { + ctx context.Context + id string + input *model.MailingAddressUpdate + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.UpdateMailingAddress(tt.args.ctx, tt.args.id, tt.args.input, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateMailingAddress() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdatePhoneNumber(t *testing.T) { + type args struct { + ctx context.Context + id string + number *string + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdatePhoneNumber(tt.args.ctx, tt.args.id, tt.args.number, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdatePhoneNumber() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdatePronouns(t *testing.T) { + type args struct { + ctx context.Context + id string + pronoun *model.PronounsInput + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.UpdatePronouns(tt.args.ctx, tt.args.id, tt.args.pronoun, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdatePronouns() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateRace(t *testing.T) { + type args struct { + ctx context.Context + id string + races []model.Race + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateRace(tt.args.ctx, tt.args.id, tt.args.races, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateRace() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateShirtSize(t *testing.T) { + type args struct { + ctx context.Context + id string + shirtSize *model.ShirtSize + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := databaseRepository.UpdateShirtSize(tt.args.ctx, tt.args.id, tt.args.shirtSize, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateShirtSize() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDatabaseRepository_UpdateUser(t *testing.T) { + type args struct { + ctx context.Context + id string + input *model.UpdatedUser + } + tests := []Test[args, *model.User]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := databaseRepository.UpdateUser(tt.args.ctx, tt.args.id, tt.args.input) + if (err != nil) != tt.wantErr { + t.Errorf("UpdateUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("UpdateUser() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDatabaseRepository_UpdateYearsOfExperience(t *testing.T) { + type args struct { + ctx context.Context + id string + years *float64 + tx pgx.Tx + } + tests := []Test[args, any]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if err := databaseRepository.UpdateYearsOfExperience(tt.args.ctx, tt.args.id, tt.args.years, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("UpdateYearsOfExperience() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNewDatabaseRepository(t *testing.T) { + type args struct { + databasePool *pgxpool.Pool + } + tests := []Test[args, *database.DatabaseRepository]{ + { + name: "creating matching database repository", + args: args{ + databasePool: databaseRepository.DatabasePool, + }, + want: &database.DatabaseRepository{ + DatabasePool: databaseRepository.DatabasePool, + PronounMap: databaseRepository.PronounMap, + PronounReverseMap: databaseRepository.PronounReverseMap, + }, + wantErr: false, + }, + { + name: "creating nil databasepool", + args: args{ + databasePool: nil, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got, _ := database.NewDatabaseRepository(context.Background(), tt.args.databasePool); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewDatabaseRepository() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestScanUser(t *testing.T) { + type args struct { + user *model.User + scannable database.Scannable + } + tests := []Test[args, *int]{ + + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := database.ScanUser(tt.args.user, tt.args.scannable) + if (err != nil) != tt.wantErr { + t.Errorf("ScanUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ScanUser() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/integration_tests/init.sql b/integration_tests/init.sql new file mode 100644 index 0000000..a7245fe --- /dev/null +++ b/integration_tests/init.sql @@ -0,0 +1,265 @@ +-- SCHEMA START +create type semester as enum ('FALL', 'SPRING', 'SUMMER'); + +create type subscription_tier as enum ('BRONZE', 'SILVER', 'GOLD', 'PLATINUM'); + +create table sponsors +( + id serial, + name varchar not null, + tier subscription_tier not null, + since date not null, + description varchar, + website varchar, + logo_url varchar, + constraint sponsors_pk + primary key (id, name) +); + +create unique index sponsors_id_uindex + on sponsors (id); + +create unique index sponsors_name_uindex + on sponsors (name); + +create table terms +( + id serial + constraint terms_pk + primary key, + year integer not null, + semester semester not null +); + +create unique index terms_id_uindex + on terms (id); + +create table hackathons +( + id serial + constraint hackathons_pk + primary key, + term_id serial + constraint hackathons_terms_id_fk + references terms, + start_date timestamp not null, + end_date timestamp not null +); + +create unique index hackathons_id_uindex + on hackathons (id); + +create unique index hackathons_term_id_uindex + on hackathons (term_id); + +create table pronouns +( + id serial + constraint pronouns_pk + primary key, + subjective varchar not null, + objective varchar not null +); + +create unique index pronouns_id_uindex + on pronouns (id); + +create table users +( + id serial + constraint users_pk + primary key, + email varchar not null, + phone_number varchar, + last_name varchar not null, + age integer, + pronoun_id integer + constraint users_pronouns_id_fk + references pronouns + deferrable initially deferred, + first_name varchar not null, + role varchar not null, + oauth_uid varchar not null + constraint users_oauth_uid_unique + unique, + oauth_provider varchar not null, + years_of_experience double precision, + shirt_size varchar not null, + race character varying[], + gender varchar +); + +create unique index users_email_uindex + on users (email); + +create unique index users_phone_number_uindex + on users (phone_number); + +create table hackathon_sponsors +( + hackathon_id integer not null + constraint hackathon_sponsors_hackathons_null_fk + references hackathons, + sponsor_id integer not null + constraint hackathon_sponsors_sponsors_null_fk + references sponsors (id) +); + +create table events +( + id serial + constraint events_pk + primary key, + hackathon_id integer not null + constraint events_hackathons_id_fk + references hackathons, + location varchar not null, + start_date timestamp not null, + end_date timestamp not null, + name varchar not null, + description varchar not null +); + +create table hackathon_applications +( + id serial + constraint hackathon_applications_pk + primary key, + user_id integer not null + constraint hackathon_applications_users_id_fk + references users, + hackathon_id integer not null + constraint hackathon_applications_hackathons_id_fk + references hackathons, + why_attend character varying[] not null, + what_do_you_want_to_learn character varying[] not null, + share_info_with_sponsors boolean not null, + application_status varchar not null, + created_time timestamp default now() not null, + status_change_time timestamp +); + +create table mailing_addresses +( + user_id integer not null + constraint mailing_addresses_pk + primary key + constraint mailing_addresses_users_id_fk + references users, + country varchar not null, + state varchar not null, + city varchar not null, + postal_code varchar not null, + address_lines character varying[] not null +); + +create table mlh_terms +( + user_id integer not null + constraint mlh_terms_pk + primary key + constraint mlh_terms_users_null_fk + references users, + send_messages boolean not null, + share_info boolean not null, + code_of_conduct boolean not null +); + +create table education_info +( + user_id integer not null + constraint education_info_pk + primary key + constraint education_info_users_id_fk + references users, + name varchar not null, + major varchar not null, + graduation_date timestamp not null, + level varchar +); + +create table event_attendance +( + event_id integer not null + constraint event_attendance_events_id_fk + references events, + user_id integer not null + constraint event_attendance_users_id_fk + references users, + time timestamp default now() not null, + constraint event_attendance_pk + primary key (event_id, user_id) +); + +create table meals +( + hackathon_id integer not null + constraint meals_hackathons_null_fk + references hackathons, + user_id integer not null + constraint meals_users_null_fk + references users, + meals character varying[] not null, + constraint meals_pk + primary key (hackathon_id, user_id) +); + +create table hackathon_checkin +( + hackathon_id integer not null + constraint hackathon_checkin_hackathons_id_fk + references hackathons, + user_id integer not null + constraint hackathon_checkin_users_id_fk + references users, + time timestamp not null, + constraint hackathon_checkin_pk + primary key (hackathon_id, user_id) +); + +create table api_keys +( + user_id integer not null + constraint api_keys_pk + primary key + constraint api_keys_users_id_fk + references users, + key varchar not null, + created timestamp not null +); + +create unique index api_keys_key_uindex + on api_keys (key); + +-- SCHEMA END + +-- INTEGRATION TEST DATA START + +INSERT INTO pronouns (subjective, objective) +VALUES ('he', 'him'); -- ID = 1 + +INSERT INTO users (email, phone_number, last_name, age, pronoun_id, first_name, role, oauth_uid, + oauth_provider, years_of_experience, shirt_size, race, gender) +VALUES ('joe.bob@example.com'::varchar, '100-200-3000'::varchar, 'Bob'::varchar, 22::integer, 1::integer, + 'Joe'::varchar, 'NORMAL'::varchar, '1'::varchar, 'GITHUB'::varchar, 3.5::double precision, 'L'::varchar, + ARRAY ['CAUCASIAN'], 'MALE'::varchar); +-- ID = 1 + +INSERT INTO mlh_terms (user_id, send_messages, share_info, code_of_conduct) +VALUES (1, true, true, true); + +INSERT INTO mailing_addresses (user_id, country, state, city, postal_code, address_lines) +VALUES (1, 'United States', 'Florida', 'Orlando', '32765', ARRAY ['1000 Abc Rd', 'APT 69']); + +INSERT INTO users (email, phone_number, last_name, age, pronoun_id, first_name, role, oauth_uid, + oauth_provider, years_of_experience, shirt_size, race, gender) +VALUES ('joe.biron@example.com'::varchar, '123-456-7890'::varchar, 'Biron'::varchar, 21::integer, 1::integer, + 'Joe'::varchar, 'NORMAL'::varchar, '4'::varchar, 'GITHUB'::varchar, 3.5::double precision, 'L'::varchar, + ARRAY ['AFRICAN_AMERICAN'], 'MALE'::varchar); +-- ID = 2 + +INSERT INTO api_keys (user_id, key, created) +VALUES (2, '1234567890abc', '2022-11-09') +-- ID = 1 + +-- INTEGRATION TEST DATA END \ No newline at end of file diff --git a/main.go b/main.go index d4dc47c..7ede256 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( "github.com/KnightHacks/knighthacks_users/graph/model" "github.com/KnightHacks/knighthacks_users/repository/database" "github.com/gin-gonic/gin" - "github.com/jackc/pgx/v4/pgxpool" + "github.com/jackc/pgx/v5/pgxpool" "github.com/vektah/gqlparser/v2/gqlerror" "log" "os" @@ -63,9 +63,13 @@ func graphqlHandler(a *auth.Auth, pool *pgxpool.Pool) gin.HandlerFunc { } }} + repository, err := database.NewDatabaseRepository(context.Background(), pool) + if err != nil { + log.Fatalf("error occured while initializing database repository err = %v\n", err) + } config := generated.Config{ Resolvers: &graph.Resolver{ - Repository: database.NewDatabaseRepository(pool), + Repository: repository, Auth: a, }, Directives: generated.DirectiveRoot{ diff --git a/repository/database/create_user.go b/repository/database/create_user.go index bffb278..9b63471 100644 --- a/repository/database/create_user.go +++ b/repository/database/create_user.go @@ -7,7 +7,7 @@ import ( sharedModels "github.com/KnightHacks/knighthacks_shared/models" "github.com/KnightHacks/knighthacks_users/graph/model" "github.com/KnightHacks/knighthacks_users/repository" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "strconv" ) @@ -27,8 +27,24 @@ func (r *DatabaseRepository) CreateUser(ctx context.Context, oAuth *model.OAuth, } } + user := &model.User{ + ID: userId, + FirstName: input.FirstName, + LastName: input.LastName, + Email: input.Email, + PhoneNumber: input.PhoneNumber, + Pronouns: &pronouns, + Age: input.Age, + Role: sharedModels.RoleNormal, + OAuth: oAuth, + Race: input.Race, + YearsOfExperience: input.YearsOfExperience, + ShirtSize: input.ShirtSize, + Gender: input.Gender, + } + // Begins the database transaction - err := r.DatabasePool.BeginTxFunc(ctx, pgx.TxOptions{}, func(tx pgx.Tx) error { + err := pgx.BeginTxFunc(ctx, r.DatabasePool, pgx.TxOptions{}, func(tx pgx.Tx) error { // Detects whether the user with the oauth_uid, for GitHub that is their github ID already exists, if // the use already exists we return an UserAlreadyExists error var discoveredId = new(int) @@ -53,18 +69,42 @@ func (r *DatabaseRepository) CreateUser(ctx context.Context, oAuth *model.OAuth, } // Insert MLH Terms - if err = r.InsertMLHTerms(ctx, tx, userIdInt, input.Mlh); err != nil { - return err + if input.Mlh != nil { + if err = r.InsertMLHTerms(ctx, tx, userIdInt, input.Mlh); err != nil { + return err + } + user.Mlh = &model.MLHTerms{ + SendMessages: input.Mlh.SendMessages, + CodeOfConduct: input.Mlh.CodeOfConduct, + ShareInfo: input.Mlh.ShareInfo, + } } // Insert Education Info - if err = r.InsertEducationInfo(ctx, tx, userIdInt, input.EducationInfo); err != nil { - return err + if input.EducationInfo != nil { + if err = r.InsertEducationInfo(ctx, tx, userIdInt, input.EducationInfo); err != nil { + return err + } + user.EducationInfo = &model.EducationInfo{ + Name: input.EducationInfo.Name, + GraduationDate: input.EducationInfo.GraduationDate, + Major: input.EducationInfo.Major, + Level: input.EducationInfo.Level, + } } // Insert Mailing Address Data - if err = r.InsertMailingAddress(ctx, tx, userIdInt, input.MailingAddress); err != nil { - return err + if input.MailingAddress != nil { + if err = r.InsertMailingAddress(ctx, tx, userIdInt, input.MailingAddress); err != nil { + return err + } + user.MailingAddress = &model.MailingAddress{ + Country: input.MailingAddress.Country, + State: input.MailingAddress.State, + City: input.MailingAddress.City, + PostalCode: input.MailingAddress.PostalCode, + AddressLines: input.MailingAddress.AddressLines, + } } userId = strconv.Itoa(userIdInt) @@ -74,16 +114,7 @@ func (r *DatabaseRepository) CreateUser(ctx context.Context, oAuth *model.OAuth, return nil, err } // TODO: look into the case where the userId is not scanned in - return &model.User{ - ID: userId, - FirstName: input.FirstName, - LastName: input.LastName, - Email: input.Email, - PhoneNumber: input.PhoneNumber, - Pronouns: &pronouns, - Age: input.Age, - OAuth: oAuth, - }, nil + return user, nil } func (r *DatabaseRepository) InsertUser(ctx context.Context, queryable database.Queryable, input *model.NewUser, pronounIdPtr *int, oAuth *model.OAuth) (int, error) { @@ -98,7 +129,7 @@ func (r *DatabaseRepository) InsertUser(ctx context.Context, queryable database. } } var userIdInt int - err := queryable.QueryRow(ctx, "INSERT INTO users (first_name, last_name, email, phone_number, age, pronoun_id, oauth_uid, oauth_provider, role,years_of_experience, shirt_size, race, gender, race) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id", + err := queryable.QueryRow(ctx, "INSERT INTO users (first_name, last_name, email, phone_number, age, pronoun_id, oauth_uid, oauth_provider, role, years_of_experience, shirt_size, race, gender) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id", input.FirstName, input.LastName, input.Email, @@ -110,9 +141,8 @@ func (r *DatabaseRepository) InsertUser(ctx context.Context, queryable database. sharedModels.RoleNormal, input.YearsOfExperience, input.ShirtSize, - input.Race, - input.Gender, raceStringArray, + input.Gender, ).Scan(&userIdInt) return userIdInt, err } @@ -128,7 +158,7 @@ func (r *DatabaseRepository) InsertMLHTerms(ctx context.Context, queryable datab } func (r *DatabaseRepository) InsertEducationInfo(ctx context.Context, queryable database.Queryable, userId int, input *model.EducationInfoInput) error { - _, err := queryable.Exec(ctx, "INSERT INTO education_info (user_id, name, major, graduation_date, level) VALUES ($1, $2, $3, $4)", + _, err := queryable.Exec(ctx, "INSERT INTO education_info (user_id, name, major, graduation_date, level) VALUES ($1, $2, $3, $4, $5)", userId, input.Name, input.Major, diff --git a/repository/database/database_repository.go b/repository/database/database_repository.go index c8c4177..bf3f3d8 100644 --- a/repository/database/database_repository.go +++ b/repository/database/database_repository.go @@ -2,9 +2,10 @@ package database import ( "context" + "fmt" "github.com/KnightHacks/knighthacks_users/graph/model" "github.com/KnightHacks/knighthacks_users/repository" - "github.com/jackc/pgx/v4/pgxpool" + "github.com/jackc/pgx/v5/pgxpool" ) // DatabaseRepository @@ -18,12 +19,22 @@ type DatabaseRepository struct { PronounReverseMap map[model.Pronouns]int } -func NewDatabaseRepository(databasePool *pgxpool.Pool) *DatabaseRepository { - return &DatabaseRepository{ +func NewDatabaseRepository(ctx context.Context, databasePool *pgxpool.Pool) (*DatabaseRepository, error) { + if databasePool == nil { + return nil, fmt.Errorf("cannot create DatabaseRepository with nil databasePool") + } + databaseRepository := &DatabaseRepository{ DatabasePool: databasePool, PronounMap: map[int]model.Pronouns{}, PronounReverseMap: map[model.Pronouns]int{}, } + if err := databaseRepository.DatabasePool.Ping(ctx); err != nil { + return nil, err + } + if err := databaseRepository.LoadPronouns(ctx); err != nil { + return nil, err + } + return databaseRepository, nil } func (r *DatabaseRepository) DeleteUser(ctx context.Context, id string) (bool, error) { diff --git a/repository/database/get_user.go b/repository/database/get_user.go index 8663de3..ab56766 100644 --- a/repository/database/get_user.go +++ b/repository/database/get_user.go @@ -6,7 +6,7 @@ import ( sharedModels "github.com/KnightHacks/knighthacks_shared/models" "github.com/KnightHacks/knighthacks_users/graph/model" "github.com/KnightHacks/knighthacks_users/repository" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" ) /* @@ -23,10 +23,10 @@ $$ | $$ |$$ ____| $$ |$$\ $$ | $$ | \____$$\ $$ ____|$$ | func (r *DatabaseRepository) GetUsers(ctx context.Context, first int, after string) ([]*model.User, int, error) { users := make([]*model.User, 0, first) var totalCount int - err := r.DatabasePool.BeginTxFunc(ctx, pgx.TxOptions{}, func(tx pgx.Tx) error { + err := pgx.BeginTxFunc(ctx, r.DatabasePool, pgx.TxOptions{}, func(tx pgx.Tx) error { rows, err := tx.Query( ctx, - "SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race FROM users WHERE id > $1 ORDER BY `id` DESC LIMIT $2", + "SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race, shirt_size, years_of_experience FROM users WHERE id > $1 ORDER BY `id` DESC LIMIT $2", after, first, ) @@ -42,10 +42,11 @@ func (r *DatabaseRepository) GetUsers(ctx context.Context, first int, after stri } // user has pronouns, but we don't know what they are if pronounId != nil { - err = r.getPronouns(ctx, tx, *pronounId) + pronouns, err := r.GetPronouns(ctx, tx, *pronounId) if err != nil { return err } + user.Pronouns = pronouns } users = append(users, &user) } @@ -65,24 +66,24 @@ func (r *DatabaseRepository) GetUsers(ctx context.Context, first int, after stri // GetUserByID returns the user by their id func (r *DatabaseRepository) GetUserByID(ctx context.Context, id string) (*model.User, error) { - return r.getUser( + return r.GetUser( ctx, - `SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race FROM users WHERE id = $1 LIMIT 1`, + `SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race, shirt_size, years_of_experience FROM users WHERE id = $1 LIMIT 1`, id, ) } // GetUserByOAuthUID returns the user by their oauth auth token func (r *DatabaseRepository) GetUserByOAuthUID(ctx context.Context, oAuthUID string, provider sharedModels.Provider) (*model.User, error) { - return r.getUser( + return r.GetUser( ctx, - `SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race FROM users WHERE oauth_uid=cast($1 as varchar) AND oauth_provider=$2 LIMIT 1`, + `SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race, shirt_size, years_of_experience FROM users WHERE oauth_uid=cast($1 as varchar) AND oauth_provider=$2 LIMIT 1`, oAuthUID, provider, ) } -func (r *DatabaseRepository) getUserWithTx(ctx context.Context, query string, tx pgx.Tx, args ...interface{}) (*model.User, error) { +func (r *DatabaseRepository) GetUserWithTx(ctx context.Context, query string, tx pgx.Tx, args ...interface{}) (*model.User, error) { var user model.User if tx == nil { @@ -105,23 +106,25 @@ func (r *DatabaseRepository) getUserWithTx(ctx context.Context, query string, tx // if the user has their pronouns set if pronounId != nil { - if err = r.getPronouns(ctx, tx, *pronounId); err != nil { + pronouns, err := r.GetPronouns(ctx, tx, *pronounId) + if err != nil { return nil, err } + user.Pronouns = pronouns } return &user, nil } -func (r *DatabaseRepository) getUser(ctx context.Context, query string, args ...interface{}) (*model.User, error) { - return r.getUserWithTx(ctx, query, nil, args...) +func (r *DatabaseRepository) GetUser(ctx context.Context, query string, args ...interface{}) (*model.User, error) { + return r.GetUserWithTx(ctx, query, nil, args...) } func (r *DatabaseRepository) SearchUser(ctx context.Context, name string) ([]*model.User, error) { const limit int = 10 users := make([]*model.User, 0, limit) - err := r.DatabasePool.BeginTxFunc(ctx, pgx.TxOptions{}, func(tx pgx.Tx) error { - rows, err := tx.Query(ctx, "SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race from users WHERE to_tsvector(first_name || ' ' || last_name) @@ to_tsquery('$1:*') LIMIT $2", name, limit) + err := pgx.BeginTxFunc(ctx, r.DatabasePool, pgx.TxOptions{}, func(tx pgx.Tx) error { + rows, err := tx.Query(ctx, "SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race, shirt_size, years_of_experience from users WHERE to_tsvector(first_name || ' ' || last_name) @@ to_tsquery('$1:*') LIMIT $2", name, limit) if err != nil { return err } @@ -133,7 +136,11 @@ func (r *DatabaseRepository) SearchUser(ctx context.Context, name string) ([]*mo return err } if pronounId != nil { - err = r.getPronouns(ctx, tx, *pronounId) + pronouns, err := r.GetPronouns(ctx, tx, *pronounId) + if err != nil { + return err + } + user.Pronouns = pronouns } if err != nil { return err @@ -151,9 +158,9 @@ func (r *DatabaseRepository) SearchUser(ctx context.Context, name string) ([]*mo // GetOAuth returns the model.OAuth object that is associated with the user's id // Used by the OAuth force resolver, this is not a common operation so making this // a force resolver is a good idea -func (r *DatabaseRepository) GetOAuth(ctx context.Context, id string) (*model.OAuth, error) { +func (r *DatabaseRepository) GetOAuth(ctx context.Context, userId string) (*model.OAuth, error) { var oAuth model.OAuth - err := r.DatabasePool.QueryRow(ctx, "SELECT oauth_uid, oauth_provider FROM users WHERE id = $1", id).Scan(&oAuth.UID, &oAuth.Provider) + err := r.DatabasePool.QueryRow(ctx, "SELECT oauth_uid, oauth_provider FROM users WHERE id = $1", userId).Scan(&oAuth.UID, &oAuth.Provider) if err != nil { return nil, err } @@ -195,8 +202,13 @@ func (r *DatabaseRepository) GetUserMLHTerms(ctx context.Context, userId string) } -func (r *DatabaseRepository) GetAPIKey(ctx context.Context, obj *model.User) (apiKey *model.APIKey, err error) { - err = r.DatabasePool.QueryRow(ctx, "SELECT key, created FROM api_keys WHERE user_id = $1", obj.ID).Scan(&apiKey.Key, &apiKey.Created) +func (r *DatabaseRepository) GetAPIKey(ctx context.Context, userId string) (apiKey *model.APIKey, err error) { + apiKey = &model.APIKey{} + err = r.DatabasePool.QueryRow( + ctx, + "SELECT key, created FROM api_keys WHERE user_id = $1", + userId, + ).Scan(&apiKey.Key, &apiKey.Created) if err != nil { return nil, err } diff --git a/repository/database/pronouns.go b/repository/database/pronouns.go index b24dcd8..f85e48d 100644 --- a/repository/database/pronouns.go +++ b/repository/database/pronouns.go @@ -5,7 +5,7 @@ import ( "errors" "github.com/KnightHacks/knighthacks_shared/database" "github.com/KnightHacks/knighthacks_users/graph/model" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" ) /* @@ -37,7 +37,7 @@ func (r *DatabaseRepository) Set(id int, pronouns model.Pronouns) { r.PronounReverseMap[pronouns] = id } -func (r *DatabaseRepository) getPronouns(ctx context.Context, queryable database.Queryable, pronounId int) error { +func (r *DatabaseRepository) GetPronouns(ctx context.Context, queryable database.Queryable, pronounId int) (*model.Pronouns, error) { pronouns, exists := r.GetById(pronounId) // does the pronoun not exist in the local cache? if !exists { @@ -47,12 +47,12 @@ func (r *DatabaseRepository) getPronouns(ctx context.Context, queryable database &pronouns.Objective, ) if err != nil { - return err + return nil, err } // set the pronoun in the local cache r.Set(pronounId, pronouns) } - return nil + return &pronouns, nil } func (r *DatabaseRepository) GetOrCreatePronoun(ctx context.Context, queryable database.Queryable, pronouns model.Pronouns, input *model.NewUser) (*int, error) { @@ -91,3 +91,22 @@ func (r *DatabaseRepository) GetOrCreatePronoun(ctx context.Context, queryable d } return &pronounId, nil } + +func (r *DatabaseRepository) LoadPronouns(ctx context.Context) error { + rows, err := r.DatabasePool.Query(ctx, "SELECT id, subjective, objective FROM pronouns") + if err != nil { + return err + } + + for rows.Next() { + var pronouns model.Pronouns + var id int + err = rows.Scan(&id, &pronouns.Subjective, &pronouns.Objective) + if err != nil { + return err + } + r.Set(id, pronouns) + } + + return nil +} diff --git a/repository/database/update_user.go b/repository/database/update_user.go index 409fb55..7cb1c9b 100644 --- a/repository/database/update_user.go +++ b/repository/database/update_user.go @@ -7,8 +7,7 @@ import ( "github.com/KnightHacks/knighthacks_shared/database" "github.com/KnightHacks/knighthacks_users/graph/model" "github.com/KnightHacks/knighthacks_users/repository" - "github.com/jackc/pgx/v4" - "math/rand" + "github.com/jackc/pgx/v5" "strconv" "time" ) @@ -63,7 +62,7 @@ func (r *DatabaseRepository) UpdateUser(ctx context.Context, id string, input *m if input.FirstName == nil && input.LastName == nil && input.Email == nil && input.PhoneNumber == nil && input.Pronouns == nil && input.Age == nil { return nil, errors.New("empty user field") } - err = r.DatabasePool.BeginTxFunc(ctx, pgx.TxOptions{}, func(tx pgx.Tx) error { + err = pgx.BeginTxFunc(ctx, r.DatabasePool, pgx.TxOptions{}, func(tx pgx.Tx) error { if err = Validate(ctx, tx, id, input.FirstName, r.UpdateFirstName); err != nil { return err } @@ -104,8 +103,8 @@ func (r *DatabaseRepository) UpdateUser(ctx context.Context, id string, input *m return err } - user, err = r.getUserWithTx(ctx, - `SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race FROM users WHERE id = $1 LIMIT 1`, + user, err = r.GetUserWithTx(ctx, + `SELECT id, first_name, last_name, email, phone_number, pronoun_id, age, role, gender, race, shirt_size, years_of_experience FROM users WHERE id = $1 LIMIT 1`, tx, id) @@ -430,7 +429,7 @@ type Scannable interface { } func ScanUser[T Scannable](user *model.User, scannable T) (*int, error) { - var pronounId *int32 + var pronounId int var userIdInt int err := scannable.Scan( &userIdInt, @@ -443,16 +442,17 @@ func ScanUser[T Scannable](user *model.User, scannable T) (*int, error) { &user.Role, &user.Gender, &user.Race, + &user.ShirtSize, + &user.YearsOfExperience, ) if err != nil { return nil, err } user.ID = strconv.Itoa(userIdInt) - if pronounId != nil { - i := int(*pronounId) - return &i, nil + if pronounId == 0 { + return nil, nil } - return nil, nil + return &pronounId, nil } func (r *DatabaseRepository) DeleteAPIKey(ctx context.Context, id string) error { @@ -463,21 +463,11 @@ func (r *DatabaseRepository) DeleteAPIKey(ctx context.Context, id string) error return nil } -func (r *DatabaseRepository) AddAPIKey(ctx context.Context, id string) (*model.APIKey, error) { - apiKey := GenerateAPIKey(100) - _, err := r.DatabasePool.Exec(ctx, "INSERT INTO api_keys (user_id, key, created) VALUES ($1, $2, $3)", id, apiKey, time.Now()) +func (r *DatabaseRepository) AddAPIKey(ctx context.Context, id string, key string) (*model.APIKey, error) { + now := time.Now() + _, err := r.DatabasePool.Exec(ctx, "INSERT INTO api_keys (user_id, key, created) VALUES ($1, $2, $3)", id, key, now) if err != nil { return nil, err } - return &model.APIKey{Key: apiKey, Created: time.Now()}, nil -} - -var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - -func GenerateAPIKey(length int) string { - b := make([]rune, length) - for i := range b { - b[i] = letterRunes[rand.Intn(len(letterRunes))] - } - return string(b) + return &model.APIKey{Key: key, Created: time.Now()}, nil } diff --git a/repository/repository.go b/repository/repository.go index a428813..bfe6c03 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -21,7 +21,7 @@ type Repository interface { SearchUser(ctx context.Context, name string) ([]*model.User, error) DeleteUser(ctx context.Context, id string) (bool, error) CreateUser(ctx context.Context, oAuth *model.OAuth, input *model.NewUser) (*model.User, error) - GetAPIKey(ctx context.Context, obj *model.User) (*model.APIKey, error) + GetAPIKey(ctx context.Context, userId string) (apiKey *model.APIKey, err error) DeleteAPIKey(ctx context.Context, id string) error - AddAPIKey(ctx context.Context, id string) (*model.APIKey, error) + AddAPIKey(ctx context.Context, id string, key string) (*model.APIKey, error) }