github.com/cayleygraph/cayley@v0.7.7/internal/http/query.go (about) 1 // Copyright 2014 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package http 16 17 import ( 18 "context" 19 "encoding/json" 20 "errors" 21 "io" 22 "io/ioutil" 23 "net/http" 24 "net/url" 25 "strconv" 26 27 "github.com/julienschmidt/httprouter" 28 29 "github.com/cayleygraph/cayley/query" 30 ) 31 32 type SuccessQueryWrapper struct { 33 Result interface{} `json:"result"` 34 } 35 36 type ErrorQueryWrapper struct { 37 Error string `json:"error"` 38 } 39 40 func WriteError(w io.Writer, err error) error { 41 enc := json.NewEncoder(w) 42 //enc.SetIndent("", " ") 43 return enc.Encode(ErrorQueryWrapper{err.Error()}) 44 } 45 46 func WriteResult(w io.Writer, result interface{}) error { 47 enc := json.NewEncoder(w) 48 //enc.SetIndent("", " ") 49 return enc.Encode(SuccessQueryWrapper{result}) 50 } 51 52 func GetQueryShape(q string, ses query.HTTP) ([]byte, error) { 53 s, err := ses.ShapeOf(q) 54 if err != nil { 55 return nil, err 56 } 57 return json.Marshal(s) 58 } 59 60 func (api *API) contextForRequest(r *http.Request) (context.Context, func()) { 61 ctx := context.TODO() // TODO(dennwc): get from request 62 cancel := func() {} 63 if api.config.Timeout > 0 { 64 ctx, cancel = context.WithTimeout(ctx, api.config.Timeout) 65 } 66 return ctx, cancel 67 } 68 69 func defaultErrorFunc(w query.ResponseWriter, err error) { 70 data, _ := json.Marshal(err.Error()) 71 w.WriteHeader(http.StatusBadRequest) 72 w.Write([]byte(`{"error" : `)) 73 w.Write(data) 74 w.Write([]byte(`}`)) 75 } 76 77 // TODO(barakmich): Turn this into proper middleware. 78 func (api *API) ServeV1Query(w http.ResponseWriter, r *http.Request, params httprouter.Params) { 79 ctx, cancel := api.contextForRequest(r) 80 defer cancel() 81 l := query.GetLanguage(params.ByName("query_lang")) 82 if l == nil { 83 jsonResponse(w, http.StatusBadRequest, "Unknown query language.") 84 return 85 } 86 errFunc := defaultErrorFunc 87 if l.HTTPError != nil { 88 errFunc = l.HTTPError 89 } 90 select { 91 case <-ctx.Done(): 92 errFunc(w, ctx.Err()) 93 return 94 default: 95 } 96 h, err := api.GetHandleForRequest(r) 97 if err != nil { 98 errFunc(w, err) 99 return 100 } 101 if l.HTTPQuery != nil { 102 defer r.Body.Close() 103 l.HTTPQuery(ctx, h.QuadStore, w, r.Body) 104 return 105 } 106 if l.HTTP == nil { 107 errFunc(w, errors.New("HTTP interface is not supported for this query language.")) 108 return 109 } 110 111 par, _ := url.ParseQuery(r.URL.RawQuery) 112 limit, _ := strconv.Atoi(par.Get("limit")) 113 if limit == 0 { 114 limit = 100 115 } 116 117 ses := l.HTTP(h.QuadStore) 118 bodyBytes, err := ioutil.ReadAll(r.Body) 119 if err != nil { 120 errFunc(w, err) 121 return 122 } 123 it, err := ses.Execute(ctx, string(bodyBytes), query.Options{ 124 Collation: query.JSON, 125 Limit: limit, 126 }) 127 if err != nil { 128 errFunc(w, err) 129 return 130 } 131 defer it.Close() 132 133 var out []interface{} 134 for it.Next(ctx) { 135 out = append(out, it.Result()) 136 } 137 if err = it.Err(); err != nil { 138 errFunc(w, err) 139 return 140 } 141 _ = WriteResult(w, out) 142 } 143 144 func (api *API) ServeV1Shape(w http.ResponseWriter, r *http.Request, params httprouter.Params) { 145 ctx, cancel := api.contextForRequest(r) 146 defer cancel() 147 select { 148 case <-ctx.Done(): 149 jsonResponse(w, http.StatusBadRequest, "Cancelled") 150 return 151 default: 152 } 153 h, err := api.GetHandleForRequest(r) 154 if err != nil { 155 jsonResponse(w, http.StatusBadRequest, err) 156 return 157 } 158 l := query.GetLanguage(params.ByName("query_lang")) 159 if l == nil { 160 jsonResponse(w, http.StatusBadRequest, "Unknown query language.") 161 return 162 } else if l.HTTP == nil { 163 jsonResponse(w, http.StatusBadRequest, "HTTP interface is not supported for this query language.") 164 return 165 } 166 ses := l.HTTP(h.QuadStore) 167 bodyBytes, err := ioutil.ReadAll(r.Body) 168 if err != nil { 169 jsonResponse(w, http.StatusBadRequest, err) 170 return 171 } 172 code := string(bodyBytes) 173 174 output, err := GetQueryShape(code, ses) 175 if err == query.ErrParseMore { 176 jsonResponse(w, http.StatusBadRequest, "Incomplete data?") 177 return 178 } else if err != nil { 179 w.WriteHeader(http.StatusBadRequest) 180 WriteError(w, err) 181 return 182 } 183 w.Write(output) 184 }