|
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) |
|
} |
|
})) |
|
} |