ecal.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. EndpointECALInst creates a new endpoint handler.
  34. */
  35. func EndpointECALInst() 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. query[k] = scope.ConvertJSONToECALObject(v)
  87. }
  88. header := map[interface{}]interface{}{}
  89. for k, v := range r.Header {
  90. header[k] = scope.ConvertJSONToECALObject(v)
  91. }
  92. proc := api.SI.Interpreter.RuntimeProvider.Processor
  93. event := engine.NewEvent(fmt.Sprintf("WebRequest"), eventKind,
  94. map[interface{}]interface{}{
  95. "path": strings.Join(resources, "/"),
  96. "pathList": resources,
  97. "bodyString": string(body),
  98. "bodyJSON": scope.ConvertJSONToECALObject(data),
  99. "query": query,
  100. "method": r.Method,
  101. "header": header,
  102. })
  103. var m engine.Monitor
  104. if m, err = proc.AddEventAndWait(event, nil); err == nil {
  105. if m != nil {
  106. var headers map[interface{}]interface{}
  107. status := 0
  108. var body []byte
  109. for _, e := range m.(*engine.RootMonitor).AllErrors() {
  110. if len(e.ErrorMap) > 0 {
  111. for _, e := range e.ErrorMap {
  112. if re, ok := e.(*util.RuntimeErrorWithDetail); ok && re.Type == dbfunc.ErrWebEventHandled {
  113. res := re.Data.(map[interface{}]interface{})
  114. if status, err = strconv.Atoi(fmt.Sprint(res["status"])); err == nil {
  115. headers, _ = res["header"].(map[interface{}]interface{})
  116. body, err = json.Marshal(scope.ConvertECALToJSONObject(res["body"]))
  117. }
  118. } else {
  119. err = e
  120. }
  121. break
  122. }
  123. break
  124. }
  125. }
  126. if status != 0 {
  127. for k, v := range headers {
  128. w.Header().Set(fmt.Sprint(k), fmt.Sprint(v))
  129. }
  130. w.WriteHeader(status)
  131. fmt.Fprintln(w, string(body))
  132. return
  133. }
  134. }
  135. }
  136. }
  137. if err != nil {
  138. api.SI.Interpreter.RuntimeProvider.Logger.LogError(err)
  139. }
  140. }
  141. }
  142. http.Error(w, "Resource was not found", http.StatusNotFound)
  143. }
  144. /*
  145. SwaggerDefs is used to describe the endpoint in swagger.
  146. */
  147. func (ee *ecalEndpoint) SwaggerDefs(s map[string]interface{}) {
  148. desc := map[string]interface{}{
  149. "summary": "Forward web requests to the ECAL backend.",
  150. "description": "The ecal endpoint forwards web requests to the ECAL backend.",
  151. "produces": []string{
  152. "text/plain",
  153. "application/json",
  154. },
  155. "responses": map[string]interface{}{
  156. "200": map[string]interface{}{
  157. "description": "A result object generated by ECAL scripts.",
  158. },
  159. "default": map[string]interface{}{
  160. "description": "Error response",
  161. "schema": map[string]interface{}{
  162. "$ref": "#/definitions/Error",
  163. },
  164. },
  165. },
  166. }
  167. s["paths"].(map[string]interface{})["/ecal"] = map[string]interface{}{
  168. "get": desc,
  169. "post": desc,
  170. "put": desc,
  171. "delete": desc,
  172. }
  173. s["paths"].(map[string]interface{})["/api"] = map[string]interface{}{
  174. "get": desc,
  175. "post": desc,
  176. "put": desc,
  177. "delete": desc,
  178. }
  179. // Add generic error object to definition
  180. s["definitions"].(map[string]interface{})["Error"] = map[string]interface{}{
  181. "description": "A human readable error mesage.",
  182. "type": "string",
  183. }
  184. }