This commit is contained in:
Dawid Wysokiński 2020-06-02 17:45:21 +02:00
commit 3bffcfa0d3
52 changed files with 10155 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.env.development
.env.production
.env
.netrc

19
Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM golang:alpine
RUN apk add git
ENV MODE=production
ENV GIN_MODE=release
ENV GOPRIVATE=github.com/tribalwarshelp
COPY ./.netrc /root/.netrc
RUN chmod 600 /root/.netrc
WORKDIR /go/src/app
COPY . .
RUN go build -o main .
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait ./wait
RUN chmod +x ./wait
CMD ./wait && ./main

20
go.mod Normal file
View File

@ -0,0 +1,20 @@
module github.com/tribalwarshelp/api
go 1.14
require (
github.com/99designs/gqlgen v0.11.3
github.com/gin-gonic/gin v1.6.3
github.com/go-pg/pg/v10 v10.0.0-beta.1
github.com/go-playground/validator/v10 v10.3.0 // indirect
github.com/joho/godotenv v1.3.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pkg/errors v0.9.1
github.com/segmentio/encoding v0.1.13 // indirect
github.com/tribalwarshelp/shared v0.0.0-20200602122915-bb9a531672b6
github.com/vektah/gqlparser/v2 v2.0.1
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect
golang.org/x/sys v0.0.0-20200602100848-8d3cce7afc34 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)

299
go.sum Normal file
View File

@ -0,0 +1,299 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4=
github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs=
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
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/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w=
github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-pg/pg/v10 v10.0.0-beta.1 h1:GD65aZVx9yR6fxxXdglBlook89oxxg2FYE11TDd9Now=
github.com/go-pg/pg/v10 v10.0.0-beta.1/go.mod h1:JYxBTzIz9dpSAa+bphD1U7A/PBCwiuw1o8pYNDSsQ+4=
github.com/go-pg/pg/v9 v9.0.0-beta.14/go.mod h1:T2Sr6bpTCOr2lUqOUMiXLMJqZHSUBKk1LdgSqjwhZfA=
github.com/go-pg/pg/v9 v9.0.3/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA=
github.com/go-pg/pg/v9 v9.1.6 h1:IqBayenvp9EWjHncRE7//SRmQuktq60oeO1/MkEx3dY=
github.com/go-pg/pg/v9 v9.1.6/go.mod h1:QM13HBLkdml4zcKOfUfGLymM6hb72aKTJLrmaH8rsFg=
github.com/go-pg/urlstruct v0.1.0/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0=
github.com/go-pg/urlstruct v0.2.6/go.mod h1:dxENwVISWSOX+k87hDt0ueEJadD+gZWv3tHzwfmZPu8=
github.com/go-pg/urlstruct v0.3.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg=
github.com/go-pg/urlstruct v0.4.0 h1:3lmbUGYQclB3UOx9akDs2T251zwkKQuPkvPTmCm07+A=
github.com/go-pg/urlstruct v0.4.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg=
github.com/go-pg/zerochecker v0.1.1 h1:av77Qe7Gs+1oYGGh51k0sbZ0bUaxJEdeP0r8YE64Dco=
github.com/go-pg/zerochecker v0.1.1/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/segmentio/encoding v0.1.12 h1:SwIDXReTDnlYqOcLachzJEczAEihST7Mx7nGlAWCJ3Q=
github.com/segmentio/encoding v0.1.12/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/segmentio/encoding v0.1.13 h1:izH8HknGvMZvlqplu+kmCmbsW5VEvz4yBsZpdUUKUDM=
github.com/segmentio/encoding v0.1.13/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
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/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/tribalwarshelp/shared v0.0.0-20200602122915-bb9a531672b6 h1:vrqEpEwkKg63/hV5cIGgmDvdcAdDm4pR0N14nC3eCjQ=
github.com/tribalwarshelp/shared v0.0.0-20200602122915-bb9a531672b6/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
github.com/vmihailenco/bufpool v0.1.5/go.mod h1:fL9i/PRTuS7AELqAHwSU1Zf1c70xhkhGe/cD5ud9pJk=
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
github.com/vmihailenco/msgpack/v4 v4.3.5/go.mod h1:DuaveEe48abshDmz5UBKyZ+yDugvaeFk5ayfrewUOaw=
github.com/vmihailenco/msgpack/v4 v4.3.7/go.mod h1:Ii+PksJlvFT5ZRcB/4YLAInMIp6a0WOCm0L3BU0aNG4=
github.com/vmihailenco/msgpack/v4 v4.3.11 h1:Q47CePddpNGNhk4GCnAx9DDtASi2rasatE0cd26cZoE=
github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/msgpack/v5 v5.0.0-alpha.2 h1:0jVpYJSRJzGY7m21n9V5uIkl7Zre64W8DR1dxEKX2g4=
github.com/vmihailenco/msgpack/v5 v5.0.0-alpha.2/go.mod h1:LDfrk4wJpSFwkzNOJxrCWiSm8c7Iqw/hXNPT2fzQfE8=
github.com/vmihailenco/tagparser v0.1.0/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
go.opentelemetry.io/otel v0.6.0 h1:+vkHm/XwJ7ekpISV2Ixew93gCrxTbuwTF5rSewnLLgw=
go.opentelemetry.io/otel v0.6.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222033325-078779b8f2d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602100848-8d3cce7afc34 h1:u6CI7A++8r4SItZHYe2cWeAEndN4p1p+3Oum/Ft2EzM=
golang.org/x/sys v0.0.0-20200602100848-8d3cce7afc34/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM=
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w=
mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ=
sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=

5
gqlgen_generate.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
cd ./graphql
go run github.com/99designs/gqlgen
cd ..

View File

