graph2: Added Log type and queries, moved loader out of old graphql pkg.
the build failed

This commit is contained in:
2018-09-15 13:14:05 +02:00
parent 0444911e73
commit 2b05d90f4a
10 changed files with 215 additions and 66 deletions
+77
View File
@@ -0,0 +1,77 @@
package loader
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/model/channel"
"github.com/graph-gophers/dataloader"
)
// Channel gets a character by key
func (loader *Loader) Channel(key, value string) (channel.Channel, error) {
if !strings.HasPrefix(key, "Channel.") {
key = "Channel." + key
}
if loader.loaders[key] == nil {
return channel.Channel{}, errors.New("unsupported key")
}
loader.loadPrimed(key)
thunk := loader.loaders[key].Load(loader.ctx, dataloader.StringKey(value))
res, err := thunk()
if err != nil {
return channel.Channel{}, err
}
channel, ok := res.(channel.Channel)
if !ok {
return channel, errors.New("incorrect type")
}
return channel, nil
}
// PrimeChannels primes channels for loading along with the first one.
func (loader *Loader) PrimeChannels(key string, values ...string) {
if !strings.HasPrefix(key, "Channel.") {
key = "Channel." + key
}
loader.prime(key, values)
}
func channelNameBatch(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
var results []*dataloader.Result
names := keys.Keys()
channels, err := channel.ListNames(names...)
if err != nil {
for range names {
results = append(results, &dataloader.Result{Data: channel.Channel{}, Error: err})
}
return results
}
for _, name := range names {
found := false
for i := range channels {
if channels[i].Name == name {
results = append(results, &dataloader.Result{Data: channels[i]})
found = true
break
}
}
if !found {
results = append(results, &dataloader.Result{Data: channel.Channel{}, Error: err})
}
}
return results
}
+145
View File
@@ -0,0 +1,145 @@
package loader
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/model/character"
"github.com/graph-gophers/dataloader"
)
// Character gets a character by key
func (loader *Loader) Character(key, value string) (character.Character, error) {
if !strings.HasPrefix(key, "Character.") {
key = "Character." + key
}
loader.loadPrimed(key)
if loader.loaders[key] == nil {
return character.Character{}, errors.New("unsupported key")
}
thunk := loader.loaders[key].Load(loader.ctx, dataloader.StringKey(value))
res, err := thunk()
if err != nil {
return character.Character{}, err
}
char, ok := res.(character.Character)
if !ok {
return character.Character{}, errors.New("incorrect type")
}
return char, nil
}
// Characters gets characters by key
func (loader *Loader) Characters(key string, values ...string) ([]character.Character, error) {
if !strings.HasPrefix(key, "Character.") {
key = "Character." + key
}
if loader.loaders[key] == nil {
return nil, errors.New("unsupported key")
}
loader.loadPrimed(key)
thunk := loader.loaders[key].LoadMany(loader.ctx, dataloader.NewKeysFromStrings(values))
res, errs := thunk()
for _, err := range errs {
if err != nil && err != ErrNotFound {
return nil, err
}
}
chars := make([]character.Character, len(res))
for i := range res {
char, ok := res[i].(character.Character)
if !ok {
return nil, errors.New("incorrect type")
}
chars[i] = char
}
return chars, nil
}
// PrimeCharacters adds a set of characters to be loaded if, and only if, characters
// are going to be loaded. This will fill up the cache and speed up subsequent dataloader
// runs.
func (loader *Loader) PrimeCharacters(key string, values ...string) {
if !strings.HasPrefix(key, "Character.") {
key = "Character." + key
}
loader.prime(key, values)
}
func characterIDBatch(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
results := make([]*dataloader.Result, 0, len(keys))
ids := keys.Keys()
characters, err := character.ListIDs(ids...)
if err != nil {
for range ids {
results = append(results, &dataloader.Result{Error: err})
}
return results
}
for _, id := range ids {
found := false
for _, character := range characters {
if character.ID == id {
results = append(results, &dataloader.Result{Data: character})
found = true
break
}
}
if !found {
results = append(results, &dataloader.Result{Data: character.Character{}, Error: ErrNotFound})
}
}
return results
}
func characterNickBatch(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
var results []*dataloader.Result
nicks := keys.Keys()
characters, err := character.ListNicks(nicks...)
if err != nil {
for range nicks {
results = append(results, &dataloader.Result{Error: err})
}
return results
}
for _, nick := range nicks {
found := false
for i := range characters {
if characters[i].HasNick(nick) {
results = append(results, &dataloader.Result{Data: characters[i]})
found = true
break
}
}
if !found {
results = append(results, &dataloader.Result{Data: character.Character{}, Error: err})
}
}
return results
}
+80
View File
@@ -0,0 +1,80 @@
package loader
import (
"context"
"errors"
"sync"
"time"
"github.com/graph-gophers/dataloader"
)
var contextKey struct{}
// ErrNotFound is returned in batches when one or more things weren't found. Usually harmless.
var ErrNotFound = errors.New("not found")
// A Loader is a collection of data loaders and functions to act on them. It's supposed to be
// request-scoped, and will thus keep things cached indefinitely.
type Loader struct {
mutex sync.Mutex
ctx context.Context
loaders map[string]*dataloader.Loader
primedKeys map[string]map[string]bool
}
// New initializes the loader.
func New() *Loader {
return &Loader{
ctx: context.Background(),
loaders: map[string]*dataloader.Loader{
"Character.id": dataloader.NewBatchedLoader(characterIDBatch, dataloader.WithWait(time.Millisecond)),
"Character.nick": dataloader.NewBatchedLoader(characterNickBatch, dataloader.WithWait(time.Millisecond)),
"Channel.name": dataloader.NewBatchedLoader(channelNameBatch, dataloader.WithWait(time.Millisecond)),
},
primedKeys: make(map[string]map[string]bool),
}
}
// FromContext gets the Loader from context.
func FromContext(ctx context.Context) *Loader {
value := ctx.Value(&contextKey)
if value == nil {
return nil
}
return value.(*Loader)
}
// ToContext gets a context with the loader as a value
func (loader *Loader) ToContext(ctx context.Context) context.Context {
loader.ctx = ctx
return context.WithValue(ctx, &contextKey, loader)
}
func (loader *Loader) prime(key string, values []string) {
loader.mutex.Lock()
if loader.primedKeys[key] == nil {
loader.primedKeys[key] = make(map[string]bool)
}
for _, value := range values {
loader.primedKeys[key][value] = true
}
loader.mutex.Unlock()
}
func (loader *Loader) loadPrimed(key string) {
loader.mutex.Lock()
if len(loader.primedKeys[key]) > 0 {
primedKeys := make([]string, 0, len(loader.primedKeys[key]))
for key := range loader.primedKeys[key] {
primedKeys = append(primedKeys, key)
}
loader.loaders[key].LoadMany(loader.ctx, dataloader.NewKeysFromStrings(primedKeys))
loader.primedKeys[key] = nil
}
loader.mutex.Unlock()
}