|
|
@@ -0,0 +1,141 @@
|
|
|
+// Package p contains an HTTP Cloud Function.
|
|
|
+package p
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "io"
|
|
|
+ "log"
|
|
|
+ "net/http"
|
|
|
+
|
|
|
+ "cloud.google.com/go/bigtable"
|
|
|
+)
|
|
|
+
|
|
|
+// fetch rows by matching payload value
|
|
|
+func FindValues(w http.ResponseWriter, r *http.Request) {
|
|
|
+ // Set CORS headers for the preflight request
|
|
|
+ if r.Method == http.MethodOptions {
|
|
|
+ w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
|
+ w.Header().Set("Access-Control-Allow-Methods", "POST")
|
|
|
+ w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
|
|
+ w.Header().Set("Access-Control-Max-Age", "3600")
|
|
|
+ w.WriteHeader(http.StatusNoContent)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // Set CORS headers for the main request.
|
|
|
+ w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
|
+
|
|
|
+ var columnFamily, columnName, value, emitterChain, emitterAddress string
|
|
|
+
|
|
|
+ // allow GET requests with querystring params, or POST requests with json body.
|
|
|
+ switch r.Method {
|
|
|
+ case http.MethodGet:
|
|
|
+ queryParams := r.URL.Query()
|
|
|
+ columnFamily = queryParams.Get("columnFamily")
|
|
|
+ columnName = queryParams.Get("columnName")
|
|
|
+ value = queryParams.Get("value")
|
|
|
+ emitterChain = queryParams.Get("emitterChain")
|
|
|
+ emitterAddress = queryParams.Get("emitterAddress")
|
|
|
+
|
|
|
+ // check for empty values
|
|
|
+ if columnFamily == "" || columnName == "" || value == "" {
|
|
|
+ fmt.Fprint(w, "query params ['columnFamily', 'columnName', 'value'] cannot be empty")
|
|
|
+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ case http.MethodPost:
|
|
|
+ // declare request body properties
|
|
|
+ var d struct {
|
|
|
+ ColumnFamily string `json:"columnFamily"`
|
|
|
+ ColumnName string `json:"columnName"`
|
|
|
+ Value string `json:"value"`
|
|
|
+ EmitterChain string `json:"emitterChain"`
|
|
|
+ EmitterAddress string `json:"emitterAddress"`
|
|
|
+ }
|
|
|
+
|
|
|
+ // deserialize request body
|
|
|
+ if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
|
|
|
+ switch err {
|
|
|
+ case io.EOF:
|
|
|
+ fmt.Fprint(w, "request body required")
|
|
|
+ return
|
|
|
+ default:
|
|
|
+ log.Printf("json.NewDecoder: %v", err)
|
|
|
+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // check for empty values
|
|
|
+ if d.ColumnFamily == "" || d.ColumnName == "" || d.Value == "" {
|
|
|
+ fmt.Fprint(w, "body values ['columnFamily', 'columnName', 'value'] cannot be empty")
|
|
|
+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ columnFamily = d.ColumnFamily
|
|
|
+ columnName = d.ColumnName
|
|
|
+ value = d.Value
|
|
|
+ emitterChain = d.EmitterChain
|
|
|
+ emitterAddress = d.EmitterAddress
|
|
|
+ default:
|
|
|
+ http.Error(w, "405 - Method Not Allowed", http.StatusMethodNotAllowed)
|
|
|
+ log.Println("Method Not Allowed")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if columnFamily != "TokenTransferPayload" &&
|
|
|
+ columnFamily != "AssetMetaPayload" &&
|
|
|
+ columnFamily != "NFTTransferPayload" &&
|
|
|
+ columnFamily != "TokenTransferDetails" {
|
|
|
+ fmt.Fprint(w, "columnFamily must be one of: ['TokenTransferPayload', 'AssetMetaPayload', 'NFTTransferPayload', 'TokenTransferDetails']")
|
|
|
+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ prefix := ""
|
|
|
+ if emitterChain != "" {
|
|
|
+ prefix = emitterChain
|
|
|
+ if emitterAddress != "" {
|
|
|
+ prefix = emitterChain + emitterAddress
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ results := []bigtable.Row{}
|
|
|
+ err := tbl.ReadRows(r.Context(), bigtable.PrefixRange(prefix), func(row bigtable.Row) bool {
|
|
|
+ results = append(results, row)
|
|
|
+ return true
|
|
|
+ }, bigtable.RowFilter(
|
|
|
+ bigtable.ConditionFilter(
|
|
|
+ bigtable.ChainFilters(
|
|
|
+ bigtable.FamilyFilter(columnFamily),
|
|
|
+ bigtable.ColumnFilter(columnName),
|
|
|
+ bigtable.ValueFilter(value),
|
|
|
+ ),
|
|
|
+ bigtable.ChainFilters(
|
|
|
+ bigtable.PassAllFilter(),
|
|
|
+ bigtable.LatestNFilter(1),
|
|
|
+ ),
|
|
|
+ bigtable.BlockAllFilter(),
|
|
|
+ )))
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ http.Error(w, "Error reading rows", http.StatusInternalServerError)
|
|
|
+ log.Printf("tbl.ReadRows(): %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ details := []Details{}
|
|
|
+ for _, result := range results {
|
|
|
+ detail := makeDetails(result)
|
|
|
+ details = append(details, *detail)
|
|
|
+ }
|
|
|
+ jsonBytes, err := json.Marshal(details)
|
|
|
+ if err != nil {
|
|
|
+ w.WriteHeader(http.StatusInternalServerError)
|
|
|
+ w.Write([]byte(err.Error()))
|
|
|
+ log.Println(err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ w.WriteHeader(http.StatusOK)
|
|
|
+ w.Write(jsonBytes)
|
|
|
+}
|