@ -0,0 +1,76 @@
package dataloaders
import (
"context"
"log"
"time"
"github.com/tribalwarshelp/api/player"
"github.com/tribalwarshelp/api/tribe"
"github.com/tribalwarshelp/shared/models"
)
type DataLoaders struct {
PlayerByID PlayerLoader
TribeByID TribeLoader
}
type Config struct {
PlayerRepo player.Repository
TribeRepo tribe.Repository
}
func New(server string, cfg Config) *DataLoaders {
return &DataLoaders{
PlayerByID: PlayerLoader{
wait: 2 * time.Millisecond,
maxBatch: 0,
fetch: func(ids []int) ([]*models.Player, []error) {
log.Println("playerbyid", ids)
players, _, err := cfg.PlayerRepo.Fetch(context.Background(), server, &models.PlayerFilter{
ID: ids,
})
if err != nil {
return nil, []error{err}
}
playerByID := make(map[int]*models.Player)
for _, player := range players {
playerByID[player.ID] = player
}
sorted := make([]*models.Player, len(ids))
for i, id := range ids {
sorted[i] = playerByID[id]
}
return sorted, nil
},
},
TribeByID: TribeLoader{
wait: 2 * time.Millisecond,
maxBatch: 0,
fetch: func(ids []int) ([]*models.Tribe, []error) {
log.Println("tribebyid", ids)
tribes, _, err := cfg.TribeRepo.Fetch(context.Background(), server, &models.TribeFilter{
ID: ids,
})
if err != nil {
return nil, []error{err}
}
tribeByID := make(map[int]*models.Tribe)
for _, tribe := range tribes {
tribeByID[tribe.ID] = tribe
}
sorted := make([]*models.Tribe, len(ids))
for i, id := range ids {
sorted[i] = tribeByID[id]
}
return sorted, nil
},
},
}
}

View File

@ -0,0 +1,224 @@
// Code generated by github.com/vektah/dataloaden, DO NOT EDIT.
package dataloaders
import (
"sync"
"time"
"github.com/tribalwarshelp/shared/models"
)
// PlayerLoaderConfig captures the config to create a new PlayerLoader
type PlayerLoaderConfig struct {
// Fetch is a method that provides the data for the loader
Fetch func(keys []int) ([]*models.Player, []error)
// Wait is how long wait before sending a batch
Wait time.Duration
// MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit
MaxBatch int
}
// NewPlayerLoader creates a new PlayerLoader given a fetch, wait, and maxBatch
func NewPlayerLoader(config PlayerLoaderConfig) *PlayerLoader {
return &PlayerLoader{
fetch: config.Fetch,
wait: config.Wait,
maxBatch: config.MaxBatch,
}
}
// PlayerLoader batches and caches requests
type PlayerLoader struct {
// this method provides the data for the loader
fetch func(keys []int) ([]*models.Player, []error)
// how long to done before sending a batch
wait time.Duration
// this will limit the maximum number of keys to send in one batch, 0 = no limit
maxBatch int
// INTERNAL
// lazily created cache
cache map[int]*models.Player
// the current batch. keys will continue to be collected until timeout is hit,
// then everything will be sent to the fetch method and out to the listeners
batch *playerLoaderBatch
// mutex to prevent races
mu sync.Mutex
}
type playerLoaderBatch struct {
keys []int
data []*models.Player
error []error
closing bool
done chan struct{}
}
// Load a Player by key, batching and caching will be applied automatically
func (l *PlayerLoader) Load(key int) (*models.Player, error) {
return l.LoadThunk(key)()
}
// LoadThunk returns a function that when called will block waiting for a Player.
// This method should be used if you want one goroutine to make requests to many
// different data loaders without blocking until the thunk is called.
func (l *PlayerLoader) LoadThunk(key int) func() (*models.Player, error) {
l.mu.Lock()
if it, ok := l.cache[key]; ok {
l.mu.Unlock()
return func() (*models.Player, error) {
return it, nil
}
}
if l.batch == nil {
l.batch = &playerLoaderBatch{done: make(chan struct{})}
}
batch := l.batch
pos := batch.keyIndex(l, key)
l.mu.Unlock()
return func() (*models.Player, error) {
<-batch.done
var data *models.Player
if pos < len(batch.data) {
data = batch.data[pos]
}
var err error
// its convenient to be able to return a single error for everything
if len(batch.error) == 1 {
err = batch.error[0]
} else if batch.error != nil {
err = batch.error[pos]
}
if err == nil {
l.mu.Lock()
l.unsafeSet(key, data)
l.mu.Unlock()
}
return data, err
}
}
// LoadAll fetches many keys at once. It will be broken into appropriate sized
// sub batches depending on how the loader is configured
func (l *PlayerLoader) LoadAll(keys []int) ([]*models.Player, []error) {
results := make([]func() (*models.Player, error), len(keys))
for i, key := range keys {
results[i] = l.LoadThunk(key)
}
players := make([]*models.Player, len(keys))
errors := make([]error, len(keys))
for i, thunk := range results {
players[i], errors[i] = thunk()
}
return players, errors
}
// LoadAllThunk returns a function that when called will block waiting for a Players.
// This method should be used if you want one goroutine to make requests to many
// different data loaders without blocking until the thunk is called.
func (l *PlayerLoader) LoadAllThunk(keys []int) func() ([]*models.Player, []error) {
results := make([]func() (*models.Player, error), len(keys))
for i, key := range keys {
results[i] = l.LoadThunk(key)
}
return func() ([]*models.Player, []error) {
players := make([]*models.Player, len(keys))
errors := make([]error, len(keys))
for i, thunk := range results {
players[i], errors[i] = thunk()
}
return players, errors
}
}
// Prime the cache with the provided key and value. If the key already exists, no change is made
// and false is returned.
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
func (l *PlayerLoader) Prime(key int, value *models.Player) bool {
l.mu.Lock()
var found bool
if _, found = l.cache[key]; !found {
// make a copy when writing to the cache, its easy to pass a pointer in from a loop var
// and end up with the whole cache pointing to the same value.
cpy := *value
l.unsafeSet(key, &cpy)
}
l.mu.Unlock()
return !found
}
// Clear the value at key from the cache, if it exists
func (l *PlayerLoader) Clear(key int) {
l.mu.Lock()
delete(l.cache, key)
l.mu.Unlock()
}
func (l *PlayerLoader) unsafeSet(key int, value *models.Player) {
if l.cache == nil {
l.cache = map[int]*models.Player{}
}
l.cache[key] = value
}
// keyIndex will return the location of the key in the batch, if its not found
// it will add the key to the batch
func (b *playerLoaderBatch) keyIndex(l *PlayerLoader, key int) int {
for i, existingKey := range b.keys {
if key == existingKey {
return i
}
}
pos := len(b.keys)
b.keys = append(b.keys, key)
if pos == 0 {
go b.startTimer(l)
}
if l.maxBatch != 0 && pos >= l.maxBatch-1 {
if !b.closing {
b.closing = true
l.batch = nil
go b.end(l)
}
}
return pos
}
func (b *playerLoaderBatch) startTimer(l *PlayerLoader) {
time.Sleep(l.wait)
l.mu.Lock()
// we must have hit a batch limit and are already finalizing this batch
if b.closing {
l.mu.Unlock()
return
}
l.batch = nil
l.mu.Unlock()
b.end(l)
}
func (b *playerLoaderBatch) end(l *PlayerLoader) {
b.data, b.error = l.fetch(b.keys)
close(b.done)
}

