ecal.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package v1
  11. import (
  12. "encoding/json"
  13. "fmt"
  14. "io/ioutil"
  15. "net/http"
  16. "strconv"
  17. "strings"
  18. "devt.de/krotik/ecal/engine"
  19. "devt.de/krotik/ecal/scope"
  20. "devt.de/krotik/ecal/util"
  21. "devt.de/krotik/eliasdb/api"
  22. "devt.de/krotik/eliasdb/ecal/dbfunc"
  23. )
  24. /*
  25. EndpointECALInternal is the ECAL endpoint URL (rooted) for internal operations. Handles everything under ecal/...
  26. */
  27. const EndpointECALInternal = api.APIRoot + "/ecal/"
  28. /*
  29. EndpointECALPublic is the ECAL endpoint URL (rooted) for public API operations. Handles everything under api/...
  30. */
  31. const EndpointECALPublic = api.APIRoot + "/api/"
  32. /*
  33. ECALEndpointInst creates a new endpoint handler.
  34. */
  35. func ECALEndpointInst() api.RestEndpointHandler {
  36. return &ecalEndpoint{}
  37. }
  38. /*
  39. Handler object for ecal operations.
  40. */
  41. type ecalEndpoint struct {
  42. *api.DefaultEndpointHandler
  43. }
  44. /*
  45. HandleGET handles a GET request.
  46. */
  47. func (ee *ecalEndpoint) HandleGET(w http.ResponseWriter, r *http.Request, resources []string) {
  48. ee.forwardRequest(w, r, resources)
  49. }
  50. /*
  51. HandlePOST handles a POST request.
  52. */
  53. func (ee *ecalEndpoint) HandlePOST(w http.ResponseWriter, r *http.Request, resources []string) {
  54. ee.forwardRequest(w, r, resources)
  55. }
  56. /*
  57. HandlePUT handles a PUT request.
  58. */
  59. func (ee *ecalEndpoint) HandlePUT(w http.ResponseWriter, r *http.Request, resources []string) {
  60. ee.forwardRequest(w, r, resources)
  61. }
  62. /*
  63. HandleDELETE handles a DELETE request.
  64. */
  65. func (ee *ecalEndpoint) HandleDELETE(w http.ResponseWriter, r *http.Request, resources []string) {
  66. ee.forwardRequest(w, r, resources)
  67. }
  68. func (ee *ecalEndpoint) forwardRequest(w http.ResponseWriter, r *http.Request, resources []string) {
  69. if api.SI != nil {
  70. // Make sure the request we are handling comes from a known path for ECAL
  71. isPublic := strings.HasPrefix(r.URL.Path, EndpointECALPublic)
  72. isInternal := strings.HasPrefix(r.URL.Path, EndpointECALInternal)
  73. if isPublic || isInternal {
  74. var eventKind []string
  75. body, err := ioutil.ReadAll(r.Body)
  76. if err == nil {
  77. if isPublic {
  78. eventKind = []string{"db", "web", "api"}
  79. } else {
  80. eventKind = []string{"db", "web", "ecal"}
  81. }
  82. var data interface{}
  83. json.Unmarshal(body, &data)
  84. query := map[interface{}]interface{}{}
  85. for k, v := range r.URL.Query() {
  86. values := make([]interface{}, 0)
  87. for _, val := range v {
  88. values = append(values, val)
  89. }
  90. query[k] = values
  91. }
  92. header := map[interface{}]interface{}{}
  93. for k, v := range r.Header {
  94. header[k] = scope.ConvertJSONToECALObject(v)
  95. }
  96. proc := api.SI.Interpreter.RuntimeProvider.Processor
  97. event := engine.NewEvent(fmt.Sprintf("WebRequest"), eventKind,
  98. map[interface{}]interface{}{
  99. "path": strings.Join(resources, "/"),
  100. "pathList": resources,
  101. "bodyString": string(body),
  102. "bodyJSON": scope.ConvertJSONToECALObject(data),
  103. "query": query,
  104. "method": r.Method,
  105. "header": header,
  106. })
  107. var m engine.Monitor
  108. if m, err = proc.AddEventAndWait(event, nil); err == nil {
  109. if m != nil {
  110. var headers map[interface{}]interface{}
  111. status := 0
  112. var body []byte
  113. for _, e := range m.(*engine.RootMonitor).AllErrors() {
  114. if len(e.ErrorMap) > 0 {
  115. for _, e := range e.ErrorMap {
  116. if re, ok := e.(*util.RuntimeErrorWithDetail); ok && re.Type == dbfunc.ErrWebEventHandled {
  117. res := re.Data.(map[interface{}]interface{})
  118. if status, err = strconv.Atoi(fmt.Sprint(res["status"])); err == nil {
  119. headers, _ = res["header"].(map[interface{}]interface{})
  120. body, err = json.Marshal(scope.ConvertECALToJSONObject(res["body"]))
  121. }
  122. } else {
  123. err = e
  124. }
  125. break
  126. }
  127. break
  128. }
  129. }
  130. if status != 0 {
  131. for k, v := range headers {
  132. w.Header().Set(fmt.Sprint(k), fmt.Sprint(v))
  133. }
  134. w.WriteHeader(status)
  135. fmt.Fprintln(w, string(body))
  136. return
  137. }
  138. }
  139. }
  140. }
  141. if err != nil {
  142. api.SI.Interpreter.RuntimeProvider.Logger.LogError(err)
  143. }
  144. }
  145. }
  146. http.Error(w, "Resource was not found", http.StatusNotFound)
  147. }
  148. /*
  149. SwaggerDefs is used to describe the endpoint in swagger.
  150. */
  151. func (ee *ecalEndpoint) SwaggerDefs(s map[string]interface{}) {
  152. desc := map[string]interface{}{
  153. "summary": "Forward web requests to the ECAL backend.",
  154. "description": "The ecal endpoint forwards web requests to the ECAL backend.",
  155. "produces": []string{
  156. "text/plain",
  157. "application/json",
  158. },
  159. "responses": map[string]interface{}{
  160. "200": map[string]interface{}{
  161. "description": "A result object generated by ECAL scripts.",
  162. },
  163. "default": map[string]interface{}{
  164. "description": "Error response",
  165. "schema": map[string]interface{}{
  166. "$ref": "#/definitions/Error",
  167. },
  168. },
  169. },
  170. }
  171. s["paths"].(map[string]interface{})["/ecal"] = map[string]interface{}{
  172. "get": desc,
  173. "post": desc,
  174. "put": desc,
  175. "delete": desc,
  176. }
  177. s["paths"].(map[string]interface{})["/api"] = map[string]interface{}{
  178. "get": desc,
  179. "post": desc,
  180. "put": desc,
  181. "delete": desc,
  182. }
  183. // Add generic error object to definition
  184. s["definitions"].(map[string]interface{})["Error"] = map[string]interface{}{
  185. "description": "A human readable error mesage.",
  186. "type": "string",
  187. }
  188. }