readrow.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. "strings"
  11. "cloud.google.com/go/bigtable"
  12. )
  13. // fetch a single row by the row key
  14. func ReadRow(w http.ResponseWriter, r *http.Request) {
  15. // Set CORS headers for the preflight request
  16. if r.Method == http.MethodOptions {
  17. w.Header().Set("Access-Control-Allow-Origin", "*")
  18. w.Header().Set("Access-Control-Allow-Methods", "POST")
  19. w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  20. w.Header().Set("Access-Control-Max-Age", "3600")
  21. w.WriteHeader(http.StatusNoContent)
  22. return
  23. }
  24. // Set CORS headers for the main request.
  25. w.Header().Set("Access-Control-Allow-Origin", "*")
  26. var emitterChain, emitterAddress, sequence, rowKey string
  27. // allow GET requests with querystring params, or POST requests with json body.
  28. switch r.Method {
  29. case http.MethodGet:
  30. queryParams := r.URL.Query()
  31. emitterChain = queryParams.Get("emitterChain")
  32. emitterAddress = queryParams.Get("emitterAddress")
  33. sequence = queryParams.Get("sequence")
  34. readyCheck := queryParams.Get("readyCheck")
  35. if readyCheck != "" {
  36. // for running in devnet
  37. w.WriteHeader(http.StatusOK)
  38. fmt.Fprint(w, html.EscapeString("ready"))
  39. return
  40. }
  41. // check for empty values
  42. if emitterChain == "" || emitterAddress == "" || sequence == "" {
  43. fmt.Fprint(w, "query params ['emitterChain', 'emitterAddress', 'sequence'] cannot be empty")
  44. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  45. return
  46. }
  47. case http.MethodPost:
  48. // declare request body properties
  49. var d struct {
  50. EmitterChain string `json:"emitterChain"`
  51. EmitterAddress string `json:"emitterAddress"`
  52. Sequence string `json:"sequence"`
  53. }
  54. // deserialize request body
  55. if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
  56. switch err {
  57. case io.EOF:
  58. fmt.Fprint(w, "request body required")
  59. return
  60. default:
  61. log.Printf("json.NewDecoder: %v", err)
  62. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  63. return
  64. }
  65. }
  66. // check for empty values
  67. if d.EmitterChain == "" || d.EmitterAddress == "" || d.Sequence == "" {
  68. fmt.Fprint(w, "body values ['emitterChain', 'emitterAddress', 'sequence'] cannot be empty")
  69. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  70. return
  71. }
  72. emitterChain = d.EmitterChain
  73. emitterAddress = d.EmitterAddress
  74. sequence = d.Sequence
  75. default:
  76. http.Error(w, "405 - Method Not Allowed", http.StatusMethodNotAllowed)
  77. log.Println("Method Not Allowed")
  78. return
  79. }
  80. // pad sequence to 16 characters
  81. if len(sequence) <= 15 {
  82. sequence = fmt.Sprintf("%016s", sequence)
  83. }
  84. // convert chain name to chainID
  85. if len(emitterChain) > 1 {
  86. chainNameMap := map[string]string{
  87. "solana": "1",
  88. "ethereum": "2",
  89. "terra": "3",
  90. "bsc": "4",
  91. "polygon": "5",
  92. }
  93. lowercaseChain := strings.ToLower(emitterChain)
  94. if _, ok := chainNameMap[lowercaseChain]; ok {
  95. emitterChain = chainNameMap[lowercaseChain]
  96. }
  97. }
  98. rowKey = emitterChain + ":" + emitterAddress + ":" + sequence
  99. row, err := tbl.ReadRow(r.Context(), rowKey, bigtable.RowFilter(bigtable.LatestNFilter(1)))
  100. if err != nil {
  101. http.Error(w, "Error reading rows", http.StatusInternalServerError)
  102. log.Printf("tbl.ReadRows(): %v", err)
  103. return
  104. }
  105. if row == nil {
  106. http.NotFound(w, r)
  107. log.Printf("did not find row for key %v", rowKey)
  108. return
  109. }
  110. details := makeDetails(row)
  111. jsonBytes, err := json.Marshal(details)
  112. if err != nil {
  113. w.WriteHeader(http.StatusInternalServerError)
  114. w.Write([]byte(err.Error()))
  115. log.Println(err.Error())
  116. return
  117. }
  118. w.WriteHeader(http.StatusOK)
  119. w.Write(jsonBytes)
  120. }