eql.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. "net/http"
  15. "devt.de/krotik/eliasdb/api"
  16. "devt.de/krotik/eliasdb/eql"
  17. "devt.de/krotik/eliasdb/eql/parser"
  18. )
  19. /*
  20. EndpointEql is the eql endpoint URL (rooted). Handles everything under eql/...
  21. */
  22. const EndpointEql = api.APIRoot + APIv1 + "/eql/"
  23. /*
  24. EqlEndpointInst creates a new endpoint handler.
  25. */
  26. func EqlEndpointInst() api.RestEndpointHandler {
  27. return &eqlEndpoint{}
  28. }
  29. /*
  30. Handler object for eql operations.
  31. */
  32. type eqlEndpoint struct {
  33. *api.DefaultEndpointHandler
  34. }
  35. /*
  36. HandlePOST handles REST calls to transform EQL queries.
  37. */
  38. func (e *eqlEndpoint) HandlePOST(w http.ResponseWriter, r *http.Request, resources []string) {
  39. dec := json.NewDecoder(r.Body)
  40. data := make(map[string]interface{})
  41. if err := dec.Decode(&data); err != nil {
  42. http.Error(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
  43. return
  44. }
  45. // Handle query and ast requests
  46. query, ok1 := data["query"]
  47. ast, ok2 := data["ast"]
  48. if ok1 || ok2 {
  49. res := make(map[string]interface{})
  50. if ok1 {
  51. resast, err := eql.ParseQuery("request", fmt.Sprint(query))
  52. if err != nil {
  53. http.Error(w, err.Error(), http.StatusBadRequest)
  54. return
  55. }
  56. res["ast"] = resast.Plain()
  57. }
  58. if ok2 {
  59. astmap, ok := ast.(map[string]interface{})
  60. if !ok {
  61. http.Error(w, "Plain AST object expected as 'ast' value", http.StatusBadRequest)
  62. return
  63. }
  64. // Try to create a proper AST from plain AST
  65. astnode, err := parser.ASTFromPlain(astmap)
  66. if err != nil {
  67. http.Error(w, err.Error(), http.StatusBadRequest)
  68. return
  69. }
  70. // Now pretty print the AST
  71. ppres, err := parser.PrettyPrint(astnode)
  72. if err != nil {
  73. http.Error(w, err.Error(), http.StatusBadRequest)
  74. return
  75. }
  76. res["query"] = ppres
  77. }
  78. w.Header().Set("content-type", "application/json; charset=utf-8")
  79. json.NewEncoder(w).Encode(res)
  80. return
  81. }
  82. http.Error(w, "Need either a query or an ast parameter", http.StatusBadRequest)
  83. }
  84. /*
  85. SwaggerDefs is used to describe the endpoint in swagger.
  86. */
  87. func (e *eqlEndpoint) SwaggerDefs(s map[string]interface{}) {
  88. s["paths"].(map[string]interface{})["/v1/eql"] = map[string]interface{}{
  89. "post": map[string]interface{}{
  90. "summary": "EQL parser and pretty printer endpoint.",
  91. "description": "The eql endpoint should be used to parse a given EQL query into an Abstract Syntax Tree or pretty print a given Abstract Syntax Tree into an EQL query.",
  92. "consumes": []string{
  93. "application/json",
  94. },
  95. "produces": []string{
  96. "text/plain",
  97. "application/json",
  98. },
  99. "parameters": []map[string]interface{}{
  100. map[string]interface{}{
  101. "name": "data",
  102. "in": "body",
  103. "description": "Query or AST which should be converted.",
  104. "required": true,
  105. "schema": map[string]interface{}{
  106. "type": "object",
  107. "properties": map[string]interface{}{
  108. "query": map[string]interface{}{
  109. "description": "Query which should be parsed.",
  110. "type": "string",
  111. },
  112. "ast": map[string]interface{}{
  113. "description": "AST which should be pretty printed.",
  114. "type": "object",
  115. },
  116. },
  117. },
  118. },
  119. },
  120. "responses": map[string]interface{}{
  121. "200": map[string]interface{}{
  122. "description": "The operation was successful.",
  123. "schema": map[string]interface{}{
  124. "type": "object",
  125. "properties": map[string]interface{}{
  126. "ast": map[string]interface{}{
  127. "description": "The resulting AST if a query was parsed.",
  128. "type": "object",
  129. },
  130. "query": map[string]interface{}{
  131. "description": "The pretty printed query if an AST was given.",
  132. "type": "string",
  133. },
  134. },
  135. },
  136. },
  137. "default": map[string]interface{}{
  138. "description": "Error response",
  139. "schema": map[string]interface{}{
  140. "$ref": "#/definitions/Error",
  141. },
  142. },
  143. },
  144. },
  145. }
  146. }