Gisle Aune
6 years ago
16 changed files with 482 additions and 14 deletions
-
6.gitignore
-
90Gopkg.lock
-
4Gopkg.toml
-
46cmd/rpdata-server/main.go
-
4graph2/combine.sh
-
19graph2/gqlgen.yml
-
21graph2/graph.go
-
22graph2/queries/character.go
-
6graph2/queries/resolver.go
-
11graph2/queries/tags.go
-
16graph2/schema/root.gql
-
87graph2/schema/types/Character.gql
-
37graph2/schema/types/Tag.gql
-
63model/character/character.go
-
58model/story/tag-kind.go
-
6model/story/tag.go
@ -0,0 +1,46 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"log" |
|||
"net/http" |
|||
"runtime/debug" |
|||
|
|||
"git.aiterp.net/rpdata/api/graph2" |
|||
"git.aiterp.net/rpdata/api/internal/store" |
|||
logModel "git.aiterp.net/rpdata/api/model/log" |
|||
"github.com/99designs/gqlgen/handler" |
|||
) |
|||
|
|||
func main() { |
|||
err := store.Init() |
|||
if err != nil { |
|||
log.Fatalln("Failed to init store:", err) |
|||
} |
|||
|
|||
http.Handle("/", handler.Playground("RPData API", "/query")) |
|||
http.Handle("/query", handler.GraphQL( |
|||
graph2.New(), |
|||
handler.RecoverFunc(func(ctx context.Context, err interface{}) error { |
|||
// send this panic somewhere
|
|||
log.Println(err) |
|||
log.Println(string(debug.Stack())) |
|||
|
|||
return fmt.Errorf("shit") |
|||
}), |
|||
)) |
|||
|
|||
go updateCharacters() |
|||
|
|||
log.Fatal(http.ListenAndServe(":8081", nil)) |
|||
} |
|||
|
|||
func updateCharacters() { |
|||
n, err := logModel.UpdateAllCharacters() |
|||
if err != nil { |
|||
log.Println("Charcter updated stopped:", err) |
|||
} |
|||
|
|||
log.Println("Updated characters on", n, "logs") |
|||
} |
@ -0,0 +1,4 @@ |
|||
#!/bin/sh |
|||
|
|||
echo "# Generated by stiching together the files under schema/ – DO NOT EDIT" >generated.gql |
|||
cat schema/root.gql schema/**/*.gql >>generated.gql |
@ -0,0 +1,19 @@ |
|||
schema: generated.gql |
|||
|
|||
exec: |
|||
filename: generated.go |
|||
package: graph2 |
|||
|
|||
model: |
|||
filename: input/generated.go |
|||
package: input |
|||
|
|||
models: |
|||
Tag: |
|||
model: git.aiterp.net/rpdata/api/model/story.Tag |
|||
TagKind: |
|||
model: git.aiterp.net/rpdata/api/model/story.TagKind |
|||
Character: |
|||
model: git.aiterp.net/rpdata/api/model/character.Character |
|||
CharactersFilter: |
|||
model: git.aiterp.net/rpdata/api/model/character.Filter |
@ -0,0 +1,21 @@ |
|||
package graph2 |
|||
|
|||
import ( |
|||
"git.aiterp.net/rpdata/api/graph2/queries" |
|||
graphql "github.com/99designs/gqlgen/graphql" |
|||
) |
|||
|
|||
//go:generate ./combine.sh
|
|||
//go:generate gorunpkg github.com/99designs/gqlgen -v
|
|||
|
|||
func New() graphql.ExecutableSchema { |
|||
return NewExecutableSchema(Config{ |
|||
Resolvers: &rootResolver{}, |
|||
}) |
|||
} |
|||
|
|||
type rootResolver struct{} |
|||
|
|||
func (r *rootResolver) Query() QueryResolver { |
|||
return &queries.Resolver |
|||
} |
@ -0,0 +1,22 @@ |
|||
package queries |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
|
|||
"git.aiterp.net/rpdata/api/model/character" |
|||
) |
|||
|
|||
func (r *resolver) Character(ctx context.Context, id *string, nick *string) (character.Character, error) { |
|||
if id != nil { |
|||
return character.FindID(*id) |
|||
} else if nick != nil { |
|||
return character.FindNick(*nick) |
|||
} else { |
|||
return character.Character{}, errors.New("You must specify either an ID or a nick") |
|||
} |
|||
} |
|||
|
|||
func (r *resolver) Characters(ctx context.Context, filter *character.Filter) ([]character.Character, error) { |
|||
return character.List(filter) |
|||
} |
@ -0,0 +1,6 @@ |
|||
package queries |
|||
|
|||
type resolver struct{} |
|||
|
|||
// Resolver has all the queries
|
|||
var Resolver resolver |
@ -0,0 +1,11 @@ |
|||
package queries |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"git.aiterp.net/rpdata/api/model/story" |
|||
) |
|||
|
|||
func (r *resolver) Tags(ctx context.Context) ([]story.Tag, error) { |
|||
return story.ListTags() |
|||
} |
@ -0,0 +1,16 @@ |
|||
schema { |
|||
query: Query |
|||
} |
|||
|
|||
type Query { |
|||
# Find character by either an ID or a nick. |
|||
character(id: String, nick: String): Character! |
|||
|
|||
# Find characters |
|||
characters(filter: CharactersFilter): [Character!]! |
|||
|
|||
|
|||
# Find all distinct tags used in stories |
|||
tags: [Tag!]! |
|||
} |
|||
|
@ -0,0 +1,87 @@ |
|||
# A Character represents an RP character |
|||
type Character { |
|||
# A unique identifier for the character |
|||
id: String! |
|||
|
|||
# The primary IRC nick belonging to the character |
|||
nick: String |
|||
|
|||
# All IRC nicks associated with this character |
|||
nicks: [String!]! |
|||
|
|||
# The character's author |
|||
author: String! |
|||
|
|||
# The character's name |
|||
name: String! |
|||
|
|||
# The name to display when space is scarce, usually the first/given name |
|||
shortName: String! |
|||
|
|||
# A short description of the character |
|||
description: String! |
|||
} |
|||
|
|||
# Filter for characters query |
|||
input CharactersFilter { |
|||
# Filter by character IDs |
|||
ids: [String!] |
|||
|
|||
# Filter by nicks |
|||
nicks: [String!] |
|||
|
|||
# Filter by names |
|||
names: [String!] |
|||
|
|||
# Filter by author |
|||
author: String |
|||
|
|||
# Filter by text search matching against the character's description |
|||
search: String |
|||
|
|||
# Filter by whether they've been part of a log. |
|||
logged: Boolean |
|||
} |
|||
|
|||
# Input for adding characters |
|||
input CharacterAddInput { |
|||
# The primary IRC nick name to recognize this character by |
|||
nick: String! |
|||
|
|||
# The character's name |
|||
name: String! |
|||
|
|||
# Optioanl shortened name. By default, it uses the first token in the name |
|||
shortName: String |
|||
|
|||
# Description for a character. |
|||
description: String |
|||
|
|||
# Optioanlly, specify another author. This needs special permissions if it's not |
|||
# your own username |
|||
author: String |
|||
} |
|||
|
|||
# Input for addNick and removeNick mutation |
|||
input CharacterNickInput { |
|||
# The ID of the character |
|||
id: String! |
|||
|
|||
# The nick to add or remove |
|||
nick: String! |
|||
} |
|||
|
|||
input CharacterEditInput { |
|||
# The id for the character to edit |
|||
id: String! |
|||
|
|||
# The full name of the character -- not the salarian full name! |
|||
name: String |
|||
|
|||
# The character's short name that is used in compact lists |
|||
shortName: String |
|||
|
|||
# A short description for the character |
|||
description: String |
|||
} |
|||
|
@ -0,0 +1,37 @@ |
|||
# A Tag is a means of associating stories that have details in common with one another. |
|||
type Tag { |
|||
# The tag's kind |
|||
kind: TagKind! |
|||
|
|||
# The tag's name |
|||
name: String! |
|||
} |
|||
|
|||
# A Tag is a means of associating stories that have details in common with one another. |
|||
input TagInput { |
|||
# The tag's kind |
|||
kind: TagKind! |
|||
|
|||
# The tag's name |
|||
name: String! |
|||
} |
|||
|
|||
# Allowed values for Tag.kind |
|||
enum TagKind { |
|||
# An organization is a catch all term for in-universe corporations, teams, groups, cults, forces, etc... |
|||
Organization |
|||
|
|||
# A character tag should have the exact full name of the character. |
|||
Character |
|||
|
|||
# A location is anything from a planet to an establishment. This may overlap with an organization, and if so, both |
|||
# kinds of tags should be used. |
|||
Location |
|||
|
|||
# An event is a plot or a part of a plot. |
|||
Event |
|||
|
|||
# None of the above, but it does still tie multiple stories together. The new story/chapter format may obsolete this tag kind. |
|||
Series |
|||
} |
|||
|
@ -0,0 +1,58 @@ |
|||
package story |
|||
|
|||
import ( |
|||
"fmt" |
|||
"io" |
|||
) |
|||
|
|||
// TagKind represents the kind of tags.
|
|||
type TagKind string |
|||
|
|||
const ( |
|||
// TagKindOrganization is a tag kind, see GraphQL documentation.
|
|||
TagKindOrganization TagKind = "Organization" |
|||
|
|||
// TagKindCharacter is a tag kind, see GraphQL documentation.
|
|||
TagKindCharacter TagKind = "Character" |
|||
|
|||
// TagKindLocation is a tag kind, see GraphQL documentation.
|
|||
TagKindLocation TagKind = "Location" |
|||
|
|||
// TagKindEvent is a tag kind, see GraphQL documentation.
|
|||
TagKindEvent TagKind = "Event" |
|||
|
|||
// TagKindSeries is a tag kind, see GraphQL documentation.
|
|||
TagKindSeries TagKind = "Series" |
|||
) |
|||
|
|||
// IsValid returns true if the TagKind is one of the constants
|
|||
func (e TagKind) IsValid() bool { |
|||
switch e { |
|||
case TagKindOrganization, TagKindCharacter, TagKindLocation, TagKindEvent, TagKindSeries: |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func (e TagKind) String() string { |
|||
return string(e) |
|||
} |
|||
|
|||
// UnmarshalGQL unmarshals
|
|||
func (e *TagKind) UnmarshalGQL(v interface{}) error { |
|||
str, ok := v.(string) |
|||
if !ok { |
|||
return fmt.Errorf("enums must be strings") |
|||
} |
|||
|
|||
*e = TagKind(str) |
|||
if !e.IsValid() { |
|||
return fmt.Errorf("%s is not a valid TagKind", str) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// MarshalGQL turns it into a JSON string
|
|||
func (e TagKind) MarshalGQL(w io.Writer) { |
|||
fmt.Fprint(w, "\""+e.String(), "\"") |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue