142 lines
4.1 KiB
Go
142 lines
4.1 KiB
Go
// 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)
|
|
}
|