BLOG-43 feat: get all posts
All checks were successful
Frontend CI / build (push) Successful in 1m56s

This commit is contained in:
SquidSpirit 2025-03-28 00:02:34 +08:00
parent a9081734b3
commit a4394eea9e
26 changed files with 726 additions and 111 deletions

View File

@ -1,28 +1,39 @@
package main
import (
"database/sql"
"log"
"net/http"
"os"
"git.squidspirit.com/squid/blog.git/backend/internal/framework/graph"
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/gateway"
"git.squidspirit.com/squid/blog.git/backend/internal/application"
"git.squidspirit.com/squid/blog.git/backend/internal/framework/api/graph"
"git.squidspirit.com/squid/blog.git/backend/internal/framework/db/postgres"
"git.squidspirit.com/squid/blog.git/backend/internal/pkg/env"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
_ "github.com/lib/pq"
"github.com/vektah/gqlparser/v2/ast"
)
const defaultPort = "8080"
func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
db, err := postgres.Connect(env.GetDSN())
if err != nil {
log.Fatal(err)
}
srv := handler.New(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
initGraphqlHandler(createResolver(db))
port := env.GetPort()
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
func initGraphqlHandler(resolver *graph.Resolver) *handler.Server {
srv := handler.New(graph.NewExecutableSchema(graph.Config{Resolvers: resolver}))
srv.AddTransport(transport.Options{})
srv.AddTransport(transport.GET{})
@ -38,6 +49,13 @@ func main() {
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))
return srv
}
func createResolver(db *sql.DB) *graph.Resolver {
postRepo := gateway.NewPostRepo(postgres.NewPostDBService(db))
return &graph.Resolver{
GetAllPostsUseCase: application.NewGetAllPostsUseCase(postRepo),
}
}

View File

