github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/extension/otto/gohan_util.go (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 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 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package otto 17 18 import ( 19 "bytes" 20 "encoding/json" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "net/http" 25 "net/url" 26 "os/exec" 27 "text/template" 28 "time" 29 30 "github.com/dop251/otto" 31 "github.com/twinj/uuid" 32 33 "github.com/cloudwan/gohan/schema" 34 ) 35 36 const ( 37 noTransactionErrorMessage = "No transaction" 38 noContextMessage = "No context provided" 39 unknownSchemaErrorMesssageFormat = "Unknown schema '%s'" 40 ) 41 42 func init() { 43 gohanUtilInit := func(env *Environment) { 44 vm := env.VM 45 builtins := map[string]interface{}{ 46 "gohan_http": func(call otto.FunctionCall) otto.Value { 47 if len(call.ArgumentList) == 4 { 48 emptyOpts, _ := otto.ToValue(map[string]interface{}{}) 49 call.ArgumentList = append(call.ArgumentList, emptyOpts) 50 } 51 VerifyCallArguments(&call, "gohan_http", 5) 52 method := call.Argument(0).String() 53 url := call.Argument(1).String() 54 headers := ConvertOttoToGo(call.Argument(2)) 55 data := ConvertOttoToGo(call.Argument(3)) 56 options := ConvertOttoToGo(call.Argument(4)) 57 log.Debug("gohan_http [%s] %s %s %s", method, headers, url, options) 58 code, headers, body, err := gohanHTTP(method, url, headers, data, options) 59 log.Debug("response code %d", code) 60 resp := map[string]interface{}{} 61 if err != nil { 62 resp["status"] = "err" 63 resp["error"] = err.Error() 64 } else { 65 resp["status"] = "success" 66 resp["status_code"] = fmt.Sprint(code) 67 resp["body"] = body 68 resp["headers"] = headers 69 } 70 log.Debug("response code %d", code) 71 value, _ := vm.ToValue(resp) 72 return value 73 }, 74 "gohan_schemas": func(call otto.FunctionCall) otto.Value { 75 VerifyCallArguments(&call, "gohan_schemas", 0) 76 manager := schema.GetManager() 77 response := []interface{}{} 78 for _, schema := range manager.OrderedSchemas() { 79 response = append(response, schema.RawData) 80 } 81 value, _ := vm.ToValue(response) 82 return value 83 }, 84 "gohan_schema_url": func(call otto.FunctionCall) otto.Value { 85 VerifyCallArguments(&call, "gohan_schema_url", 1) 86 schemaID := call.Argument(0).String() 87 manager := schema.GetManager() 88 schema, ok := manager.Schema(schemaID) 89 if !ok { 90 ThrowOttoException(&call, unknownSchemaErrorMesssageFormat, schemaID) 91 } 92 value, _ := vm.ToValue(schema.URL) 93 return value 94 }, 95 "gohan_policies": func(call otto.FunctionCall) otto.Value { 96 VerifyCallArguments(&call, "gohan_policies", 0) 97 manager := schema.GetManager() 98 response := []interface{}{} 99 for _, policy := range manager.Policies() { 100 response = append(response, policy.RawData) 101 } 102 value, _ := vm.ToValue(response) 103 return value 104 }, 105 "gohan_uuid": func(call otto.FunctionCall) otto.Value { 106 value, _ := vm.ToValue(uuid.NewV4().String()) 107 return value 108 }, 109 "gohan_sleep": func(call otto.FunctionCall) otto.Value { 110 if len(call.ArgumentList) != 1 { 111 panic("Wrong number of arguments in gohan_schema_url call.") 112 } 113 rawSleep, _ := call.Argument(0).Export() 114 var sleep time.Duration 115 switch rawSleep.(type) { 116 case int: 117 sleep = time.Duration(rawSleep.(int)) 118 case int64: 119 sleep = time.Duration(rawSleep.(int64)) 120 } 121 time.Sleep(sleep * time.Millisecond) 122 return otto.NullValue() 123 }, 124 "gohan_template": func(call otto.FunctionCall) otto.Value { 125 if len(call.ArgumentList) != 2 { 126 panic("Wrong number of arguments in gohan_db_create call.") 127 } 128 rawTemplate, _ := call.Argument(0).Export() 129 templateString, ok := rawTemplate.(string) 130 if !ok { 131 value, _ := vm.ToValue(rawTemplate) 132 return value 133 } 134 data := ConvertOttoToGo(call.Argument(1)) 135 t := template.Must(template.New("tmpl").Parse(templateString)) 136 b := bytes.NewBuffer(make([]byte, 0, 100)) 137 t.Execute(b, data) 138 value, _ := vm.ToValue(b.String()) 139 return value 140 }, 141 "gohan_exec": func(call otto.FunctionCall) otto.Value { 142 if len(call.ArgumentList) != 2 { 143 panic("Wrong number of arguments in gohan_db_create call.") 144 } 145 rawCommand, _ := call.Argument(0).Export() 146 command, ok := rawCommand.(string) 147 if !ok { 148 return otto.NullValue() 149 } 150 stringArgs := []string{} 151 rawArgs, _ := call.Argument(1).Export() 152 args, ok := rawArgs.([]interface{}) 153 if !ok { 154 return otto.NullValue() 155 } 156 for _, arg := range args { 157 stringArgs = append(stringArgs, arg.(string)) 158 } 159 out, err := exec.Command(command, stringArgs...).Output() 160 resp := map[string]string{} 161 if err != nil { 162 resp["status"] = "error" 163 resp["output"] = err.Error() 164 } else { 165 resp["status"] = "success" 166 resp["output"] = string(out) 167 } 168 value, _ := vm.ToValue(resp) 169 return value 170 }, 171 } 172 173 for name, object := range builtins { 174 vm.Set(name, object) 175 } 176 177 } 178 RegisterInit(gohanUtilInit) 179 } 180 181 func gohanHTTP(method, rawURL string, headers interface{}, postData interface{}, options interface{}) (int, http.Header, string, error) { 182 client := &http.Client{} 183 var reader io.Reader 184 if postData != nil { 185 log.Debug("post data %v", postData) 186 jsonByte, err := json.Marshal(postData) 187 if err != nil { 188 return 0, http.Header{}, "", err 189 } 190 log.Debug("request data: %s", string(jsonByte)) 191 reader = bytes.NewBuffer(jsonByte) 192 } 193 request, err := http.NewRequest(method, rawURL, reader) 194 if headers != nil { 195 headerMap := headers.(map[string]interface{}) 196 for key, value := range headerMap { 197 request.Header.Add(key, value.(string)) 198 } 199 } 200 if err != nil { 201 return 0, http.Header{}, "", err 202 } 203 204 if options != nil { 205 if value, ok := options.(map[string]interface{})["opaque_url"]; ok { 206 if value.(bool) == true { 207 request.URL = &url.URL{ 208 Scheme: request.URL.Scheme, 209 Host: request.URL.Host, 210 Opaque: rawURL, 211 } 212 } 213 } 214 } 215 resp, err := client.Do(request) 216 if err != nil { 217 return 0, http.Header{}, "", err 218 } 219 defer resp.Body.Close() 220 221 body, err := ioutil.ReadAll(resp.Body) 222 if err != nil { 223 return 0, resp.Header, "", err 224 } 225 return resp.StatusCode, resp.Header, string(body), nil 226 }