View File

@ -0,0 +1,224 @@
// Code generated by github.com/vektah/dataloaden, DO NOT EDIT.
package dataloaders
import (
"sync"
"time"
"github.com/tribalwarshelp/shared/models"
)
// TribeLoaderConfig captures the config to create a new TribeLoader
type TribeLoaderConfig struct {
// Fetch is a method that provides the data for the loader
Fetch func(keys []int) ([]*models.Tribe, []error)
// Wait is how long wait before sending a batch
Wait time.Duration
// MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit
MaxBatch int
}
// NewTribeLoader creates a new TribeLoader given a fetch, wait, and maxBatch
func NewTribeLoader(config TribeLoaderConfig) *TribeLoader {
return &TribeLoader{
fetch: config.Fetch,
wait: config.Wait,
maxBatch: config.MaxBatch,
}
}
// TribeLoader batches and caches requests
type TribeLoader struct {
// this method provides the data for the loader
fetch func(keys []int) ([]*models.Tribe, []error)
// how long to done before sending a batch
wait time.Duration
// this will limit the maximum number of keys to send in one batch, 0 = no limit
maxBatch int
// INTERNAL
// lazily created cache
cache map[int]*models.Tribe
// the current batch. keys will continue to be collected until timeout is hit,
// then everything will be sent to the fetch method and out to the listeners
batch *tribeLoaderBatch
// mutex to prevent races
mu sync.Mutex
}
type tribeLoaderBatch struct {
keys []int
data []*models.Tribe
error []error
closing bool
done chan struct{}
}
// Load a Tribe by key, batching and caching will be applied automatically
func (l *TribeLoader) Load(key int) (*models.Tribe, error) {
return l.LoadThunk(key)()
}
// LoadThunk returns a function that when called will block waiting for a Tribe.
// This method should be used if you want one goroutine to make requests to many
// different data loaders without blocking until the thunk is called.
func (l *TribeLoader) LoadThunk(key int) func() (*models.Tribe, error) {
l.mu.Lock()
if it, ok := l.cache[key]; ok {
l.mu.Unlock()
return func() (*models.Tribe, error) {
return it, nil
}
}
if l.batch == nil {
l.batch = &tribeLoaderBatch{done: make(chan struct{})}
}
batch := l.batch
pos := batch.keyIndex(l, key)
l.mu.Unlock()
return func() (*models.Tribe, error) {
<-batch.done
var data *models.Tribe
if pos < len(batch.data) {
data = batch.data[pos]
}
var err error
// its convenient to be able to return a single error for everything
if len(batch.error) == 1 {
err = batch.error[0]
} else if batch.error != nil {
err = batch.error[pos]
}
if err == nil {
l.mu.Lock()
l.unsafeSet(key, data)
l.mu.Unlock()
}
return data, err
}
}
// LoadAll fetches many keys at once. It will be broken into appropriate sized
// sub batches depending on how the loader is configured
func (l *TribeLoader) LoadAll(keys []int) ([]*models.Tribe, []error) {
results := make([]func() (*models.Tribe, error), len(keys))
for i, key := range keys {
results[i] = l.LoadThunk(key)
}
tribes := make([]*models.Tribe, len(keys))
errors := make([]error, len(keys))
for i, thunk := range results {
tribes[i], errors[i] = thunk()
}
return tribes, errors
}
// LoadAllThunk returns a function that when called will block waiting for a Tribes.
// This method should be used if you want one goroutine to make requests to many
// different data loaders without blocking until the thunk is called.
func (l *TribeLoader) LoadAllThunk(keys []int) func() ([]*models.Tribe, []error) {
results := make([]func() (*models.Tribe, error), len(keys))
for i, key := range keys {
results[i] = l.LoadThunk(key)
}
return func() ([]*models.Tribe, []error) {
tribes := make([]*models.Tribe, len(keys))
errors := make([]error, len(keys))
for i, thunk := range results {
tribes[i], errors[i] = thunk()
}
return tribes, errors
}
}
// Prime the cache with the provided key and value. If the key already exists, no change is made
// and false is returned.
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
func (l *TribeLoader) Prime(key int, value *models.Tribe) bool {
l.mu.Lock()
var found bool
if _, found = l.cache[key]; !found {
// make a copy when writing to the cache, its easy to pass a pointer in from a loop var
// and end up with the whole cache pointing to the same value.
cpy := *value
l.unsafeSet(key, &cpy)
}
l.mu.Unlock()
return !found
}
// Clear the value at key from the cache, if it exists
func (l *TribeLoader) Clear(key int) {
l.mu.Lock()
delete(l.cache, key)
l.mu.Unlock()
}
func (l *TribeLoader) unsafeSet(key int, value *models.Tribe) {
if l.cache == nil {
l.cache = map[int]*models.Tribe{}
}
l.cache[key] = value
}
// keyIndex will return the location of the key in the batch, if its not found
// it will add the key to the batch
func (b *tribeLoaderBatch) keyIndex(l *TribeLoader, key int) int {
for i, existingKey := range b.keys {
if key == existingKey {
return i
}
}
pos := len(b.keys)
b.keys = append(b.keys, key)
if pos == 0 {
go b.startTimer(l)
}
if l.maxBatch != 0 && pos >= l.maxBatch-1 {
if !b.closing {
b.closing = true
l.batch = nil
go b.end(l)
}
}
return pos
}
func (b *tribeLoaderBatch) startTimer(l *TribeLoader) {
time.Sleep(l.wait)
l.mu.Lock()
// we must have hit a batch limit and are already finalizing this batch
if b.closing {
l.mu.Unlock()
return
}
l.batch = nil
l.mu.Unlock()
b.end(l)
}
func (b *tribeLoaderBatch) end(l *TribeLoader) {
b.data, b.error = l.fetch(b.keys)
close(b.done)
}

View File

