diff --git a/Gopkg.lock b/Gopkg.lock index b8c507f..0b53026 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -249,6 +249,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5395eefae297e64dc7575e2e7a24cd739dabbeaae561de7d876249f57a23619a" + inputs-digest = "3c9430afa8b9260026925c8f496af934e2e573a2bd4587bb634fd12a20ce6c2c" solver-name = "gps-cdcl" solver-version = 1 diff --git a/graph2/gqlgen.yml b/graph2/gqlgen.yml index 630a38f..8b141fa 100644 --- a/graph2/gqlgen.yml +++ b/graph2/gqlgen.yml @@ -20,4 +20,8 @@ models: Channel: model: git.aiterp.net/rpdata/api/model/channel.Channel ChannelsFilter: - model: git.aiterp.net/rpdata/api/model/channel.Filter \ No newline at end of file + model: git.aiterp.net/rpdata/api/model/channel.Filter + Post: + model: git.aiterp.net/rpdata/api/model/log.Post + Date: + model: git.aiterp.net/rpdata/api/model/scalars.Date \ No newline at end of file diff --git a/graph2/queries/post.go b/graph2/queries/post.go new file mode 100644 index 0000000..5c40623 --- /dev/null +++ b/graph2/queries/post.go @@ -0,0 +1,15 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/model/log" +) + +func (r *resolver) Post(ctx context.Context, id string) (log.Post, error) { + return log.FindPostID(id) +} + +func (r *resolver) Posts(ctx context.Context, ids []string) ([]log.Post, error) { + return log.ListPostIDs(ids...) +} diff --git a/graph2/schema/root.gql b/graph2/schema/root.gql index c19fea7..8635550 100644 --- a/graph2/schema/root.gql +++ b/graph2/schema/root.gql @@ -17,7 +17,17 @@ type Query { channels(filter: ChannelsFilter): [Channel!]! + # Find post by ID. + post(id: String!): Post! + + # Find posts by IDs. It's meant to allow other parts of the UI to link to a cluster of posts, e.g. for a room description for the + # Mapp should it ever become a thing. This does not have a filter, since it's meant to be queried in the logs' response's selection set. + posts(ids: [String!]!): [Post!]! + + # Find all distinct tags used in stories tags: [Tag!]! } +scalar Date + diff --git a/graph2/schema/types/Post.gql b/graph2/schema/types/Post.gql new file mode 100644 index 0000000..4fd6f0a --- /dev/null +++ b/graph2/schema/types/Post.gql @@ -0,0 +1,68 @@ +# A Post is a part of a log +type Post { + # The post's ID + id: String! + + # The post's Log ID. This is the closest thing to a link back since this API graph doesn't have any cycles. + logId: String! + + # The date and time of posting + time: Date! + + # The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'. + kind: String! + + # The character nick + nick: String! + + # The post's text, which purpose depends on the kind + text: String! + + # The post's position, used for reordering + position: Int! +} + +# Input for the addPost mutation +input AddPostInput { + # The log's ID that this post should be a part of + logId: String! + + # The date and time of posting, in a RFC3339 format with up to a nanosecond's precision + time: Date! + + # The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'. + kind: String! + + # The character nick, or command invoker for non-RP stuff + nick: String! + + # The post's text, which purpose depends on the kind + text: String! +} + +# Input for the editPost mutation +input EditPostInput { + # The Post ID + id: String! + + # The date and time of posting, in a RFC3339 format with up to a nanosecond's precision + time: Date + + # The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'. + kind: String + + # The character nick, or command invoker for non-RP stuff + nick: String + + # The post's text, which purpose depends on the kind + text: String +} + +# Input for the movePost mutation +input MovePostInput { + # The Post ID + id: String! + + # Target index + toPosition: Int! +} \ No newline at end of file diff --git a/model/scalars/date.go b/model/scalars/date.go new file mode 100644 index 0000000..081872d --- /dev/null +++ b/model/scalars/date.go @@ -0,0 +1,28 @@ +package scalars + +import ( + "fmt" + "io" + "time" + + "github.com/99designs/gqlgen/graphql" +) + +// MarshalDate marshals time into gql Date type +func MarshalDate(t time.Time) graphql.Marshaler { + return graphql.WriterFunc(func(w io.Writer) { + w.Write([]byte(`"` + t.Format(time.RFC3339Nano) + `"`)) + }) +} + +// UnmarshalDate unmarshals time from gql Date type +func UnmarshalDate(v interface{}) (time.Time, error) { + switch v := v.(type) { + case string: + return time.Parse(time.RFC3339Nano, v) + case int: + return time.Unix(0, int64(v)*1000000), nil + default: + return time.Time{}, fmt.Errorf("%T is not a valid time", v) + } +}