transaction.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // Package p contains an HTTP Cloud Function.
  2. package p
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "html"
  7. "io"
  8. "log"
  9. "net/http"
  10. "cloud.google.com/go/bigtable"
  11. )
  12. // fetch a single row by transaction identifier
  13. func Transaction(w http.ResponseWriter, r *http.Request) {
  14. // Set CORS headers for the preflight request
  15. if r.Method == http.MethodOptions {
  16. w.Header().Set("Access-Control-Allow-Origin", "*")
  17. w.Header().Set("Access-Control-Allow-Methods", "POST")
  18. w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  19. w.Header().Set("Access-Control-Max-Age", "3600")
  20. w.WriteHeader(http.StatusNoContent)
  21. return
  22. }
  23. // Set CORS headers for the main request.
  24. w.Header().Set("Access-Control-Allow-Origin", "*")
  25. var transactionID string
  26. // allow GET requests with querystring params, or POST requests with json body.
  27. switch r.Method {
  28. case http.MethodGet:
  29. queryParams := r.URL.Query()
  30. transactionID = queryParams.Get("id")
  31. readyCheck := queryParams.Get("readyCheck")
  32. if readyCheck != "" {
  33. // for running in devnet
  34. w.WriteHeader(http.StatusOK)
  35. fmt.Fprint(w, html.EscapeString("ready"))
  36. return
  37. }
  38. case http.MethodPost:
  39. // declare request body properties
  40. var d struct {
  41. ID string `json:"id"`
  42. }
  43. // deserialize request body
  44. if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
  45. switch err {
  46. case io.EOF:
  47. // do nothing, empty body is ok
  48. default:
  49. log.Printf("json.NewDecoder: %v", err)
  50. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  51. return
  52. }
  53. }
  54. transactionID = d.ID
  55. default:
  56. http.Error(w, "405 - Method Not Allowed", http.StatusMethodNotAllowed)
  57. log.Println("Method Not Allowed")
  58. return
  59. }
  60. if transactionID == "" {
  61. fmt.Fprint(w, "id cannot be blank")
  62. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  63. }
  64. var result bigtable.Row
  65. readErr := tbl.ReadRows(r.Context(), bigtable.PrefixRange(""), func(row bigtable.Row) bool {
  66. result = row
  67. return true
  68. }, bigtable.RowFilter(bigtable.ValueFilter(transactionID)))
  69. if readErr != nil {
  70. log.Fatalf("failed to read rows: %v", readErr)
  71. }
  72. if result == nil {
  73. http.NotFound(w, r)
  74. log.Printf("did not find row with transaction ID %v", transactionID)
  75. return
  76. }
  77. key := result.Key()
  78. row, err := tbl.ReadRow(r.Context(), key, bigtable.RowFilter(bigtable.LatestNFilter(1)))
  79. if err != nil {
  80. w.WriteHeader(http.StatusInternalServerError)
  81. w.Write([]byte(err.Error()))
  82. log.Fatalf("Could not read row with key %s: %v", key, err)
  83. }
  84. details := makeDetails(row)
  85. jsonBytes, err := json.Marshal(details)
  86. if err != nil {
  87. w.WriteHeader(http.StatusInternalServerError)
  88. w.Write([]byte(err.Error()))
  89. log.Println(err.Error())
  90. return
  91. }
  92. w.WriteHeader(http.StatusOK)
  93. w.Write(jsonBytes)
  94. }