@ -0,0 +1,42 @@
package httpdelivery
import (
"fmt"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/gin-gonic/gin"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/api/graphql/resolvers"
)
func Attach(g *gin.RouterGroup, r *resolvers.Resolver) error {
if r == nil {
return fmt.Errorf("Graphql resolver cannot be nil")
}
g.POST("/graphql", graphqlHandler(r))
g.GET("/", playgroundHandler())
return nil
}
// Defining the Graphql handler
func graphqlHandler(r *resolvers.Resolver) gin.HandlerFunc {
// NewExecutableSchema and Config are in the generated.go file
// Resolver is in the resolver.go file
cfg := generated.Config{Resolvers: r}
h := handler.NewDefaultServer(generated.NewExecutableSchema(cfg))
return func(c *gin.Context) {
c.Header("Cache-Control", "no-store, must-revalidate")
h.ServeHTTP(c.Writer, c.Request)
}
}
// Defining the Playground handler
func playgroundHandler() gin.HandlerFunc {
h := playground.Handler("Playground", "/graphql")
return func(c *gin.Context) {
h.ServeHTTP(c.Writer, c.Request)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package generated
import (
"github.com/tribalwarshelp/shared/models"
)
type LangVersionList struct {
Items []*models.LangVersion `json:"items"`
Total int `json:"total"`
}
type PlayerList struct {
Items []*models.Player `json:"items"`
Total int `json:"total"`
}
type ServerList struct {
Items []*models.Server `json:"items"`
Total int `json:"total"`
}
type TribeList struct {
Items []*models.Tribe `json:"items"`
Total int `json:"total"`
}
type VillageList struct {
Items []*models.Village `json:"items"`
Total int `json:"total"`
}

38
graphql/gqlgen.yml Normal file
View File

@ -0,0 +1,38 @@
schema:
- 'schema/*.graphql'
exec:
filename: generated/generated.go
package: generated
model:
filename: generated/models.go
package: generated
resolver:
filename: resolvers/resolver.go
package: resolvers
type: Resolver
struct_tag: gqlgen
models:
LanguageTag:
model: github.com/tribalwarshelp/shared/models.LanguageTag
ServerStatus:
model: github.com/tribalwarshelp/shared/models.ServerStatus
LangVersion:
model: github.com/tribalwarshelp/shared/models.LangVersion
LangVersionFilter:
model: github.com/tribalwarshelp/shared/models.LangVersionFilter
Server:
model: github.com/tribalwarshelp/shared/models.Server
ServerFilter:
model: github.com/tribalwarshelp/shared/models.ServerFilter
Player:
model: github.com/tribalwarshelp/shared/models.Player
PlayerFilter:
model: github.com/tribalwarshelp/shared/models.PlayerFilter
Tribe:
model: github.com/tribalwarshelp/shared/models.Tribe
TribeFilter:
model: github.com/tribalwarshelp/shared/models.TribeFilter
Village:
model: github.com/tribalwarshelp/shared/models.Village
VillageFilter:
model: github.com/tribalwarshelp/shared/models.VillageFilter

View File

@ -0,0 +1,19 @@
package resolvers
import (
"context"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/shared/models"
)
func (r *queryResolver) LangVersions(ctx context.Context, filter *models.LangVersionFilter) (*generated.LangVersionList, error) {
var err error
list := &generated.LangVersionList{}
list.Items, list.Total, err = r.LangVersionUcase.Fetch(ctx, filter)
return list, err
}
func (r *queryResolver) LangVersion(ctx context.Context, tag models.LanguageTag) (*models.LangVersion, error) {
return r.LangVersionUcase.GetByTag(ctx, tag)
}

View File

@ -0,0 +1,43 @@
package resolvers
import (
"context"
"github.com/99designs/gqlgen/graphql"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/api/middleware"
"github.com/tribalwarshelp/shared/models"
)
func (r *playerResolver) Tribe(ctx context.Context, obj *models.Player) (*models.Tribe, error) {
if obj.Tribe != nil {
return obj.Tribe, nil
}
rctx := graphql.GetFieldContext(ctx)
server, ok := rctx.Parent.Parent.Parent.Args["server"].(string)
if ok {
dataloaders := middleware.DataLoadersFromContext(ctx)
if dataloaders != nil {
if dataloader, ok := dataloaders[server]; ok {
tribe, _ := dataloader.TribeByID.Load(obj.TribeID)
if tribe != nil {
return tribe, nil
}
}
}
}
return nil, nil
}
func (r *queryResolver) Players(ctx context.Context, server string, filter *models.PlayerFilter) (*generated.PlayerList, error) {
var err error
list := &generated.PlayerList{}
list.Items, list.Total, err = r.PlayerUcase.Fetch(ctx, server, filter)
return list, err
}
func (r *queryResolver) Player(ctx context.Context, server string, id int) (*models.Player, error) {
return r.PlayerUcase.GetByID(ctx, server, id)
}

View File

@ -0,0 +1,27 @@
package resolvers
import (
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/api/langversion"
"github.com/tribalwarshelp/api/player"
"github.com/tribalwarshelp/api/server"
"github.com/tribalwarshelp/api/tribe"
"github.com/tribalwarshelp/api/village"
)
type Resolver struct {
LangVersionUcase langversion.Usecase
ServerUcase server.Usecase
PlayerUcase player.Usecase
TribeUcase tribe.Usecase
VillageUcase village.Usecase
}
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
func (r *Resolver) Player() generated.PlayerResolver { return &playerResolver{r} }
func (r *Resolver) Village() generated.VillageResolver { return &villageResolver{r} }
type queryResolver struct{ *Resolver }
type playerResolver struct{ *Resolver }
type villageResolver struct{ *Resolver }

View File

@ -0,0 +1,19 @@
package resolvers
import (
"context"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/shared/models"
)
func (r *queryResolver) Servers(ctx context.Context, filter *models.ServerFilter) (*generated.ServerList, error) {
var err error
list := &generated.ServerList{}
list.Items, list.Total, err = r.ServerUcase.Fetch(ctx, filter)
return list, err
}
func (r *queryResolver) Server(ctx context.Context, key string) (*models.Server, error) {
return r.ServerUcase.GetByKey(ctx, key)
}

View File

@ -0,0 +1,19 @@
package resolvers
import (
"context"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/shared/models"
)
func (r *queryResolver) Tribes(ctx context.Context, server string, filter *models.TribeFilter) (*generated.TribeList, error) {
var err error
list := &generated.TribeList{}
list.Items, list.Total, err = r.TribeUcase.Fetch(ctx, server, filter)
return list, err
}
func (r *queryResolver) Tribe(ctx context.Context, server string, id int) (*models.Tribe, error) {
return r.TribeUcase.GetByID(ctx, server, id)
}

View File

@ -0,0 +1,43 @@
package resolvers
import (
"context"
"github.com/99designs/gqlgen/graphql"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/api/middleware"
"github.com/tribalwarshelp/shared/models"
)
func (r *villageResolver) Player(ctx context.Context, obj *models.Village) (*models.Player, error) {
if obj.Player != nil {
return obj.Player, nil
}
rctx := graphql.GetFieldContext(ctx)
server, ok := rctx.Parent.Parent.Parent.Args["server"].(string)
if ok {
dataloaders := middleware.DataLoadersFromContext(ctx)
if dataloaders != nil {
if dataloader, ok := dataloaders[server]; ok {
tribe, _ := dataloader.PlayerByID.Load(obj.PlayerID)
if tribe != nil {
return tribe, nil
}
}
}
}
return nil, nil
}
func (r *queryResolver) Villages(ctx context.Context, server string, filter *models.VillageFilter) (*generated.VillageList, error) {
var err error
list := &generated.VillageList{}
list.Items, list.Total, err = r.VillageUcase.Fetch(ctx, server, filter)
return list, err
}
func (r *queryResolver) Village(ctx context.Context, server string, id int) (*models.Village, error) {
return r.VillageUcase.GetByID(ctx, server, id)
}

View File

@ -0,0 +1,4 @@
directive @goField(
forceResolver: Boolean
name: String
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION

View File

@ -0,0 +1,36 @@
enum LanguageTag {
PL
EN
DE
}
type LangVersion {
tag: LanguageTag!
name: String!
host: String!
timezone: String!
}
input LangVersionFilter {
tag: [LanguageTag!]
tagNEQ: [LanguageTag!]
host: [String!]
hostNEQ: [String!]
hostMATCH: String
hostIEQ: String
offset: Int
limit: Int
sort: String
}
type LangVersionList {
items: [LangVersion!]
total: Int!
}
extend type Query {
langVersions(filter: LangVersionFilter): LangVersionList!
langVersion(tag: LanguageTag!): LangVersion
}

View File

@ -0,0 +1,111 @@
type Player {
id: Int!
name: String!
totalVillages: Int!
points: Int!
rank: Int!
exist: Boolean!
rankAtt: Int!
scoreAtt: Int!
rankDef: Int!
scoreDef: Int!
rankSup: Int!
scoreSup: Int!
rankTotal: Int!
scoreTotal: Int!
tribe: Tribe @goField(forceResolver: true)
}
type PlayerList {
items: [Player!]
total: Int!
}
input PlayerFilter {
id: [Int!]
idNEQ: [Int!]
exist: Boolean
name: [String!]
nameNEQ: [String!]
nameMATCH: String
nameIEQ: String
totalVillages: Int
totalVillagesGT: Int
totalVillagesGTE: Int
totalVillagesLT: Int
totalVillagesLTE: Int
points: Int
pointsGT: Int
pointsGTE: Int
pointsLT: Int
pointsLTE: Int
rank: Int
rankGT: Int
rankGTE: Int
rankLT: Int
rankLTE: Int
rankAtt: Int
rankAttGT: Int
rankAttGTE: Int
rankAttLT: Int
rankAttLTE: Int
scoreAtt: Int
scoreAttGT: Int
scoreAttGTE: Int
scoreAttLT: Int
scoreAttLTE: Int
rankDef: Int
rankDefGT: Int
rankDefGTE: Int
rankDefLT: Int
rankDefLTE: Int
scoreDef: Int
scoreDefGT: Int
scoreDefGTE: Int
scoreDefLT: Int
scoreDefLTE: Int
rankSup: Int
rankSupGT: Int
rankSupGTE: Int
rankSupLT: Int
rankSupLTE: Int
scoreSup: Int
scoreSupGT: Int
scoreSupGTE: Int
scoreSupLT: Int
scoreSupLTE: Int
rankTotal: Int
rankTotalGT: Int
rankTotalGTE: Int
rankTotalLT: Int
rankTotalLTE: Int
scoreTotal: Int
scoreTotalGT: Int
scoreTotalGTE: Int
scoreTotalLT: Int
scoreTotalLTE: Int
tribeID: [Int!]
offset: Int
limit: Int
sort: String
}
extend type Query {
players(server: String!, filter: PlayerFilter): PlayerList!
player(server: String!, id: Int!): Player
}

View File

@ -0,0 +1,41 @@
enum ServerStatus {
OPEN
CLOSED
}
type Server {
id: Int!
key: String!
status: ServerStatus!
langVersionTag: LanguageTag!
}
type ServerList {
items: [Server!]
total: Int!
}
input ServerFilter {
id: [Int!]
idNEQ: [Int!]
key: [String!]
keyNEQ: [String!]
keyMATCH: String
keyIEQ: String
status: [ServerStatus!]
statusNEQ: [ServerStatus!]
langVersionTag: [LanguageTag!]
langVersionTagNEQ: [LanguageTag!]
offset: Int
limit: Int
sort: String
}
extend type Query {
servers(filter: ServerFilter): ServerList!
server(key: String!): Server
}

View File

@ -0,0 +1,114 @@
type Tribe {
id: Int!
name: String!
tag: String!
totalMembers: Int!
totalVillages: Int!
points: Int!
allPoints: Int!
rank: Int!
exist: Boolean!
rankAtt: Int!
scoreAtt: Int!
rankDef: Int!
scoreDef: Int!
rankTotal: Int!
scoreTotal: Int!
}
type TribeList {
items: [Tribe!]
total: Int!
}
input TribeFilter {
id: [Int!]
idNEQ: [Int!]
exist: Boolean
tag: [String!]
tagNEQ: [String!]
tagMATCH: String
tagIEQ: String
name: [String!]
nameNEQ: [String!]
nameMATCH: String
nameIEQ: String
totalMembers: Int
totalMembersGT: Int
totalMembersGTE: Int
totalMembersLT: Int
totalMembersLTE: Int
totalVillages: Int
totalVillagesGT: Int
totalVillagesGTE: Int
totalVillagesLT: Int
totalVillagesLTE: Int
points: Int
pointsGT: Int
pointsGTE: Int
pointsLT: Int
pointsLTE: Int
allPoints: Int
allPointsGT: Int
allPointsGTE: Int
allPointsLT: Int
allPointsLTE: Int
rank: Int
rankGT: Int
rankGTE: Int
rankLT: Int
rankLTE: Int
rankAtt: Int
rankAttGT: Int
rankAttGTE: Int
rankAttLT: Int
rankAttLTE: Int
scoreAtt: Int
scoreAttGT: Int
scoreAttGTE: Int
scoreAttLT: Int
scoreAttLTE: Int
rankDef: Int
rankDefGT: Int
rankDefGTE: Int
rankDefLT: Int
rankDefLTE: Int
scoreDef: Int
scoreDefGT: Int
scoreDefGTE: Int
scoreDefLT: Int
scoreDefLTE: Int
rankTotal: Int
rankTotalGT: Int
rankTotalGTE: Int
rankTotalLT: Int
rankTotalLTE: Int
scoreTotal: Int
scoreTotalGT: Int
scoreTotalGTE: Int
scoreTotalLT: Int
scoreTotalLTE: Int
offset: Int
limit: Int
sort: String
}
extend type Query {
tribes(server: String!, filter: TribeFilter): TribeList!
tribe(server: String!, id: Int!): Tribe
}

View File

@ -0,0 +1,47 @@
type Village {
id: Int!
name: String!
points: Int!
x: Int!
y: Int!
bonus: Int!
player: Player @goField(forceResolver: true)
}
type VillageList {
items: [Village!]
total: Int!
}
input VillageFilter {
id: [Int!]
idNEQ: [Int!]
name: [String!]
nameNEQ: [String!]
nameMATCH: String
nameIEQ: String
points: Int
pointsGT: Int
pointsGTE: Int
pointsLT: Int
pointsLTE: Int
bonus: Int
bonusGT: Int
bonusGTE: Int
bonusLT: Int
bonusLTE: Int
playerID: [Int!]
offset: Int
limit: Int
sort: String
}
extend type Query {
villages(server: String!, filter: VillageFilter): VillageList!
village(server: String!, id: Int!): Village
}

5
langversion/constants.go Normal file
View File

@ -0,0 +1,5 @@
package langversion
const (
PaginationLimit = 100
)

11
langversion/repository.go Normal file
View File

@ -0,0 +1,11 @@
package langversion
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Repository interface {
Fetch(ctx context.Context, filter *models.LangVersionFilter) ([]*models.LangVersion, int, error)
}

View File

@ -0,0 +1,48 @@
package repository
import (
"context"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/langversion"
"github.com/tribalwarshelp/shared/models"
)
type pgRepository struct {
*pg.DB
}
func NewPGRepository(db *pg.DB) (langversion.Repository, error) {
if err := db.CreateTable((*models.LangVersion)(nil), &orm.CreateTableOptions{
IfNotExists: true,
}); err != nil {
return nil, errors.Wrap(err, "Cannot create 'lang_versions' table")
}
return &pgRepository{db}, nil
}
func (repo *pgRepository) Fetch(ctx context.Context, f *models.LangVersionFilter) ([]*models.LangVersion, int, error) {
var err error
data := []*models.LangVersion{}
query := repo.Model(&data).Context(ctx)
if f != nil {
query = query.
WhereStruct(f).
Limit(f.Limit).
Offset(f.Offset)
if f.Sort != "" {
query = query.Order(f.Sort)
}
}
total, err := query.SelectAndCount()
if err != nil && err != pg.ErrNoRows {
return nil, 0, errors.Wrap(err, "Internal server error")
}
return data, total, nil
}

12
langversion/usecase.go Normal file
View File

@ -0,0 +1,12 @@
package langversion
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Usecase interface {
Fetch(ctx context.Context, filter *models.LangVersionFilter) ([]*models.LangVersion, int, error)
GetByTag(ctx context.Context, tag models.LanguageTag) (*models.LangVersion, error)
}

View File

@ -0,0 +1,43 @@
package usecase
import (
"context"
"fmt"
"github.com/tribalwarshelp/api/langversion"
"github.com/tribalwarshelp/shared/models"
)
type usecase struct {
repo langversion.Repository
}
func New(repo langversion.Repository) langversion.Usecase {
return &usecase{
repo,
}
}
func (ucase *usecase) Fetch(ctx context.Context, filter *models.LangVersionFilter) ([]*models.LangVersion, int, error) {
if filter == nil {
filter = &models.LangVersionFilter{}
}
if filter.Limit > langversion.PaginationLimit || filter.Limit <= 0 {
filter.Limit = langversion.PaginationLimit
}
return ucase.repo.Fetch(ctx, filter)
}
func (ucase *usecase) GetByTag(ctx context.Context, tag models.LanguageTag) (*models.LangVersion, error) {
langversions, total, err := ucase.repo.Fetch(ctx, &models.LangVersionFilter{
Tag: []models.LanguageTag{tag},
Limit: 1,
})
if err != nil {
return nil, err
}
if total == 0 {
return nil, fmt.Errorf("There is no lang version with tag: %s.", tag)
}
return langversions[0], nil
}

106
main.go Normal file
View File

@ -0,0 +1,106 @@
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"time"
"github.com/tribalwarshelp/shared/mode"
httpdelivery "github.com/tribalwarshelp/api/graphql/delivery/http"
"github.com/tribalwarshelp/api/graphql/resolvers"
"github.com/tribalwarshelp/api/graphql/dataloaders"
langversionrepo "github.com/tribalwarshelp/api/langversion/repository"
langversionucase "github.com/tribalwarshelp/api/langversion/usecase"
"github.com/tribalwarshelp/api/middleware"
playerrepo "github.com/tribalwarshelp/api/player/repository"
playerucase "github.com/tribalwarshelp/api/player/usecase"
serverrepo "github.com/tribalwarshelp/api/server/repository"
serverucase "github.com/tribalwarshelp/api/server/usecase"
triberepo "github.com/tribalwarshelp/api/tribe/repository"
tribeucase "github.com/tribalwarshelp/api/tribe/usecase"
villagerepo "github.com/tribalwarshelp/api/village/repository"
villageucase "github.com/tribalwarshelp/api/village/usecase"
"github.com/go-pg/pg/v10"
"github.com/joho/godotenv"
"github.com/gin-gonic/gin"
)
func init() {
os.Setenv("TZ", "UTC")
if mode.Get() == mode.DevelopmentMode {
godotenv.Load(".env.development")
}
}
func main() {
db := pg.Connect(&pg.Options{
User: os.Getenv("DB_USER"),
Password: os.Getenv("DB_PASSWORD"),
Database: os.Getenv("DB_NAME"),
Addr: os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT"),
})
defer func() {
if err := db.Close(); err != nil {
log.Fatal("Database disconnecting:", err)
}
}()
langversionRepo, err := langversionrepo.NewPGRepository(db)
if err != nil {
log.Fatal(err)
}
serverRepo, err := serverrepo.NewPGRepository(db)
if err != nil {
log.Fatal(err)
}
tribeRepo := triberepo.NewPGRepository(db)
playerRepo := playerrepo.NewPGRepository(db)
villageRepo := villagerepo.NewPGRepository(db)
router := gin.Default()
v1 := router.Group("")
v1.Use(middleware.DataLoadersToContext(serverRepo, dataloaders.Config{
PlayerRepo: playerRepo,
TribeRepo: tribeRepo,
}))
httpdelivery.Attach(v1, &resolvers.Resolver{
LangVersionUcase: langversionucase.New(langversionRepo),
ServerUcase: serverucase.New(serverRepo),
TribeUcase: tribeucase.New(tribeRepo),
PlayerUcase: playerucase.New(playerRepo),
VillageUcase: villageucase.New(villageRepo),
})
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
go func() {
// service connections
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("Shutdown Server ...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")
}

View File

@ -0,0 +1,3 @@
package middleware
type ContextKey string

View File

@ -0,0 +1,48 @@
package middleware
import (
"context"
"net/http"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/tribalwarshelp/api/graphql/dataloaders"
"github.com/tribalwarshelp/api/server"
"github.com/gin-gonic/gin"
)
var DataloadersContextKey ContextKey = "dataloaders"
func DataLoadersToContext(serverRepo server.Repository, cfg dataloaders.Config) gin.HandlerFunc {
return func(c *gin.Context) {
ctx := c.Request.Context()
loaders := make(map[string]*dataloaders.DataLoaders)
servers, _, err := serverRepo.Fetch(c.Request.Context(), nil)
if err != nil {
c.JSON(http.StatusOK, &gqlerror.Error{
Message: err.Error(),
})
c.Abort()
return
}
for _, server := range servers {
loaders[server.Key] = dataloaders.New(server.Key, cfg)
}
c.Request = c.Request.WithContext(StoreDataLoadersInContext(ctx, loaders))
c.Next()
}
}
func StoreDataLoadersInContext(ctx context.Context, loaders map[string]*dataloaders.DataLoaders) context.Context {
return context.WithValue(ctx, DataloadersContextKey, loaders)
}
func DataLoadersFromContext(ctx context.Context) map[string]*dataloaders.DataLoaders {
dl := ctx.Value(DataloadersContextKey)
if dl == nil {
return nil
}
return dl.(map[string]*dataloaders.DataLoaders)
}

5
player/constants.go Normal file
View File

@ -0,0 +1,5 @@
package player
const (
PaginationLimit = 100
)

11
player/repository.go Normal file
View File

@ -0,0 +1,11 @@
package player
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Repository interface {
Fetch(ctx context.Context, server string, filter *models.PlayerFilter) ([]*models.Player, int, error)
}

View File

@ -0,0 +1,47 @@
package repository
import (
"context"
"fmt"
"strings"
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/player"
"github.com/tribalwarshelp/shared/models"
)
type pgRepository struct {
*pg.DB
}
func NewPGRepository(db *pg.DB) player.Repository {
return &pgRepository{db}
}
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.PlayerFilter) ([]*models.Player, int, error) {
var err error
data := []*models.Player{}
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
if f != nil {
query = query.
WhereStruct(f).
Limit(f.Limit).
Offset(f.Offset)
if f.Sort != "" {
query = query.Order(f.Sort)
}
}
total, err := query.SelectAndCount()
if err != nil && err != pg.ErrNoRows {
if strings.Contains(err.Error(), `relation "`+server) {
return nil, 0, fmt.Errorf("Server not found")
}
return nil, 0, errors.Wrap(err, "Internal server error")
}
return data, total, nil
}

12
player/usecase.go Normal file
View File

@ -0,0 +1,12 @@
package player
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.PlayerFilter) ([]*models.Player, int, error)
GetByID(ctx context.Context, server string, id int) (*models.Player, error)
}

