Verified Commit 87218d79 authored by Daniel Sonck's avatar Daniel Sonck
Browse files

Add initial graphql and bump protocol library for bugfixes

parent 9841c755
# Federation forward declarations, not to be included but for IDE support
scalar _FieldSet
directive @external on FIELD_DEFINITION
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
directive @extends on OBJECT | INTERFACE
\ No newline at end of file
......@@ -3,15 +3,24 @@ module go.touhou.fm/radio/server/playlist
go 1.14
require (
github.com/codeofthrone/goclover v0.0.0-20191119102127-7db53b198ae0 //indirect
github.com/99designs/gqlgen v0.12.2
github.com/fsnotify/fsnotify v1.4.9
github.com/jstemmer/go-junit-report v0.9.1 //indirect
github.com/gin-gonic/gin v1.6.3
github.com/kr/pretty v0.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/ory/go-acc v0.2.6 // indirect
github.com/mitchellh/mapstructure v1.3.2 // indirect
github.com/pelletier/go-toml v1.8.0 // indirect
github.com/pkg/math v0.0.0-20141027224758-f2ed9e40e245
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.5.1 // indirect
github.com/valyala/fastjson v1.5.4
go.touhou.fm/radio/lib/protocol v0.3.7
github.com/vektah/gqlparser/v2 v2.0.1
go.touhou.fm/radio/lib/protocol v0.4.2
go.uber.org/zap v1.15.0
gopkg.in/ini.v1 v1.57.0 // indirect
)
This diff is collapsed.
schema:
- graphql/**/*.graphqls
exec:
filename: internal/graphql/generated/generated.go
package: generated
federation:
filename: internal/graphql/generated/federation.go
package: generated
model:
filename: internal/graphql/model/models_gen.go
package: model
resolver:
layout: follow-schema
dir: internal/graphql/resolver
package: resolver
omit_slice_element_pointers: true
autobind:
- "go.touhou.fm/radio/server/playlist/internal/graphql/model"
models:
ID:
model:
- github.com/99designs/gqlgen/graphql.ID
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Int:
model:
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int32
Int64:
model:
- github.com/99designs/gqlgen/graphql.Int64
Date:
model:
- github.com/99designs/gqlgen/graphql.Time
\ No newline at end of file
extend type Query {
playlist: [PlayListItem!]!
}
\ No newline at end of file
type PlayListItem @key(fields: "file start stop") {
id: Int!
file: String!
start: Int64!
stop: Int64!
}
\ No newline at end of file
type Query {
playlistVersion: String!
}
\ No newline at end of file
scalar Int64
\ No newline at end of file
package daemon
import (
"context"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"github.com/valyala/fastjson"
"go.touhou.fm/radio/lib/protocol"
"go.touhou.fm/radio/lib/protocol/channels"
"go.touhou.fm/radio/server/playlist/internal/deck"
"go.touhou.fm/radio/server/playlist/internal/playlist"
"go.uber.org/zap"
"net"
"net/http"
"sync"
)
......@@ -34,15 +40,24 @@ type daemon struct {
log *zap.Logger
terminate <-chan struct{}
// GraphQL
srv *http.Server
}
func (p *daemon) Run(wg *sync.WaitGroup) {
wg.Add(1)
wg.Add(2)
go func() {
p.run()
p.log.Info("stopped")
wg.Done()
}()
go func() {
if err := p.srv.ListenAndServe(); err != nil {
p.log.Warn("webserver failed", zap.Error(err))
}
wg.Done()
}()
}
func (p *daemon) run() {
......@@ -92,6 +107,9 @@ func (p *daemon) run() {
p.sendToPlayer(v)
}
case <-p.terminate:
if err := p.srv.Shutdown(context.TODO()); err != nil {
p.log.Warn("failed to shutdown server", zap.Error(err))
}
return
}
}
......@@ -159,7 +177,9 @@ func (p *daemon) Loaded(loaded *protocol.Loaded) {
}
func Make(playerChannels channels.Producer, producerChannels channels.Consumer, done <-chan struct{}, connected <-chan struct{}, log *zap.Logger) (Runner, error) {
return &daemon{
r := gin.Default()
d := &daemon{
provider: protocolChannels{
toSend: producerChannels.ConsumerRecv(),
receivedMessages: producerChannels.ConsumerSend(),
......@@ -170,6 +190,28 @@ func Make(playerChannels channels.Producer, producerChannels channels.Consumer,
},
newProvider: connected,
terminate: done,
srv: &http.Server{
Addr: net.JoinHostPort(viper.GetString("graphql.address"), viper.GetString("graphql.port")),
Handler: r,
},
log: log.With(zap.String("component", "playlist")),
}, nil
}
h, err := d.graphqlHandler()
if err != nil {
return nil, err
}
graphiql := playground.Handler("Playlist","")
r.POST("", h)
r.GET("", func(c *gin.Context) {
graphiql.ServeHTTP(c.Writer,c.Request)
})
return d, nil
}
func (p *daemon) Playlist() *playlist.Playlist {
return &p.songs
}
\ No newline at end of file
package daemon
import (
"github.com/99designs/gqlgen/graphql/handler"
"github.com/gin-gonic/gin"
"go.touhou.fm/radio/server/playlist/internal/graphql/generated"
"go.touhou.fm/radio/server/playlist/internal/graphql/resolver"
)
func (p *daemon) graphqlHandler() (gin.HandlerFunc, error) {
h := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolver.Resolver{
PlaylistProvider: p,
}}))
return func(c *gin.Context) {
h.ServeHTTP(c.Writer, c.Request)
}, nil
}
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package generated
import (
"context"
"errors"
"fmt"
"strings"
"github.com/99designs/gqlgen/plugin/federation/fedruntime"
)
func (ec *executionContext) __resolve__service(ctx context.Context) (fedruntime.Service, error) {
if ec.DisableIntrospection {
return fedruntime.Service{}, errors.New("federated introspection disabled")
}
var sdl []string
for _, src := range sources {
if src.BuiltIn {
continue
}
sdl = append(sdl, src.Input)
}
return fedruntime.Service{
SDL: strings.Join(sdl, "\n"),
}, nil
}
func (ec *executionContext) __resolve_entities(ctx context.Context, representations []map[string]interface{}) ([]fedruntime.Entity, error) {
list := []fedruntime.Entity{}
for _, rep := range representations {
typeName, ok := rep["__typename"].(string)
if !ok {
return nil, errors.New("__typename must be an existing string")
}
switch typeName {
case "PlayListItem":
id0, err := ec.unmarshalNString2string(ctx, rep["file"])
if err != nil {
return nil, errors.New(fmt.Sprintf("Field %s undefined in schema.", "file"))
}
id1, err := ec.unmarshalNInt642int64(ctx, rep["start"])
if err != nil {
return nil, errors.New(fmt.Sprintf("Field %s undefined in schema.", "start"))
}
id2, err := ec.unmarshalNInt642int64(ctx, rep["stop"])
if err != nil {
return nil, errors.New(fmt.Sprintf("Field %s undefined in schema.", "stop"))
}
entity, err := ec.resolvers.Entity().FindPlayListItemByFileAndStartAndStop(ctx,
id0, id1, id2)
if err != nil {
return nil, err
}
list = append(list, entity)
default:
return nil, errors.New("unknown type: " + typeName)
}
}
return list, nil
}
This diff is collapsed.
package model
import "go.touhou.fm/radio/lib/protocol"
type PlayListItem struct {
Id int
*CueTrack
}
func (PlayListItem) IsEntity() {}
type CueTrack protocol.Song
func (CueTrack) IsEntity() {}
\ No newline at end of file
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package model
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
import (
"context"
"fmt"
"go.touhou.fm/radio/lib/protocol"
"go.touhou.fm/radio/server/playlist/internal/graphql/generated"
"go.touhou.fm/radio/server/playlist/internal/graphql/model"
)
func (r *entityResolver) FindPlayListItemByFileAndStartAndStop(ctx context.Context, file string, start int64, stop int64) (*model.PlayListItem, error) {
playlist := r.Playlist()
count := 0
song := protocol.Song{File: file, Start: start, Stop: stop}
for item := playlist.Start(); item != nil; item = item.Next() {
if song == item.Song {
return &model.PlayListItem{Id: count, CueTrack: (*model.CueTrack)(&item.Song)}, nil
}
count++
}
return nil, fmt.Errorf("no playlist item found by %v", song)
}
// Entity returns generated.EntityResolver implementation.
func (r *Resolver) Entity() generated.EntityResolver { return &entityResolver{r} }
type entityResolver struct{ *Resolver }
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
import (
"context"
"fmt"
"go.touhou.fm/radio/server/playlist/internal"
"go.touhou.fm/radio/server/playlist/internal/graphql/generated"
"go.touhou.fm/radio/server/playlist/internal/graphql/model"
)
func (r *queryResolver) PlaylistVersion(ctx context.Context) (string, error) {
return fmt.Sprintf("%s (%s)", internal.VersionText, internal.GoVersionText), nil
}
func (r *queryResolver) Playlist(ctx context.Context) ([]model.PlayListItem, error) {
var result []model.PlayListItem
playlist := r.PlaylistProvider.Playlist()
count := 0
for item := playlist.Start(); item != nil; item = item.Next() {
result = append(result, model.PlayListItem{
Id: count,
CueTrack: (*model.CueTrack)(&item.Song),
})
count++
}
return result, nil
}
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type queryResolver struct{ *Resolver }
package resolver
import "go.touhou.fm/radio/server/playlist/internal/playlist"
//go:generate go run github.com/99designs/gqlgen
type PlaylistProvider interface {
Playlist() *playlist.Playlist
}
type Resolver struct {
PlaylistProvider
}
\ No newline at end of file
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
......@@ -49,7 +49,7 @@ func (p *ListItem) Distance(pos *ListItem) (result int, err error) {
return
}
func (p *Playlist) Duration() (result uint64) {
func (p *Playlist) Duration() (result int64) {
ptr := p.first
for ptr != nil {
result += ptr.Song.Stop - ptr.Song.Start
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment