From cdf4e6f6673ec4e018453910f74c423d36e46e46 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 9 Jun 2019 12:37:17 +0200 Subject: [PATCH] Started work on new layered setup, making a huge mess. --- .gitignore | 1 + cmd/rpdata-server/main.go | 17 +++++-- database/database.go | 22 +++++++++ database/mongodb/db.go | 44 ++++++++++++++++++ database/mongodb/tags.go | 72 +++++++++++++++++++++++++++++ go.mod | 3 ++ go.sum | 15 +++++++ graph2/complexity.go | 76 +++++++++++++++---------------- graph2/gqlgen.yml | 8 ++-- graph2/graph.go | 32 +++++++------ graph2/queries/channel.go | 6 +-- graph2/queries/chapter.go | 10 ++--- graph2/queries/character.go | 12 ++--- graph2/queries/comment.go | 21 ++++----- graph2/queries/log.go | 13 +++--- graph2/queries/post.go | 15 +++---- graph2/queries/resolver.go | 13 ++++-- graph2/queries/story.go | 17 ++++--- graph2/queries/tags.go | 17 +++---- graph2/queries/unknownnicks.go | 5 +-- graph2/schema/root.gql | 2 +- graph2/schema/types/Tag.gql | 9 ++++ graph2/types/change.go | 8 ++-- internal/auth/permissions.go | 2 + internal/auth/permitted.go | 53 ++++++++++++++++++++++ internal/auth/token.go | 9 ++++ internal/config/config.go | 82 ++++++++++++++++++++++------------ models/tag-kind.go | 47 ------------------- models/tag.go | 53 +++++++++++++++++++++- repositories/repository.go | 11 +++++ repositories/tag.go | 13 ++++++ services/services.go | 17 +++++++ services/tags.go | 23 ++++++++++ 33 files changed, 539 insertions(+), 209 deletions(-) create mode 100644 database/database.go create mode 100644 database/mongodb/db.go create mode 100644 database/mongodb/tags.go create mode 100644 internal/auth/permitted.go delete mode 100644 models/tag-kind.go create mode 100644 repositories/repository.go create mode 100644 repositories/tag.go create mode 100644 services/services.go create mode 100644 services/tags.go diff --git a/.gitignore b/.gitignore index 6673993..a077423 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ rpdata-* !rpdata-*/ .vscode +/graph2/graphcore/*_gen.go generated.gql generated.go \ No newline at end of file diff --git a/cmd/rpdata-server/main.go b/cmd/rpdata-server/main.go index d5d0a1d..fd3fdac 100644 --- a/cmd/rpdata-server/main.go +++ b/cmd/rpdata-server/main.go @@ -8,14 +8,17 @@ import ( "runtime/debug" "strings" + "git.aiterp.net/rpdata/api/database" "git.aiterp.net/rpdata/api/graph2" "git.aiterp.net/rpdata/api/internal/auth" + "git.aiterp.net/rpdata/api/internal/config" "git.aiterp.net/rpdata/api/internal/instrumentation" "git.aiterp.net/rpdata/api/internal/loader" "git.aiterp.net/rpdata/api/internal/store" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/logs" + "git.aiterp.net/rpdata/api/services" "github.com/99designs/gqlgen/handler" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -26,10 +29,17 @@ func main() { log.Fatalln("Failed to init store:", err) } + repos, stopFn, err := database.Init(config.Global().Database) + if err != nil { + log.Fatalln("Failed to init db:", err) + } + defer stopFn() + services := services.NewBundle(repos) + instrumentation.Register() http.Handle("/", handler.Playground("RPData API", "/graphql")) - http.Handle("/graphql", queryHandler()) + http.Handle("/graphql", queryHandler(services)) http.Handle("/metrics", promhttp.Handler()) @@ -77,9 +87,9 @@ func logListedChanges() { log.Println("Change subscription closed.") } -func queryHandler() http.HandlerFunc { +func queryHandler(services *services.Bundle) http.HandlerFunc { handler := handler.GraphQL( - graph2.New(), + graph2.New(services), handler.RecoverFunc(func(ctx context.Context, err interface{}) error { log.Println(err) log.Println(string(debug.Stack())) @@ -90,7 +100,6 @@ func queryHandler() http.HandlerFunc { handler.RequestMiddleware(instrumentation.RequestMiddleware()), handler.ResolverMiddleware(instrumentation.ResolverMiddleware()), ) - return func(w http.ResponseWriter, r *http.Request) { l := loader.New() r = r.WithContext(l.ToContext(r.Context())) diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..8aea3d5 --- /dev/null +++ b/database/database.go @@ -0,0 +1,22 @@ +package database + +import ( + "errors" + + "git.aiterp.net/rpdata/api/database/mongodb" + "git.aiterp.net/rpdata/api/internal/config" + "git.aiterp.net/rpdata/api/repositories" +) + +// ErrDriverUnrecognized is returned if the driver is not recognized +var ErrDriverUnrecognized = errors.New("Driver not recognized, check your config or update rpdata") + +// Init sets up the database. +func Init(config config.Database) (bundle *repositories.Bundle, closeFn func() error, err error) { + switch config.Driver { + case "mongo", "mgo", "mongodb": + return mongodb.Init(config) + default: + return nil, nil, ErrDriverUnrecognized + } +} diff --git a/database/mongodb/db.go b/database/mongodb/db.go new file mode 100644 index 0000000..841a638 --- /dev/null +++ b/database/mongodb/db.go @@ -0,0 +1,44 @@ +package mongodb + +import ( + "fmt" + "time" + + "git.aiterp.net/rpdata/api/internal/config" + "git.aiterp.net/rpdata/api/repositories" + "github.com/globalsign/mgo" +) + +// Init initializes the mongodb database +func Init(cfg config.Database) (bundle *repositories.Bundle, closeFn func() error, err error) { + port := cfg.Port + if port <= 0 { + port = 27017 + } + + session, err := mgo.DialWithInfo(&mgo.DialInfo{ + Addrs: []string{fmt.Sprintf("%s:%d", cfg.Host, port)}, + Timeout: 30 * time.Second, + Database: cfg.Db, + Username: cfg.Username, + Password: cfg.Password, + Mechanism: cfg.Mechanism, + Source: cfg.Db, + }) + if err != nil { + return + } + + db := session.DB(cfg.Db) + + bundle = &repositories.Bundle{ + Tags: newTagRepository(db), + } + + closeFn = func() error { + session.Close() + return nil + } + + return +} diff --git a/database/mongodb/tags.go b/database/mongodb/tags.go new file mode 100644 index 0000000..11a8c08 --- /dev/null +++ b/database/mongodb/tags.go @@ -0,0 +1,72 @@ +package mongodb + +import ( + "context" + "sort" + "strings" + + "git.aiterp.net/rpdata/api/models" + "git.aiterp.net/rpdata/api/repositories" + "github.com/globalsign/mgo" + "go.mongodb.org/mongo-driver/bson" +) + +type tagRepository struct { + stories *mgo.Collection +} + +func newTagRepository(db *mgo.Database) repositories.TagRepository { + return &tagRepository{ + stories: db.C("story.stories"), + } +} + +func (r *tagRepository) Find(ctx context.Context, kind, name string) (*models.Tag, error) { + tags := make([]*models.Tag, 0, 1) + err := r.stories.Find(bson.M{"listed": true, "tags": bson.M{"kind": kind, "name": name}}).Distinct("tag", &tags) + if err != nil { + return nil, err + } else if len(tags) == 0 { + return nil, repositories.ErrNotFound + } + + return tags[0], nil +} + +func (r *tagRepository) List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) { + tags := make([]*models.Tag, 0, 64) + + query := bson.M{"listed": true, "tags": bson.M{"$ne": nil}} + if filter.Kind != nil { + query["tags.kind"] = *filter.Kind + } + + err := r.stories.Find(query).Distinct("tags", &tags) + if err != nil { + return nil, err + } + + if filter.Kind != nil { + // While it's unsorted, delete any incorrect tags with replacement. + for i := 0; i < len(tags); i++ { + if tags[i].Kind != *filter.Kind { + tags[i] = tags[len(tags)-1] + tags = tags[:len(tags)-1] + i-- + } + } + } + + sort.Slice(tags, func(i, j int) bool { + if filter.Kind == nil { + kindCmp := strings.Compare(string(tags[i].Kind), string(tags[j].Kind)) + if kindCmp != 0 { + return kindCmp < 0 + } + } + + return strings.Compare(tags[i].Name, tags[j].Name) < 0 + }) + + return tags, nil +} diff --git a/go.mod b/go.mod index feeda49..7f8e726 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/go-ini/ini v1.35.0 github.com/go-sql-driver/mysql v1.4.0 github.com/golang/protobuf v1.3.1 // indirect + github.com/google/go-cmp v0.3.0 // indirect github.com/gorilla/websocket v1.4.0 github.com/graph-gophers/dataloader v0.0.0-20180104184831-78139374585c github.com/hashicorp/golang-lru v0.5.0 @@ -28,8 +29,10 @@ require ( github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e // indirect github.com/sirupsen/logrus v1.2.0 github.com/stretchr/testify v1.3.0 + github.com/tidwall/pretty v1.0.0 // indirect github.com/urfave/cli v1.20.0 github.com/vektah/gqlparser v1.1.2 + go.mongodb.org/mongo-driver v1.0.3 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20190514140710-3ec191127204 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a diff --git a/go.sum b/go.sum index 3527574..8cffd58 100644 --- a/go.sum +++ b/go.sum @@ -13,12 +13,14 @@ github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+s github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -34,12 +36,15 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -54,8 +59,10 @@ github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiEr github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +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/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -73,6 +80,7 @@ github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/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_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -91,6 +99,7 @@ github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e h1:zK8d1aZ+gw/Ne github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= @@ -101,7 +110,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/dataloaden v0.2.0/go.mod h1:vxM6NuRlgiR0M6wbVTJeKp9vQIs81ZMfCYO+4yq/jbE= @@ -112,6 +124,8 @@ github.com/vektah/gqlparser v1.1.0 h1:3668p2gUlO+PiS81x957Rpr3/FPRWG6cxgCXAvTS1h github.com/vektah/gqlparser v1.1.0/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= golang.org/x/crypto v0.0.0-20180411161317-d6449816ce06 h1:EOqG0JqGlLr+punVB69jvWCv/ErZKGlC7PMdyHfv+Bc= golang.org/x/crypto v0.0.0-20180411161317-d6449816ce06/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= @@ -146,6 +160,7 @@ golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBn google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/graph2/complexity.go b/graph2/complexity.go index a46ab5e..436879b 100644 --- a/graph2/complexity.go +++ b/graph2/complexity.go @@ -1,17 +1,17 @@ package graph2 import ( - input "git.aiterp.net/rpdata/api/graph2/input" - changes "git.aiterp.net/rpdata/api/models/changes" - channels "git.aiterp.net/rpdata/api/models/channels" - characters "git.aiterp.net/rpdata/api/models/characters" - files "git.aiterp.net/rpdata/api/models/files" - logs "git.aiterp.net/rpdata/api/models/logs" - posts "git.aiterp.net/rpdata/api/models/posts" - stories "git.aiterp.net/rpdata/api/models/stories" + "git.aiterp.net/rpdata/api/graph2/graphcore" + "git.aiterp.net/rpdata/api/models/changes" + "git.aiterp.net/rpdata/api/models/channels" + "git.aiterp.net/rpdata/api/models/characters" + "git.aiterp.net/rpdata/api/models/files" + "git.aiterp.net/rpdata/api/models/logs" + "git.aiterp.net/rpdata/api/models/posts" + "git.aiterp.net/rpdata/api/models/stories" ) -func complexity() (cr ComplexityRoot) { +func complexity() (cr graphcore.ComplexityRoot) { subListComplexity := 25 bigSublistComplexity := 75 hugeSublistComplexity := 100 @@ -37,7 +37,7 @@ func complexity() (cr ComplexityRoot) { cr.Query.Posts = func(childComplexity int, filter *posts.Filter) int { return childComplexity + listComplexity } - cr.Query.UnknownNicks = func(childComplexity int, filter *input.UnknownNicksFilter) int { + cr.Query.UnknownNicks = func(childComplexity int, filter *graphcore.UnknownNicksFilter) int { return childComplexity + listComplexity } cr.Query.Log = func(childComplexity int, id string) int { @@ -56,7 +56,7 @@ func complexity() (cr ComplexityRoot) { cr.Query.Comment = func(childComplexity int, id string) int { return childComplexity + findComplexity } - cr.Query.Tags = func(childComplexity int) int { + cr.Query.Tags = func(childComplexity int, filter *graphcore.TagFilter) int { return childComplexity + listComplexity } cr.Query.Story = func(childComplexity int, id string) int { @@ -78,85 +78,85 @@ func complexity() (cr ComplexityRoot) { return childComplexity + findComplexity } - cr.Mutation.AddStory = func(childComplexity int, input input.StoryAddInput) int { + cr.Mutation.AddStory = func(childComplexity int, input graphcore.StoryAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddStoryTag = func(childComplexity int, input input.StoryTagAddInput) int { + cr.Mutation.AddStoryTag = func(childComplexity int, input graphcore.StoryTagAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveStoryTag = func(childComplexity int, input input.StoryTagRemoveInput) int { + cr.Mutation.RemoveStoryTag = func(childComplexity int, input graphcore.StoryTagRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditStory = func(childComplexity int, input input.StoryEditInput) int { + cr.Mutation.EditStory = func(childComplexity int, input graphcore.StoryEditInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveStory = func(childComplexity int, input input.StoryRemoveInput) int { + cr.Mutation.RemoveStory = func(childComplexity int, input graphcore.StoryRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddChapter = func(childComplexity int, input input.ChapterAddInput) int { + cr.Mutation.AddChapter = func(childComplexity int, input graphcore.ChapterAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditChapter = func(childComplexity int, input input.ChapterEditInput) int { + cr.Mutation.EditChapter = func(childComplexity int, input graphcore.ChapterEditInput) int { return childComplexity + mutationComplexity } - cr.Mutation.MoveChapter = func(childComplexity int, input input.ChapterMoveInput) int { + cr.Mutation.MoveChapter = func(childComplexity int, input graphcore.ChapterMoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveChapter = func(childComplexity int, input input.ChapterRemoveInput) int { + cr.Mutation.RemoveChapter = func(childComplexity int, input graphcore.ChapterRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddComment = func(childComplexity int, input input.CommentAddInput) int { + cr.Mutation.AddComment = func(childComplexity int, input graphcore.CommentAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditComment = func(childComplexity int, input input.CommentEditInput) int { + cr.Mutation.EditComment = func(childComplexity int, input graphcore.CommentEditInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveComment = func(childComplexity int, input input.CommentRemoveInput) int { + cr.Mutation.RemoveComment = func(childComplexity int, input graphcore.CommentRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddLog = func(childComplexity int, input input.LogAddInput) int { + cr.Mutation.AddLog = func(childComplexity int, input graphcore.LogAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.ImportLog = func(childComplexity int, input input.LogImportInput) int { + cr.Mutation.ImportLog = func(childComplexity int, input graphcore.LogImportInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditLog = func(childComplexity int, input input.LogEditInput) int { + cr.Mutation.EditLog = func(childComplexity int, input graphcore.LogEditInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveLog = func(childComplexity int, input input.LogRemoveInput) int { + cr.Mutation.RemoveLog = func(childComplexity int, input graphcore.LogRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddPost = func(childComplexity int, input input.PostAddInput) int { + cr.Mutation.AddPost = func(childComplexity int, input graphcore.PostAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditPost = func(childComplexity int, input input.PostEditInput) int { + cr.Mutation.EditPost = func(childComplexity int, input graphcore.PostEditInput) int { return childComplexity + mutationComplexity } - cr.Mutation.MovePost = func(childComplexity int, input input.PostMoveInput) int { + cr.Mutation.MovePost = func(childComplexity int, input graphcore.PostMoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemovePost = func(childComplexity int, input input.PostRemoveInput) int { + cr.Mutation.RemovePost = func(childComplexity int, input graphcore.PostRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddCharacter = func(childComplexity int, input input.CharacterAddInput) int { + cr.Mutation.AddCharacter = func(childComplexity int, input graphcore.CharacterAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddCharacterNick = func(childComplexity int, input input.CharacterNickInput) int { + cr.Mutation.AddCharacterNick = func(childComplexity int, input graphcore.CharacterNickInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveCharacterNick = func(childComplexity int, input input.CharacterNickInput) int { + cr.Mutation.RemoveCharacterNick = func(childComplexity int, input graphcore.CharacterNickInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditCharacter = func(childComplexity int, input input.CharacterEditInput) int { + cr.Mutation.EditCharacter = func(childComplexity int, input graphcore.CharacterEditInput) int { return childComplexity + mutationComplexity } - cr.Mutation.RemoveCharacter = func(childComplexity int, input input.CharacterRemoveInput) int { + cr.Mutation.RemoveCharacter = func(childComplexity int, input graphcore.CharacterRemoveInput) int { return childComplexity + mutationComplexity } - cr.Mutation.AddChannel = func(childComplexity int, input input.ChannelAddInput) int { + cr.Mutation.AddChannel = func(childComplexity int, input graphcore.ChannelAddInput) int { return childComplexity + mutationComplexity } - cr.Mutation.EditChannel = func(childComplexity int, input input.ChannelEditInput) int { + cr.Mutation.EditChannel = func(childComplexity int, input graphcore.ChannelEditInput) int { return childComplexity + mutationComplexity } diff --git a/graph2/gqlgen.yml b/graph2/gqlgen.yml index 4d2243a..f01d360 100644 --- a/graph2/gqlgen.yml +++ b/graph2/gqlgen.yml @@ -3,12 +3,12 @@ schema: - schema/types/*.gql exec: - filename: generated.go - package: graph2 + filename: graphcore/exec_gen.go + package: graphcore model: - filename: input/generated.go - package: input + filename: graphcore/input_gen.go + package: graphcore models: Tag: diff --git a/graph2/graph.go b/graph2/graph.go index b6f8832..3df0d18 100644 --- a/graph2/graph.go +++ b/graph2/graph.go @@ -1,55 +1,59 @@ package graph2 import ( + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/graph2/queries" "git.aiterp.net/rpdata/api/graph2/types" + "git.aiterp.net/rpdata/api/services" graphql "github.com/99designs/gqlgen/graphql" ) //go:generate go run github.com/99designs/gqlgen -v // New creates a new GraphQL schema. -func New() graphql.ExecutableSchema { - return NewExecutableSchema(Config{ - Resolvers: &rootResolver{}, +func New(s *services.Bundle) graphql.ExecutableSchema { + return graphcore.NewExecutableSchema(graphcore.Config{ + Resolvers: &rootResolver{s}, Complexity: complexity(), }) } -type rootResolver struct{} +type rootResolver struct { + s *services.Bundle +} -func (r *rootResolver) Query() QueryResolver { - return &queries.Resolver +func (r *rootResolver) Query() graphcore.QueryResolver { + return queries.QueryResolver(r.s) } -func (r *rootResolver) Mutation() MutationResolver { +func (r *rootResolver) Mutation() graphcore.MutationResolver { return queries.MutationResolver } -func (r *rootResolver) Subscription() SubscriptionResolver { +func (r *rootResolver) Subscription() graphcore.SubscriptionResolver { return queries.SubscriptionResolver } -func (r *rootResolver) Log() LogResolver { +func (r *rootResolver) Log() graphcore.LogResolver { return &types.LogResolver } -func (r *rootResolver) Comment() CommentResolver { +func (r *rootResolver) Comment() graphcore.CommentResolver { return &types.CommentResolver } -func (r *rootResolver) Chapter() ChapterResolver { +func (r *rootResolver) Chapter() graphcore.ChapterResolver { return &types.ChapterResolver } -func (r *rootResolver) Story() StoryResolver { +func (r *rootResolver) Story() graphcore.StoryResolver { return &types.StoryResolver } -func (r *rootResolver) Change() ChangeResolver { +func (r *rootResolver) Change() graphcore.ChangeResolver { return &types.ChangeResolver } -func (r *rootResolver) Token() TokenResolver { +func (r *rootResolver) Token() graphcore.TokenResolver { return &types.TokenResolver } diff --git a/graph2/queries/channel.go b/graph2/queries/channel.go index f061e0f..86416e6 100644 --- a/graph2/queries/channel.go +++ b/graph2/queries/channel.go @@ -4,7 +4,7 @@ import ( "context" "errors" - "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" @@ -39,7 +39,7 @@ func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]*mo // Mutations -func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAddInput) (*models.Channel, error) { +func (r *mutationResolver) AddChannel(ctx context.Context, input graphcore.ChannelAddInput) (*models.Channel, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("channel.add") { return nil, errors.New("You are not permitted to add channels") @@ -72,7 +72,7 @@ func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAd return &channel, nil } -func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelEditInput) (*models.Channel, error) { +func (r *mutationResolver) EditChannel(ctx context.Context, input graphcore.ChannelEditInput) (*models.Channel, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("channel.edit") { return nil, errors.New("You are not permitted to edit channels") diff --git a/graph2/queries/chapter.go b/graph2/queries/chapter.go index c807b2b..479cb5b 100644 --- a/graph2/queries/chapter.go +++ b/graph2/queries/chapter.go @@ -5,7 +5,7 @@ import ( "errors" "time" - "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" @@ -28,7 +28,7 @@ func (r *resolver) Chapter(ctx context.Context, id string) (*models.Chapter, err // Mutations -func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAddInput) (*models.Chapter, error) { +func (r *mutationResolver) AddChapter(ctx context.Context, input graphcore.ChapterAddInput) (*models.Chapter, error) { story, err := stories.FindID(input.StoryID) if err != nil { return nil, errors.New("Story not found") @@ -67,7 +67,7 @@ func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAd return &chapter, nil } -func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterMoveInput) (*models.Chapter, error) { +func (r *mutationResolver) MoveChapter(ctx context.Context, input graphcore.ChapterMoveInput) (*models.Chapter, error) { chapter, err := chapters.FindID(input.ID) if err != nil { return nil, errors.New("Chapter not found") @@ -111,7 +111,7 @@ func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterM return &chapter, nil } -func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterEditInput) (*models.Chapter, error) { +func (r *mutationResolver) EditChapter(ctx context.Context, input graphcore.ChapterEditInput) (*models.Chapter, error) { chapter, err := chapters.FindID(input.ID) if err != nil { return nil, errors.New("Chapter not found") @@ -143,7 +143,7 @@ func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterE return &chapter, nil } -func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.ChapterRemoveInput) (*models.Chapter, error) { +func (r *mutationResolver) RemoveChapter(ctx context.Context, input graphcore.ChapterRemoveInput) (*models.Chapter, error) { chapter, err := chapters.FindID(input.ID) if err != nil { return nil, errors.New("Chapter not found") diff --git a/graph2/queries/character.go b/graph2/queries/character.go index 84726bd..59c4884 100644 --- a/graph2/queries/character.go +++ b/graph2/queries/character.go @@ -5,7 +5,7 @@ import ( "errors" "strings" - "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" @@ -52,7 +52,7 @@ func (r *resolver) Characters(ctx context.Context, filter *characters.Filter) ([ // Mutations -func (r *mutationResolver) AddCharacter(ctx context.Context, input input.CharacterAddInput) (*models.Character, error) { +func (r *mutationResolver) AddCharacter(ctx context.Context, input graphcore.CharacterAddInput) (*models.Character, error) { token := auth.TokenFromContext(ctx) if !token.Permitted("member", "character.add") { return nil, errors.New("You are not permitted to add characters") @@ -94,7 +94,7 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact return &character, nil } -func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.CharacterNickInput) (*models.Character, error) { +func (r *mutationResolver) AddCharacterNick(ctx context.Context, input graphcore.CharacterNickInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { return nil, errors.New("Character not found") @@ -122,7 +122,7 @@ func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.Cha return &character, nil } -func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.CharacterNickInput) (*models.Character, error) { +func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input graphcore.CharacterNickInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { return nil, errors.New("Character not found") @@ -144,7 +144,7 @@ func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input. return &character, nil } -func (r *mutationResolver) EditCharacter(ctx context.Context, input input.CharacterEditInput) (*models.Character, error) { +func (r *mutationResolver) EditCharacter(ctx context.Context, input graphcore.CharacterEditInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { return nil, errors.New("Character not found") @@ -172,7 +172,7 @@ func (r *mutationResolver) EditCharacter(ctx context.Context, input input.Charac return &character, nil } -func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.CharacterRemoveInput) (*models.Character, error) { +func (r *mutationResolver) RemoveCharacter(ctx context.Context, input graphcore.CharacterRemoveInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { return nil, errors.New("Character not found") diff --git a/graph2/queries/comment.go b/graph2/queries/comment.go index 155666f..65877ad 100644 --- a/graph2/queries/comment.go +++ b/graph2/queries/comment.go @@ -6,20 +6,15 @@ import ( "log" "time" - "git.aiterp.net/rpdata/api/models/stories" - - "git.aiterp.net/rpdata/api/models/characters" - + "git.aiterp.net/rpdata/api/graph2/graphcore" + "git.aiterp.net/rpdata/api/internal/auth" + "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" - "git.aiterp.net/rpdata/api/models/changes" - - "git.aiterp.net/rpdata/api/graph2/input" - "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models/chapters" + "git.aiterp.net/rpdata/api/models/characters" "git.aiterp.net/rpdata/api/models/comments" - - "git.aiterp.net/rpdata/api/models" + "git.aiterp.net/rpdata/api/models/stories" ) // Queries @@ -35,7 +30,7 @@ func (r *resolver) Comment(ctx context.Context, id string) (*models.Comment, err // Mutations -func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAddInput) (*models.Comment, error) { +func (r *mutationResolver) AddComment(ctx context.Context, input graphcore.CommentAddInput) (*models.Comment, error) { chapter, err := chapters.FindID(input.ChapterID) if err != nil { return nil, errors.New("Chapter not found") @@ -90,7 +85,7 @@ func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAd return &comment, nil } -func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentEditInput) (*models.Comment, error) { +func (r *mutationResolver) EditComment(ctx context.Context, input graphcore.CommentEditInput) (*models.Comment, error) { comment, err := comments.Find(input.CommentID) if err != nil { return nil, errors.New("Comment not found") @@ -141,7 +136,7 @@ func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentE return &comment, nil } -func (r *mutationResolver) RemoveComment(ctx context.Context, input input.CommentRemoveInput) (*models.Comment, error) { +func (r *mutationResolver) RemoveComment(ctx context.Context, input graphcore.CommentRemoveInput) (*models.Comment, error) { comment, err := comments.Find(input.CommentID) if err != nil { return nil, errors.New("Comment not found") diff --git a/graph2/queries/log.go b/graph2/queries/log.go index 2942aa9..2fdcf1d 100644 --- a/graph2/queries/log.go +++ b/graph2/queries/log.go @@ -6,14 +6,13 @@ import ( "strings" "time" - "git.aiterp.net/rpdata/api/models/channels" - - "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/loader" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" "git.aiterp.net/rpdata/api/models/changes" + "git.aiterp.net/rpdata/api/models/channels" "git.aiterp.net/rpdata/api/models/logs" "github.com/99designs/gqlgen/graphql" ) @@ -66,7 +65,7 @@ func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]*models.Log // Mutations -func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) (*models.Log, error) { +func (r *mutationResolver) AddLog(ctx context.Context, input graphcore.LogAddInput) (*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.add") { return nil, errors.New("You are not permitted to add logs") @@ -107,7 +106,7 @@ func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) return &log, nil } -func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportInput) ([]*models.Log, error) { +func (r *mutationResolver) ImportLog(ctx context.Context, input graphcore.LogImportInput) ([]*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.add") { return nil, errors.New("You are not permitted to add logs") @@ -151,7 +150,7 @@ func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportI return newLogs, nil } -func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput) (*models.Log, error) { +func (r *mutationResolver) EditLog(ctx context.Context, input graphcore.LogEditInput) (*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.edit") { return nil, errors.New("You are not permitted to edit logs") @@ -172,7 +171,7 @@ func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput return &log, nil } -func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveInput) (*models.Log, error) { +func (r *mutationResolver) RemoveLog(ctx context.Context, input graphcore.LogRemoveInput) (*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.remove") { return nil, errors.New("You are not permitted to remove logs") diff --git a/graph2/queries/post.go b/graph2/queries/post.go index 3699c35..7c208e4 100644 --- a/graph2/queries/post.go +++ b/graph2/queries/post.go @@ -4,13 +4,12 @@ import ( "context" "errors" + "git.aiterp.net/rpdata/api/graph2/graphcore" + "git.aiterp.net/rpdata/api/internal/auth" + "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" "git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/logs" - - "git.aiterp.net/rpdata/api/graph2/input" - "git.aiterp.net/rpdata/api/internal/auth" - "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/posts" ) @@ -58,7 +57,7 @@ func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]*models.P // Mutation -func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (*models.Post, error) { +func (r *mutationResolver) AddPost(ctx context.Context, input graphcore.PostAddInput) (*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.add") { return nil, errors.New("You are not permitted to edit logs") @@ -80,7 +79,7 @@ func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput return &post, nil } -func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (*models.Post, error) { +func (r *mutationResolver) EditPost(ctx context.Context, input graphcore.PostEditInput) (*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.edit") { return nil, errors.New("You are not permitted to edit logs") @@ -119,7 +118,7 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp return &post, nil } -func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInput) ([]*models.Post, error) { +func (r *mutationResolver) MovePost(ctx context.Context, input graphcore.PostMoveInput) ([]*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.move") { return nil, errors.New("You are not permitted to edit logs") @@ -152,7 +151,7 @@ func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInp return posts2, nil } -func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (*models.Post, error) { +func (r *mutationResolver) RemovePost(ctx context.Context, input graphcore.PostRemoveInput) (*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.remove") { return nil, errors.New("You are not permitted to edit logs") diff --git a/graph2/queries/resolver.go b/graph2/queries/resolver.go index 613ba80..b672203 100644 --- a/graph2/queries/resolver.go +++ b/graph2/queries/resolver.go @@ -1,11 +1,18 @@ package queries -type resolver struct{} +import ( + "git.aiterp.net/rpdata/api/graph2/graphcore" + "git.aiterp.net/rpdata/api/services" +) + +type resolver struct{ s *services.Bundle } type mutationResolver struct{} type subscriptionResolver struct{} -// Resolver has all the queries -var Resolver resolver +// QueryResolver has all the queries +func QueryResolver(s *services.Bundle) graphcore.QueryResolver { + return &resolver{s: s} +} // MutationResolver brings the mutagens. var MutationResolver *mutationResolver diff --git a/graph2/queries/story.go b/graph2/queries/story.go index ed4be75..147fc61 100644 --- a/graph2/queries/story.go +++ b/graph2/queries/story.go @@ -5,12 +5,11 @@ import ( "errors" "time" - "git.aiterp.net/rpdata/api/models/changekeys" - "git.aiterp.net/rpdata/api/models/changes" - - "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models" + "git.aiterp.net/rpdata/api/models/changekeys" + "git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/chapters" "git.aiterp.net/rpdata/api/models/stories" ) @@ -53,7 +52,7 @@ func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]*mode // Mutations -func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInput) (*models.Story, error) { +func (r *mutationResolver) AddStory(ctx context.Context, input graphcore.StoryAddInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) if token == nil || !token.Permitted("member", "story.add") { return nil, errors.New("Permission denied") @@ -91,7 +90,7 @@ func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInp return &story, nil } -func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTagAddInput) (*models.Story, error) { +func (r *mutationResolver) AddStoryTag(ctx context.Context, input graphcore.StoryTagAddInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) @@ -119,7 +118,7 @@ func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTag return &story, nil } -func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.StoryTagRemoveInput) (*models.Story, error) { +func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input graphcore.StoryTagRemoveInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) @@ -147,7 +146,7 @@ func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.Story return &story, nil } -func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditInput) (*models.Story, error) { +func (r *mutationResolver) EditStory(ctx context.Context, input graphcore.StoryEditInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) @@ -173,7 +172,7 @@ func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditI return &story, nil } -func (r *mutationResolver) RemoveStory(ctx context.Context, input input.StoryRemoveInput) (*models.Story, error) { +func (r *mutationResolver) RemoveStory(ctx context.Context, input graphcore.StoryRemoveInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) diff --git a/graph2/queries/tags.go b/graph2/queries/tags.go index eff8922..411a6a3 100644 --- a/graph2/queries/tags.go +++ b/graph2/queries/tags.go @@ -3,20 +3,15 @@ package queries import ( "context" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/models" - "git.aiterp.net/rpdata/api/models/tags" ) -func (r *resolver) Tags(ctx context.Context) ([]*models.Tag, error) { - tags, err := tags.List() - if err != nil { - return nil, err +func (r *resolver) Tags(ctx context.Context, filter *graphcore.TagFilter) ([]*models.Tag, error) { + tagFilter := models.TagFilter{} + if filter != nil && filter.Kind != nil { + tagFilter.Kind = filter.Kind } - tags2 := make([]*models.Tag, len(tags)) - for i := range tags { - tags2[i] = &tags[i] - } - - return tags2, nil + return r.s.Tags.ListTags(ctx, tagFilter) } diff --git a/graph2/queries/unknownnicks.go b/graph2/queries/unknownnicks.go index 3f7133b..73c31b6 100644 --- a/graph2/queries/unknownnicks.go +++ b/graph2/queries/unknownnicks.go @@ -3,15 +3,14 @@ package queries import ( "context" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/unknownnicks" - - "git.aiterp.net/rpdata/api/graph2/input" ) /// Queries -func (r *resolver) UnknownNicks(ctx context.Context, filter *input.UnknownNicksFilter) ([]*models.UnknownNick, error) { +func (r *resolver) UnknownNicks(ctx context.Context, filter *graphcore.UnknownNicksFilter) ([]*models.UnknownNick, error) { limit := 100 if filter != nil { if filter.Limit > 0 { diff --git a/graph2/schema/root.gql b/graph2/schema/root.gql index 611edff..cad292c 100644 --- a/graph2/schema/root.gql +++ b/graph2/schema/root.gql @@ -46,7 +46,7 @@ type Query { # Find all distinct tags used in stories - tags: [Tag!]! + tags(filter: TagFilter): [Tag!]! # Find story by ID diff --git a/graph2/schema/types/Tag.gql b/graph2/schema/types/Tag.gql index 139530e..7994d55 100644 --- a/graph2/schema/types/Tag.gql +++ b/graph2/schema/types/Tag.gql @@ -16,6 +16,15 @@ input TagInput { name: String! } +""" +A TagFilter is a filter for the tags query. +""" +input TagFilter { + "Filter by kind" + kind: TagKind +} + + # Allowed values for Tag.kind enum TagKind { # An organization is a catch all term for in-universe corporations, teams, groups, cults, forces, etc... diff --git a/graph2/types/change.go b/graph2/types/change.go index 224ce58..b6624fb 100644 --- a/graph2/types/change.go +++ b/graph2/types/change.go @@ -4,18 +4,18 @@ import ( "context" "log" - "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/graph2/graphcore" "git.aiterp.net/rpdata/api/models" ) type changeResolver struct{} -func (r *changeResolver) Objects(ctx context.Context, obj *models.Change) ([]input.ChangeObject, error) { +func (r *changeResolver) Objects(ctx context.Context, obj *models.Change) ([]graphcore.ChangeObject, error) { objects := obj.Objects() - results := make([]input.ChangeObject, 0, len(objects)) + results := make([]graphcore.ChangeObject, 0, len(objects)) for _, object := range objects { - if changeObject, ok := object.(input.ChangeObject); ok { + if changeObject, ok := object.(graphcore.ChangeObject); ok { results = append(results, changeObject) } else { log.Printf("Type %T needs ChangeObject impl", object) diff --git a/internal/auth/permissions.go b/internal/auth/permissions.go index 2954e25..216ace6 100644 --- a/internal/auth/permissions.go +++ b/internal/auth/permissions.go @@ -11,6 +11,8 @@ func AllPermissions() map[string]string { "channel.add": "Can add channels", "channel.edit": "Can edit channels", "channel.remove": "Can remove channels", + "comment.edit": "Can edit non-owned comments", + "comment.remove": "Can remove non-owned comments", "log.add": "Can add logs", "log.edit": "Can edit logs", "log.remove": "Can remove logs", diff --git a/internal/auth/permitted.go b/internal/auth/permitted.go new file mode 100644 index 0000000..833bf22 --- /dev/null +++ b/internal/auth/permitted.go @@ -0,0 +1,53 @@ +package auth + +import ( + "context" + "reflect" + + "git.aiterp.net/rpdata/api/models" +) + +// CheckPermission does some magic. +func CheckPermission(ctx context.Context, op string, obj interface{}) error { + token := TokenFromContext(ctx) + if token == nil { + return ErrUnauthenticated + } + + if reflect.TypeOf(obj).Kind() != reflect.Ptr { + return CheckPermission(ctx, op, &obj) + } + + var authorized = false + + switch v := obj.(type) { + case *models.Channel: + authorized = token.Permitted("channel." + op) + case *models.Character: + authorized = token.PermittedUser(v.Author, "member", "character."+op) + case *models.Chapter: + authorized = token.PermittedUser(v.Author, "member", "chapter."+op) + case *models.Comment: + if op == "add" && v.Author != token.UserID { + return ErrInvalidOperation + } + + authorized = token.PermittedUser(v.Author, "member", "comment."+op) + case *models.File: + authorized = token.PermittedUser(v.Author, "member", "file."+op) + case *models.Log: + authorized = token.Permitted("log." + op) + case *models.Post: + authorized = token.Permitted("post." + op) + case *models.Story: + authorized = token.PermittedUser(v.Author, "member", "story."+op) + case *models.User: + authorized = token.Permitted("user." + op) + } + + if !authorized { + return ErrUnauthorized + } + + return nil +} diff --git a/internal/auth/token.go b/internal/auth/token.go index f42e421..209431d 100644 --- a/internal/auth/token.go +++ b/internal/auth/token.go @@ -36,6 +36,15 @@ var ErrWrongPermissions = errors.New("User does not have these permissions") // ErrDeletedUser is returned by CheckToken if the key can represent this user, but the user doesn't exist. var ErrDeletedUser = errors.New("User was not found") +// ErrUnauthenticated is returned when the user is not authenticated +var ErrUnauthenticated = errors.New("You are not authenticated") + +// ErrUnauthorized is returned when the user doesn't have access to this resource +var ErrUnauthorized = errors.New("You are not authorized to perform this action") + +// ErrInvalidOperation is returned for operations that should never be allowed +var ErrInvalidOperation = errors.New("No permission exists for this operation") + // TokenFromContext gets the token from context. func TokenFromContext(ctx context.Context) *models.Token { token, ok := ctx.Value(contextKey).(*models.Token) diff --git a/internal/config/config.go b/internal/config/config.go index 7ca9858..edd9888 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,56 +5,82 @@ import ( "errors" "log" "os" + "strings" "sync" + + "gopkg.in/yaml.v2" ) var globalMutex sync.Mutex var global *Config -// Config is configuration +// Config is configuration data type Config struct { - Space struct { - Enabled bool `json:"enabled"` - Host string `json:"host"` - AccessKey string `json:"accessKey"` - SecretKey string `json:"secretKey"` - Bucket string `json:"bucket"` - MaxSize int64 `json:"maxSize"` - Root string `json:"root"` - } `json:"space"` - - Database struct { - Host string `json:"host"` - Port int `json:"port"` - Db string `json:"db"` - Username string `json:"username"` - Password string `json:"password"` - Mechanism string `json:"mechanism"` - } `json:"database"` - - Wiki struct { - URL string `json:"url"` - } `json:"wiki"` + Space Space `json:"space" yaml:"space"` + Database Database `json:"database" yaml:"database"` + Wiki Wiki `json:"wiki" yaml:"wiki"` +} + +// Space is configuration for spaces. +type Space struct { + Enabled bool `json:"enabled" yaml:"enabled"` + Host string `json:"host" yaml:"host"` + AccessKey string `json:"accessKey" yaml:"accessKey"` + SecretKey string `json:"secretKey" yaml:"secretKey"` + Bucket string `json:"bucket" yaml:"bucket"` + MaxSize int64 `json:"maxSize" yaml:"maxSize"` + Root string `json:"root" yaml:"root"` +} + +// Database is configuration for spaces. +type Database struct { + Driver string `json:"driver" yaml:"driver"` + Host string `json:"host" yaml:"host"` + Port int `json:"port" yaml:"port"` + Db string `json:"db" yaml:"db"` + Username string `json:"username" yaml:"username"` + Password string `json:"password" yaml:"password"` + Mechanism string `json:"mechanism" yaml:"mechanism"` +} + +// Wiki is the wiki stuff. +type Wiki struct { + URL string `json:"url" yaml:"url"` } // Load loads config stuff func (config *Config) Load(filename string) error { + log.Println("Trying to load config from " + filename) + + stat, err := os.Stat(filename) + if err != nil { + return err + } + + if stat.Size() < 1 { + return errors.New("File is empty") + } + file, err := os.Open(filename) if err != nil { return err } - return json.NewDecoder(file).Decode(config) + if strings.HasSuffix(filename, ".json") { + return json.NewDecoder(file).Decode(config) + } + + return yaml.NewDecoder(file).Decode(config) } // LoadAny loads the first of these files it can find func (config *Config) LoadAny(filenames ...string) error { for _, filename := range filenames { - if err := config.Load(filename); err == nil { - return nil + if err := config.Load(filename); err != nil { + continue } - *config = Config{} + return nil } return errors.New("Failed to load configuration files") @@ -65,7 +91,7 @@ func Global() Config { globalMutex.Lock() if global == nil { global = &Config{} - err := global.LoadAny("/etc/aiterp/rpdata.json", "./config.json") + err := global.LoadAny("/etc/aiterp/rpdata.yaml", "/etc/aiterp/rpdata.json", "./config.yaml", "./config.json") if err != nil { log.Fatalln(err) } diff --git a/models/tag-kind.go b/models/tag-kind.go deleted file mode 100644 index 4c692a9..0000000 --- a/models/tag-kind.go +++ /dev/null @@ -1,47 +0,0 @@ -package models - -import ( - "fmt" - "io" -) - -// TagKind represents the kind of tags. -type TagKind string - -const ( - // TagKindOrganization is a tag kind, see GraphQL documentation. - TagKindOrganization TagKind = "Organization" - - // TagKindCharacter is a tag kind, see GraphQL documentation. - TagKindCharacter TagKind = "Character" - - // TagKindLocation is a tag kind, see GraphQL documentation. - TagKindLocation TagKind = "Location" - - // TagKindEvent is a tag kind, see GraphQL documentation. - TagKindEvent TagKind = "Event" - - // TagKindSeries is a tag kind, see GraphQL documentation. - TagKindSeries TagKind = "Series" -) - -// UnmarshalGQL unmarshals -func (e *TagKind) UnmarshalGQL(v interface{}) error { - str, ok := v.(string) - if !ok { - return fmt.Errorf("enums must be strings") - } - - *e = TagKind(str) - switch *e { - case TagKindOrganization, TagKindCharacter, TagKindLocation, TagKindEvent, TagKindSeries: - return nil - default: - return fmt.Errorf("%s is not a valid TagKind", str) - } -} - -// MarshalGQL turns it into a JSON string -func (e TagKind) MarshalGQL(w io.Writer) { - fmt.Fprint(w, "\""+string(e), "\"") -} diff --git a/models/tag.go b/models/tag.go index d848e20..acb651c 100644 --- a/models/tag.go +++ b/models/tag.go @@ -1,5 +1,10 @@ package models +import ( + "fmt" + "io" +) + // A Tag associates a story with other content, like other stories, logs and more. type Tag struct { Kind TagKind `bson:"kind"` @@ -15,4 +20,50 @@ func (tag *Tag) Equal(other Tag) bool { // ChangeObject in GQL. func (*Tag) IsChangeObject() { panic("this method is a dummy, and so is its caller") -} \ No newline at end of file +} + +// TagKind represents the kind of tags. +type TagKind string + +const ( + // TagKindOrganization is a tag kind, see GraphQL documentation. + TagKindOrganization TagKind = "Organization" + + // TagKindCharacter is a tag kind, see GraphQL documentation. + TagKindCharacter TagKind = "Character" + + // TagKindLocation is a tag kind, see GraphQL documentation. + TagKindLocation TagKind = "Location" + + // TagKindEvent is a tag kind, see GraphQL documentation. + TagKindEvent TagKind = "Event" + + // TagKindSeries is a tag kind, see GraphQL documentation. + TagKindSeries TagKind = "Series" +) + +// UnmarshalGQL unmarshals +func (e *TagKind) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = TagKind(str) + switch *e { + case TagKindOrganization, TagKindCharacter, TagKindLocation, TagKindEvent, TagKindSeries: + return nil + default: + return fmt.Errorf("%s is not a valid TagKind", str) + } +} + +// MarshalGQL turns it into a JSON string +func (e TagKind) MarshalGQL(w io.Writer) { + fmt.Fprint(w, "\""+string(e), "\"") +} + +// TagFilter is a filter of tags. +type TagFilter struct { + Kind *TagKind `bson:"kind,omitempty"` +} diff --git a/repositories/repository.go b/repositories/repository.go new file mode 100644 index 0000000..4f06cca --- /dev/null +++ b/repositories/repository.go @@ -0,0 +1,11 @@ +package repositories + +import "errors" + +// A Bundle is a set of repositories. +type Bundle struct { + Tags TagRepository +} + +// ErrNotFound should be returned instead of any database-specific not found error. +var ErrNotFound = errors.New("Resource not found") diff --git a/repositories/tag.go b/repositories/tag.go new file mode 100644 index 0000000..24a1e89 --- /dev/null +++ b/repositories/tag.go @@ -0,0 +1,13 @@ +package repositories + +import ( + "context" + + "git.aiterp.net/rpdata/api/models" +) + +// TagRepository is an interface for a database using logs. +type TagRepository interface { + Find(ctx context.Context, kind, name string) (*models.Tag, error) + List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) +} diff --git a/services/services.go b/services/services.go new file mode 100644 index 0000000..e7df004 --- /dev/null +++ b/services/services.go @@ -0,0 +1,17 @@ +package services + +import "git.aiterp.net/rpdata/api/repositories" + +// A Bundle contains all services, like a bean bag in the more caffeinated language family. +type Bundle struct { + Tags *TagService +} + +// NewBundle creates a new bundle. +func NewBundle(repos *repositories.Bundle) *Bundle { + bundle := &Bundle{} + + bundle.Tags = &TagService{tags: repos.Tags} + + return bundle +} diff --git a/services/tags.go b/services/tags.go new file mode 100644 index 0000000..b6bb737 --- /dev/null +++ b/services/tags.go @@ -0,0 +1,23 @@ +package services + +import ( + "context" + + "git.aiterp.net/rpdata/api/models" + "git.aiterp.net/rpdata/api/repositories" +) + +// TagService is a service for tag-related functions. +type TagService struct { + tags repositories.TagRepository +} + +// FindTag finds one tag. +func (s *TagService) FindTag(ctx context.Context, source, id string) (*models.Tag, error) { + return s.tags.Find(ctx, source, id) +} + +// ListTags lists tags. +func (s *TagService) ListTags(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) { + return s.tags.List(ctx, filter) +}