View File

@ -0,0 +1,41 @@
package usecase
import (
"context"
"fmt"
"github.com/tribalwarshelp/api/player"
"github.com/tribalwarshelp/shared/models"
)
type usecase struct {
repo player.Repository
}
func New(repo player.Repository) player.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.PlayerFilter) ([]*models.Player, int, error) {
if filter == nil {
filter = &models.PlayerFilter{}
}
if filter.Limit > player.PaginationLimit || filter.Limit <= 0 {
filter.Limit = player.PaginationLimit
}
return ucase.repo.Fetch(ctx, server, filter)
}
func (ucase *usecase) GetByID(ctx context.Context, server string, id int) (*models.Player, error) {
players, total, err := ucase.repo.Fetch(ctx, server, &models.PlayerFilter{
ID: []int{id},
Limit: 1,
})
if err != nil {
return nil, err
}
if total == 0 {
return nil, fmt.Errorf("Player with id: %d not found.", id)
}
return players[0], nil
}

5
server/constants.go Normal file
View File

@ -0,0 +1,5 @@
package server
const (
PaginationLimit = 100
)

11
server/repository.go Normal file
View File

@ -0,0 +1,11 @@
package server
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Repository interface {
Fetch(ctx context.Context, filter *models.ServerFilter) ([]*models.Server, int, error)
}

