Compare commits

..
19 Commits
Author SHA1 Message Date
gisle 162ccf6bfb The great module madness continus, now with pointers.
the build was successful
2019-05-17 11:51:23 +02:00
gisle 7e3dc49635 Basic stuff, doesn't compile 2019-05-16 15:57:08 +02:00
gisle aff0d99842 graph2: Added exception for open logs query in complexity.
the build was successful
2019-04-27 11:37:01 +02:00
gisle 00150118d2 graph2: Added complexity to prevent queries that can take down the server.
the build was successful
2019-04-25 20:47:20 +02:00
gisle 332a1eb32b Fixed rpdata-as2storyimport not compiling. 2019-03-17 21:32:21 +01:00
gisle db4d46c368 Added story to comment changes.
the build was successful
2019-03-17 20:44:30 +01:00
gisle a8c8610ca2 models: Fixed wrong database mapping for comment source.
the build was successful
2019-01-17 21:19:13 +01:00
gisle d09c17d30d graph2: Changed addComment error message on can-comment check to be consistent with others.
the build was successful
2019-01-16 22:07:00 +01:00
gisle e3e3ea07db graph2: Changed Chapter to not return any comments if mode is disabled, even if there are comments.
the build was successful
2019-01-16 22:05:40 +01:00
gisle 22592439da graph2, models: Added removeComment mutation.
the build was successful
2019-01-16 22:03:28 +01:00
gisle dfa9fb4025 graph2: Changed Comment to return null instead of a zero-date if it's cleared.
the build was successful
2019-01-16 21:50:53 +01:00
gisle 0e2a68a2d4 graph2, models: Added editComment mutation. 2019-01-16 21:45:00 +01:00
gisle 1eefa65527 models: Fixed wrong database mapping for comment id. 2019-01-16 21:41:36 +01:00
gisle f9b6af58c9 graph2: Changed Comment character resolver to use loader.
the build was successful
2019-01-15 21:42:44 +01:00
gisle 56ecf48b57 graph2, models: Added addComment mutation.
the build was successful
2019-01-15 21:34:57 +01:00
gisle 06fbfc90c5 graph2, model: Added Comment type to Change. 2019-01-14 19:58:05 +01:00
gisle 39a4fef506 models, graph2: Added comment removal on chapter removal.
the build was successful
2019-01-14 18:01:43 +01:00
gisle f9fe0bb5f8 models, graph2: Added comment options to chapter mutations.
the build was successful
2019-01-14 17:55:38 +01:00
gisle d22ab387dd models, graph2: Added Comment type and queries, and hooked it up to chapter.
the build was successful
2019-01-14 17:03:16 +01:00
55 changed files with 1301 additions and 512 deletions
+3 -7
View File
@@ -1,16 +1,12 @@
## 1. Build Server ## 1. Build Server
# Use Go 1.10 # Use Go 1.12
FROM golang:1.11 AS build-server FROM golang:1.12 AS build-server
# Load repository into docker # Load repository into docker
WORKDIR /go/src/git.aiterp.net/rpdata/api/ WORKDIR /project/
COPY . . COPY . .
RUN rm -rf ./vendor
# Install build tools and dependencies # Install build tools and dependencies
RUN go get -u github.com/golang/dep/cmd/dep
RUN go get -u github.com/vektah/gorunpkg
RUN dep ensure
RUN go generate ./... RUN go generate ./...
# Build server and tools (CGO disabled to make them 100% static) # Build server and tools (CGO disabled to make them 100% static)
Generated
-250
View File
@@ -1,250 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/99designs/gqlgen"
packages = [
".",
"cmd",
"codegen",
"codegen/templates",
"complexity",
"graphql",
"graphql/introspection",
"handler",
"internal/gopath"
]
revision = "636435b68700211441303f1a5ed92f3768ba5774"
version = "v0.5.1"
[[projects]]
name = "github.com/agnivade/levenshtein"
packages = ["."]
revision = "3d21ba515fe27b856f230847e856431ae1724adc"
version = "v1.0.0"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
branch = "master"
name = "github.com/dustin/go-humanize"
packages = ["."]
revision = "bb3d318650d48840a39aa21a027c6630e198e626"
[[projects]]
branch = "master"
name = "github.com/globalsign/mgo"
packages = [
".",
"bson",
"internal/json",
"internal/sasl",
"internal/scram"
]
revision = "f76e4f9da92ecd56e3be26f5ba92580af1ef97b4"
[[projects]]
name = "github.com/go-ini/ini"
packages = ["."]
revision = "ace140f73450505f33e8b8418216792275ae82a7"
version = "v1.35.0"
[[projects]]
name = "github.com/go-sql-driver/mysql"
packages = ["."]
revision = "d523deb1b23d913de5bdada721a6071e71283618"
version = "v1.4.0"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d"
version = "v1.4.0"
[[projects]]
name = "github.com/graph-gophers/dataloader"
packages = ["."]
revision = "78139374585c29dcb97b8f33089ed11959e4be59"
version = "v5"
[[projects]]
name = "github.com/hashicorp/golang-lru"
packages = [
".",
"simplelru"
]
revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768"
version = "v0.5.0"
[[projects]]
branch = "master"
name = "github.com/jmoiron/sqlx"
packages = [
".",
"reflectx"
]
revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849"
[[projects]]
name = "github.com/minio/minio-go"
packages = [
".",
"pkg/credentials",
"pkg/encrypt",
"pkg/s3signer",
"pkg/s3utils",
"pkg/set"
]
revision = "3d2d02921f0510e9d1f66ef77a265b8dddd36992"
version = "6.0.0"
[[projects]]
branch = "master"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
[[projects]]
name = "github.com/opentracing/opentracing-go"
packages = [
".",
"log"
]
revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
version = "v1.0.2"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
[[projects]]
name = "github.com/urfave/cli"
packages = ["."]
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
version = "v1.20.0"
[[projects]]
branch = "master"
name = "github.com/vektah/gqlparser"
packages = [
".",
"ast",
"gqlerror",
"lexer",
"parser",
"validator",
"validator/rules"
]
revision = "14e83ae06ec152e6d0afb9766a00e0c0918aa8fc"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"argon2",
"blake2b",
"ssh/terminal"
]
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"idna",
"lex/httplex"
]
revision = "a35a21de978d84ffc92f010a153705b170b2f9d1"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
]
revision = "2f57af4873d00d535c5c9028850aa2152e6a5566"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
"go/buildutil",
"go/internal/cgo",
"go/loader",
"imports",
"internal/fastwalk"
]
revision = "677d2ff680c188ddb7dcd2bfa6bc7d3f2f2f75b2"
[[projects]]
name = "google.golang.org/appengine"
packages = ["cloudsql"]
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
version = "v1.1.0"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "eb8fc099a909f5d8f756b24fbcb6cc2856e132bd3ed16b8fe512f22fabb99596"
solver-name = "gps-cdcl"
solver-version = 1
-43
View File
@@ -1,43 +0,0 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
required = [ "github.com/99designs/gqlgen" ]
[[constraint]]
branch = "master"
name = "github.com/globalsign/mgo"
[[constraint]]
name = "github.com/minio/minio-go"
version = "6.0.0"
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.2"
+1 -1
View File
@@ -104,7 +104,7 @@ func main() {
} }
} }
chapter, err := chapters.Add(story, title, result.Author[5:], result.Source, publishDate, &fictionalDate) chapter, err := chapters.Add(story, title, result.Author[5:], result.Source, publishDate, &fictionalDate, models.ChapterCommentModeDisabled)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
+6 -5
View File
@@ -8,15 +8,13 @@ import (
"runtime/debug" "runtime/debug"
"strings" "strings"
"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/graph2" "git.aiterp.net/rpdata/api/graph2"
"git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/internal/loader" "git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/internal/store" "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"
"github.com/99designs/gqlgen/handler" "github.com/99designs/gqlgen/handler"
) )
@@ -64,6 +62,8 @@ func logListedChanges() {
log.Printf("\tTag: (%s,%s)", object.Kind, object.Name) log.Printf("\tTag: (%s,%s)", object.Kind, object.Name)
case models.Chapter: case models.Chapter:
log.Printf("\tChapter: %s", object.ID) log.Printf("\tChapter: %s", object.ID)
case models.Comment:
log.Printf("\tComment: %s", object.ID)
} }
} }
} }
@@ -80,6 +80,7 @@ func queryHandler() http.HandlerFunc {
return fmt.Errorf("The server failed to serve your request due to an internal error, it has been logged") return fmt.Errorf("The server failed to serve your request due to an internal error, it has been logged")
}), }),
handler.ComplexityLimit(300),
) )
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
+34
View File
@@ -0,0 +1,34 @@
module git.aiterp.net/rpdata/api
go 1.12
require (
github.com/99designs/gqlgen v0.8.0
github.com/agnivade/levenshtein v1.0.1
github.com/davecgh/go-spew v1.1.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e
github.com/go-ini/ini v1.35.0
github.com/go-sql-driver/mysql v1.4.0
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
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747
github.com/opentracing/opentracing-go v1.0.2
github.com/pkg/errors v0.8.1
github.com/pmezard/go-difflib v1.0.0
github.com/sirupsen/logrus v1.0.5
github.com/stretchr/testify v1.3.0
github.com/urfave/cli v1.20.0
github.com/vektah/gqlparser v1.1.0
golang.org/x/crypto v0.0.0-20180411161317-d6449816ce06
golang.org/x/net v0.0.0-20180416171110-a35a21de978d
golang.org/x/sys v0.0.0-20180416112224-2f57af4873d0
golang.org/x/text v0.3.0
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6
google.golang.org/appengine v1.1.0
gopkg.in/yaml.v2 v2.2.2
)
+90
View File
@@ -0,0 +1,90 @@
github.com/99designs/gqlgen v0.5.1 h1:cRsbpZgX83PrXb0/hj5EkNdmaVN4l/Eii81Z8LCexgY=
github.com/99designs/gqlgen v0.5.1/go.mod h1:KSQDfLlTTGmzlRgLGm6HeKKKo598l5E2svEM6Nz2Jnw=
github.com/99designs/gqlgen v0.8.0 h1:ZBteuSgeeFwn+mztjDqQZQdVa9pqslmPnDeCovcnc0Y=
github.com/99designs/gqlgen v0.8.0/go.mod h1:st7qHA6ssU3uRZkmv+wzrzgX4srvIqEIdE5iuRW8GhE=
github.com/99designs/gqlgen v0.8.3 h1:I6bMglXNKkn4KlvkSMzqZw53e1N2FF9Gud4NmsOxqiA=
github.com/99designs/gqlgen v0.8.3/go.mod h1:aLyJw9xUgdJxZ8EqNQxo2pGFhXXJ/hq8t7J4yn8TgI4=
github.com/agnivade/levenshtein v1.0.0 h1:q+77q31bLT5jhN3BHKA1276nUEdbz7XjDa0o5dRKcZ0=
github.com/agnivade/levenshtein v1.0.0/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e h1:hmrbHva/wKD6X4M7pgHVbg/KfVV1wwgr1NEkQaVD3rU=
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-ini/ini v1.35.0 h1:D/my3+xOfqZMkJpciRcyqU7XMBUgiZa9qXjZIa8uv2k=
github.com/go-ini/ini v1.35.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
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/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
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=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/graph-gophers/dataloader v0.0.0-20180104184831-78139374585c h1:94S+uoVVMpQAEOrqGjCDyUdML4dJDkh6aC4MYmXECg4=
github.com/graph-gophers/dataloader v0.0.0-20180104184831-78139374585c/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE=
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05 h1:YEyFCqmHcmbcTLqZ7yEAg7VIUat6PuDzGbIctZy888k=
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 h1:eQox4Rh4ewJF+mqYPxCkmBAirRnPaHEB26UkNuPyjlk=
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
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=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/stretchr/objx v0.1.0/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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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=
github.com/vektah/gqlparser v0.0.0-20180831041411-14e83ae06ec1 h1:FYOXUtr3sYR9shto7Q/aQ1B0Onyk77aws9wGOORiz+I=
github.com/vektah/gqlparser v0.0.0-20180831041411-14e83ae06ec1/go.mod h1:K4QdSSpS2XiHHwzb18kWh3iBljB8rLC8okGXsnQy3Nc=
github.com/vektah/gqlparser v1.1.0 h1:3668p2gUlO+PiS81x957Rpr3/FPRWG6cxgCXAvTS1hw=
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=
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/net v0.0.0-20180404174746-b3c676e531a6/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180416171110-a35a21de978d h1:O2P57H5Cc+d+DJos+iweraI9rmzMYwV+45vkZdDY0Oo=
golang.org/x/net v0.0.0-20180416171110-a35a21de978d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20180416112224-2f57af4873d0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1 h1:dzEuQYa6+a3gROnSlgly5ERUm4SZKJt+dh+4iSbO+bI=
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
+182
View File
@@ -0,0 +1,182 @@
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"
)
func complexity() (cr ComplexityRoot) {
subListComplexity := 25
bigSublistComplexity := 75
hugeSublistComplexity := 100
findComplexity := 100
listComplexity := 200
mutationComplexity := 100
cr.Query.Character = func(childComplexity int, id *string, nick *string) int {
return childComplexity + findComplexity
}
cr.Query.Characters = func(childComplexity int, filter *characters.Filter) int {
return childComplexity + listComplexity
}
cr.Query.Channel = func(childComplexity int, name string) int {
return childComplexity + findComplexity
}
cr.Query.Channels = func(childComplexity int, filter *channels.Filter) int {
return childComplexity + listComplexity
}
cr.Query.Post = func(childComplexity int, id string) int {
return childComplexity + findComplexity
}
cr.Query.Posts = func(childComplexity int, filter *posts.Filter) int {
return childComplexity + listComplexity
}
cr.Query.UnknownNicks = func(childComplexity int, filter *input.UnknownNicksFilter) int {
return childComplexity + listComplexity
}
cr.Query.Log = func(childComplexity int, id string) int {
return childComplexity + findComplexity
}
cr.Query.Logs = func(childComplexity int, filter *logs.Filter) int {
if filter != nil && filter.Open != nil && *filter.Open == true {
return childComplexity + findComplexity
}
return childComplexity + listComplexity
}
cr.Query.Chapter = func(childComplexity int, id string) int {
return childComplexity + findComplexity
}
cr.Query.Comment = func(childComplexity int, id string) int {
return childComplexity + findComplexity
}
cr.Query.Tags = func(childComplexity int) int {
return childComplexity + listComplexity
}
cr.Query.Story = func(childComplexity int, id string) int {
return childComplexity + findComplexity
}
cr.Query.Stories = func(childComplexity int, filter *stories.Filter) int {
return childComplexity + listComplexity
}
cr.Query.File = func(childComplexity int, id string) int {
return childComplexity + findComplexity
}
cr.Query.Files = func(childComplexity int, filter *files.Filter) int {
return childComplexity + listComplexity
}
cr.Query.Changes = func(childComplexity int, filter *changes.Filter) int {
return childComplexity + listComplexity
}
cr.Query.Token = func(childComplexity int) int {
return childComplexity + findComplexity
}
cr.Mutation.AddStory = func(childComplexity int, input input.StoryAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddStoryTag = func(childComplexity int, input input.StoryTagAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveStoryTag = func(childComplexity int, input input.StoryTagRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditStory = func(childComplexity int, input input.StoryEditInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveStory = func(childComplexity int, input input.StoryRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddChapter = func(childComplexity int, input input.ChapterAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditChapter = func(childComplexity int, input input.ChapterEditInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.MoveChapter = func(childComplexity int, input input.ChapterMoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveChapter = func(childComplexity int, input input.ChapterRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddComment = func(childComplexity int, input input.CommentAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditComment = func(childComplexity int, input input.CommentEditInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveComment = func(childComplexity int, input input.CommentRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddLog = func(childComplexity int, input input.LogAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.ImportLog = func(childComplexity int, input input.LogImportInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditLog = func(childComplexity int, input input.LogEditInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveLog = func(childComplexity int, input input.LogRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddPost = func(childComplexity int, input input.PostAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditPost = func(childComplexity int, input input.PostEditInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.MovePost = func(childComplexity int, input input.PostMoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemovePost = func(childComplexity int, input input.PostRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddCharacter = func(childComplexity int, input input.CharacterAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddCharacterNick = func(childComplexity int, input input.CharacterNickInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveCharacterNick = func(childComplexity int, input input.CharacterNickInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditCharacter = func(childComplexity int, input input.CharacterEditInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.RemoveCharacter = func(childComplexity int, input input.CharacterRemoveInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.AddChannel = func(childComplexity int, input input.ChannelAddInput) int {
return childComplexity + mutationComplexity
}
cr.Mutation.EditChannel = func(childComplexity int, input input.ChannelEditInput) int {
return childComplexity + mutationComplexity
}
cr.Log.Characters = func(childComplexity int) int {
return childComplexity + subListComplexity
}
cr.Log.Posts = func(childComplexity int, kinds []string) int {
return childComplexity + hugeSublistComplexity
}
cr.Story.Chapters = func(childComplexity int) int {
return childComplexity + bigSublistComplexity
}
cr.Story.Tags = func(childComplexity int) int {
return childComplexity + subListComplexity
}
cr.Chapter.Comments = func(childComplexity int, limit *int) int {
return childComplexity + subListComplexity
}
return
}
+7
View File
@@ -35,6 +35,13 @@ models:
model: git.aiterp.net/rpdata/api/models/logs.Filter model: git.aiterp.net/rpdata/api/models/logs.Filter
LogImporter: LogImporter:
model: git.aiterp.net/rpdata/api/models.LogImporter model: git.aiterp.net/rpdata/api/models.LogImporter
Comment:
model: git.aiterp.net/rpdata/api/models.Comment
fields:
fictionalDate:
resolver: true
ChapterCommentMode:
model: git.aiterp.net/rpdata/api/models.ChapterCommentMode
Chapter: Chapter:
model: git.aiterp.net/rpdata/api/models.Chapter model: git.aiterp.net/rpdata/api/models.Chapter
fields: fields:
+6 -5
View File
@@ -7,12 +7,13 @@ import (
) )
//go:generate ./combine.sh //go:generate ./combine.sh
//go:generate gorunpkg github.com/99designs/gqlgen -v //go:generate go run github.com/99designs/gqlgen -v
// New creates a new GraphQL schema. // New creates a new GraphQL schema.
func New() graphql.ExecutableSchema { func New() graphql.ExecutableSchema {
return NewExecutableSchema(Config{ return NewExecutableSchema(Config{
Resolvers: &rootResolver{}, Resolvers: &rootResolver{},
Complexity: complexity(),
}) })
} }
@@ -34,6 +35,10 @@ func (r *rootResolver) Log() LogResolver {
return &types.LogResolver return &types.LogResolver
} }
func (r *rootResolver) Comment() CommentResolver {
return &types.CommentResolver
}
func (r *rootResolver) Chapter() ChapterResolver { func (r *rootResolver) Chapter() ChapterResolver {
return &types.ChapterResolver return &types.ChapterResolver
} }
@@ -42,10 +47,6 @@ func (r *rootResolver) Story() StoryResolver {
return &types.StoryResolver return &types.StoryResolver
} }
func (r *rootResolver) File() FileResolver {
return &types.FileResolver
}
func (r *rootResolver) Change() ChangeResolver { func (r *rootResolver) Change() ChangeResolver {
return &types.ChangeResolver return &types.ChangeResolver
} }
+1 -1
View File
@@ -16,7 +16,7 @@ func (r *resolver) Changes(ctx context.Context, filter *changes.Filter) ([]model
/// Subscriptions /// Subscriptions
func (r *subscriptionResolver) Changes(ctx context.Context, keys []models.ChangeKey) (<-chan models.Change, error) { func (r *subscriptionResolver) Changes(ctx context.Context, keys []models.ChangeKey) (<-chan *models.Change, error) {
if len(keys) == 0 { if len(keys) == 0 {
return nil, errors.New("At least one key is required for a subscription") return nil, errors.New("At least one key is required for a subscription")
} }
+16 -11
View File
@@ -14,8 +14,13 @@ import (
// Queries // Queries
func (r *resolver) Channel(ctx context.Context, name string) (models.Channel, error) { func (r *resolver) Channel(ctx context.Context, name string) (*models.Channel, error) {
return channels.FindName(name) channel, err := channels.FindName(name)
if err != nil {
return nil, err
}
return &channel, nil
} }
func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]models.Channel, error) { func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]models.Channel, error) {
@@ -24,10 +29,10 @@ func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]mod
// Mutations // Mutations
func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAddInput) (models.Channel, error) { func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAddInput) (*models.Channel, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("channel.add") { if !token.Authenticated() || !token.Permitted("channel.add") {
return models.Channel{}, errors.New("You are not permitted to add channels") return nil, errors.New("You are not permitted to add channels")
} }
logged := false logged := false
@@ -49,31 +54,31 @@ func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAd
channel, err := channels.Add(input.Name, logged, hub, eventName, locationName) channel, err := channels.Add(input.Name, logged, hub, eventName, locationName)
if err != nil { if err != nil {
return models.Channel{}, errors.New("Failed to add channel: " + err.Error()) return nil, errors.New("Failed to add channel: " + err.Error())
} }
go changes.Submit("Channel", "add", token.UserID, true, changekeys.Listed(channel), channel) go changes.Submit("Channel", "add", token.UserID, true, changekeys.Listed(channel), channel)
return channel, nil return &channel, nil
} }
func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelEditInput) (models.Channel, error) { func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelEditInput) (*models.Channel, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("channel.edit") { if !token.Authenticated() || !token.Permitted("channel.edit") {
return models.Channel{}, errors.New("You are not permitted to edit channels") return nil, errors.New("You are not permitted to edit channels")
} }
channel, err := channels.FindName(input.Name) channel, err := channels.FindName(input.Name)
if err != nil { if err != nil {
return models.Channel{}, errors.New("Channel not found") return nil, errors.New("Channel not found")
} }
channel, err = channels.Edit(channel, input.Logged, input.Hub, input.EventName, input.LocationName) channel, err = channels.Edit(channel, input.Logged, input.Hub, input.EventName, input.LocationName)
if err != nil { if err != nil {
return models.Channel{}, errors.New("Failed to edit channel: " + err.Error()) return nil, errors.New("Failed to edit channel: " + err.Error())
} }
go changes.Submit("Channel", "edit", token.UserID, true, changekeys.Listed(channel), channel) go changes.Submit("Channel", "edit", token.UserID, true, changekeys.Listed(channel), channel)
return channel, nil return &channel, nil
} }
+48 -34
View File
@@ -5,83 +5,92 @@ import (
"errors" "errors"
"time" "time"
"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/changekeys" "git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models/stories"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/chapters" "git.aiterp.net/rpdata/api/models/chapters"
"git.aiterp.net/rpdata/api/models/comments"
"git.aiterp.net/rpdata/api/models/stories"
) )
// Queries // Queries
func (r *resolver) Chapter(ctx context.Context, id string) (models.Chapter, error) { func (r *resolver) Chapter(ctx context.Context, id string) (*models.Chapter, error) {
return chapters.FindID(id) chapter, err := chapters.FindID(id)
if err != nil {
return nil, err
}
return &chapter, nil
} }
// Mutations // Mutations
func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAddInput) (models.Chapter, error) { func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAddInput) (*models.Chapter, error) {
story, err := stories.FindID(input.StoryID) story, err := stories.FindID(input.StoryID)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Story not found") return nil, errors.New("Story not found")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.add") { if !token.Permitted("member", "story.add") {
return models.Chapter{}, errors.New("Unauthorized") return nil, errors.New("Unauthorized")
} }
author := token.UserID author := token.UserID
if input.Author != nil && *input.Author != author { if input.Author != nil && *input.Author != author {
if !token.Permitted("story.add") { if !token.Permitted("story.add") {
return models.Chapter{}, errors.New("False pretender") return nil, errors.New("False pretender")
} }
author = *input.Author author = *input.Author
} }
if !story.Open && story.Author != author { if !story.Open && story.Author != author {
return models.Chapter{}, errors.New("Story is not open") return nil, errors.New("Story is not open")
} }
chapter, err := chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate) commentMode := models.ChapterCommentModeDisabled
if input.CommentMode != nil {
commentMode = *input.CommentMode
}
chapter, err := chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate, commentMode)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Failed to create chapter: " + err.Error()) return nil, errors.New("Failed to create chapter: " + err.Error())
} }
go changes.Submit("Chapter", "add", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter) go changes.Submit("Chapter", "add", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter)
return chapter, nil return &chapter, nil
} }
func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterMoveInput) (models.Chapter, error) { func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterMoveInput) (*models.Chapter, error) {
chapter, err := chapters.FindID(input.ID) chapter, err := chapters.FindID(input.ID)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Chapter not found") return nil, errors.New("Chapter not found")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.move") { if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.move") {
return models.Chapter{}, errors.New("You are not allowed to move this chapter") return nil, errors.New("You are not allowed to move this chapter")
} }
target, err := stories.FindID(input.StoryID) target, err := stories.FindID(input.StoryID)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Target story not found") return nil, errors.New("Target story not found")
} }
if !target.Open && !token.PermittedUser(target.Author, "member", "chapter.move") { if !target.Open && !token.PermittedUser(target.Author, "member", "chapter.move") {
return models.Chapter{}, errors.New("You are not permitted to move chapters to this story") return nil, errors.New("You are not permitted to move chapters to this story")
} }
oldStoryID := chapter.StoryID oldStoryID := chapter.StoryID
chapter, err = chapters.Move(chapter, target) chapter, err = chapters.Move(chapter, target)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Failed to move chapter: " + err.Error()) return nil, errors.New("Failed to move chapter: " + err.Error())
} }
go func() { go func() {
@@ -99,27 +108,27 @@ func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterM
changes.Submit("Chapter", "move-in", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter) changes.Submit("Chapter", "move-in", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter)
}() }()
return chapter, nil return &chapter, nil
} }
func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterEditInput) (models.Chapter, error) { func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterEditInput) (*models.Chapter, error) {
chapter, err := chapters.FindID(input.ID) chapter, err := chapters.FindID(input.ID)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Chapter not found") return nil, errors.New("Chapter not found")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.edit") { if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.edit") {
return models.Chapter{}, errors.New("Unauthorized") return nil, errors.New("Unauthorized")
} }
if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true { if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true {
input.FictionalDate = &time.Time{} input.FictionalDate = &time.Time{}
} }
chapter, err = chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate) chapter, err = chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate, input.CommentMode, input.CommentsLocked)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Failed to edit chapter: " + err.Error()) return nil, errors.New("Failed to edit chapter: " + err.Error())
} }
go func() { go func() {
@@ -131,23 +140,28 @@ func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterE
changes.Submit("Chapter", "edit", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter) changes.Submit("Chapter", "edit", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter)
}() }()
return chapter, nil return &chapter, nil
} }
func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.ChapterRemoveInput) (models.Chapter, error) { func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.ChapterRemoveInput) (*models.Chapter, error) {
chapter, err := chapters.FindID(input.ID) chapter, err := chapters.FindID(input.ID)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Chapter not found") return nil, errors.New("Chapter not found")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.remove") { if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.remove") {
return models.Chapter{}, errors.New("Unauthorized") return nil, errors.New("Unauthorized")
} }
chapter, err = chapters.Remove(chapter) chapter, err = chapters.Remove(chapter)
if err != nil { if err != nil {
return models.Chapter{}, errors.New("Failed to remove chapter: " + err.Error()) return nil, errors.New("Failed to remove chapter: " + err.Error())
}
err = comments.RemoveChapter(chapter)
if err != nil {
return nil, errors.New("Chapter was removed, but comment removal failed: " + err.Error())
} }
go func() { go func() {
@@ -159,5 +173,5 @@ func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.Chapte
changes.Submit("Chapter", "remove", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter) changes.Submit("Chapter", "remove", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter)
}() }()
return chapter, nil return &chapter, nil
} }
+43 -33
View File
@@ -16,13 +16,23 @@ import (
// Queries // Queries
func (r *resolver) Character(ctx context.Context, id *string, nick *string) (models.Character, error) { func (r *resolver) Character(ctx context.Context, id *string, nick *string) (*models.Character, error) {
if id != nil { if id != nil {
return characters.FindID(*id) character, err := characters.FindID(*id)
if err != nil {
return nil, err
}
return &character, nil
} else if nick != nil { } else if nick != nil {
return characters.FindNick(*nick) character, err := characters.FindNick(*nick)
if err != nil {
return nil, err
}
return &character, nil
} else { } else {
return models.Character{}, errors.New("You must specify either an ID or a nick") return nil, errors.New("You must specify either an ID or a nick")
} }
} }
@@ -32,13 +42,13 @@ func (r *resolver) Characters(ctx context.Context, filter *characters.Filter) ([
// Mutations // Mutations
func (r *mutationResolver) AddCharacter(ctx context.Context, input input.CharacterAddInput) (models.Character, error) { func (r *mutationResolver) AddCharacter(ctx context.Context, input input.CharacterAddInput) (*models.Character, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "character.add") { if !token.Permitted("member", "character.add") {
return models.Character{}, errors.New("You are not permitted to add characters") return nil, errors.New("You are not permitted to add characters")
} }
if len(input.Name) < 2 || len(input.Nick) < 2 { if len(input.Name) < 2 || len(input.Nick) < 2 {
return models.Character{}, errors.New("You need to provide a name and a nick (min length: 2)") return nil, errors.New("You need to provide a name and a nick (min length: 2)")
} }
shortName := "" shortName := ""
@@ -56,7 +66,7 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact
author := token.UserID author := token.UserID
if input.Author != nil && *input.Author != author { if input.Author != nil && *input.Author != author {
if !token.Permitted("character.add") { if !token.Permitted("character.add") {
return models.Character{}, errors.New("You are only permitted to add your own characters") return nil, errors.New("You are only permitted to add your own characters")
} }
author = *input.Author author = *input.Author
@@ -66,109 +76,109 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact
character, err := characters.Add(input.Nick, input.Name, shortName, author, description) character, err := characters.Add(input.Nick, input.Name, shortName, author, description)
if err != nil { if err != nil {
return models.Character{}, errors.New("Adding character failed: " + err.Error()) return nil, errors.New("Adding character failed: " + err.Error())
} }
go changes.Submit("Character", "add", token.UserID, true, changekeys.Listed(character), character) go changes.Submit("Character", "add", token.UserID, true, changekeys.Listed(character), character)
return character, nil return &character, nil
} }
func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.CharacterNickInput) (models.Character, error) { func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.CharacterNickInput) (*models.Character, error) {
character, err := characters.FindID(input.ID) character, err := characters.FindID(input.ID)
if err != nil { if err != nil {
return models.Character{}, errors.New("Character not found") return nil, errors.New("Character not found")
} }
if len(input.Nick) < 2 { if len(input.Nick) < 2 {
return models.Character{}, errors.New("You need to provide a valid nick (min length: 2)") return nil, errors.New("You need to provide a valid nick (min length: 2)")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") { if !token.PermittedUser(character.Author, "member", "character.edit") {
return models.Character{}, errors.New("You are not permitted to edit this character") return nil, errors.New("You are not permitted to edit this character")
} }
logs.ScheduleFullUpdate() logs.ScheduleFullUpdate()
character, err = characters.AddNick(character, input.Nick) character, err = characters.AddNick(character, input.Nick)
if err != nil { if err != nil {
return models.Character{}, errors.New("Failed to add nick: " + err.Error()) return nil, errors.New("Failed to add nick: " + err.Error())
} }
go logs.ScheduleFullUpdate() go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return character, nil return &character, nil
} }
func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.CharacterNickInput) (models.Character, error) { func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.CharacterNickInput) (*models.Character, error) {
character, err := characters.FindID(input.ID) character, err := characters.FindID(input.ID)
if err != nil { if err != nil {
return models.Character{}, errors.New("Character not found") return nil, errors.New("Character not found")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") { if !token.PermittedUser(character.Author, "member", "character.edit") {
return models.Character{}, errors.New("You are not permitted to edit this character") return nil, errors.New("You are not permitted to edit this character")
} }
character, err = characters.RemoveNick(character, input.Nick) character, err = characters.RemoveNick(character, input.Nick)
if err != nil { if err != nil {
return models.Character{}, errors.New("Failed to remove nick: " + err.Error()) return nil, errors.New("Failed to remove nick: " + err.Error())
} }
go logs.ScheduleFullUpdate() go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return character, nil return &character, nil
} }
func (r *mutationResolver) EditCharacter(ctx context.Context, input input.CharacterEditInput) (models.Character, error) { func (r *mutationResolver) EditCharacter(ctx context.Context, input input.CharacterEditInput) (*models.Character, error) {
character, err := characters.FindID(input.ID) character, err := characters.FindID(input.ID)
if err != nil { if err != nil {
return models.Character{}, errors.New("Character not found") return nil, errors.New("Character not found")
} }
if input.Name != nil && len(*input.Name) < 2 { if input.Name != nil && len(*input.Name) < 2 {
return models.Character{}, errors.New("You need to provide a valid name (min length: 2)") return nil, errors.New("You need to provide a valid name (min length: 2)")
} }
if input.ShortName != nil && len(*input.ShortName) < 2 { if input.ShortName != nil && len(*input.ShortName) < 2 {
return models.Character{}, errors.New("You need to provide a valid short name (min length: 2)") return nil, errors.New("You need to provide a valid short name (min length: 2)")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") { if !token.PermittedUser(character.Author, "member", "character.edit") {
return models.Character{}, errors.New("You are not permitted to edit this character") return nil, errors.New("You are not permitted to edit this character")
} }
character, err = characters.Edit(character, input.Name, input.ShortName, input.Description) character, err = characters.Edit(character, input.Name, input.ShortName, input.Description)
if err != nil { if err != nil {
return models.Character{}, errors.New("Failed to edit character: " + err.Error()) return nil, errors.New("Failed to edit character: " + err.Error())
} }
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return character, nil return &character, nil
} }
func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.CharacterRemoveInput) (models.Character, error) { func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.CharacterRemoveInput) (*models.Character, error) {
character, err := characters.FindID(input.ID) character, err := characters.FindID(input.ID)
if err != nil { if err != nil {
return models.Character{}, errors.New("Character not found") return nil, errors.New("Character not found")
} }
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.remove") { if !token.PermittedUser(character.Author, "member", "character.remove") {
return models.Character{}, errors.New("You are not permitted to remove this character") return nil, errors.New("You are not permitted to remove this character")
} }
character, err = characters.Remove(character) character, err = characters.Remove(character)
if err != nil { if err != nil {
return models.Character{}, errors.New("Failed to remove character: " + err.Error()) return nil, errors.New("Failed to remove character: " + err.Error())
} }
go changes.Submit("Character", "remove", token.UserID, true, changekeys.Listed(character), character) go changes.Submit("Character", "remove", token.UserID, true, changekeys.Listed(character), character)
return character, nil return &character, nil
} }
+180
View File
@@ -0,0 +1,180 @@
package queries
import (
"context"
"errors"
"log"
"time"
"git.aiterp.net/rpdata/api/models/stories"
"git.aiterp.net/rpdata/api/models/characters"
"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/comments"
"git.aiterp.net/rpdata/api/models"
)
// Queries
func (r *resolver) Comment(ctx context.Context, id string) (*models.Comment, error) {
comment, err := comments.Find(id)
if err != nil {
return nil, err
}
return &comment, nil
}
// Mutations
func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAddInput) (*models.Comment, error) {
chapter, err := chapters.FindID(input.ChapterID)
if err != nil {
return nil, errors.New("Chapter not found")
}
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.edit") {
return nil, errors.New("Unauthorized")
}
if !chapter.CanComment() {
return nil, errors.New("Comments are disabled or locked")
}
var characterPtr *models.Character
if input.CharacterID != nil {
character, err := characters.FindID(*input.CharacterID)
if err != nil {
return nil, errors.New("Character not found")
} else if character.Author != token.UserID {
return nil, errors.New("That is not your character")
}
characterPtr = &character
}
fictionalDate := time.Time{}
if input.FictionalDate != nil {
fictionalDate = *input.FictionalDate
}
subject := ""
if input.Subject != nil {
subject = *input.Subject
}
comment, err := comments.Add(chapter, subject, token.UserID, input.Source, input.CharacterName, characterPtr, time.Now(), fictionalDate)
if err != nil {
return nil, errors.New("Failed to add comment: " + err.Error())
}
go func() {
story, err := stories.FindID(chapter.StoryID)
if err != nil {
log.Println("WARNING: Couldn't log comment change:", err)
return
}
changes.Submit("Comment", "add", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story)
}()
return &comment, nil
}
func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentEditInput) (*models.Comment, error) {
comment, err := comments.Find(input.CommentID)
if err != nil {
return nil, errors.New("Comment not found")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(comment.Author, "member", "story.edit") {
return nil, errors.New("You cannot edit this comment")
}
chapter, err := chapters.FindID(comment.ChapterID)
if err != nil {
return nil, errors.New("Comment's chapter not found")
}
if !chapter.CanComment() {
return nil, errors.New("Comments are disabled or locked")
}
if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true {
input.FictionalDate = &time.Time{}
}
if input.CharacterID != nil && *input.CharacterID != "" {
character, err := characters.FindID(*input.CharacterID)
if err != nil {
return nil, errors.New("Character not found")
} else if character.Author != token.UserID {
return nil, errors.New("That is not your character")
}
}
comment, err = comments.Edit(comment, input.Source, input.CharacterName, input.CharacterID, input.Subject, input.FictionalDate)
if err != nil {
return nil, errors.New("Could not post comment: " + err.Error())
}
go func() {
story, err := stories.FindID(chapter.StoryID)
if err != nil {
log.Println("WARNING: Couldn't log comment change:", err)
return
}
changes.Submit("Comment", "edit", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story)
}()
return &comment, nil
}
func (r *mutationResolver) RemoveComment(ctx context.Context, input input.CommentRemoveInput) (*models.Comment, error) {
comment, err := comments.Find(input.CommentID)
if err != nil {
return nil, errors.New("Comment not found")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(comment.Author, "member", "story.edit") {
return nil, errors.New("You cannot remove this comment")
}
chapter, err := chapters.FindID(comment.ChapterID)
if err != nil {
return nil, errors.New("Comment's chapter not found")
}
if !chapter.CanComment() {
return nil, errors.New("Comments are disabled or locked")
}
err = comments.Remove(comment)
if err != nil {
return nil, errors.New("Failed to remove comment: " + err.Error())
}
go func() {
story, err := stories.FindID(chapter.StoryID)
if err != nil {
log.Println("WARNING: Couldn't log comment change:", err)
return
}
changes.Submit("Comment", "remove", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story)
}()
return &comment, nil
}
+7 -2
View File
@@ -8,8 +8,13 @@ import (
"git.aiterp.net/rpdata/api/models/files" "git.aiterp.net/rpdata/api/models/files"
) )
func (r *resolver) File(ctx context.Context, id string) (models.File, error) { func (r *resolver) File(ctx context.Context, id string) (*models.File, error) {
return files.FindID(id) file, err := files.FindID(id)
if err != nil {
return nil, err
}
return &file, nil
} }
func (r *resolver) Files(ctx context.Context, filter *files.Filter) ([]models.File, error) { func (r *resolver) Files(ctx context.Context, filter *files.Filter) ([]models.File, error) {
+21 -16
View File
@@ -20,8 +20,13 @@ import (
// Queries // Queries
func (r *resolver) Log(ctx context.Context, id string) (models.Log, error) { func (r *resolver) Log(ctx context.Context, id string) (*models.Log, error) {
return logs.FindID(id) log, err := logs.FindID(id)
if err != nil {
return nil, err
}
return &log, nil
} }
func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]models.Log, error) { func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]models.Log, error) {
@@ -56,10 +61,10 @@ func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]models.Log,
// Mutations // Mutations
func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) (models.Log, error) { func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) (*models.Log, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("log.add") { if !token.Authenticated() || !token.Permitted("log.add") {
return models.Log{}, errors.New("You are not permitted to add logs") return nil, errors.New("You are not permitted to add logs")
} }
open := input.Open != nil && *input.Open == true open := input.Open != nil && *input.Open == true
@@ -80,7 +85,7 @@ func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput)
log, err := logs.Add(input.Date, input.Channel, title, event, description, open) log, err := logs.Add(input.Date, input.Channel, title, event, description, open)
if !token.Authenticated() || !token.Permitted("log.add") { if !token.Authenticated() || !token.Permitted("log.add") {
return models.Log{}, errors.New("Failed to create log: " + err.Error()) return nil, errors.New("Failed to create log: " + err.Error())
} }
go func() { go func() {
@@ -94,7 +99,7 @@ func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput)
changes.Submit("Log", "add", token.UserID, true, changekeys.Listed(log), log) changes.Submit("Log", "add", token.UserID, true, changekeys.Listed(log), log)
}() }()
return log, nil return &log, nil
} }
func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportInput) ([]models.Log, error) { func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportInput) ([]models.Log, error) {
@@ -141,44 +146,44 @@ func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportI
return newLogs, nil return newLogs, nil
} }
func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput) (models.Log, error) { func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput) (*models.Log, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("log.edit") { if !token.Authenticated() || !token.Permitted("log.edit") {
return models.Log{}, errors.New("You are not permitted to edit logs") return nil, errors.New("You are not permitted to edit logs")
} }
log, err := logs.FindID(input.ID) log, err := logs.FindID(input.ID)
if err != nil { if err != nil {
return models.Log{}, errors.New("Log not found") return nil, errors.New("Log not found")
} }
log, err = logs.Edit(log, input.Title, input.Event, input.Description, input.Open) log, err = logs.Edit(log, input.Title, input.Event, input.Description, input.Open)
if err != nil { if err != nil {
return models.Log{}, errors.New("Failed to edit log: " + err.Error()) return nil, errors.New("Failed to edit log: " + err.Error())
} }
go changes.Submit("Log", "edit", token.UserID, true, changekeys.Listed(log), log) go changes.Submit("Log", "edit", token.UserID, true, changekeys.Listed(log), log)
return log, nil return &log, nil
} }
func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveInput) (models.Log, error) { func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveInput) (*models.Log, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("log.remove") { if !token.Authenticated() || !token.Permitted("log.remove") {
return models.Log{}, errors.New("You are not permitted to remove logs") return nil, errors.New("You are not permitted to remove logs")
} }
log, err := logs.FindID(input.ID) log, err := logs.FindID(input.ID)
if err != nil { if err != nil {
return models.Log{}, errors.New("Log not found") return nil, errors.New("Log not found")
} }
log, err = logs.Remove(log) log, err = logs.Remove(log)
if err != nil { if err != nil {
return models.Log{}, errors.New("Failed to remove log: " + err.Error()) return nil, errors.New("Failed to remove log: " + err.Error())
} }
go changes.Submit("Log", "remove", token.UserID, true, changekeys.Listed(log), log) go changes.Submit("Log", "remove", token.UserID, true, changekeys.Listed(log), log)
return log, nil return &log, nil
} }
+22 -17
View File
@@ -16,8 +16,13 @@ import (
// Queries // Queries
func (r *resolver) Post(ctx context.Context, id string) (models.Post, error) { func (r *resolver) Post(ctx context.Context, id string) (*models.Post, error) {
return posts.FindID(id) post, err := posts.FindID(id)
if err != nil {
return nil, err
}
return &post, nil
} }
func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Post, error) { func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Post, error) {
@@ -43,37 +48,37 @@ func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Po
// Mutation // Mutation
func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (models.Post, error) { func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (*models.Post, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.add") { if !token.Authenticated() || !token.Permitted("post.add") {
return models.Post{}, errors.New("You are not permitted to edit logs") return nil, errors.New("You are not permitted to edit logs")
} }
log, err := logs.FindID(input.LogID) log, err := logs.FindID(input.LogID)
if err != nil { if err != nil {
return models.Post{}, err return nil, err
} }
post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text) post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
if err != nil { if err != nil {
return models.Post{}, err return nil, err
} }
go logs.UpdateCharacters(log, nil) go logs.UpdateCharacters(log, nil)
go changes.Submit("Post", "add", token.UserID, true, changekeys.Many(log, post), post) go changes.Submit("Post", "add", token.UserID, true, changekeys.Many(log, post), post)
return post, nil return &post, nil
} }
func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) { func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (*models.Post, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.edit") { if !token.Authenticated() || !token.Permitted("post.edit") {
return models.Post{}, errors.New("You are not permitted to edit logs") return nil, errors.New("You are not permitted to edit logs")
} }
post, err := posts.FindID(input.ID) post, err := posts.FindID(input.ID)
if err != nil { if err != nil {
return models.Post{}, errors.New("Post not found") return nil, errors.New("Post not found")
} }
if input.Nick != nil { if input.Nick != nil {
@@ -89,7 +94,7 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp
post, err = posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text) post, err = posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text)
if err != nil { if err != nil {
return models.Post{}, errors.New("Adding post failed: " + err.Error()) return nil, errors.New("Adding post failed: " + err.Error())
} }
go func() { go func() {
@@ -101,7 +106,7 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp
changes.Submit("Post", "edit", token.UserID, true, changekeys.Many(log, post), post) changes.Submit("Post", "edit", token.UserID, true, changekeys.Many(log, post), post)
}() }()
return post, nil return &post, nil
} }
func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInput) ([]models.Post, error) { func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInput) ([]models.Post, error) {
@@ -132,20 +137,20 @@ func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInp
return posts, nil return posts, nil
} }
func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (models.Post, error) { func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (*models.Post, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.remove") { if !token.Authenticated() || !token.Permitted("post.remove") {
return models.Post{}, errors.New("You are not permitted to edit logs") return nil, errors.New("You are not permitted to edit logs")
} }
post, err := posts.FindID(input.ID) post, err := posts.FindID(input.ID)
if err != nil { if err != nil {
return models.Post{}, errors.New("Post not found (before removing, of course)") return nil, errors.New("Post not found (before removing, of course)")
} }
post, err = posts.Remove(post) post, err = posts.Remove(post)
if err != nil { if err != nil {
return models.Post{}, errors.New("Could not remove post: " + err.Error()) return nil, errors.New("Could not remove post: " + err.Error())
} }
go func() { go func() {
@@ -158,5 +163,5 @@ func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemov
changes.Submit("Post", "remove", token.UserID, true, changekeys.Many(log, post), post) changes.Submit("Post", "remove", token.UserID, true, changekeys.Many(log, post), post)
}() }()
return post, nil return &post, nil
} }
+35 -30
View File
@@ -15,8 +15,13 @@ import (
"git.aiterp.net/rpdata/api/models/stories" "git.aiterp.net/rpdata/api/models/stories"
) )
func (r *resolver) Story(ctx context.Context, id string) (models.Story, error) { func (r *resolver) Story(ctx context.Context, id string) (*models.Story, error) {
return stories.FindID(id) story, err := stories.FindID(id)
if err != nil {
return nil, err
}
return &story, nil
} }
func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]models.Story, error) { func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]models.Story, error) {
@@ -38,16 +43,16 @@ func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]model
// Mutations // Mutations
func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInput) (models.Story, error) { func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInput) (*models.Story, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if token == nil || !token.Permitted("member", "story.add") { if token == nil || !token.Permitted("member", "story.add") {
return models.Story{}, errors.New("Permission denied") return nil, errors.New("Permission denied")
} }
author := token.UserID author := token.UserID
if input.Author != nil && *input.Author != author { if input.Author != nil && *input.Author != author {
if !token.Permitted("story.add") { if !token.Permitted("story.add") {
return models.Story{}, errors.New("You are not permitted to add a story in another author's name") return nil, errors.New("You are not permitted to add a story in another author's name")
} }
author = *input.Author author = *input.Author
@@ -63,80 +68,80 @@ func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInp
story, err := stories.Add(input.Name, author, input.Category, listed, open, input.Tags, time.Now(), fictionalDate) story, err := stories.Add(input.Name, author, input.Category, listed, open, input.Tags, time.Now(), fictionalDate)
if err != nil { if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error()) return nil, errors.New("Failed to add story: " + err.Error())
} }
go changes.Submit("Story", "add", token.UserID, story.Listed, changekeys.Listed(story), story) go changes.Submit("Story", "add", token.UserID, story.Listed, changekeys.Listed(story), story)
return story, nil return &story, nil
} }
func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTagAddInput) (models.Story, error) { func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTagAddInput) (*models.Story, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID) story, err := stories.FindID(input.ID)
if err != nil { if err != nil {
return models.Story{}, errors.New("Story not found") return nil, errors.New("Story not found")
} }
if story.Open { if story.Open {
if !token.Permitted("member") { if !token.Permitted("member") {
return models.Story{}, errors.New("You are not permitted to edit this story") return nil, errors.New("You are not permitted to edit this story")
} }
} else { } else {
if !token.PermittedUser(story.Author, "member", "story.edit") { if !token.PermittedUser(story.Author, "member", "story.edit") {
return models.Story{}, errors.New("You are not permitted to edit this story") return nil, errors.New("You are not permitted to edit this story")
} }
} }
story, err = stories.AddTag(story, input.Tag) story, err = stories.AddTag(story, input.Tag)
if err != nil { if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error()) return nil, errors.New("Failed to add story: " + err.Error())
} }
go changes.Submit("Story", "tag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag) go changes.Submit("Story", "tag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag)
return story, nil return &story, nil
} }
func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.StoryTagRemoveInput) (models.Story, error) { func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.StoryTagRemoveInput) (*models.Story, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID) story, err := stories.FindID(input.ID)
if err != nil { if err != nil {
return models.Story{}, errors.New("Story not found") return nil, errors.New("Story not found")
} }
if story.Open { if story.Open {
if !token.Permitted("member") { if !token.Permitted("member") {
return models.Story{}, errors.New("You are not permitted to edit this story") return nil, errors.New("You are not permitted to edit this story")
} }
} else { } else {
if !token.PermittedUser(story.Author, "member", "story.edit") { if !token.PermittedUser(story.Author, "member", "story.edit") {
return models.Story{}, errors.New("You are not permitted to edit this story") return nil, errors.New("You are not permitted to edit this story")
} }
} }
story, err = stories.RemoveTag(story, input.Tag) story, err = stories.RemoveTag(story, input.Tag)
if err != nil { if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error()) return nil, errors.New("Failed to add story: " + err.Error())
} }
go changes.Submit("Story", "untag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag) go changes.Submit("Story", "untag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag)
return story, nil return &story, nil
} }
func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditInput) (models.Story, error) { func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditInput) (*models.Story, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID) story, err := stories.FindID(input.ID)
if err != nil { if err != nil {
return models.Story{}, errors.New("Story not found") return nil, errors.New("Story not found")
} }
if !token.PermittedUser(story.Author, "member", "story.edit") { if !token.PermittedUser(story.Author, "member", "story.edit") {
return models.Story{}, errors.New("You are not permitted to remove this story") return nil, errors.New("You are not permitted to remove this story")
} }
if input.ClearFictionalDate != nil && *input.ClearFictionalDate { if input.ClearFictionalDate != nil && *input.ClearFictionalDate {
@@ -145,37 +150,37 @@ func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditI
story, err = stories.Edit(story, input.Name, input.Category, input.Listed, input.Open, input.FictionalDate) story, err = stories.Edit(story, input.Name, input.Category, input.Listed, input.Open, input.FictionalDate)
if err != nil { if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error()) return nil, errors.New("Failed to add story: " + err.Error())
} }
go changes.Submit("Story", "edit", token.UserID, story.Listed, changekeys.Listed(story), story) go changes.Submit("Story", "edit", token.UserID, story.Listed, changekeys.Listed(story), story)
return story, nil return &story, nil
} }
func (r *mutationResolver) RemoveStory(ctx context.Context, input input.StoryRemoveInput) (models.Story, error) { func (r *mutationResolver) RemoveStory(ctx context.Context, input input.StoryRemoveInput) (*models.Story, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID) story, err := stories.FindID(input.ID)
if err != nil { if err != nil {
return models.Story{}, errors.New("Story not found") return nil, errors.New("Story not found")
} }
if !token.PermittedUser(story.Author, "member", "story.remove") { if !token.PermittedUser(story.Author, "member", "story.remove") {
return models.Story{}, errors.New("You are not permitted to remove this story") return nil, errors.New("You are not permitted to remove this story")
} }
story, err = stories.Remove(story) story, err = stories.Remove(story)
if err != nil { if err != nil {
return models.Story{}, err return nil, err
} }
err = chapters.RemoveStory(story) err = chapters.RemoveStory(story)
if err != nil { if err != nil {
return models.Story{}, errors.New("Failed to remove chapters, but story is removed: " + err.Error()) return nil, errors.New("Failed to remove chapters, but story is removed: " + err.Error())
} }
go changes.Submit("Story", "remove", token.UserID, story.Listed, changekeys.Listed(story), story) go changes.Submit("Story", "remove", token.UserID, story.Listed, changekeys.Listed(story), story)
return story, nil return &story, nil
} }
+3 -3
View File
@@ -8,11 +8,11 @@ import (
"git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models"
) )
func (r *resolver) Token(ctx context.Context) (models.Token, error) { func (r *resolver) Token(ctx context.Context) (*models.Token, error) {
token := auth.TokenFromContext(ctx) token := auth.TokenFromContext(ctx)
if !token.Authenticated() { if !token.Authenticated() {
return models.Token{}, errors.New("No (valid) token") return nil, errors.New("No (valid) token")
} }
return *token, nil return token, nil
} }
+14
View File
@@ -41,6 +41,10 @@ type Query {
chapter(id: String!): Chapter! chapter(id: String!): Chapter!
# Find comment by ID
comment(id: String!): Comment!
# Find all distinct tags used in stories # Find all distinct tags used in stories
tags: [Tag!]! tags: [Tag!]!
@@ -97,6 +101,16 @@ type Mutation {
removeChapter(input: ChapterRemoveInput!): Chapter! removeChapter(input: ChapterRemoveInput!): Chapter!
"Add a comment to a chapter."
addComment(input: CommentAddInput!): Comment!
"Edit a comment in a chapter."
editComment(input: CommentEditInput!): Comment!
"Remove a comemnt in a chapter."
removeComment(input: CommentRemoveInput!): Comment!
# Add a new log # Add a new log
addLog(input: LogAddInput!): Log! addLog(input: LogAddInput!): Log!
+1 -1
View File
@@ -27,7 +27,7 @@ type Change {
objects: [ChangeObject!]! objects: [ChangeObject!]!
} }
union ChangeObject = Character | Channel | Log | Post | Story | Tag | Chapter union ChangeObject = Character | Channel | Log | Post | Story | Tag | Chapter | Comment
""" """
A change key is a key associated with a change, used for filtering A change key is a key associated with a change, used for filtering
+38
View File
@@ -20,6 +20,35 @@ type Chapter {
# The date of edit. # The date of edit.
editedDate: Date! editedDate: Date!
"The comment mode."
commentMode: ChapterCommentMode!
"Whether the author has locket comments."
commentsLocked: Boolean!
"A shorthand for checking whether a logged-in user can comment on this chapter."
canComment: Boolean!
"Get all chapter comments."
comments(limit: Int): [Comment!]!
}
"""
The possibles modes of comments for a chapter.
"""
enum ChapterCommentMode {
"Comments are disabled and hidden."
Disabled
"Comments should be shown as typical news article comments."
Article
"Comments should be shown in a compact form suitable for many small messages."
Chat
"Comments are going to be shown as omni-tool messages."
Message
} }
# Input for addChapter mutation # Input for addChapter mutation
@@ -41,6 +70,9 @@ input ChapterAddInput {
# Optionally, assign a fictional date for the chapter # Optionally, assign a fictional date for the chapter
fictionalDate: Date fictionalDate: Date
"The comment mode for the chapter."
commentMode: ChapterCommentMode
} }
# Input for editChapter mutation # Input for editChapter mutation
@@ -59,6 +91,12 @@ input ChapterEditInput {
# Remove the fictional date for a chapter # Remove the fictional date for a chapter
clearFictionalDate: Boolean clearFictionalDate: Boolean
"Set the comment mode for the chapter"
commentMode: ChapterCommentMode
"Set whether the chapter comments are locked"
commentsLocked: Boolean
} }
# Input for moveChapter mutation # Input for moveChapter mutation
+91
View File
@@ -0,0 +1,91 @@
"""
A Comment represents a comment to a story chapter.
"""
type Comment {
"A unique ID of the change."
id: String!
"subject"
subject: String!
"The chapter ID the comment belongs to."
chapterId: String!
"The comment's author."
author: String!
"The displayed name of the character. This may be the same as character.name, but it does not need to be."
characterName: String!
"The character associated with the comment."
character: Character
"The fictional (IC) date of the comment."
fictionalDate: Date
"The date of creation."
createdDate: Date!
"The date of the last edit."
editedDate: Date!
"The markdown source of the comment."
source: String!
}
"""
Input for the addComment mutation
"""
input CommentAddInput {
"What chapter to comment on."
chapterId: String!
"The markdown source of the comment."
source: String!
"The name of the character associated with this comment."
characterName: String!
"Optioanlly, a character ID."
characterId: String
"Optional in-universe date of the comment."
fictionalDate: Date
"Optional subject to add to the comment, only shown in some modes."
subject: String
}
"""
Input for the editComment mutation
"""
input CommentEditInput {
"What comment to edit."
commentId: String!
"Change the markdown source."
source: String
"Change the name of the character associated with this comment."
characterName: String
"Change the character assocaited with this comment."
characterId: String
"Change the optional in-universe date of the comment."
fictionalDate: Date
"Set to clear the fictional date."
clearFictionalDate: Boolean
"Change the optional subject for the comment, only shown in some modes."
subject: String
}
"""
Input for the removeComment mutation
"""
input CommentRemoveInput {
"What comment to edit."
commentId: String!
}
+5 -3
View File
@@ -12,9 +12,11 @@ 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) ([]input.ChangeObject, error) {
objects := obj.Objects() objects := obj.Objects()
results := make([]input.ChangeObject, len(objects)) results := make([]input.ChangeObject, 0, len(objects))
for i := range objects { for _, object := range objects {
results[i] = objects[i] if changeObject, ok := object.(input.ChangeObject); ok {
results = append(results, changeObject)
}
} }
return results, nil return results, nil
+19
View File
@@ -2,9 +2,11 @@ package types
import ( import (
"context" "context"
"errors"
"time" "time"
"git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/comments"
) )
type chapterResolver struct{} type chapterResolver struct{}
@@ -17,5 +19,22 @@ func (r *chapterResolver) FictionalDate(ctx context.Context, chapter *models.Cha
return &chapter.FictionalDate, nil return &chapter.FictionalDate, nil
} }
func (r *chapterResolver) Comments(ctx context.Context, chapter *models.Chapter, limit *int) ([]models.Comment, error) {
limitValue := 0
if limit != nil {
if *limit < 0 {
return nil, errors.New("Limit cannot be negative")
}
limitValue = *limit
}
if !chapter.CommentMode.IsEnabled() {
return nil, nil
}
return comments.ListChapterID(chapter.ID, limitValue)
}
// ChapterResolver is a resolver // ChapterResolver is a resolver
var ChapterResolver chapterResolver var ChapterResolver chapterResolver
+41
View File
@@ -0,0 +1,41 @@
package types
import (
"context"
"errors"
"time"
"git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/models"
)
type commentResolver struct{}
func (r *commentResolver) Character(ctx context.Context, obj *models.Comment) (*models.Character, error) {
if obj.CharacterID == "" {
return nil, nil
}
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
character, err := loader.Character("id", obj.CharacterID)
if err != nil {
return nil, err
}
return &character, nil
}
func (r *commentResolver) FictionalDate(ctx context.Context, obj *models.Comment) (*time.Time, error) {
if obj.FictionalDate.IsZero() {
return nil, nil
}
return &obj.FictionalDate, nil
}
// CommentResolver is a resolver
var CommentResolver commentResolver
-16
View File
@@ -1,16 +0,0 @@
package types
import (
"context"
"git.aiterp.net/rpdata/api/models"
)
type fileResolver struct{}
func (r *fileResolver) Size(ctx context.Context, file *models.File) (int, error) {
return int(file.Size), nil
}
// FileResolver is a resolver
var FileResolver fileResolver
+2 -2
View File
@@ -11,10 +11,10 @@ import (
type logResolver struct{} type logResolver struct{}
func (r *logResolver) Channel(ctx context.Context, log *models.Log) (models.Channel, error) { func (r *logResolver) Channel(ctx context.Context, log *models.Log) (*models.Channel, error) {
loader := loader.FromContext(ctx) loader := loader.FromContext(ctx)
if loader == nil { if loader == nil {
return models.Channel{}, errors.New("no loader") return nil, errors.New("no loader")
} }
return loader.Channel("name", log.ChannelName) return loader.Channel("name", log.ChannelName)
+7 -2
View File
@@ -9,8 +9,13 @@ import (
type tokenResolver struct{} type tokenResolver struct{}
func (r *tokenResolver) User(ctx context.Context, token *models.Token) (models.User, error) { func (r *tokenResolver) User(ctx context.Context, token *models.Token) (*models.User, error) {
return users.Find(token.UserID) user, err := users.Find(token.UserID)
if err != nil {
return nil, err
}
return &user, nil
} }
// TokenResolver is a resolver // TokenResolver is a resolver
+5 -5
View File
@@ -11,13 +11,13 @@ import (
) )
// Channel gets a character by key // Channel gets a character by key
func (loader *Loader) Channel(key, value string) (models.Channel, error) { func (loader *Loader) Channel(key, value string) (*models.Channel, error) {
if !strings.HasPrefix(key, "Channel.") { if !strings.HasPrefix(key, "Channel.") {
key = "Channel." + key key = "Channel." + key
} }
if loader.loaders[key] == nil { if loader.loaders[key] == nil {
return models.Channel{}, errors.New("unsupported key") return nil, errors.New("unsupported key")
} }
loader.loadPrimed(key) loader.loadPrimed(key)
@@ -25,15 +25,15 @@ func (loader *Loader) Channel(key, value string) (models.Channel, error) {
thunk := loader.loaders[key].Load(loader.ctx, dataloader.StringKey(value)) thunk := loader.loaders[key].Load(loader.ctx, dataloader.StringKey(value))
res, err := thunk() res, err := thunk()
if err != nil { if err != nil {
return models.Channel{}, err return nil, err
} }
channel, ok := res.(models.Channel) channel, ok := res.(models.Channel)
if !ok { if !ok {
return channel, errors.New("incorrect type") return nil, errors.New("incorrect type")
} }
return channel, nil return &channel, nil
} }
// PrimeChannels primes channels for loading along with the first one. // PrimeChannels primes channels for loading along with the first one.
+3 -1
View File
@@ -24,12 +24,14 @@ const (
ChangeModelTag ChangeModel = "Tag" ChangeModelTag ChangeModel = "Tag"
// ChangeModelChapter is a value of ChangeModel // ChangeModelChapter is a value of ChangeModel
ChangeModelChapter ChangeModel = "Chapter" ChangeModelChapter ChangeModel = "Chapter"
// ChangeModelComment is a value of ChangeModel
ChangeModelComment ChangeModel = "Comment"
) )
// IsValid returns true if the underlying string is one of the correct values. // IsValid returns true if the underlying string is one of the correct values.
func (e ChangeModel) IsValid() bool { func (e ChangeModel) IsValid() bool {
switch e { switch e {
case ChangeModelCharacter, ChangeModelChannel, ChangeModelLog, ChangeModelPost, ChangeModelStory, ChangeModelTag, ChangeModelChapter: case ChangeModelCharacter, ChangeModelChannel, ChangeModelLog, ChangeModelPost, ChangeModelStory, ChangeModelTag, ChangeModelChapter, ChangeModelComment:
return true return true
} }
return false return false
+5 -1
View File
@@ -19,6 +19,7 @@ type Change struct {
Stories []Story `bson:"stories"` Stories []Story `bson:"stories"`
Tags []Tag `bson:"tags"` Tags []Tag `bson:"tags"`
Chapters []Chapter `bson:"chapters"` Chapters []Chapter `bson:"chapters"`
Comments []Comment `bson:"comments"`
} }
// ChangeKey is a key for a change that can be used when subscribing to them. // ChangeKey is a key for a change that can be used when subscribing to them.
@@ -29,7 +30,7 @@ type ChangeKey struct {
// Objects makes a combined, mixed array of all the models stored in this change. // Objects makes a combined, mixed array of all the models stored in this change.
func (change *Change) Objects() []interface{} { func (change *Change) Objects() []interface{} {
data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Channels)+len(change.Posts)+len(change.Stories)+len(change.Tags)+len(change.Chapters)) data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Channels)+len(change.Posts)+len(change.Stories)+len(change.Tags)+len(change.Chapters)+len(change.Comments))
for _, log := range change.Logs { for _, log := range change.Logs {
data = append(data, log) data = append(data, log)
@@ -52,6 +53,9 @@ func (change *Change) Objects() []interface{} {
for _, chapter := range change.Chapters { for _, chapter := range change.Chapters {
data = append(data, chapter) data = append(data, chapter)
} }
for _, comment := range change.Comments {
data = append(data, comment)
}
return data return data
} }
+2
View File
@@ -22,6 +22,7 @@ func TestMany(t *testing.T) {
}, },
}, },
models.Post{ID: "P5"}, models.Post{ID: "P5"},
models.Comment{ID: "SCC1234"},
} }
expectations := []models.ChangeKey{ expectations := []models.ChangeKey{
@@ -33,6 +34,7 @@ func TestMany(t *testing.T) {
{Model: "Character", ID: "C17"}, {Model: "Character", ID: "C17"},
{Model: "Channel", ID: "#Stuff"}, {Model: "Channel", ID: "#Stuff"},
{Model: "Post", ID: "P5"}, {Model: "Post", ID: "P5"},
{Model: "Comment", ID: "SCC1234"},
} }
results := changekeys.Many(data...) results := changekeys.Many(data...)
+5
View File
@@ -28,6 +28,11 @@ func TestOne(t *testing.T) {
models.Log{ID: "2018-10-23_210303325_RedrockAgency", ShortID: "L807"}, models.Log{ID: "2018-10-23_210303325_RedrockAgency", ShortID: "L807"},
models.ChangeKey{Model: "Log", ID: "2018-10-23_210303325_RedrockAgency"}, models.ChangeKey{Model: "Log", ID: "2018-10-23_210303325_RedrockAgency"},
}, },
{
"Comment_SCC1234",
models.Comment{ID: "SCC1234"},
models.ChangeKey{Model: "Comment", ID: "SCC1234"},
},
} }
for _, row := range table { for _, row := range table {
+4
View File
@@ -81,6 +81,10 @@ func Submit(model, op, author string, listed bool, keys []models.ChangeKey, obje
change.Chapters = append(change.Chapters, object) change.Chapters = append(change.Chapters, object)
case []models.Chapter: case []models.Chapter:
change.Chapters = append(change.Chapters, object...) change.Chapters = append(change.Chapters, object...)
case models.Comment:
change.Comments = append(change.Comments, object)
case []models.Comment:
change.Comments = append(change.Comments, object...)
default: default:
log.Printf("Warning: unrecognized object in change: %#+v", object) log.Printf("Warning: unrecognized object in change: %#+v", object)
} }
+7 -5
View File
@@ -12,13 +12,13 @@ var subList []*subscription
type subscription struct { type subscription struct {
Keys map[string]bool Keys map[string]bool
Channel chan<- models.Change Channel chan<- *models.Change
WildCard bool WildCard bool
} }
// Subscribe subscribes to all changes. // Subscribe subscribes to all changes.
func Subscribe(ctx context.Context, keys []models.ChangeKey, wildcard bool) <-chan models.Change { func Subscribe(ctx context.Context, keys []models.ChangeKey, wildcard bool) <-chan *models.Change {
channel := make(chan models.Change, 64) channel := make(chan *models.Change, 64)
sub := &subscription{ sub := &subscription{
Keys: make(map[string]bool, len(keys)), Keys: make(map[string]bool, len(keys)),
Channel: channel, Channel: channel,
@@ -58,16 +58,18 @@ func pushToSubscribers(change models.Change) {
subMutex.Lock() subMutex.Lock()
SubLoop: SubLoop:
for _, sub := range subList { for _, sub := range subList {
changeCopy := change
if sub.WildCard && change.Listed { if sub.WildCard && change.Listed {
select { select {
case sub.Channel <- change: case sub.Channel <- &changeCopy:
default: default:
} }
} else { } else {
for _, key := range keys { for _, key := range keys {
if sub.Keys[key] { if sub.Keys[key] {
select { select {
case sub.Channel <- change: case sub.Channel <- &changeCopy:
default: default:
} }
+6
View File
@@ -8,3 +8,9 @@ type Channel struct {
EventName string `bson:"eventName,omitempty"` EventName string `bson:"eventName,omitempty"`
LocationName string `bson:"locationName,omitempty"` LocationName string `bson:"locationName,omitempty"`
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Channel) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+57
View File
@@ -0,0 +1,57 @@
package models
import (
"fmt"
"io"
)
// ChapterCommentMode represents the kind of tags.
type ChapterCommentMode string
const (
// ChapterCommentModeDisabled is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeDisabled ChapterCommentMode = "Disabled"
// ChapterCommentModeArticle is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeArticle ChapterCommentMode = "Article"
// ChapterCommentModeChat is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeChat ChapterCommentMode = "Chat"
// ChapterCommentModeMessage is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeMessage ChapterCommentMode = "Message"
)
// UnmarshalGQL unmarshals
func (e *ChapterCommentMode) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = ChapterCommentMode(str)
switch *e {
case ChapterCommentModeDisabled, ChapterCommentModeArticle, ChapterCommentModeChat, ChapterCommentModeMessage:
return nil
default:
return fmt.Errorf("%s is not a valid ChapterCommentMode", str)
}
}
// IsEnabled returns true if comments are enabled.
func (e ChapterCommentMode) IsEnabled() bool {
return e != ChapterCommentModeDisabled && len(e) != 0
}
// MarshalGQL turns it into a JSON string
func (e ChapterCommentMode) MarshalGQL(w io.Writer) {
// Backwards compatibility: Empty value means disabled.
if len(e) == 0 {
w.Write(disabledChapterCommentModeBytes)
return
}
fmt.Fprint(w, "\""+string(e)+"\"")
}
var disabledChapterCommentModeBytes = []byte(`"Disabled"`)
+13
View File
@@ -12,4 +12,17 @@ type Chapter struct {
CreatedDate time.Time `bson:"createdDate"` CreatedDate time.Time `bson:"createdDate"`
FictionalDate time.Time `bson:"fictionalDate,omitempty"` FictionalDate time.Time `bson:"fictionalDate,omitempty"`
EditedDate time.Time `bson:"editedDate"` EditedDate time.Time `bson:"editedDate"`
CommentMode ChapterCommentMode `bson:"commentMode"`
CommentsLocked bool `bson:"commentsLocked"`
}
// CanComment returns true if the chapter can be commented to.
func (chapter *Chapter) CanComment() bool {
return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled()
}
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Chapter) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
} }
+3 -1
View File
@@ -8,7 +8,7 @@ import (
) )
// Add adds a new chapter. // Add adds a new chapter.
func Add(story models.Story, title, author, source string, createdDate time.Time, finctionalDate *time.Time) (models.Chapter, error) { func Add(story models.Story, title, author, source string, createdDate time.Time, finctionalDate *time.Time, commentMode models.ChapterCommentMode) (models.Chapter, error) {
chapter := models.Chapter{ chapter := models.Chapter{
ID: makeChapterID(), ID: makeChapterID(),
StoryID: story.ID, StoryID: story.ID,
@@ -17,6 +17,8 @@ func Add(story models.Story, title, author, source string, createdDate time.Time
Source: source, Source: source,
CreatedDate: createdDate, CreatedDate: createdDate,
EditedDate: createdDate, EditedDate: createdDate,
CommentMode: commentMode,
CommentsLocked: false,
} }
if finctionalDate != nil { if finctionalDate != nil {
+9 -1
View File
@@ -9,7 +9,7 @@ import (
// Edit edits a chapter, and updates EditedDate. While many Edit functions cheat if there's nothing to // Edit edits a chapter, and updates EditedDate. While many Edit functions cheat if there's nothing to
// change, this functill will due to EditedDate. // change, this functill will due to EditedDate.
func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Time) (models.Chapter, error) { func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Time, commentMode *models.ChapterCommentMode, commentsLocked *bool) (models.Chapter, error) {
now := time.Now() now := time.Now()
changes := bson.M{"editedDate": now} changes := bson.M{"editedDate": now}
@@ -28,6 +28,14 @@ func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Tim
changes["fictionalDate"] = *fictionalDate changes["fictionalDate"] = *fictionalDate
edited.FictionalDate = *fictionalDate edited.FictionalDate = *fictionalDate
} }
if commentMode != nil && *commentMode != chapter.CommentMode {
changes["commentMode"] = *commentMode
edited.CommentMode = *commentMode
}
if commentsLocked != nil && *commentsLocked != chapter.CommentsLocked {
changes["commentsLocked"] = *commentsLocked
edited.CommentsLocked = *commentsLocked
}
err := collection.UpdateId(chapter.ID, bson.M{"$set": changes}) err := collection.UpdateId(chapter.ID, bson.M{"$set": changes})
if err != nil { if err != nil {
+6
View File
@@ -29,3 +29,9 @@ func (character *Character) HasNick(nick string) bool {
return false return false
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Character) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+23
View File
@@ -0,0 +1,23 @@
package models
import "time"
// A Comment is a comment on a chapter.
type Comment struct {
ID string `bson:"_id"`
ChapterID string `bson:"chapterId"`
Subject string `bson:"subject"`
Author string `bson:"author"`
CharacterName string `bson:"characterName"`
CharacterID string `bson:"characterId"`
FictionalDate time.Time `bson:"fictionalDate"`
CreatedDate time.Time `bson:"createdDate"`
EditedDate time.Time `bson:"editeddDate"`
Source string `bson:"source"`
}
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Comment) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+35
View File
@@ -0,0 +1,35 @@
package comments
import (
"time"
"git.aiterp.net/rpdata/api/models"
)
// Add adds a comment.
func Add(chapter models.Chapter, subject, author, source, characterName string, character *models.Character, createdDate time.Time, fictionalDate time.Time) (models.Comment, error) {
characterID := ""
if character != nil {
characterID = character.ID
}
comment := models.Comment{
ID: makeCommentID(),
ChapterID: chapter.ID,
Subject: subject,
Author: author,
CharacterName: characterName,
CharacterID: characterID,
FictionalDate: fictionalDate,
CreatedDate: createdDate,
EditedDate: createdDate,
Source: source,
}
err := collection.Insert(comment)
if err != nil {
return models.Comment{}, err
}
return comment, nil
}
+66
View File
@@ -0,0 +1,66 @@
package comments
import (
"crypto/rand"
"encoding/binary"
"strconv"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo"
)
var collection *mgo.Collection
func find(query interface{}) (models.Comment, error) {
comment := models.Comment{}
err := collection.Find(query).One(&comment)
return comment, err
}
func list(query interface{}, limit int) ([]models.Comment, error) {
allocSize := 32
if limit >= 0 {
allocSize = limit
} else {
limit = 0
}
comments := make([]models.Comment, 0, allocSize)
err := collection.Find(query).Sort("createdDate").Limit(limit).All(&comments)
if err != nil {
return nil, err
}
return comments, nil
}
func makeCommentID() string {
result := "SCC"
offset := 0
data := make([]byte, 48)
rand.Read(data)
for len(result) < 32 {
result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36)
offset += 8
if offset >= 48 {
rand.Read(data)
offset = 0
}
}
return result[:32]
}
func init() {
store.HandleInit(func(db *mgo.Database) {
collection = db.C("story.comments")
collection.EnsureIndexKey("chapterId")
collection.EnsureIndexKey("author")
collection.EnsureIndexKey("createdDate")
})
}
+44
View File
@@ -0,0 +1,44 @@
package comments
import (
"time"
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo/bson"
)
// Edit edits a comment.
func Edit(comment models.Comment, source, characterName, characterID, subject *string, fictionalDate *time.Time) (models.Comment, error) {
changes := make(bson.M, 6)
comment.EditedDate = time.Now()
changes["editedDate"] = comment.EditedDate
if source != nil {
comment.Source = *source
changes["source"] = *source
}
if characterName != nil {
comment.CharacterName = *characterName
changes["characterName"] = *characterName
}
if characterID != nil {
comment.CharacterID = *characterID
changes["characterId"] = *characterID
}
if subject != nil {
comment.Subject = *subject
changes["subject"] = *subject
}
if fictionalDate != nil {
comment.FictionalDate = *fictionalDate
changes["fictionalDate"] = *fictionalDate
}
err := collection.UpdateId(comment.ID, bson.M{"$set": changes})
if err != nil {
return models.Comment{}, err
}
return comment, nil
}
+11
View File
@@ -0,0 +1,11 @@
package comments
import (
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo/bson"
)
// Find finds a comment by ID.
func Find(id string) (models.Comment, error) {
return find(bson.M{"_id": id})
}
+11
View File
@@ -0,0 +1,11 @@
package comments
import (
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo/bson"
)
// ListChapterID lists all comments by chapter-ID
func ListChapterID(chapterID string, limit int) ([]models.Comment, error) {
return list(bson.M{"chapterId": chapterID}, limit)
}
+17
View File
@@ -0,0 +1,17 @@
package comments
import (
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo/bson"
)
// Remove removes one comment.
func Remove(comment models.Comment) error {
return collection.RemoveId(comment.ID)
}
// RemoveChapter removes all comments for the given chapter.
func RemoveChapter(chapter models.Chapter) error {
_, err := collection.RemoveAll(bson.M{"chapterId": chapter.ID})
return err
}
+6
View File
@@ -14,3 +14,9 @@ type File struct {
Author string `bson:"author" json:"author"` Author string `bson:"author" json:"author"`
URL string `bson:"url,omitempty" json:"url,omitempty"` URL string `bson:"url,omitempty" json:"url,omitempty"`
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*File) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+6
View File
@@ -14,3 +14,9 @@ type Log struct {
Open bool `bson:"open"` Open bool `bson:"open"`
CharacterIDs []string `bson:"characterIds"` CharacterIDs []string `bson:"characterIds"`
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Log) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+6
View File
@@ -12,3 +12,9 @@ type Post struct {
Text string `bson:"text"` Text string `bson:"text"`
Position int `bson:"position"` Position int `bson:"position"`
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Post) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+6
View File
@@ -16,3 +16,9 @@ type Story struct {
FictionalDate time.Time `bson:"fictionalDate,omitempty"` FictionalDate time.Time `bson:"fictionalDate,omitempty"`
UpdatedDate time.Time `bson:"updatedDate"` UpdatedDate time.Time `bson:"updatedDate"`
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Story) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
+6
View File
@@ -10,3 +10,9 @@ type Tag struct {
func (tag *Tag) Equal(other Tag) bool { func (tag *Tag) Equal(other Tag) bool {
return tag.Kind == other.Kind && tag.Name == other.Name return tag.Kind == other.Kind && tag.Name == other.Name
} }
// IsChangeObject is an interface implementation to identify it as a valid
// ChangeObject in GQL.
func (*Tag) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}