Skip to content

Commit

Permalink
fix(schema): fix "1M article bug"
Browse files Browse the repository at this point in the history
refactore GraphQL parameter parsing
  • Loading branch information
ncarlier committed Sep 9, 2023
1 parent f7cf7bf commit f9c3135
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 179 deletions.
6 changes: 3 additions & 3 deletions pkg/api/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func download() http.Handler {
helper.WriteProblemDetail(w, downloadProblem, "missing article ID", http.StatusBadRequest)
return
}
idArticle, ok := helper.ConvGQLStringToUint(id)
if !ok {
idArticle := helper.ConvGraphQLID(id)
if idArticle == nil {
helper.WriteProblemDetail(w, downloadProblem, "invalid article ID", http.StatusBadRequest)
return
}
Expand All @@ -32,7 +32,7 @@ func download() http.Handler {
}

// Archive the article
asset, err := service.Lookup().DownloadArticle(r.Context(), idArticle, format)
asset, err := service.Lookup().DownloadArticle(r.Context(), *idArticle, format)
if err != nil {
helper.WriteProblemDetail(w, downloadProblem, err.Error(), http.StatusInternalServerError)
return
Expand Down
11 changes: 11 additions & 0 deletions pkg/helper/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package helper

import (
"encoding/json"
"fmt"
"net/http"
)

Expand All @@ -23,3 +24,13 @@ func WriteProblemDetail(w http.ResponseWriter, title string, detail string, stat
w.WriteHeader(status)
json.NewEncoder(w).Encode(err)
}

// InvalidParameterError create invalid parameter error
func InvalidParameterError(name string) error {
return fmt.Errorf("a parameter is not valid: %s", name)
}

// RequireParameterError create require parameter error
func RequireParameterError(name string) error {
return fmt.Errorf("a required parameter is missing: %s", name)
}
72 changes: 24 additions & 48 deletions pkg/helper/graphql.go
Original file line number Diff line number Diff line change
@@ -1,62 +1,38 @@
package helper

import "strconv"
import (
"math/big"
)

// ConvGQLStringToUint converts ID GraphQL interface object to unsigned integer
func ConvGQLStringToUint(id interface{}) (uint, bool) {
str, ok := id.(string)
if !ok {
return 0, ok
}
ui64, err := strconv.ParseUint(str, 10, 32)
if err != nil {
return 0, false
}
return uint(ui64), true
}

// ConvGQLIntToUint converts GraphQL int to unsigned integer
func ConvGQLIntToUint(val interface{}) (uint, bool) {
if iVal, ok := val.(int); ok {
return uint(iVal), true
}
return 0, false
}

// GetGQLStringParameter return GraphQL string parameter
func GetGQLStringParameter(name string, args map[string]interface{}) *string {
if val, ok := args[name]; ok {
s := val.(string)
return &s
// ParseGraphQLArgument parse GraphQL argument
func ParseGraphQLArgument[T string | bool | uint](args map[string]interface{}, name string) *T {
if arg, exist := args[name]; exist {
if val, ok := arg.(T); ok {
return &val
}
}
return nil
}

// GetGQLBoolParameter return GraphQL string parameter
func GetGQLBoolParameter(name string, args map[string]interface{}) *bool {
if val, ok := args[name]; ok {
s := val.(bool)
return &s
// ParseGraphQLID parse GraphQL argument as uint
func ParseGraphQLID(args map[string]interface{}, name string) *uint {
if arg, exist := args[name]; exist {
return ConvGraphQLID(arg)
}
return nil
}

// GetGQLUintParameter return GraphQL string parameter
func GetGQLUintParameter(name string, args map[string]interface{}) *uint {
if val, ok := args[name]; ok {
switch v := val.(type) {
case int:
s := uint(v)
return &s
case string:
ui64, err := strconv.ParseUint(v, 10, 32)
if err != nil {
return nil
}
s := uint(ui64)
return &s
default:
return nil
// ParseGraphQLID parse GraphQL argument as uint
func ConvGraphQLID(id interface{}) *uint {
switch val := id.(type) {
case int:
v := uint(val)
return &v
case string:
if f, _, err := big.ParseFloat(val, 10, 0, big.ToNearestEven); err == nil {
v64, _ := f.Uint64()
v := uint(v64)
return &v
}
}
return nil
Expand Down
30 changes: 11 additions & 19 deletions pkg/schema/admin/user/mutations.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package user

import (
"errors"

"github.com/graphql-go/graphql"
"github.com/ncarlier/readflow/pkg/helper"
"github.com/ncarlier/readflow/pkg/model"
Expand All @@ -22,7 +20,10 @@ var registerUserMutationField = &graphql.Field{
}

func registerUserResolver(p graphql.ResolveParams) (interface{}, error) {
username := helper.GetGQLStringParameter("username", p.Args)
username := helper.ParseGraphQLArgument[string](p.Args, "username")
if username == nil {
return nil, helper.RequireParameterError("username")
}
return service.Lookup().GetOrRegisterUser(p.Context, *username)
}

Expand All @@ -47,24 +48,15 @@ var updateUserMutationField = &graphql.Field{
}

func updateUserResolver(p graphql.ResolveParams) (interface{}, error) {
uid, ok := helper.ConvGQLStringToUint(p.Args["uid"])
if !ok {
return nil, errors.New("invalid user ID")
uid := helper.ParseGraphQLID(p.Args, "uid")
if uid == nil {
return nil, helper.InvalidParameterError("uid")
}
form := model.UserForm{
ID: uid,
}
if val, ok := p.Args["enabled"]; ok {
b := val.(bool)
form.Enabled = &b
}
if val, ok := p.Args["plan"]; ok {
s := val.(string)
form.Plan = &s
}
if val, ok := p.Args["customer_id"]; ok {
s := val.(string)
form.CustomerID = &s
ID: *uid,
Enabled: helper.ParseGraphQLArgument[bool](p.Args, "enabled"),
Plan: helper.ParseGraphQLArgument[string](p.Args, "plan"),
CustomerID: helper.ParseGraphQLArgument[string](p.Args, "customer_id"),
}
return service.Lookup().UpdateUser(p.Context, form)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/schema/admin/user/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ var userQueryField = &graphql.Field{
}

func userResolver(p graphql.ResolveParams) (interface{}, error) {
uid := helper.GetGQLUintParameter("uid", p.Args)
username := helper.GetGQLStringParameter("username", p.Args)
uid := helper.ParseGraphQLID(p.Args, "uid")
username := helper.ParseGraphQLArgument[string](p.Args, "username")
if uid != nil {
return service.Lookup().GetUserByID(p.Context, *uid)
} else if username != nil {
Expand Down
38 changes: 14 additions & 24 deletions pkg/schema/article/mutations.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package article

import (
"errors"

"github.com/graphql-go/graphql"

"github.com/ncarlier/readflow/pkg/helper"
Expand Down Expand Up @@ -39,18 +37,18 @@ var updateArticleMutationField = &graphql.Field{
}

func updateArticleResolver(p graphql.ResolveParams) (interface{}, error) {
id, ok := helper.ConvGQLStringToUint(p.Args["id"])
if !ok {
return nil, errors.New("invalid article ID")
id := helper.ParseGraphQLID(p.Args, "id")
if id == nil {
return nil, helper.InvalidParameterError("id")
}

form := model.ArticleUpdateForm{
ID: id,
Title: helper.GetGQLStringParameter("title", p.Args),
Text: helper.GetGQLStringParameter("text", p.Args),
CategoryID: helper.GetGQLUintParameter("category_id", p.Args),
Status: helper.GetGQLStringParameter("status", p.Args),
Stars: helper.GetGQLUintParameter("stars", p.Args),
ID: *id,
Title: helper.ParseGraphQLArgument[string](p.Args, "title"),
Text: helper.ParseGraphQLArgument[string](p.Args, "text"),
CategoryID: helper.ParseGraphQLID(p.Args, "category_id"),
Status: helper.ParseGraphQLArgument[string](p.Args, "status"),
Stars: helper.ParseGraphQLArgument[uint](p.Args, "stars"),
}

article, err := service.Lookup().UpdateArticle(p.Context, form)
Expand Down Expand Up @@ -79,13 +77,10 @@ var markAllArticlesAsReadMutationField = &graphql.Field{
}

func markAllArticlesAsReadResolver(p graphql.ResolveParams) (interface{}, error) {
var categoryID *uint
if val, ok := helper.ConvGQLStringToUint(p.Args["category"]); ok {
categoryID = &val
}
status := helper.GetGQLStringParameter("status", p.Args)
categoryID := helper.ParseGraphQLID(p.Args, "category")
status := helper.ParseGraphQLArgument[string](p.Args, "status")
if status == nil || *status == "read" {
return nil, errors.New("invalid status")
return nil, helper.InvalidParameterError("status")
}

_, err := service.Lookup().MarkAllArticlesAsRead(p.Context, *status, categoryID)
Expand All @@ -110,14 +105,9 @@ var addArticleMutationField = &graphql.Field{
}

func addArticleResolver(p graphql.ResolveParams) (interface{}, error) {
var categoryID *uint
if val, ok := helper.ConvGQLStringToUint(p.Args["category"]); ok {
categoryID = &val
}
url, _ := p.Args["url"].(string)
form := model.ArticleCreateForm{
URL: &url,
CategoryID: categoryID,
URL: helper.ParseGraphQLArgument[string](p.Args, "url"),
CategoryID: helper.ParseGraphQLID(p.Args, "category"),
}

return service.Lookup().CreateArticle(p.Context, form, service.ArticleCreationOptions{})
Expand Down
26 changes: 12 additions & 14 deletions pkg/schema/article/queries.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package article

import (
"errors"

"github.com/graphql-go/graphql"

"github.com/ncarlier/readflow/pkg/helper"
Expand Down Expand Up @@ -55,14 +53,14 @@ var articlesQueryField = &graphql.Field{

func articlesResolver(p graphql.ResolveParams) (interface{}, error) {
pageRequest := model.ArticlesPageRequest{
Limit: helper.GetGQLUintParameter("limit", p.Args),
SortOrder: helper.GetGQLStringParameter("sortOrder", p.Args),
SortBy: helper.GetGQLStringParameter("sortBy", p.Args),
AfterCursor: helper.GetGQLUintParameter("afterCursor", p.Args),
Category: helper.GetGQLUintParameter("category", p.Args),
Status: helper.GetGQLStringParameter("status", p.Args),
Starred: helper.GetGQLBoolParameter("starred", p.Args),
Query: helper.GetGQLStringParameter("query", p.Args),
Limit: helper.ParseGraphQLArgument[uint](p.Args, "limit"),
SortOrder: helper.ParseGraphQLArgument[string](p.Args, "sortOrder"),
SortBy: helper.ParseGraphQLArgument[string](p.Args, "sortBy"),
AfterCursor: helper.ParseGraphQLArgument[uint](p.Args, "afterCursor"),
Category: helper.ParseGraphQLArgument[uint](p.Args, "category"),
Status: helper.ParseGraphQLArgument[string](p.Args, "status"),
Starred: helper.ParseGraphQLArgument[bool](p.Args, "starred"),
Query: helper.ParseGraphQLArgument[string](p.Args, "query"),
}

return service.Lookup().GetArticles(p.Context, pageRequest)
Expand All @@ -79,12 +77,12 @@ var articleQueryField = &graphql.Field{
}

func articleResolver(p graphql.ResolveParams) (interface{}, error) {
id, ok := helper.ConvGQLStringToUint(p.Args["id"])
if !ok {
return nil, errors.New("invalid article ID")
id := helper.ParseGraphQLID(p.Args, "id")
if id == nil {
return nil, helper.InvalidParameterError("id")
}

return service.Lookup().GetArticle(p.Context, id)
return service.Lookup().GetArticle(p.Context, *id)
}

func init() {
Expand Down
16 changes: 7 additions & 9 deletions pkg/schema/category/mutations.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package category

import (
"errors"

"github.com/graphql-go/graphql"
"github.com/ncarlier/readflow/pkg/helper"
"github.com/ncarlier/readflow/pkg/model"
Expand All @@ -27,16 +25,16 @@ var createOrUpdateCategoryMutationField = &graphql.Field{
}

func createOrUpdateCategoryResolver(p graphql.ResolveParams) (interface{}, error) {
title := helper.GetGQLStringParameter("title", p.Args)
if id, ok := helper.ConvGQLStringToUint(p.Args["id"]); ok {
title := helper.ParseGraphQLArgument[string](p.Args, "title")
if id := helper.ParseGraphQLID(p.Args, "id"); id != nil {
form := model.CategoryUpdateForm{
ID: id,
ID: *id,
Title: title,
}
return service.Lookup().UpdateCategory(p.Context, form)
}
if title == nil {
return nil, errors.New("title is required when creating a new category")
return nil, helper.RequireParameterError("title")
}
form := model.CategoryCreateForm{
Title: *title,
Expand All @@ -58,12 +56,12 @@ var deleteCategoriesMutationField = &graphql.Field{
func deleteCategoriesResolver(p graphql.ResolveParams) (interface{}, error) {
idsArg, ok := p.Args["ids"].([]interface{})
if !ok {
return nil, errors.New("invalid category ID")
return nil, helper.InvalidParameterError("ids")
}
var ids []uint
for _, v := range idsArg {
if id, ok := helper.ConvGQLStringToUint(v); ok {
ids = append(ids, id)
if id := helper.ConvGraphQLID(v); id != nil {
ids = append(ids, *id)
}
}

Expand Down
10 changes: 4 additions & 6 deletions pkg/schema/category/queries.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package category

import (
"errors"

"github.com/graphql-go/graphql"
"github.com/ncarlier/readflow/pkg/helper"
"github.com/ncarlier/readflow/pkg/model"
Expand Down Expand Up @@ -39,11 +37,11 @@ var categoryQueryField = &graphql.Field{
}

func categoryResolver(p graphql.ResolveParams) (interface{}, error) {
id, ok := helper.ConvGQLStringToUint(p.Args["id"])
if !ok {
return nil, errors.New("invalid category ID")
id := helper.ParseGraphQLID(p.Args, "id")
if id == nil {
return nil, helper.InvalidParameterError("id")
}
return service.Lookup().GetCategory(p.Context, id)
return service.Lookup().GetCategory(p.Context, *id)
}

func init() {
Expand Down
Loading

0 comments on commit f9c3135

Please sign in to comment.