View File

@ -0,0 +1,48 @@
package repository
import (
"context"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/server"
"github.com/tribalwarshelp/shared/models"
)
type pgRepository struct {
*pg.DB
}
func NewPGRepository(db *pg.DB) (server.Repository, error) {
if err := db.CreateTable((*models.Server)(nil), &orm.CreateTableOptions{
IfNotExists: true,
}); err != nil {
return nil, errors.Wrap(err, "Cannot create 'servers' table")
}
return &pgRepository{db}, nil
}
func (repo *pgRepository) Fetch(ctx context.Context, f *models.ServerFilter) ([]*models.Server, int, error) {
var err error
data := []*models.Server{}
query := repo.Model(&data).Context(ctx)
if f != nil {
query = query.
WhereStruct(f).
Limit(f.Limit).
Offset(f.Offset)
if f.Sort != "" {
query = query.Order(f.Sort)
}
}
total, err := query.SelectAndCount()
if err != nil && err != pg.ErrNoRows {
return nil, 0, errors.Wrap(err, "Internal server error")
}
return data, total, nil
}

12
server/usecase.go Normal file
View File

@ -0,0 +1,12 @@
package server
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Usecase interface {
Fetch(ctx context.Context, filter *models.ServerFilter) ([]*models.Server, int, error)
GetByKey(ctx context.Context, key string) (*models.Server, error)
}