@ -4,6 +4,8 @@ go 1.24.1
require (
github.com/99designs/gqlgen v0.17.66
github.com/lib/pq v1.10.9
github.com/thoas/go-funk v0.9.3
github.com/vektah/gqlparser/v2 v2.5.22
)
@ -11,16 +13,20 @@ require (
github.com/agnivade/levenshtein v1.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/google/go-cmp v0.7.0 // 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/kr/pretty v0.3.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // 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.5 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.30.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.31.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -12,47 +12,68 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/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-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/vektah/gqlparser/v2 v2.5.22 h1:yaaeJ0fu+nv1vUMW0Hl+aS1eiv1vMfapBNjpffAda1I=
github.com/vektah/gqlparser/v2 v2.5.22/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
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.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,6 +1,6 @@
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- internal/framework/graph/*.graphqls
- internal/framework/api/graph/*.graphqls
# Where should the generated server code go?
exec:
@ -8,7 +8,7 @@ exec:
layout: single-file # Only other option is "follow-schema," ie multi-file.
# Only for single-file layout:
filename: internal/framework/graph/generated.go
filename: internal/framework/api/graph/generated.go
# Only for follow-schema layout:
# dir: graph
@ -27,8 +27,8 @@ exec:
# Where should any generated models go?
model:
filename: internal/framework/graph/model/models_gen.go
package: model
filename: internal/adapter/controller/graphdto/dtos.go
package: graphdto
# Optional: Pass in a path to a new gotpl template to use for generating the models
# model_template: [your/path/model.gotpl]
@ -42,7 +42,7 @@ resolver:
# filename: graph/resolver.go
# Only for follow-schema layout:
dir: internal/framework/graph
dir: internal/framework/api/graph
filename_template: "{name}.resolvers.go"
# Optional: turn on to not generate template comments above resolvers
@ -127,10 +127,7 @@ autobind:
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.Uint32
# gqlgen provides a default GraphQL UUID convenience wrapper for github.com/google/uuid
# but you can override this to provide your own GraphQL UUID implementation
UUID:

View File

@ -1,20 +1,21 @@
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package model
package graphdto
type Label struct {
ID string `json:"id"`
ID uint32 `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
}
type Post struct {
ID string `json:"id"`
ID uint32 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Description string `json:"description"`
PreviewImageURL string `json:"previewImageUrl"`
Labels []*Label `json:"labels"`
PublishedTime *string `json:"publishedTime,omitempty"`
}
type Query struct {

View File

@ -0,0 +1,11 @@
package graphdto
import "git.squidspirit.com/squid/blog.git/backend/internal/domain"
func NewLabelDTO(labelEntity *domain.Label) *Label {
return &Label{
ID: labelEntity.ID,
Name: labelEntity.Name,
Color: labelEntity.Color,
}
}

View File

@ -0,0 +1,19 @@
package graphdto
import (
"git.squidspirit.com/squid/blog.git/backend/internal/domain"
"github.com/thoas/go-funk"
)
func NewPostDTO(entity *domain.Post) *Post {
return &Post{
ID: entity.ID,
Title: entity.Title,
Content: entity.Content,
Description: entity.Description,
PreviewImageURL: entity.PreviewImageURL,
Labels: funk.Map(entity.Labels, func(label *domain.Label) *Label {
return NewLabelDTO(label)
}).([]*Label),
}
}

View File

@ -0,0 +1,33 @@
package controller
import (
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/controller/graphdto"
"git.squidspirit.com/squid/blog.git/backend/internal/application"
"git.squidspirit.com/squid/blog.git/backend/internal/domain"
"github.com/thoas/go-funk"
)
type QueryPostsController interface {
Handle() ([]*graphdto.Post, error)
}
type queryPostsControllerImpl struct {
getAllPostsUseCase application.GetAllPostsUseCase
}
func NewQueryPostsController(getAllPostsUseCase application.GetAllPostsUseCase) QueryPostsController {
return &queryPostsControllerImpl{
getAllPostsUseCase: getAllPostsUseCase,
}
}
func (c *queryPostsControllerImpl) Handle() ([]*graphdto.Post, error) {
entities, err := c.getAllPostsUseCase.Execute()
if err != nil {
return nil, err
}
return funk.Map(entities, func(entity *domain.Post) *graphdto.Post {
return graphdto.NewPostDTO(entity)
}).([]*graphdto.Post), nil
}

View File

@ -0,0 +1,23 @@
package dbdto
import (
"time"
"git.squidspirit.com/squid/blog.git/backend/internal/domain"
)
type Label struct {
ID uint32 `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
CreatedTime time.Time
UpdatedTime time.Time
}
func (l *Label) ToEntity() *domain.Label {
return &domain.Label{
ID: l.ID,
Name: l.Name,
Color: l.Color,
}
}

View File

@ -0,0 +1,34 @@
package dbdto
import (
"time"
"git.squidspirit.com/squid/blog.git/backend/internal/domain"
"github.com/thoas/go-funk"
)
type Post struct {
ID uint32
Title string
Content string
Description string
PreviewImageURL string
Labels []*Label
PublishedTime *time.Time
CreatedTime time.Time
UpdatedTime time.Time
}
func (p *Post) ToEntity() *domain.Post {
return &domain.Post{
ID: p.ID,
Title: p.Title,
Content: p.Content,
Description: p.Description,
PreviewImageURL: p.PreviewImageURL,
Labels: funk.Map(p.Labels, func(label *Label) *domain.Label {
return label.ToEntity()
}).([]*domain.Label),
PublishedTime: p.PublishedTime,
}
}

View File

@ -0,0 +1,34 @@
package gateway
import (
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/gateway/dbdto"
"git.squidspirit.com/squid/blog.git/backend/internal/application"
"git.squidspirit.com/squid/blog.git/backend/internal/domain"
"github.com/thoas/go-funk"
)
type postRepo struct {
dbService PostDBService
}
type PostDBService interface {
QueryAll() ([]*dbdto.Post, error)
}
func NewPostRepo(dbService PostDBService) application.PostRepo {
return &postRepo{dbService}
}
func (r *postRepo) GetAll() ([]*domain.Post, error) {
postDtos, err := r.dbService.QueryAll()
if err != nil {
return nil, err
}
return funk.Map(postDtos, func(postDto *dbdto.Post) *domain.Post {
return postDto.ToEntity()
}).([]*domain.Post), nil
}
func (r *postRepo) GetByID(id int) (*domain.Post, error) {
panic("unimplemented")
}

View File

@ -0,0 +1,21 @@
package application
import "git.squidspirit.com/squid/blog.git/backend/internal/domain"
type GetAllPostsUseCase interface {
Execute() ([]*domain.Post, error)
}
type getAllPostsUseCaseImpl struct {
postRepo PostRepo
}
func NewGetAllPostsUseCase(postRepo PostRepo) GetAllPostsUseCase {
return &getAllPostsUseCaseImpl{
postRepo: postRepo,
}
}
func (uc *getAllPostsUseCaseImpl) Execute() ([]*domain.Post, error) {
return uc.postRepo.GetAll()
}

View File

@ -0,0 +1,7 @@
package application
import "git.squidspirit.com/squid/blog.git/backend/internal/domain"
type LabelRepo interface {
GetByIDs(ids []int) ([]*domain.Label, error)
}

View File

@ -0,0 +1,8 @@
package application
import "git.squidspirit.com/squid/blog.git/backend/internal/domain"
type PostRepo interface {
GetAll() ([]*domain.Post, error)
GetByID(id int) (*domain.Post, error)
}

View File

@ -0,0 +1,7 @@
package domain
type Label struct {
ID uint32
Name string
Color string
}

View File

@ -0,0 +1,13 @@
package domain
import "time"
type Post struct {
ID uint32
Title string
Content string
Description string
PreviewImageURL string
Labels []*Label
PublishedTime *time.Time
}

View File

@ -12,7 +12,7 @@ import (
"sync"
"sync/atomic"
"git.squidspirit.com/squid/blog.git/backend/internal/framework/graph/model"
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/controller/graphdto"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/introspection"
gqlparser "github.com/vektah/gqlparser/v2"
@ -58,18 +58,21 @@ type ComplexityRoot struct {
ID func(childComplexity int) int
Labels func(childComplexity int) int
PreviewImageURL func(childComplexity int) int
PublishedTime func(childComplexity int) int
Title func(childComplexity int) int
}
Query struct {
Post func(childComplexity int, id string) int
Label func(childComplexity int, id uint32) int
Post func(childComplexity int, id uint32) int
Posts func(childComplexity int) int
}
}
type QueryResolver interface {
Posts(ctx context.Context) ([]*model.Post, error)
Post(ctx context.Context, id string) (*model.Post, error)
Posts(ctx context.Context) ([]*graphdto.Post, error)
Post(ctx context.Context, id uint32) (*graphdto.Post, error)
Label(ctx context.Context, id uint32) (*graphdto.Label, error)
}
type executableSchema struct {
@ -147,6 +150,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Post.PreviewImageURL(childComplexity), true
case "Post.publishedTime":
if e.complexity.Post.PublishedTime == nil {
break
}
return e.complexity.Post.PublishedTime(childComplexity), true
case "Post.title":
if e.complexity.Post.Title == nil {
break
@ -154,6 +164,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Post.Title(childComplexity), true
case "Query.label":
if e.complexity.Query.Label == nil {
break
}
args, err := ec.field_Query_label_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.Label(childComplexity, args["id"].(uint32)), true
case "Query.post":
if e.complexity.Query.Post == nil {
break
@ -164,7 +186,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}
return e.complexity.Query.Post(childComplexity, args["id"].(string)), true
return e.complexity.Query.Post(childComplexity, args["id"].(uint32)), true
case "Query.posts":
if e.complexity.Query.Posts == nil {
@ -304,6 +326,29 @@ func (ec *executionContext) field_Query___type_argsName(
return zeroVal, nil
}
func (ec *executionContext) field_Query_label_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
arg0, err := ec.field_Query_label_argsID(ctx, rawArgs)
if err != nil {
return nil, err
}
args["id"] = arg0
return args, nil
}
func (ec *executionContext) field_Query_label_argsID(
ctx context.Context,
rawArgs map[string]any,
) (uint32, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
if tmp, ok := rawArgs["id"]; ok {
return ec.unmarshalNID2uint32(ctx, tmp)
}
var zeroVal uint32
return zeroVal, nil
}
func (ec *executionContext) field_Query_post_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@ -317,13 +362,13 @@ func (ec *executionContext) field_Query_post_args(ctx context.Context, rawArgs m
func (ec *executionContext) field_Query_post_argsID(
ctx context.Context,
rawArgs map[string]any,
) (string, error) {
) (uint32, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
if tmp, ok := rawArgs["id"]; ok {
return ec.unmarshalNID2string(ctx, tmp)
return ec.unmarshalNID2uint32(ctx, tmp)
}
var zeroVal string
var zeroVal uint32
return zeroVal, nil
}
@ -427,7 +472,7 @@ func (ec *executionContext) field___Type_fields_argsIncludeDeprecated(
// region **************************** field.gotpl *****************************
func (ec *executionContext) _Label_id(ctx context.Context, field graphql.CollectedField, obj *model.Label) (ret graphql.Marshaler) {
func (ec *executionContext) _Label_id(ctx context.Context, field graphql.CollectedField, obj *graphdto.Label) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Label_id(ctx, field)
if err != nil {
return graphql.Null
@ -453,9 +498,9 @@ func (ec *executionContext) _Label_id(ctx context.Context, field graphql.Collect
}
return graphql.Null
}
res := resTmp.(string)
res := resTmp.(uint32)
fc.Result = res
return ec.marshalNID2string(ctx, field.Selections, res)
return ec.marshalNID2uint32(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Label_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -471,7 +516,7 @@ func (ec *executionContext) fieldContext_Label_id(_ context.Context, field graph
return fc, nil
}
func (ec *executionContext) _Label_name(ctx context.Context, field graphql.CollectedField, obj *model.Label) (ret graphql.Marshaler) {
func (ec *executionContext) _Label_name(ctx context.Context, field graphql.CollectedField, obj *graphdto.Label) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Label_name(ctx, field)
if err != nil {
return graphql.Null
@ -515,7 +560,7 @@ func (ec *executionContext) fieldContext_Label_name(_ context.Context, field gra
return fc, nil
}
func (ec *executionContext) _Label_color(ctx context.Context, field graphql.CollectedField, obj *model.Label) (ret graphql.Marshaler) {
func (ec *executionContext) _Label_color(ctx context.Context, field graphql.CollectedField, obj *graphdto.Label) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Label_color(ctx, field)
if err != nil {
return graphql.Null
@ -559,7 +604,7 @@ func (ec *executionContext) fieldContext_Label_color(_ context.Context, field gr
return fc, nil
}
func (ec *executionContext) _Post_id(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) {
func (ec *executionContext) _Post_id(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_id(ctx, field)
if err != nil {
return graphql.Null
@ -585,9 +630,9 @@ func (ec *executionContext) _Post_id(ctx context.Context, field graphql.Collecte
}
return graphql.Null
}
res := resTmp.(string)
res := resTmp.(uint32)
fc.Result = res
return ec.marshalNID2string(ctx, field.Selections, res)
return ec.marshalNID2uint32(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Post_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -603,7 +648,7 @@ func (ec *executionContext) fieldContext_Post_id(_ context.Context, field graphq
return fc, nil
}
func (ec *executionContext) _Post_title(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) {
func (ec *executionContext) _Post_title(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_title(ctx, field)
if err != nil {
return graphql.Null
@ -647,7 +692,7 @@ func (ec *executionContext) fieldContext_Post_title(_ context.Context, field gra
return fc, nil
}
func (ec *executionContext) _Post_content(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) {
func (ec *executionContext) _Post_content(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_content(ctx, field)
if err != nil {
return graphql.Null
@ -691,7 +736,7 @@ func (ec *executionContext) fieldContext_Post_content(_ context.Context, field g
return fc, nil
}
func (ec *executionContext) _Post_description(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) {
func (ec *executionContext) _Post_description(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_description(ctx, field)
if err != nil {
return graphql.Null
@ -735,7 +780,7 @@ func (ec *executionContext) fieldContext_Post_description(_ context.Context, fie
return fc, nil
}
func (ec *executionContext) _Post_previewImageUrl(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) {
func (ec *executionContext) _Post_previewImageUrl(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_previewImageUrl(ctx, field)
if err != nil {
return graphql.Null
@ -779,7 +824,7 @@ func (ec *executionContext) fieldContext_Post_previewImageUrl(_ context.Context,
return fc, nil
}
func (ec *executionContext) _Post_labels(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) {
func (ec *executionContext) _Post_labels(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_labels(ctx, field)
if err != nil {
return graphql.Null
@ -805,9 +850,9 @@ func (ec *executionContext) _Post_labels(ctx context.Context, field graphql.Coll
}
return graphql.Null
}
res := resTmp.([]*model.Label)
res := resTmp.([]*graphdto.Label)
fc.Result = res
return ec.marshalNLabel2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐLabelᚄ(ctx, field.Selections, res)
return ec.marshalNLabel2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐLabelᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Post_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -831,6 +876,47 @@ func (ec *executionContext) fieldContext_Post_labels(_ context.Context, field gr
return fc, nil
}
func (ec *executionContext) _Post_publishedTime(ctx context.Context, field graphql.CollectedField, obj *graphdto.Post) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Post_publishedTime(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return obj.PublishedTime, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalODate2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Post_publishedTime(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Post",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Date does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Query_posts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query_posts(ctx, field)
if err != nil {
@ -857,9 +943,9 @@ func (ec *executionContext) _Query_posts(ctx context.Context, field graphql.Coll
}
return graphql.Null
}
res := resTmp.([]*model.Post)
res := resTmp.([]*graphdto.Post)
fc.Result = res
return ec.marshalNPost2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐPostᚄ(ctx, field.Selections, res)
return ec.marshalNPost2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐPostᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Query_posts(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -882,6 +968,8 @@ func (ec *executionContext) fieldContext_Query_posts(_ context.Context, field gr
return ec.fieldContext_Post_previewImageUrl(ctx, field)
case "labels":
return ec.fieldContext_Post_labels(ctx, field)
case "publishedTime":
return ec.fieldContext_Post_publishedTime(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type Post", field.Name)
},
@ -903,7 +991,7 @@ func (ec *executionContext) _Query_post(ctx context.Context, field graphql.Colle
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Post(rctx, fc.Args["id"].(string))
return ec.resolvers.Query().Post(rctx, fc.Args["id"].(uint32))
})
if err != nil {
ec.Error(ctx, err)
@ -915,9 +1003,9 @@ func (ec *executionContext) _Query_post(ctx context.Context, field graphql.Colle
}
return graphql.Null
}
res := resTmp.(*model.Post)
res := resTmp.(*graphdto.Post)
fc.Result = res
return ec.marshalNPost2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐPost(ctx, field.Selections, res)
return ec.marshalNPost2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐPost(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Query_post(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -940,6 +1028,8 @@ func (ec *executionContext) fieldContext_Query_post(ctx context.Context, field g
return ec.fieldContext_Post_previewImageUrl(ctx, field)
case "labels":
return ec.fieldContext_Post_labels(ctx, field)
case "publishedTime":
return ec.fieldContext_Post_publishedTime(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type Post", field.Name)
},
@ -958,6 +1048,69 @@ func (ec *executionContext) fieldContext_Query_post(ctx context.Context, field g
return fc, nil
}
func (ec *executionContext) _Query_label(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query_label(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Label(rctx, fc.Args["id"].(uint32))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*graphdto.Label)
fc.Result = res
return ec.marshalNLabel2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐLabel(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Query_label(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Query",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "id":
return ec.fieldContext_Label_id(ctx, field)
case "name":
return ec.fieldContext_Label_name(ctx, field)
case "color":
return ec.fieldContext_Label_color(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type Label", field.Name)
},
}
defer func() {
if r := recover(); r != nil {
err = ec.Recover(ctx, r)
ec.Error(ctx, err)
}
}()
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_Query_label_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
return fc, nil
}
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query___type(ctx, field)
if err != nil {
@ -3050,7 +3203,7 @@ func (ec *executionContext) fieldContext___Type_isOneOf(_ context.Context, field
var labelImplementors = []string{"Label"}
func (ec *executionContext) _Label(ctx context.Context, sel ast.SelectionSet, obj *model.Label) graphql.Marshaler {
func (ec *executionContext) _Label(ctx context.Context, sel ast.SelectionSet, obj *graphdto.Label) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, labelImplementors)
out := graphql.NewFieldSet(fields)
@ -3099,7 +3252,7 @@ func (ec *executionContext) _Label(ctx context.Context, sel ast.SelectionSet, ob
var postImplementors = []string{"Post"}
func (ec *executionContext) _Post(ctx context.Context, sel ast.SelectionSet, obj *model.Post) graphql.Marshaler {
func (ec *executionContext) _Post(ctx context.Context, sel ast.SelectionSet, obj *graphdto.Post) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, postImplementors)
out := graphql.NewFieldSet(fields)
@ -3138,6 +3291,8 @@ func (ec *executionContext) _Post(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "publishedTime":
out.Values[i] = ec._Post_publishedTime(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -3223,6 +3378,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
case "label":
field := field
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_label(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&fs.Invalids, 1)
}
return res
}
rrm := func(ctx context.Context) graphql.Marshaler {
return ec.OperationContext.RootResolverMiddleware(ctx,
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
case "__type":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
@ -3605,13 +3782,13 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
return res
}
func (ec *executionContext) unmarshalNID2string(ctx context.Context, v any) (string, error) {
res, err := graphql.UnmarshalID(v)
func (ec *executionContext) unmarshalNID2uint32(ctx context.Context, v any) (uint32, error) {
res, err := graphql.UnmarshalUint32(v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {
res := graphql.MarshalID(v)
func (ec *executionContext) marshalNID2uint32(ctx context.Context, sel ast.SelectionSet, v uint32) graphql.Marshaler {
res := graphql.MarshalUint32(v)
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
@ -3620,7 +3797,11 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec
return res
}
func (ec *executionContext) marshalNLabel2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐLabelᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Label) graphql.Marshaler {
func (ec *executionContext) marshalNLabel2gitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐLabel(ctx context.Context, sel ast.SelectionSet, v graphdto.Label) graphql.Marshaler {
return ec._Label(ctx, sel, &v)
}
func (ec *executionContext) marshalNLabel2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐLabelᚄ(ctx context.Context, sel ast.SelectionSet, v []*graphdto.Label) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
@ -3644,7 +3825,7 @@ func (ec *executionContext) marshalNLabel2ᚕᚖgitᚗsquidspiritᚗcomᚋsquid
if !isLen1 {
defer wg.Done()
}
ret[i] = ec.marshalNLabel2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐLabel(ctx, sel, v[i])
ret[i] = ec.marshalNLabel2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐLabel(ctx, sel, v[i])
}
if isLen1 {
f(i)
@ -3664,7 +3845,7 @@ func (ec *executionContext) marshalNLabel2ᚕᚖgitᚗsquidspiritᚗcomᚋsquid
return ret
}
func (ec *executionContext) marshalNLabel2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐLabel(ctx context.Context, sel ast.SelectionSet, v *model.Label) graphql.Marshaler {
func (ec *executionContext) marshalNLabel2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐLabel(ctx context.Context, sel ast.SelectionSet, v *graphdto.Label) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
@ -3674,11 +3855,11 @@ func (ec *executionContext) marshalNLabel2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋb
return ec._Label(ctx, sel, v)
}
func (ec *executionContext) marshalNPost2gitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐPost(ctx context.Context, sel ast.SelectionSet, v model.Post) graphql.Marshaler {
func (ec *executionContext) marshalNPost2gitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐPost(ctx context.Context, sel ast.SelectionSet, v graphdto.Post) graphql.Marshaler {
return ec._Post(ctx, sel, &v)
}
func (ec *executionContext) marshalNPost2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐPostᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Post) graphql.Marshaler {
func (ec *executionContext) marshalNPost2ᚕᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐPostᚄ(ctx context.Context, sel ast.SelectionSet, v []*graphdto.Post) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
@ -3702,7 +3883,7 @@ func (ec *executionContext) marshalNPost2ᚕᚖgitᚗsquidspiritᚗcomᚋsquid
if !isLen1 {
defer wg.Done()
}
ret[i] = ec.marshalNPost2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐPost(ctx, sel, v[i])
ret[i] = ec.marshalNPost2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐPost(ctx, sel, v[i])
}
if isLen1 {
f(i)
@ -3722,7 +3903,7 @@ func (ec *executionContext) marshalNPost2ᚕᚖgitᚗsquidspiritᚗcomᚋsquid
return ret
}
func (ec *executionContext) marshalNPost2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋframeworkᚋgraphᚋmodelᚐPost(ctx context.Context, sel ast.SelectionSet, v *model.Post) graphql.Marshaler {
func (ec *executionContext) marshalNPost2ᚖgitᚗsquidspiritᚗcomᚋsquidᚋblogᚗgitᚋbackendᚋinternalᚋadapterᚋcontrollerᚋgraphdtoᚐPost(ctx context.Context, sel ast.SelectionSet, v *graphdto.Post) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
@ -4026,6 +4207,22 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
return res
}
func (ec *executionContext) unmarshalODate2ᚖstring(ctx context.Context, v any) (*string, error) {
if v == nil {
return nil, nil
}
res, err := graphql.UnmarshalString(v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalODate2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler {
if v == nil {
return graphql.Null
}
res := graphql.MarshalString(*v)
return res
}
func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v any) (*string, error) {
if v == nil {
return nil, nil

View File

@ -1,9 +1,11 @@
package graph
import "git.squidspirit.com/squid/blog.git/backend/internal/application"
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
// db
GetAllPostsUseCase application.GetAllPostsUseCase
}

View File

@ -1,6 +1,4 @@
# GraphQL schema example
#
# https://gqlgen.com/getting-started/
scalar Date
type Label {
id: ID!
@ -15,9 +13,11 @@ type Post {
description: String!
previewImageUrl: String!
labels: [Label!]!
publishedTime: Date
}
type Query {
posts: [Post!]!
post(id: ID!): Post!
label(id: ID!): Label!
}

View File

@ -0,0 +1,38 @@
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.66
import (
"context"
"fmt"
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/controller"
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/controller/graphdto"
)
// Posts is the resolver for the posts field.
func (r *queryResolver) Posts(ctx context.Context) ([]*graphdto.Post, error) {
c := controller.NewQueryPostsController(r.GetAllPostsUseCase)
dtos, err := c.Handle()
if err != nil {
return nil, err
}
return dtos, nil
}
// Post is the resolver for the post field.
func (r *queryResolver) Post(ctx context.Context, id uint32) (*graphdto.Post, error) {
panic(fmt.Errorf("not implemented: Post - post"))
}
// Label is the resolver for the label field.
func (r *queryResolver) Label(ctx context.Context, id uint32) (*graphdto.Label, error) {
panic(fmt.Errorf("not implemented: Label - label"))
}
// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
type queryResolver struct{ *Resolver }

View File

@ -0,0 +1,31 @@
BEGIN;
CREATE TABLE IF NOT EXISTS "post" (
"id" SERIAL PRIMARY KEY,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"description" TEXT NOT NULL,
"preview_image_url" TEXT NOT NULL,
"published_time" TIMESTAMP,
"created_time" TIMESTAMP NOT NULL,
"updated_time" TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS "label" (
"id" SERIAL PRIMARY KEY,
"name" TEXT NOT NULL,
"color" TEXT NOT NULL,
"created_time" TIMESTAMP NOT NULL,
"updated_time" TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS "post_label" (
"post_id" SERIAL NOT NULL,
"label_id" SERIAL NOT NULL,
"label_order" INTEGER NOT NULL,
PRIMARY KEY ("post_id", "label_id"),
FOREIGN KEY ("post_id") REFERENCES "post" ("id") ON DELETE CASCADE,
FOREIGN KEY ("label_id") REFERENCES "label" ("id") ON DELETE CASCADE
);
COMMIT;

View File

@ -0,0 +1,79 @@
package postgres
import (
"context"
"database/sql"
"encoding/json"
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/gateway"
"git.squidspirit.com/squid/blog.git/backend/internal/adapter/gateway/dbdto"
)
type postDBService struct {
db *sql.DB
}
func NewPostDBService(db *sql.DB) gateway.PostDBService {
return &postDBService{db}
}
func (srv *postDBService) QueryAll() ([]*dbdto.Post, error) {
ctx := context.Background()
rows, err := srv.db.QueryContext(ctx, `
SELECT
p.*,
COALESCE(
jsonb_agg(
jsonb_build_object(
'id', l.id,
'name', l.name,
'color', l.color
) ORDER BY pl.label_order ASC
)
FILTER (WHERE l.id IS NOT NULL),
'[]'::jsonb
) AS labels
FROM post p
LEFT JOIN post_label pl ON p.id = pl.post_id
LEFT JOIN label l ON pl.label_id = l.id
GROUP BY p.id
ORDER BY p.created_time DESC
`)
if err != nil {
return nil, err
}
defer rows.Close()
var labelsJSON []byte
var postDtos []*dbdto.Post
for rows.Next() {
var postDto dbdto.Post
err = rows.Scan(
&postDto.ID,
&postDto.Title,
&postDto.Content,
&postDto.Description,
&postDto.PreviewImageURL,
&postDto.PublishedTime,
&postDto.CreatedTime,
&postDto.UpdatedTime,
&labelsJSON,
)
if err != nil {
return nil, err
}
err := json.Unmarshal(labelsJSON, &postDto.Labels)
if err != nil {
return nil, err
}
postDtos = append(postDtos, &postDto)
}
return postDtos, nil
}

View File

@ -0,0 +1,21 @@
package postgres
import (
"database/sql"
_ "github.com/lib/pq"
)
func Connect(dsn string) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, err
}
err = db.Ping()
if err != nil {
return nil, err
}
return db, nil
}

View File

@ -1,27 +0,0 @@
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.66
import (
"context"
"fmt"
model1 "git.squidspirit.com/squid/blog.git/backend/internal/framework/graph/model"
)
// Posts is the resolver for the posts field.
func (r *queryResolver) Posts(ctx context.Context) ([]*model1.Post, error) {
panic(fmt.Errorf("not implemented: Posts - posts"))
}
// Post is the resolver for the post field.
func (r *queryResolver) Post(ctx context.Context, id string) (*model1.Post, error) {
panic(fmt.Errorf("not implemented: Post - post"))
}
// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
type queryResolver struct{ *Resolver }

21
backend/internal/pkg/env/env.go vendored Normal file
View File

@ -0,0 +1,21 @@
package env
import (
"os"
)
func GetPort() string {
port, exists := os.LookupEnv("PORT")
if !exists {
return "8000"
}
return port
}
func GetDSN() string {
dsn, exists := os.LookupEnv("DSN")
if !exists {
return "postgres://postgres@127.0.0.1/postgres?sslmode=disable"
}
return dsn
}