From 2b05d90f4aa14d1130e6b1fc83859c9ba5aea3c7 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sat, 15 Sep 2018 13:14:05 +0200 Subject: [PATCH] graph2: Added Log type and queries, moved loader out of old graphql pkg. --- cmd/rpdata-server/main.go | 23 +++++- graph2/gqlgen.yml | 4 + graph2/queries/log.go | 34 ++++++++ graph2/schema/root.gql | 8 ++ graph2/schema/types/Log.gql | 97 +++++++++++++++++++++++ {graphql => internal}/loader/channel.go | 0 {graphql => internal}/loader/character.go | 0 {graphql => internal}/loader/loader.go | 0 model/log/filter.go | 23 +----- model/log/log.go | 80 +++++++++++-------- 10 files changed, 209 insertions(+), 60 deletions(-) create mode 100644 graph2/queries/log.go create mode 100644 graph2/schema/types/Log.gql rename {graphql => internal}/loader/channel.go (100%) rename {graphql => internal}/loader/character.go (100%) rename {graphql => internal}/loader/loader.go (100%) diff --git a/cmd/rpdata-server/main.go b/cmd/rpdata-server/main.go index 7b7a5c2..0ab6de6 100644 --- a/cmd/rpdata-server/main.go +++ b/cmd/rpdata-server/main.go @@ -8,6 +8,8 @@ import ( "runtime/debug" "git.aiterp.net/rpdata/api/graph2" + "git.aiterp.net/rpdata/api/internal/auth" + "git.aiterp.net/rpdata/api/internal/loader" "git.aiterp.net/rpdata/api/internal/store" logModel "git.aiterp.net/rpdata/api/model/log" "github.com/99designs/gqlgen/handler" @@ -20,7 +22,15 @@ func main() { } http.Handle("/", handler.Playground("RPData API", "/query")) - http.Handle("/query", handler.GraphQL( + http.Handle("/query", queryHandler()) + + go updateCharacters() + + log.Fatal(http.ListenAndServe(":8081", nil)) +} + +func queryHandler() http.HandlerFunc { + handler := handler.GraphQL( graph2.New(), handler.RecoverFunc(func(ctx context.Context, err interface{}) error { // send this panic somewhere @@ -29,11 +39,16 @@ func main() { return fmt.Errorf("shit") }), - )) + ) - go updateCharacters() + return func(w http.ResponseWriter, r *http.Request) { + l := loader.New() + r = r.WithContext(l.ToContext(r.Context())) - log.Fatal(http.ListenAndServe(":8081", nil)) + r = auth.RequestWithToken(r) + + handler.ServeHTTP(w, r) + } } func updateCharacters() { diff --git a/graph2/gqlgen.yml b/graph2/gqlgen.yml index 8b141fa..70a22d0 100644 --- a/graph2/gqlgen.yml +++ b/graph2/gqlgen.yml @@ -23,5 +23,9 @@ models: model: git.aiterp.net/rpdata/api/model/channel.Filter Post: model: git.aiterp.net/rpdata/api/model/log.Post + Log: + model: git.aiterp.net/rpdata/api/model/log.Log + LogsFilter: + model: git.aiterp.net/rpdata/api/model/log.Filter Date: model: git.aiterp.net/rpdata/api/model/scalars.Date \ No newline at end of file diff --git a/graph2/queries/log.go b/graph2/queries/log.go new file mode 100644 index 0000000..8eb3c14 --- /dev/null +++ b/graph2/queries/log.go @@ -0,0 +1,34 @@ +package queries + +import ( + "context" + "errors" + + "git.aiterp.net/rpdata/api/internal/loader" + "git.aiterp.net/rpdata/api/model/log" +) + +func (r *resolver) Log(ctx context.Context, id string) (log.Log, error) { + return log.FindID(id) +} + +func (r *resolver) Logs(ctx context.Context, filter *log.Filter) ([]log.Log, error) { + logs, err := log.List(filter) + if err != nil { + return nil, err + } + + if len(logs) >= 100 { + loader := loader.FromContext(ctx) + if loader == nil { + return nil, errors.New("no loader") + } + + for _, log := range logs { + loader.PrimeCharacters("id", log.CharacterIDs...) + loader.PrimeChannels("name", log.ChannelName) + } + } + + return logs, nil +} diff --git a/graph2/schema/root.gql b/graph2/schema/root.gql index 8635550..2f8d59b 100644 --- a/graph2/schema/root.gql +++ b/graph2/schema/root.gql @@ -25,9 +25,17 @@ type Query { posts(ids: [String!]!): [Post!]! + # Find log by ID + log(id: String!): Log! + + # Find logs + logs(filter: LogsFilter): [Log!]! + + # Find all distinct tags used in stories tags: [Tag!]! } +# A Date represents a RFC3339 encoded date with up to millisecond precision. scalar Date diff --git a/graph2/schema/types/Log.gql b/graph2/schema/types/Log.gql new file mode 100644 index 0000000..cddffd7 --- /dev/null +++ b/graph2/schema/types/Log.gql @@ -0,0 +1,97 @@ +# A Log is the "file" of an RP session +type Log { + # A unique identifier for the log. + id: String! + + # A secondary unique identifier for the log. This is a lot shorter and more suitable for storage when + # the link doesn't need to be as expressive. + shortId: String! + + # The date for a log. + date: Date! + + # The channel of a log. + channelName: String! + + # Information about the channel this log is in. + channel: Channel! + + # The session's title. + title: String! + + # The log's event, which is the same as the tags in the previous logbot site. + # Empty string means that it's no event. + event: String! + + # The description of a session, which is empty if unset. + description: String! + + # Whether the log session is open. + open: Boolean! + + # The characters involved in the log file. + characters: [Character!]! + + # The posts of the logfile, which can be filtered by kinds. + posts(kinds:[String!]): [Post!]! +} + +# Filter for logs query +input LogsFilter { + # Channels to limit results to (inclusive) + channels: [String!] + + # Events to limit results to (inclusive) + events: [String!] + + # Characters to limit results to (exclusive) + characters: [String!] + + # Search post content + search: String + + # Limit by whether it's open or not + open: Boolean + + # Limit the amount of results you get back + limit: Int +} + +# Input for addLog mutation +input LogAddInput { + # The date of the log. + date: Date! + + # The channel where the log is set + channel: String! + + # Optional: The log title + title: String + + # Optional: Whether the log is open + open: Boolean + + # Optional: The log event name + event: String + + # Optional: A short description of the log + description: String +} + +# Input for addLog mutation +input LogEditInput { + # The id of the log + id: String! + + # The log title + title: String + + # The log event name + event: String + + # A short description of the log + description: String + + # Open/close the log + open: Boolean +} \ No newline at end of file diff --git a/graphql/loader/channel.go b/internal/loader/channel.go similarity index 100% rename from graphql/loader/channel.go rename to internal/loader/channel.go diff --git a/graphql/loader/character.go b/internal/loader/character.go similarity index 100% rename from graphql/loader/character.go rename to internal/loader/character.go diff --git a/graphql/loader/loader.go b/internal/loader/loader.go similarity index 100% rename from graphql/loader/loader.go rename to internal/loader/loader.go diff --git a/model/log/filter.go b/model/log/filter.go index 95e86d9..ad30ed2 100644 --- a/model/log/filter.go +++ b/model/log/filter.go @@ -7,26 +7,5 @@ type Filter struct { Channels *[]string Events *[]string Open *bool - Limit *int32 -} - -// NewFilter makes a new filter -func NewFilter() *Filter { - return &Filter{} -} - -// WithLimit adds a max amount of results to be returned. -func (filter *Filter) WithLimit(limit int) *Filter { - limitPtr := int32(limit) - filter.Limit = &limitPtr - - return filter -} - -// WithOpen filters on whether a log is open. -func (filter *Filter) WithOpen(limit int) *Filter { - limitPtr := int32(limit) - filter.Limit = &limitPtr - - return filter + Limit int } diff --git a/model/log/log.go b/model/log/log.go index f4968c6..da5443f 100644 --- a/model/log/log.go +++ b/model/log/log.go @@ -31,7 +31,7 @@ type Log struct { ID string `bson:"_id"` ShortID string `bson:"shortId"` Date time.Time `bson:"date"` - Channel string `bson:"channel"` + ChannelName string `bson:"channel"` Title string `bson:"title,omitempty"` Event string `bson:"event,omitempty"` Description string `bson:"description,omitempty"` @@ -55,7 +55,7 @@ func New(date time.Time, channelName, title, event, description string, open boo ID: MakeLogID(date, channelName), ShortID: "L" + strconv.Itoa(nextID), Date: date, - Channel: channelName, + ChannelName: channelName, Title: title, Event: event, Description: description, @@ -84,43 +84,44 @@ func FindID(id string) (Log, error) { // List lists all logs func List(filter *Filter) ([]Log, error) { query := bson.M{} + limit := 0 - // Run a text search - if filter.Search != nil { - searchResults := make([]string, 0, 32) + if filter != nil { + // Run a text search + if filter.Search != nil { + searchResults := make([]string, 0, 32) - postMutex.RLock() - err := postCollection.Find(bson.M{"$text": bson.M{"$search": *filter.Search}}).Distinct("logId", &searchResults) - if err != nil { - return nil, err - } - postMutex.RUnlock() + postMutex.RLock() + err := postCollection.Find(bson.M{"$text": bson.M{"$search": *filter.Search}}).Distinct("logId", &searchResults) + if err != nil { + return nil, err + } + postMutex.RUnlock() - // Posts always use shortId to refer to the log - query["shortId"] = bson.M{"$in": searchResults} - } + // Posts always use shortId to refer to the log + query["shortId"] = bson.M{"$in": searchResults} + } - // Find logs including any of the specified events and channels - if filter.Channels != nil { - query["channel"] = bson.M{"$in": *filter.Channels} - } - if filter.Events != nil { - query["event"] = bson.M{"$in": *filter.Events} - } + // Find logs including any of the specified events and channels + if filter.Channels != nil { + query["channel"] = bson.M{"$in": *filter.Channels} + } + if filter.Events != nil { + query["event"] = bson.M{"$in": *filter.Events} + } - // Find logs including all of the specified character IDs. - if filter.Characters != nil { - query["characterIds"] = bson.M{"$all": *filter.Characters} - } + // Find logs including all of the specified character IDs. + if filter.Characters != nil { + query["characterIds"] = bson.M{"$all": *filter.Characters} + } - // Limit to only open logs - if filter.Open != nil { - query["open"] = *filter.Open - } + // Limit to only open logs + if filter.Open != nil { + query["open"] = *filter.Open + } - limit := 0 - if filter.Limit != nil { - limit = int(*filter.Limit) + // Set the limit from the filter + limit = filter.Limit } return listLog(query, limit) @@ -168,9 +169,20 @@ func (log *Log) Edit(title *string, event *string, description *string, open *bo return nil } +// Characters get all the characters for the character IDs stored in the +// log file. +func (log *Log) Characters() ([]character.Character, error) { + return character.ListIDs(log.CharacterIDs...) +} + +// Channel gets the channel. +func (log *Log) Channel() (channel.Channel, error) { + return channel.FindName(log.ChannelName) +} + // Posts gets all the posts under the log. If no kinds are specified, it // will get all posts -func (log *Log) Posts(kinds ...string) ([]Post, error) { +func (log *Log) Posts(kinds []string) ([]Post, error) { postMutex.RLock() defer postMutex.RUnlock() @@ -238,7 +250,7 @@ func (log *Log) UpdateCharacters() error { characterUpdateMutex.Lock() defer characterUpdateMutex.Unlock() - posts, err := log.Posts() + posts, err := log.Posts([]string{"action", "text", "chars"}) if err != nil { return err }