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  }