Skip to content

Commit

Permalink
pkg/server/*: Add graphql API for create, update, delete.
Browse files Browse the repository at this point in the history
  • Loading branch information
yifan-gu committed Sep 15, 2022
1 parent 3f2031b commit 30e70b6
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 18 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Parser/Exporter:
- [x] List parsers and exporters
- [x] MongoDB exporter
- [x] JSON parser
- [ ] One click export from Kindle app

Server Backend:
- [x] Database storage
Expand All @@ -70,10 +71,11 @@ Server Backend:
- [x] Timestamp(created, last modified)
- [ ] Server REST API?
- [x] Graphql API READ
- [ ] Graphql API Create
- [ ] Graphql API Update
- [ ] Graphql API Delete
- [ ] Handle Graphql null fields
- [x] Graphql API Create
- [x] Graphql API Update
- [x] Graphql API Delete
- [ ] Handle Graphql null fields?
- [ ] Graphql API tests, mocked storage

App:
- [ ] Search by tags, keywords, book, author
Expand Down
27 changes: 27 additions & 0 deletions pkg/model/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,25 @@ Copyright © 2022 Yifan Gu <guyifan1121@gmail.com>

package model

import (
"errors"
"fmt"
)

const (
// MarkTypeHighlight is a highlight marking.
MarkTypeHighlight = "HIGHLIGHT"
// MarkTypeNote is a note marking.
MarkTypeNote = "NOTE"
)

var (
typeMaps = map[string]struct{}{
MarkTypeHighlight: struct{}{},
MarkTypeNote: struct{}{},
}
)

// Book defines the details of a Book object, which also contains a list of marks.
type Book struct {
Title string `json:"title"`
Expand Down Expand Up @@ -40,3 +52,18 @@ type Location struct {
Page *int `json:"page,omitempty"`
Location *int `json:"location,omitempty"`
}

func isSupportedType(typ string) bool {
_, ok := typeMaps[typ]
return ok
}

func ValidateMark(m *Mark) error {
if !isSupportedType(m.Type) {
return errors.New(fmt.Sprintf("Type %v is not supported", m.Type))
}
if m.Data == "" && m.UserNote == "" {
return errors.New("Expect 'data' or 'note' to be set")
}
return nil
}
115 changes: 114 additions & 1 deletion pkg/server/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@ var int64Type = graphql.NewScalar(graphql.ScalarConfig{
},
})

var locationInputType = graphql.NewInputObject(
graphql.InputObjectConfig{
Name: "LocationInput",
Fields: graphql.InputObjectConfigFieldMap{
"chapter": &graphql.InputObjectFieldConfig{
Type: graphql.String,
},
"page": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
"location": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
},
},
)

var locationType = graphql.NewObject(
graphql.ObjectConfig{
Name: "Location",
Expand Down Expand Up @@ -162,6 +179,9 @@ func (s *server) graphqlQueryType() *graphql.Object {
"tags": &graphql.ArgumentConfig{
Type: graphql.NewList(graphql.String),
},
"location": &graphql.ArgumentConfig{
Type: locationInputType,
},
"createdBefore": &graphql.ArgumentConfig{
Type: graphql.Int,
},
Expand All @@ -185,10 +205,103 @@ func (s *server) graphqlQueryType() *graphql.Object {
)
}

func (s *server) graphqlMutationType() *graphql.Object {
return graphql.NewObject(
graphql.ObjectConfig{
Name: "Mutation",
Fields: graphql.Fields{
// Create a new mark
// http://localhost:11212/marks?query=mutation+_{createOne(type:"",title:"",author:"",data:"",note:"",tags:[]){type,title,author,data,note,tags}}
"createOne": &graphql.Field{
Type: markType,
Description: "Create a new mark",
Args: graphql.FieldConfigArgument{
"type": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String), // TODO(yifan): Use Enum?
},
"title": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"author": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"section": &graphql.ArgumentConfig{
Type: graphql.String,
},
"location": &graphql.ArgumentConfig{
Type: locationInputType,
},
"data": &graphql.ArgumentConfig{
Type: graphql.String,
},
"note": &graphql.ArgumentConfig{
Type: graphql.String,
},
"tags": &graphql.ArgumentConfig{
Type: graphql.NewList(graphql.String),
},
},
Resolve: s.createOneMark,
},
// Update a mark by id
// http://localhost:11212/marks?query=mutation+_{updateOne(id:1,type:"",title:"",author:"",data:"",note:"",tags:[]){type,title,author,data,note,tags}}
"updateOne": &graphql.Field{
Type: markType,
Description: "Update a mark by its ID",
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"type": &graphql.ArgumentConfig{
Type: graphql.String, // TODO(yifan): Use Enum?
},
"title": &graphql.ArgumentConfig{
Type: graphql.String,
},
"author": &graphql.ArgumentConfig{
Type: graphql.String,
},
"section": &graphql.ArgumentConfig{
Type: graphql.String,
},
"location": &graphql.ArgumentConfig{
Type: locationInputType,
},
"data": &graphql.ArgumentConfig{
Type: graphql.String,
},
"note": &graphql.ArgumentConfig{
Type: graphql.String,
},
"tags": &graphql.ArgumentConfig{
Type: graphql.NewList(graphql.String),
},
},
Resolve: s.updateOneMarkByID,
},
// Delete a mark by id
// http://localhost:11212/marks?query=mutation+_{delete(id:1,){type,title,author,data,note,tags}}
"deleteOne": &graphql.Field{
Type: markType,
Description: "Delete a mark by its ID",
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
Resolve: s.deleteOneMarkByID,
},
},
},
)

}

func (s *server) graphqlSchema() graphql.Schema {
schema, err := graphql.NewSchema(
graphql.SchemaConfig{
Query: s.graphqlQueryType(),
Query: s.graphqlQueryType(),
Mutation: s.graphqlMutationType(),
},
)
if err != nil {
Expand Down
Loading

0 comments on commit 30e70b6

Please sign in to comment.