View File

@ -0,0 +1,41 @@
package usecase
import (
"context"
"fmt"
"github.com/tribalwarshelp/api/server"
"github.com/tribalwarshelp/shared/models"
)
type usecase struct {
repo server.Repository
}
func New(repo server.Repository) server.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, filter *models.ServerFilter) ([]*models.Server, int, error) {
if filter == nil {
filter = &models.ServerFilter{}
}
if filter.Limit > server.PaginationLimit || filter.Limit <= 0 {
filter.Limit = server.PaginationLimit
}
return ucase.repo.Fetch(ctx, filter)
}
func (ucase *usecase) GetByKey(ctx context.Context, key string) (*models.Server, error) {
servers, total, err := ucase.repo.Fetch(ctx, &models.ServerFilter{
Key: []string{key},
Limit: 1,
})
if err != nil {
return nil, err
}
if total == 0 {
return nil, fmt.Errorf("Server with key: %s not found.", key)
}
return servers[0], nil
}

5
tribe/constants.go Normal file
View File

@ -0,0 +1,5 @@
package tribe
const (
PaginationLimit = 100
)

11
tribe/repository.go Normal file
View File

@ -0,0 +1,11 @@
package tribe
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Repository interface {
Fetch(ctx context.Context, server string, filter *models.TribeFilter) ([]*models.Tribe, int, error)
}

