Skip to content

Instantly share code, notes, and snippets.

@birowo
Last active October 31, 2018 09:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save birowo/b7f8427660e24b247a97744a32c09129 to your computer and use it in GitHub Desktop.
Save birowo/b7f8427660e24b247a97744a32c09129 to your computer and use it in GitHub Desktop.
contoh implementasi custom scalar di https://github.com/graph-gophers/graphql-go . menambahkan/modifikasi pada latihan graphql terdahulu: https://gist.github.com/birowo/01be2785bdd3b8a875ac1803be79888f

di latihan terdahulu kita sudah pakai built-in scalar(https://graphql.org/learn/schema/#scalar-types) misal String, Int, Boolean & ada juga Float

di latihan ini akan kita buat implementasi custom scalar (di contoh ini CustScalar(di file custScalar.go)) lainnya berdasar contoh yang sudah ada yaitu Time(di file time.go) & ID(di file id.go)

skrip file custScalar.go ini harus ditaruh di package graphql yaitu di directory: %GOPATH%\src\github.com\graph-gophers\graphql-go seperti contoh yang ada yatitu id.go & time.go

package main
import (
"encoding/json"
"net/http"
"sync"
"github.com/graph-gophers/graphql-go"
)
type tmap struct {
lock,
unlock func()
kv map[string]*string
}
func newMap() *tmap {
mu := &sync.Mutex{}
return &tmap{mu.Lock, mu.Unlock, map[string]*string{}}
}
func (kv *tmap) set(k, v string) {
kv.kv[k] = &v
}
func (kv *tmap) get(k string) (*string, bool) {
v, ok := kv.kv[k]
return v, ok
}
type keysVals struct {
ks []string
vs []*string
}
func (kv *tmap) getAll() *keysVals {
vMap := kv.kv
ks := make([]string, len(vMap))
vs := make([]*string, len(vMap))
i := 0
for k, v := range vMap {
ks[i] = k
vs[i] = v
i++
}
return &keysVals{ks, vs}
}
func (kv *tmap) del(k string) {
delete(kv.kv, k)
}
type (
Resolver struct{}
)
var kv = newMap()
func (_ *Resolver) Ins(args struct {
K string
V string
}) bool {
kv.lock()
_, ok := kv.get(args.K)
allowSet := !ok
if allowSet {
kv.set(args.K, args.V)
}
kv.unlock()
return allowSet
}
func (_ *Resolver) BulkIns(args struct {
Ks []string
Vs []string
}) int32 {
ks, vs := args.Ks, args.Vs
l := len(ks)
if len(vs) < l {
l = len(vs)
}
i := int32(0)
kv.lock()
for j, k := range ks[:l] {
_, ok := kv.get(k)
if !ok {
kv.set(k, vs[j])
i++
}
}
kv.unlock()
return i
}
func (_ *Resolver) Get(args struct{ K string }) *string {
ret, _ := kv.get(args.K)
return ret
}
func (this *keysVals) Ks() []string {
return this.ks
}
func (this *keysVals) Vs() []*string {
return this.vs
}
func (_ *Resolver) GetAll() *keysVals {
kv.lock()
ret := kv.getAll()
kv.unlock()
return ret
}
func (_ *Resolver) Update(args struct{ K, V string }) bool {
kv.lock()
_, ok := kv.get(args.K)
if ok {
kv.set(args.K, args.V)
}
kv.unlock()
return ok
}
func (_ *Resolver) Del(args struct{ K string }) bool {
kv.lock()
_, ok := kv.get(args.K)
if ok {
kv.del(args.K)
}
kv.unlock()
return ok
}
func (_ *Resolver) Fld1() *graphql.CustScalar {
return &graphql.CustScalar{CustFld: []byte(`"coba custom scalar"`)}
}
var s = `
scalar CustScalar
schema {
query: Query
mutation: Mutation
}
type KeysVals {
ks: [String!]!
vs: [String]!
}
type Query {
get(k: String!): String
getAll(): KeysVals
fld1: CustScalar
}
type Mutation {
ins(k: String!, v: String!): Boolean!
bulkIns(ks: [String!]!, vs:[String!]!): Int!
update(k: String!, v: String!): Boolean!
del(k: String!): Boolean!
}
`
func main() {
defer func() {
println(recover())
}()
schema := graphql.MustParseSchema(s, &Resolver{})
http.ListenAndServe(":8080", http.HandlerFunc(func(hrw http.ResponseWriter, hr *http.Request) {
switch hr.Method {
case "GET":
query := hr.URL.Query()
if len(query) == 0 {
hrw.Header().Set("Content-Type", "text/html")
hrw.Write([]byte(`
<form method="POST" target="response">
mutation:<br>
<textarea name="mutation"></textarea><br>
<input type="submit" value="submit">
</form>
<form target="response">
query:<br>
<textarea name="query"></textarea><br>
<input type="submit" value="submit">
</form>
response:
<div style="width:300px; height:100px; overflow:hidden; resize:both">
<iframe name="response" style="width:100%; height:100%"></iframe>
</div>
`))
return
}
for k, v := range query {
switch k {
case "query":
hrw.Header().Set("Content-Type", "text/html")
encoder := json.NewEncoder(hrw)
encoder.SetIndent("<br>", ". ")
encoder.Encode(schema.Exec(hr.Context(), v[0], "", nil))
default:
http.Error(hrw, "resource not found", http.StatusNotFound)
}
return
}
case "POST":
hr.ParseForm()
for k, v := range hr.PostForm {
switch k {
case "mutation":
hrw.Header().Set("Content-Type", "application/json")
json.NewEncoder(hrw).Encode(schema.Exec(hr.Context(), v[0], "", nil))
default:
http.Error(hrw, "resource not found", http.StatusNotFound)
}
return
}
default:
http.Error(hrw, "method not allowed", http.StatusMethodNotAllowed)
}
}))
}
//implementasikan skrip untuk CustScalar ini di package graphql
//yaitu di directory: %GOPATH%\src\github.com\graph-gophers\graphql-go
package graphql
import (
"encoding/json"
)
type CustScalar struct {
CustFld []byte
}
func (CustScalar) ImplementsGraphQLType(name string) bool {
return name == "CustScalar"
}
func (_ *CustScalar) UnmarshalGraphQL(input interface{}) error {
return nil
}
func (this CustScalar) MarshalJSON() ([]byte, error) {
// validasi json terhadap data yang akan dipakai diperlukan
// karena jika tidak valid akan runtime error
var js json.RawMessage
err := json.Unmarshal(this.CustFld, &js)
if err != nil {
return []byte(`"error:` + err.Error() + `"`), nil
}
return this.CustFld, nil
}

folder_graphql-go graph-gophers_coba_custom_scalar

coba introspection queries: graphql_introspection-queries

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment