API: Rig up query { pastes }
This commit is contained in:
parent
606a523585
commit
80518d74f4
|
@ -39,6 +39,7 @@ type Config struct {
|
|||
|
||||
type ResolverRoot interface {
|
||||
Mutation() MutationResolver
|
||||
Paste() PasteResolver
|
||||
Query() QueryResolver
|
||||
User() UserResolver
|
||||
}
|
||||
|
@ -105,6 +106,10 @@ type MutationResolver interface {
|
|||
Update(ctx context.Context, id string, visibility model.Visibility) (*model.Paste, error)
|
||||
Delete(ctx context.Context, id string) (*model.Paste, error)
|
||||
}
|
||||
type PasteResolver interface {
|
||||
Files(ctx context.Context, obj *model.Paste) ([]*model.File, error)
|
||||
User(ctx context.Context, obj *model.Paste) (model.Entity, error)
|
||||
}
|
||||
type QueryResolver interface {
|
||||
Version(ctx context.Context) (*model.Version, error)
|
||||
Me(ctx context.Context) (*model.User, error)
|
||||
|
@ -1161,14 +1166,14 @@ func (ec *executionContext) _Paste_visibility(ctx context.Context, field graphql
|
|||
Object: "Paste",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Visibility, nil
|
||||
return obj.Visibility(), nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
|
@ -1196,14 +1201,14 @@ func (ec *executionContext) _Paste_files(ctx context.Context, field graphql.Coll
|
|||
Object: "Paste",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Files, nil
|
||||
return ec.resolvers.Paste().Files(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
|
@ -1231,15 +1236,15 @@ func (ec *executionContext) _Paste_user(ctx context.Context, field graphql.Colle
|
|||
Object: "Paste",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.User, nil
|
||||
return ec.resolvers.Paste().User(rctx, obj)
|
||||
}
|
||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||
scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋpasteᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "PROFILE")
|
||||
|
@ -3264,28 +3269,46 @@ func (ec *executionContext) _Paste(ctx context.Context, sel ast.SelectionSet, ob
|
|||
case "id":
|
||||
out.Values[i] = ec._Paste_id(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "created":
|
||||
out.Values[i] = ec._Paste_created(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "visibility":
|
||||
out.Values[i] = ec._Paste_visibility(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "files":
|
||||
out.Values[i] = ec._Paste_files(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Paste_files(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "user":
|
||||
out.Values[i] = ec._Paste_user(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Paste_user(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
|
|
@ -21,14 +21,6 @@ type File struct {
|
|||
Contents string `json:"contents"`
|
||||
}
|
||||
|
||||
type Paste struct {
|
||||
ID string `json:"id"`
|
||||
Created time.Time `json:"created"`
|
||||
Visibility Visibility `json:"visibility"`
|
||||
Files []*File `json:"files"`
|
||||
User Entity `json:"user"`
|
||||
}
|
||||
|
||||
type PasteCursor struct {
|
||||
Results []*Paste `json:"results"`
|
||||
Cursor *model.Cursor `json:"cursor"`
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
|
||||
"git.sr.ht/~sircmpwn/core-go/database"
|
||||
"git.sr.ht/~sircmpwn/core-go/model"
|
||||
)
|
||||
|
||||
type Paste struct {
|
||||
ID string `json:"id"`
|
||||
Created time.Time `json:"created"`
|
||||
|
||||
UserID int
|
||||
RawVisibility string
|
||||
|
||||
alias string
|
||||
fields *database.ModelFields
|
||||
}
|
||||
|
||||
func (paste *Paste) Visibility() Visibility {
|
||||
vis := Visibility(strings.ToUpper(paste.RawVisibility))
|
||||
if !vis.IsValid() {
|
||||
panic(fmt.Sprintf("Database invariant broken: paste ID %s has invalid visibility", paste.ID))
|
||||
}
|
||||
return vis
|
||||
}
|
||||
|
||||
func (paste *Paste) As(alias string) *Paste {
|
||||
paste.alias = alias
|
||||
return paste
|
||||
}
|
||||
|
||||
func (paste *Paste) Alias() string {
|
||||
return paste.alias
|
||||
}
|
||||
|
||||
func (paste *Paste) Table() string {
|
||||
return "paste"
|
||||
}
|
||||
|
||||
func (paste *Paste) Fields() *database.ModelFields {
|
||||
if paste.fields != nil {
|
||||
return paste.fields
|
||||
}
|
||||
paste.fields = &database.ModelFields{
|
||||
Fields: []*database.FieldMap{
|
||||
{ "created", "created", &paste.Created },
|
||||
{ "visibility", "visibility", &paste.RawVisibility },
|
||||
|
||||
// Always fetch:
|
||||
{ "sha", "", &paste.ID },
|
||||
{ "user_id", "", &paste.UserID },
|
||||
},
|
||||
}
|
||||
return paste.fields
|
||||
}
|
||||
|
||||
func (paste *Paste) QueryWithCursor(ctx context.Context,
|
||||
runner sq.BaseRunner, q sq.SelectBuilder,
|
||||
cur *model.Cursor) ([]*Paste, *model.Cursor) {
|
||||
var (
|
||||
err error
|
||||
rows *sql.Rows
|
||||
)
|
||||
|
||||
if cur.Next != "" {
|
||||
next, _ := strconv.Atoi(cur.Next)
|
||||
q = q.Where(database.WithAlias(paste.alias, "id") + "<= ?", next)
|
||||
}
|
||||
q = q.
|
||||
Column(database.WithAlias(paste.alias, "id")).
|
||||
OrderBy(database.WithAlias(paste.alias, "id") + " DESC").
|
||||
Limit(uint64(cur.Count + 1))
|
||||
|
||||
if rows, err = q.RunWith(runner).QueryContext(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var (
|
||||
pastes []*Paste
|
||||
id int
|
||||
)
|
||||
for rows.Next() {
|
||||
var paste Paste
|
||||
if err := rows.Scan(append(
|
||||
database.Scan(ctx, &paste),
|
||||
&id)...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pastes = append(pastes, &paste)
|
||||
}
|
||||
|
||||
if len(pastes) > cur.Count {
|
||||
cur = &model.Cursor{
|
||||
Count: cur.Count,
|
||||
Next: strconv.Itoa(id),
|
||||
Search: cur.Search,
|
||||
}
|
||||
pastes = pastes[:cur.Count]
|
||||
} else {
|
||||
cur = nil
|
||||
}
|
||||
|
||||
return pastes, cur
|
||||
}
|
|
@ -5,9 +5,11 @@ package graph
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"git.sr.ht/~sircmpwn/core-go/auth"
|
||||
"git.sr.ht/~sircmpwn/core-go/database"
|
||||
coremodel "git.sr.ht/~sircmpwn/core-go/model"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph/api"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph/model"
|
||||
|
@ -27,6 +29,14 @@ func (r *mutationResolver) Delete(ctx context.Context, id string) (*model.Paste,
|
|||
panic(fmt.Errorf("not implemented"))
|
||||
}
|
||||
|
||||
func (r *pasteResolver) Files(ctx context.Context, obj *model.Paste) ([]*model.File, error) {
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
}
|
||||
|
||||
func (r *pasteResolver) User(ctx context.Context, obj *model.Paste) (model.Entity, error) {
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
}
|
||||
|
||||
func (r *queryResolver) Version(ctx context.Context) (*model.Version, error) {
|
||||
return &model.Version{
|
||||
Major: 0,
|
||||
|
@ -50,7 +60,27 @@ func (r *queryResolver) User(ctx context.Context, username string) (*model.User,
|
|||
}
|
||||
|
||||
func (r *queryResolver) Pastes(ctx context.Context, cursor *coremodel.Cursor) (*model.PasteCursor, error) {
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
if cursor == nil {
|
||||
cursor = coremodel.NewCursor(nil)
|
||||
}
|
||||
|
||||
var pastes []*model.Paste
|
||||
if err := database.WithTx(ctx, &sql.TxOptions{
|
||||
Isolation: 0,
|
||||
ReadOnly: true,
|
||||
}, func(tx *sql.Tx) error {
|
||||
paste := (&model.Paste{}).As(`paste`)
|
||||
query := database.
|
||||
Select(ctx, paste).
|
||||
From(`paste`).
|
||||
Where(`paste.user_id = ?`, auth.ForContext(ctx).UserID)
|
||||
pastes, cursor = paste.QueryWithCursor(ctx, tx, query, cursor)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PasteCursor{pastes, cursor}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Paste(ctx context.Context, id string) (*model.Paste, error) {
|
||||
|
@ -64,6 +94,9 @@ func (r *userResolver) Pastes(ctx context.Context, obj *model.User, cursor *core
|
|||
// Mutation returns api.MutationResolver implementation.
|
||||
func (r *Resolver) Mutation() api.MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Paste returns api.PasteResolver implementation.
|
||||
func (r *Resolver) Paste() api.PasteResolver { return &pasteResolver{r} }
|
||||
|
||||
// Query returns api.QueryResolver implementation.
|
||||
func (r *Resolver) Query() api.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
|
@ -71,5 +104,6 @@ func (r *Resolver) Query() api.QueryResolver { return &queryResolver{r} }
|
|||
func (r *Resolver) User() api.UserResolver { return &userResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type pasteResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type userResolver struct{ *Resolver }
|
||||
|
|
Loading…
Reference in New Issue