View File

@ -0,0 +1,47 @@
package repository
import (
"context"
"fmt"
"strings"
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/tribe"
"github.com/tribalwarshelp/shared/models"
)
type pgRepository struct {
*pg.DB
}
func NewPGRepository(db *pg.DB) tribe.Repository {
return &pgRepository{db}
}
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.TribeFilter) ([]*models.Tribe, int, error) {
var err error
data := []*models.Tribe{}
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
if f != nil {
query = query.
WhereStruct(f).
Limit(f.Limit).
Offset(f.Offset)
if f.Sort != "" {
query = query.Order(f.Sort)
}
}
total, err := query.SelectAndCount()
if err != nil && err != pg.ErrNoRows {
if strings.Contains(err.Error(), `relation "`+server) {
return nil, 0, fmt.Errorf("Server not found")
}
return nil, 0, errors.Wrap(err, "Internal server error")
}
return data, total, nil
}

12
tribe/usecase.go Normal file
View File

@ -0,0 +1,12 @@
package tribe
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.TribeFilter) ([]*models.Tribe, int, error)
GetByID(ctx context.Context, server string, id int) (*models.Tribe, error)
}

View File

@ -0,0 +1,41 @@
package usecase
import (
"context"
"fmt"
"github.com/tribalwarshelp/api/tribe"
"github.com/tribalwarshelp/shared/models"
)
type usecase struct {
repo tribe.Repository
}
func New(repo tribe.Repository) tribe.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.TribeFilter) ([]*models.Tribe, int, error) {
if filter == nil {
filter = &models.TribeFilter{}
}
if filter.Limit > tribe.PaginationLimit || filter.Limit <= 0 {
filter.Limit = tribe.PaginationLimit
}
return ucase.repo.Fetch(ctx, server, filter)
}
func (ucase *usecase) GetByID(ctx context.Context, server string, id int) (*models.Tribe, error) {
tribes, total, err := ucase.repo.Fetch(ctx, server, &models.TribeFilter{
ID: []int{id},
Limit: 1,
})
if err != nil {
return nil, err
}
if total == 0 {
return nil, fmt.Errorf("Tribe with id: %d not found.", id)
}
return tribes[0], nil
}

5
village/constants.go Normal file
View File

@ -0,0 +1,5 @@
package village
const (
PaginationLimit = 100
)

11
village/repository.go Normal file
View File

@ -0,0 +1,11 @@
package village
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Repository interface {
Fetch(ctx context.Context, server string, filter *models.VillageFilter) ([]*models.Village, int, error)
}

View File

@ -0,0 +1,47 @@
package repository
import (
"context"
"fmt"
"strings"
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/village"
"github.com/tribalwarshelp/shared/models"
)
type pgRepository struct {
*pg.DB
}
func NewPGRepository(db *pg.DB) village.Repository {
return &pgRepository{db}
}
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.VillageFilter) ([]*models.Village, int, error) {
var err error
data := []*models.Village{}
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
if f != nil {
query = query.
WhereStruct(f).
Limit(f.Limit).
Offset(f.Offset)
if f.Sort != "" {
query = query.Order(f.Sort)
}
}
total, err := query.SelectAndCount()
if err != nil && err != pg.ErrNoRows {
if strings.Contains(err.Error(), `relation "`+server) {
return nil, 0, fmt.Errorf("Server not found")
}
return nil, 0, errors.Wrap(err, "Internal server error")
}
return data, total, nil
}

12
village/usecase.go Normal file
View File

@ -0,0 +1,12 @@
package village
import (
"context"
"github.com/tribalwarshelp/shared/models"
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.VillageFilter) ([]*models.Village, int, error)
GetByID(ctx context.Context, server string, id int) (*models.Village, error)
}

View File

@ -0,0 +1,41 @@
package usecase
import (
"context"
"fmt"
"github.com/tribalwarshelp/api/village"
"github.com/tribalwarshelp/shared/models"
)
type usecase struct {
repo village.Repository
}
func New(repo village.Repository) village.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.VillageFilter) ([]*models.Village, int, error) {
if filter == nil {
filter = &models.VillageFilter{}
}
if filter.Limit > village.PaginationLimit || filter.Limit <= 0 {
filter.Limit = village.PaginationLimit
}
return ucase.repo.Fetch(ctx, server, filter)
}
func (ucase *usecase) GetByID(ctx context.Context, server string, id int) (*models.Village, error) {
villages, total, err := ucase.repo.Fetch(ctx, server, &models.VillageFilter{
ID: []int{id},
Limit: 1,
})
if err != nil {
return nil, err
}
if total == 0 {
return nil, fmt.Errorf("Village with id: %d not found.", id)
}
return villages[0], nil
}