First commit
This commit is contained in:
commit
1161a56ec9
25 changed files with 5288 additions and 0 deletions
5
.dockerignore
Normal file
5
.dockerignore
Normal file
|
@ -0,0 +1,5 @@
|
|||
Dockerfile
|
||||
gqlgen.yml
|
||||
*.graphqls
|
||||
deployments/
|
||||
Makefile
|
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
12
Dockerfile
Normal file
12
Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
FROM golang:1.22.4
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /ozon-task cmd/server.go
|
||||
|
||||
CMD ["/ozon-task"]
|
16
Makefile
Normal file
16
Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
docker-postgres: docker
|
||||
docker compose -f deployments/docker-compose/postgres/docker-compose.yml up
|
||||
|
||||
docker-standalone: docker
|
||||
docker compose -f deployments/docker-compose/standalone/docker-compose.yml up
|
||||
|
||||
docker: go-generate Dockerfile
|
||||
docker build --tag ozon-task .
|
||||
|
||||
go-generate:
|
||||
go generate ./...
|
||||
|
||||
clean: docker-clean
|
||||
|
||||
docker-clean:
|
||||
docker container rm postgres-ozon-task-1 postgres-postgres-1 && docker volume rm postgres_postgres-data
|
36
cmd/server.go
Normal file
36
cmd/server.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"git.obamna.ru/erius/ozon-task/graph"
|
||||
"git.obamna.ru/erius/ozon-task/internal/storage"
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
)
|
||||
|
||||
const defaultPort = "8080"
|
||||
|
||||
var port = os.Getenv("APP_PORT")
|
||||
|
||||
func main() {
|
||||
if port == "" {
|
||||
port = defaultPort
|
||||
}
|
||||
|
||||
s, err := storage.InitStorage()
|
||||
if err != nil {
|
||||
log.Panic("could not connect to database")
|
||||
}
|
||||
|
||||
res := graph.InitResolver(s)
|
||||
srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: res}))
|
||||
|
||||
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
|
||||
http.Handle("/query", srv)
|
||||
|
||||
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
|
||||
log.Fatal(http.ListenAndServe(":"+port, nil))
|
||||
}
|
23
deployments/docker-compose/postgres/docker-compose.yml
Normal file
23
deployments/docker-compose/postgres/docker-compose.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
ozon-task:
|
||||
image: ozon-task:latest
|
||||
ports:
|
||||
- 8080:8080
|
||||
env_file: ozon-task.env
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
hostname: postgres
|
||||
env_file: postgres.env
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
11
deployments/docker-compose/postgres/ozon-task.env
Normal file
11
deployments/docker-compose/postgres/ozon-task.env
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Define storage type for the app
|
||||
# inmemory (default) - store data in memory (non-persistent)
|
||||
# postgres - store data in postgres database
|
||||
APP_STORAGE=postgres
|
||||
|
||||
# Postgres configuration for APP_STORAGE=postgres
|
||||
APP_POSTGRES_HOST=postgres
|
||||
APP_POSTGRES_PORT=5432
|
||||
APP_POSTGRES_USER=username
|
||||
APP_POSTGRES_PASSWORD=password
|
||||
APP_POSTGRES_DB=database
|
3
deployments/docker-compose/postgres/postgres.env
Normal file
3
deployments/docker-compose/postgres/postgres.env
Normal file
|
@ -0,0 +1,3 @@
|
|||
POSTGRES_USER=username
|
||||
POSTGRES_PASSWORD=password
|
||||
POSTGRES_DB=database
|
6
deployments/docker-compose/standalone/docker-compose.yml
Normal file
6
deployments/docker-compose/standalone/docker-compose.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
ozon-task:
|
||||
image: ozon-task:latest
|
||||
ports:
|
||||
- 8080:8080
|
||||
env_file: ozon-task.env
|
4
deployments/docker-compose/standalone/ozon-task.env
Normal file
4
deployments/docker-compose/standalone/ozon-task.env
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Define storage type for the app
|
||||
# inmemory (default) - store data in memory (non-persistent)
|
||||
# postgres - store data in postgres database
|
||||
APP_STORAGE=inmemory
|
37
go.mod
Normal file
37
go.mod
Normal file
|
@ -0,0 +1,37 @@
|
|||
module git.obamna.ru/erius/ozon-task
|
||||
|
||||
go 1.22.4
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.17.49
|
||||
github.com/vektah/gqlparser/v2 v2.5.16
|
||||
gorm.io/driver/postgres v1.5.9
|
||||
gorm.io/gorm v1.25.10
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sosodev/duration v1.3.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.2 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
89
go.sum
Normal file
89
go.sum
Normal file
|
@ -0,0 +1,89 @@
|
|||
github.com/99designs/gqlgen v0.17.49 h1:b3hNGexHd33fBSAd4NDT/c3NCcQzcAVkknhN9ym36YQ=
|
||||
github.com/99designs/gqlgen v0.17.49/go.mod h1:tC8YFVZMed81x7UJ7ORUwXF4Kn6SXuucFqQBhN8+BU0=
|
||||
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
|
||||
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
|
||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
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/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
|
||||
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
|
||||
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
|
||||
github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8=
|
||||
github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
88
gqlgen.yml
Normal file
88
gqlgen.yml
Normal file
|
@ -0,0 +1,88 @@
|
|||
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
|
||||
schema:
|
||||
- graph/*.graphqls
|
||||
|
||||
# Where should the generated server code go?
|
||||
exec:
|
||||
filename: graph/generated.go
|
||||
package: graph
|
||||
|
||||
# Uncomment to enable federation
|
||||
# federation:
|
||||
# filename: graph/federation.go
|
||||
# package: graph
|
||||
|
||||
# Where should any generated models go?
|
||||
model:
|
||||
filename: graph/model/models_gen.go
|
||||
package: model
|
||||
|
||||
# Where should the resolver implementations go?
|
||||
resolver:
|
||||
layout: follow-schema
|
||||
dir: graph
|
||||
package: graph
|
||||
filename_template: "{name}.resolvers.go"
|
||||
# Optional: turn on to not generate template comments above resolvers
|
||||
# omit_template_comment: false
|
||||
|
||||
# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
|
||||
# struct_tag: json
|
||||
|
||||
# Optional: turn on to use []Thing instead of []*Thing
|
||||
# omit_slice_element_pointers: false
|
||||
|
||||
# Optional: turn on to omit Is<Name>() methods to interface and unions
|
||||
# omit_interface_checks : true
|
||||
|
||||
# Optional: turn on to skip generation of ComplexityRoot struct content and Complexity function
|
||||
# omit_complexity: false
|
||||
|
||||
# Optional: turn on to not generate any file notice comments in generated files
|
||||
# omit_gqlgen_file_notice: false
|
||||
|
||||
# Optional: turn on to exclude the gqlgen version in the generated file notice. No effect if `omit_gqlgen_file_notice` is true.
|
||||
# omit_gqlgen_version_in_file_notice: false
|
||||
|
||||
# Optional: turn off to make struct-type struct fields not use pointers
|
||||
# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing }
|
||||
# struct_fields_always_pointers: true
|
||||
|
||||
# Optional: turn off to make resolvers return values instead of pointers for structs
|
||||
# resolvers_always_return_pointers: true
|
||||
|
||||
# Optional: turn on to return pointers instead of values in unmarshalInput
|
||||
# return_pointers_in_unmarshalinput: false
|
||||
|
||||
# Optional: wrap nullable input fields with Omittable
|
||||
# nullable_input_omittable: true
|
||||
|
||||
# Optional: set to speed up generation time by not performing a final validation pass.
|
||||
# skip_validation: true
|
||||
|
||||
# Optional: set to skip running `go mod tidy` when generating server code
|
||||
# skip_mod_tidy: true
|
||||
|
||||
# gqlgen will search for any type names in the schema in these go packages
|
||||
# if they match it will use them, otherwise it will generate them.
|
||||
autobind:
|
||||
- "git.obamna.ru/erius/ozon-task/graph/model"
|
||||
|
||||
# This section declares type mapping between the GraphQL and go type systems
|
||||
#
|
||||
# The first line in each type will be used as defaults for resolver arguments and
|
||||
# modelgen, the others will be allowed when binding to fields. Configure them to
|
||||
# your liking
|
||||
models:
|
||||
ID:
|
||||
model:
|
||||
- github.com/99designs/gqlgen/graphql.ID
|
||||
- github.com/99designs/gqlgen/graphql.Int
|
||||
- github.com/99designs/gqlgen/graphql.Int64
|
||||
- github.com/99designs/gqlgen/graphql.Int32
|
||||
- github.com/99designs/gqlgen/graphql.Uint
|
||||
Int:
|
||||
model:
|
||||
- github.com/99designs/gqlgen/graphql.Int
|
||||
- github.com/99designs/gqlgen/graphql.Int64
|
||||
- github.com/99designs/gqlgen/graphql.Int32
|
4593
graph/generated.go
Normal file
4593
graph/generated.go
Normal file
File diff suppressed because it is too large
Load diff
61
graph/model/data.go
Normal file
61
graph/model/data.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const CommentLengthLimit = 2000
|
||||
|
||||
type Comment struct {
|
||||
// db fields
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
||||
// db foreign key to reference parent post
|
||||
PostID uint
|
||||
|
||||
Post *Post `json:"post"`
|
||||
Author string `json:"author"`
|
||||
Contents string `json:"contents"`
|
||||
|
||||
// db key to reference parent comment
|
||||
ReplyToID *uint
|
||||
|
||||
ReplyTo *Comment `json:"reply_to,omitempty"`
|
||||
Replies []*Comment `json:"replies" gorm:"many2many:comment_replies"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
// db fields
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Contents string `json:"contents"`
|
||||
Comments []*Comment `json:"comments" gorm:"foreignKey:PostID"`
|
||||
AllowComments bool `json:"allowComments"`
|
||||
}
|
||||
|
||||
func PostFromInput(input *PostInput) *Post {
|
||||
return &Post{
|
||||
Author: input.Author,
|
||||
Title: input.Title,
|
||||
Contents: input.Contents,
|
||||
AllowComments: input.AllowComments,
|
||||
}
|
||||
}
|
||||
|
||||
func CommentFromInput(input *CommentInput) *Comment {
|
||||
return &Comment{
|
||||
Author: input.Author,
|
||||
Contents: input.Contents,
|
||||
Replies: make([]*Comment, 0),
|
||||
}
|
||||
}
|
9
graph/model/models_gen.go
Normal file
9
graph/model/models_gen.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
type Mutation struct {
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
}
|
19
graph/model/mutation.go
Normal file
19
graph/model/mutation.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package model
|
||||
|
||||
type AddResult struct {
|
||||
ItemID *uint `json:"item_id,omitempty"`
|
||||
}
|
||||
|
||||
type CommentInput struct {
|
||||
ParentPostID *uint `json:"parent_post_id,omitempty"`
|
||||
ParentCommentID *uint `json:"parent_comment_id,omitempty"`
|
||||
Author string `json:"author"`
|
||||
Contents string `json:"contents"`
|
||||
}
|
||||
|
||||
type PostInput struct {
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Contents string `json:"contents"`
|
||||
AllowComments bool `json:"allowComments"`
|
||||
}
|
19
graph/resolver.go
Normal file
19
graph/resolver.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package graph
|
||||
|
||||
//go:generate go run github.com/99designs/gqlgen generate
|
||||
|
||||
import (
|
||||
"git.obamna.ru/erius/ozon-task/internal/storage"
|
||||
)
|
||||
|
||||
// This file will not be regenerated automatically.
|
||||
//
|
||||
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||
|
||||
type Resolver struct {
|
||||
Storage storage.Storage
|
||||
}
|
||||
|
||||
func InitResolver(s storage.Storage) *Resolver {
|
||||
return &Resolver{Storage: s}
|
||||
}
|
44
graph/schema.graphqls
Normal file
44
graph/schema.graphqls
Normal file
|
@ -0,0 +1,44 @@
|
|||
type Post {
|
||||
id: ID!
|
||||
title: String!
|
||||
author: String!
|
||||
contents: String!
|
||||
comments: [Comment!]!
|
||||
allowComments: Boolean!
|
||||
}
|
||||
|
||||
type Comment {
|
||||
id: ID!
|
||||
post: Post!
|
||||
author: String!
|
||||
contents: String!
|
||||
replyTo: Comment
|
||||
replies: [Comment!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
allPosts: [Post!]!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
addPost(input: PostInput): AddResult!
|
||||
addComment(input: CommentInput): AddResult!
|
||||
}
|
||||
|
||||
input PostInput {
|
||||
title: String!
|
||||
author: String!
|
||||
contents: String!
|
||||
allowComments: Boolean! = true
|
||||
}
|
||||
|
||||
input CommentInput {
|
||||
parentPostId: ID
|
||||
parentCommentId: ID
|
||||
author: String!
|
||||
contents: String!
|
||||
}
|
||||
|
||||
type AddResult {
|
||||
itemId: ID!
|
||||
}
|
42
graph/schema.resolvers.go
Normal file
42
graph/schema.resolvers.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package graph
|
||||
|
||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.49
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.obamna.ru/erius/ozon-task/graph/model"
|
||||
)
|
||||
|
||||
// AddPost is the resolver for the add_post field.
|
||||
func (r *mutationResolver) AddPost(ctx context.Context, input *model.PostInput) (*model.AddResult, error) {
|
||||
return r.Storage.AddPost(input)
|
||||
}
|
||||
|
||||
// AddComment is the resolver for the add_comment field.
|
||||
func (r *mutationResolver) AddComment(ctx context.Context, input *model.CommentInput) (*model.AddResult, error) {
|
||||
if len(input.Contents) > model.CommentLengthLimit {
|
||||
return nil, fmt.Errorf("exceeded max comment length of %d chars", model.CommentLengthLimit)
|
||||
}
|
||||
if input.ParentCommentID == nil && input.ParentPostID == nil {
|
||||
return nil, fmt.Errorf("parent post or parent comment ids weren't specified")
|
||||
}
|
||||
return r.Storage.AddComment(input)
|
||||
}
|
||||
|
||||
// AllPosts is the resolver for the all_posts field.
|
||||
func (r *queryResolver) AllPosts(ctx context.Context) ([]*model.Post, error) {
|
||||
return r.Storage.GetPosts()
|
||||
}
|
||||
|
||||
// Mutation returns MutationResolver implementation.
|
||||
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Query returns QueryResolver implementation.
|
||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
62
internal/storage/db/database.go
Normal file
62
internal/storage/db/database.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"git.obamna.ru/erius/ozon-task/graph/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (s *Database) AddPost(input *model.PostInput) (*model.AddResult, error) {
|
||||
post := model.PostFromInput(input)
|
||||
err := s.db.Create(post).Error
|
||||
return &model.AddResult{ItemID: &post.ID}, err
|
||||
}
|
||||
|
||||
func (s *Database) AddComment(input *model.CommentInput) (*model.AddResult, error) {
|
||||
comment := model.CommentFromInput(input)
|
||||
if input.ParentPostID == nil {
|
||||
// multiple operations performed in one transaction
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
var parent model.Comment
|
||||
// find parent comment in db
|
||||
err := s.db.First(&parent, *input.ParentCommentID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// set new comment fields to reference parent post and comment
|
||||
comment.ReplyTo = &parent
|
||||
comment.PostID = parent.PostID
|
||||
// insert comment
|
||||
err = s.db.Create(comment).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add new reply to parent and save
|
||||
parent.Replies = append(parent.Replies, comment)
|
||||
err = s.db.Save(parent).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
comment.PostID = *input.ParentPostID
|
||||
err := s.db.Create(comment).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &model.AddResult{ItemID: &comment.ID}, nil
|
||||
}
|
||||
|
||||
func (s Database) GetPosts() ([]*model.Post, error) {
|
||||
var posts []*model.Post
|
||||
err := s.db.Find(&posts).Error
|
||||
return posts, err
|
||||
}
|
29
internal/storage/db/postgres.go
Normal file
29
internal/storage/db/postgres.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.obamna.ru/erius/ozon-task/graph/model"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
host = os.Getenv("APP_POSTGRES_HOST")
|
||||
port = os.Getenv("APP_POSTGRES_PORT")
|
||||
user = os.Getenv("APP_POSTGRES_USER")
|
||||
passwd = os.Getenv("APP_POSTGRES_PASSWORD")
|
||||
db = os.Getenv("APP_POSTGRES_DB")
|
||||
con = fmt.Sprintf("postgres://%s:%s@%s:%s/%s", user, passwd, host, port, db)
|
||||
)
|
||||
|
||||
func InitPostgres() (*Database, error) {
|
||||
// PrepareStmt is true for caching complex sql statements when adding comments or replies
|
||||
db, err := gorm.Open(postgres.Open(con), &gorm.Config{PrepareStmt: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.AutoMigrate(&model.Post{}, &model.Comment{})
|
||||
return &Database{db}, nil
|
||||
}
|
41
internal/storage/memory.go
Normal file
41
internal/storage/memory.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"git.obamna.ru/erius/ozon-task/graph/model"
|
||||
)
|
||||
|
||||
type InMemory struct {
|
||||
postId uint
|
||||
commentId uint
|
||||
posts []*model.Post
|
||||
comments []*model.Comment
|
||||
}
|
||||
|
||||
func InitInMemory() *InMemory {
|
||||
return &InMemory{
|
||||
postId: 0,
|
||||
commentId: 0,
|
||||
posts: make([]*model.Post, 0),
|
||||
comments: make([]*model.Comment, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *InMemory) AddPost(input *model.PostInput) (*model.AddResult, error) {
|
||||
post := model.PostFromInput(input)
|
||||
post.ID = s.postId
|
||||
s.posts = append(s.posts, post)
|
||||
s.postId++
|
||||
return &model.AddResult{ItemID: &post.ID}, nil
|
||||
}
|
||||
|
||||
func (s *InMemory) AddComment(input *model.CommentInput) (*model.AddResult, error) {
|
||||
comment := model.CommentFromInput(input)
|
||||
comment.ID = s.commentId
|
||||
s.comments = append(s.comments, comment)
|
||||
s.commentId++
|
||||
return &model.AddResult{ItemID: &comment.ID}, nil
|
||||
}
|
||||
|
||||
func (s InMemory) GetPosts() ([]*model.Post, error) {
|
||||
return s.posts, nil
|
||||
}
|
32
internal/storage/storage.go
Normal file
32
internal/storage/storage.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.obamna.ru/erius/ozon-task/graph/model"
|
||||
"git.obamna.ru/erius/ozon-task/internal/storage/db"
|
||||
)
|
||||
|
||||
const (
|
||||
inMemory = "inmemory"
|
||||
postgres = "postgres"
|
||||
)
|
||||
|
||||
var storage = os.Getenv("APP_STORAGE")
|
||||
|
||||
type Storage interface {
|
||||
AddPost(input *model.PostInput) (*model.AddResult, error)
|
||||
AddComment(input *model.CommentInput) (*model.AddResult, error)
|
||||
GetPosts() ([]*model.Post, error)
|
||||
}
|
||||
|
||||
func InitStorage() (Storage, error) {
|
||||
switch storage {
|
||||
case inMemory:
|
||||
return InitInMemory(), nil
|
||||
case postgres:
|
||||
return db.InitPostgres()
|
||||
default:
|
||||
return InitInMemory(), nil
|
||||
}
|
||||
}
|
7
tools.go
Normal file
7
tools.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build tools
|
||||
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/99designs/gqlgen"
|
||||
)
|
Loading…
Reference in a new issue