aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorterminaldweller <devi@terminaldweller.com>2024-02-29 00:34:46 +0000
committerterminaldweller <devi@terminaldweller.com>2024-02-29 00:34:46 +0000
commit7baae789e2be9bfc3671f66ff0ad6a7da684c993 (patch)
treec082fb963af6b91f60493bd03e0ba13757e2aeb3
parentfixes #5 (diff)
downloadhived-7baae789e2be9bfc3671f66ff0ad6a7da684c993.tar.gz
hived-7baae789e2be9bfc3671f66ff0ad6a7da684c993.zip
added new ticker endpoint that periodically sends crypto prices over telegram
-rw-r--r--hived/alertHandlers.go188
-rw-r--r--hived/hived.go243
-rw-r--r--hived/hived_responses.go2
-rw-r--r--hived/hived_test.go12
-rw-r--r--hived/tickerHandlers.go171
-rwxr-xr-xtest/endpoints.sh24
6 files changed, 483 insertions, 157 deletions
diff --git a/hived/alertHandlers.go b/hived/alertHandlers.go
new file mode 100644
index 0000000..fd49835
--- /dev/null
+++ b/hived/alertHandlers.go
@@ -0,0 +1,188 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "time"
+
+ "github.com/rs/zerolog/log"
+)
+
+func (alertHandler Handler) HandleAlertPost(writer http.ResponseWriter, request *http.Request) {
+ var bodyJSON addAlertJSONType
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ err := json.NewDecoder(request.Body).Decode(&bodyJSON)
+ if err != nil {
+ log.Printf(err.Error())
+
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "not all parameters are valid.",
+ })
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+ }
+
+ if bodyJSON.Name == "" || bodyJSON.Expr == "" {
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "not all parameters are valid.",
+ })
+ if err != nil {
+ log.Error().Err(errFailedUnmarshall)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ defer cancel()
+
+ key := "alert:" + bodyJSON.Name
+ alertHandler.rdb.Set(ctx, bodyJSON.Name, bodyJSON.Expr, 0)
+ alertHandler.rdb.SAdd(ctx, "alertkeys", key)
+
+ err = json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": true,
+ "error": "",
+ })
+
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+}
+
+func (alertHandler Handler) HandleAlertDelete(writer http.ResponseWriter, request *http.Request) {
+ var identifier string
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ params := request.URL.Query()
+
+ for key, value := range params {
+ switch key {
+ case "key":
+ identifier = value[0]
+ default:
+ log.Error().Err(errUnknownParam)
+ }
+ }
+
+ if identifier == "" {
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "Id parameter is not valid.",
+ })
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ defer cancel()
+
+ alertHandler.rdb.Del(ctx, identifier)
+ setKey := "alert:" + identifier
+ alertHandler.rdb.SRem(ctx, "alertkeys", setKey)
+ log.Printf(setKey)
+
+ err := json.NewEncoder(writer).Encode(struct {
+ IsSuccessful bool `json:"isSuccessful"`
+ Err string `json:"err"`
+ }{IsSuccessful: true, Err: ""})
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+}
+
+func (alertHandler Handler) HandleAlertGet(writer http.ResponseWriter, request *http.Request) {
+ var identifier string
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ params := request.URL.Query()
+ for key, value := range params {
+ switch key {
+ case "key":
+ identifier = value[0]
+ default:
+ log.Error().Err(errUnknownParam)
+ }
+ }
+
+ if identifier == "" {
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "Id parameter is not valid.",
+ })
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ defer cancel()
+
+ redisResult := alertHandler.rdb.Get(ctx, identifier)
+
+ redisResultString, err := redisResult.Result()
+ if err != nil {
+ log.Err(err)
+ }
+
+ var ErrorString string
+ if err == nil {
+ ErrorString = ""
+ } else {
+ ErrorString = err.Error()
+ }
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ err = json.NewEncoder(writer).Encode(struct {
+ IsSuccessful bool `json:"isSuccessful"`
+ Error string `json:"error"`
+ Key string `json:"key"`
+ Expr string `json:"expr"`
+ }{IsSuccessful: true, Error: ErrorString, Key: identifier, Expr: redisResultString})
+
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+}
+
+func alertHandler(writer http.ResponseWriter, request *http.Request) {
+ addSecureHeaders(&writer)
+
+ handler := Handler{rdb: rdb}
+
+ switch request.Method {
+ case http.MethodPost:
+ handler.HandleAlertPost(writer, request)
+ case http.MethodPut:
+ http.Error(writer, "Method is not supported.", http.StatusNotFound)
+ case http.MethodPatch:
+ http.Error(writer, "Method is not supported.", http.StatusNotFound)
+ case http.MethodDelete:
+ handler.HandleAlertDelete(writer, request)
+ case http.MethodGet:
+ handler.HandleAlertGet(writer, request)
+ default:
+ http.Error(writer, "Method is not supported.", http.StatusNotFound)
+ }
+}
diff --git a/hived/hived.go b/hived/hived.go
index 580a1f8..d72d427 100644
--- a/hived/hived.go
+++ b/hived/hived.go
@@ -37,6 +37,7 @@ const (
redisContextTimeout = 2
pingTimeout = 5
alertCheckIntervalDefault = 600
+ tickerCheckIntervalDefault = 600
redisCacheDurationMultiplier = 1_000_000
cacheDurationdefault = 300_000
telegramTimeout = 10
@@ -139,19 +140,24 @@ func getPrice(ctx context.Context,
priceChan chan<- priceChanStruct,
errChan chan<- errorChanStruct,
) {
+ fmt.Println("zcheckpoint 1: ", name)
val, err := rdb.Get(ctx, name+"_price").Float64()
+ fmt.Println("zcheckpoint 2")
if err != nil {
+ fmt.Println("zcheckpoint 3: ", name)
source := chooseGetPriceSource()
if source == CryptoCompareSource {
getPriceFromCryptoCompare(ctx, name, unit, waitGroup, priceChan, errChan)
}
} else {
+ fmt.Println("zcheckpoint 4: ", name)
priceChan <- priceChanStruct{name: name, price: val}
errChan <- errorChanStruct{hasError: false, err: nil}
waitGroup.Done()
}
+ fmt.Println("zcheckpoint 5: ", name)
}
func getPriceFromCryptoCompareErrorHandler(
@@ -175,12 +181,14 @@ func getPriceFromCryptoCompare(
errChan chan<- errorChanStruct,
) {
defer wg.Done()
+ fmt.Println("xcheckpoint 1")
params := "fsym=" + url.QueryEscape(name) + "&" +
"tsyms=" + url.QueryEscape(unit)
path := cryptocomparePriceURL + params
client := GetProxiedClient()
+ fmt.Println("xcheckpoint 2")
req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil)
if err != nil {
@@ -188,6 +196,7 @@ func getPriceFromCryptoCompare(
return
}
+ fmt.Println("xcheckpoint 3")
resp, err := client.Do(req)
if err != nil {
@@ -196,6 +205,7 @@ func getPriceFromCryptoCompare(
return
}
defer resp.Body.Close()
+ fmt.Println("xcheckpoint 4")
jsonBody := make(map[string]float64)
@@ -204,12 +214,15 @@ func getPriceFromCryptoCompare(
getPriceFromCryptoCompareErrorHandler(err, name, priceChan, errChan)
}
+ fmt.Println("xcheckpoint 5")
+
// add a price cache
err = rdb.Set(ctx, name+"_price", jsonBody[unit], time.Duration(*cacheDuration*redisCacheDurationMultiplier)).Err()
if err != nil {
log.Error().Err(err)
}
+ fmt.Println("xcheckpoint 6")
priceChan <- priceChanStruct{name: name, price: jsonBody[unit]}
errChan <- errorChanStruct{hasError: false, err: nil}
@@ -405,6 +418,14 @@ type alertsType struct {
Alerts []alertType `json:"alerts"`
}
+type tickerType struct {
+ Name string `json:"name"`
+}
+
+type tickersType struct {
+ Tickers []tickerType `json:"tickers"`
+}
+
func getAlerts() alertsType {
var alerts alertsType
@@ -426,6 +447,24 @@ func getAlerts() alertsType {
return alerts
}
+func getTickers() tickersType {
+ var tickers tickersType
+
+ ctx := context.Background()
+ keys := rdb.SMembersMap(ctx, "tickerkeys")
+ tickers.Tickers = make([]tickerType, len(keys.Val()))
+ vals := keys.Val()
+
+ tickerIndex := 0
+
+ for key := range vals {
+ tickers.Tickers[tickerIndex].Name = key[7:]
+ tickerIndex++
+ }
+
+ return tickers
+}
+
// FIXME- there is a crash here
func alertManagerWorker(alert alertType) {
expression, err := govaluate.NewEvaluableExpression(alert.Expr)
@@ -505,7 +544,6 @@ func alertManagerWorker(alert alertType) {
if err == nil {
log.Error().Err(err)
}
-
sendToTg("telebot:8000", msgText, tokenInt)
}
@@ -523,184 +561,96 @@ func alertManager(alertsCheckInterval int64) {
}
}
-type addAlertJSONType struct {
- Name string `json:"name"`
- Expr string `json:"expr"`
-}
-
-func (alertHandler AlertHandler) HandleAlertPost(writer http.ResponseWriter, request *http.Request) {
- var bodyJSON addAlertJSONType
-
- writer.Header().Add("Content-Type", "application/json")
-
- err := json.NewDecoder(request.Body).Decode(&bodyJSON)
- if err != nil {
- log.Printf(err.Error())
+func tickerManager(tickerCheckInterval int64) {
+ for {
+ tickers := getTickers()
- err := json.NewEncoder(writer).Encode(map[string]interface{}{
- "isSuccessful": false,
- "error": "not all parameters are valid.",
- })
- if err != nil {
- log.Error().Err(err)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
- }
- }
+ log.Info().Msg(fmt.Sprintf("%v", tickers))
- if bodyJSON.Name == "" || bodyJSON.Expr == "" {
- err := json.NewEncoder(writer).Encode(map[string]interface{}{
- "isSuccessful": false,
- "error": "not all parameters are valid.",
- })
- if err != nil {
- log.Error().Err(errFailedUnmarshall)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
+ for tickerIndex := range tickers.Tickers {
+ go tickerManagerWorker(tickers.Tickers[tickerIndex])
}
- return
- }
-
- ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
- defer cancel()
-
- key := "alert:" + bodyJSON.Name
- alertHandler.rdb.Set(ctx, bodyJSON.Name, bodyJSON.Expr, 0)
- alertHandler.rdb.SAdd(ctx, "alertkeys", key)
-
- err = json.NewEncoder(writer).Encode(map[string]interface{}{
- "isSuccessful": true,
- "error": "",
- })
-
- if err != nil {
- log.Error().Err(err)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
+ time.Sleep(time.Second * time.Duration(tickerCheckInterval))
}
}
-func (alertHandler AlertHandler) HandleAlertDelete(writer http.ResponseWriter, request *http.Request) {
- var identifier string
-
- writer.Header().Add("Content-Type", "application/json")
-
- params := request.URL.Query()
-
- for key, value := range params {
- switch key {
- case "key":
- identifier = value[0]
- default:
- log.Error().Err(errUnknownParam)
- }
- }
+func tickerManagerWorker(ticker tickerType) {
+ var waitGroup sync.WaitGroup
- if identifier == "" {
- err := json.NewEncoder(writer).Encode(map[string]interface{}{
- "isSuccessful": false,
- "error": "Id parameter is not valid.",
- })
- if err != nil {
- log.Error().Err(err)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
- }
+ priceChan := make(chan priceChanStruct, 1)
+ errChan := make(chan errorChanStruct, 1)
- return
- }
+ defer close(errChan)
+ defer close(priceChan)
+ waitGroup.Add(1)
- ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ ctx, cancel := context.WithTimeout(context.Background(), getTimeout*time.Second)
defer cancel()
- alertHandler.rdb.Del(ctx, identifier)
- setKey := "alert:" + identifier
- alertHandler.rdb.SRem(ctx, "alertkeys", setKey)
- log.Printf(setKey)
-
- err := json.NewEncoder(writer).Encode(struct {
- IsSuccessful bool `json:"isSuccessful"`
- Err string `json:"err"`
- }{IsSuccessful: true, Err: ""})
- if err != nil {
- log.Error().Err(err)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
- }
-}
-
-func (alertHandler AlertHandler) HandleAlertGet(writer http.ResponseWriter, request *http.Request) {
- var identifier string
+ fmt.Println("checkpoint 1: ", ticker.Name)
+ go getPrice(ctx, ticker.Name, "USD", &waitGroup, priceChan, errChan)
+ fmt.Println("checkpoint 2")
- writer.Header().Add("Content-Type", "application/json")
+ waitGroup.Wait()
+ fmt.Println("checkpoint 3")
- params := request.URL.Query()
- for key, value := range params {
- switch key {
- case "key":
- identifier = value[0]
- default:
- log.Error().Err(errUnknownParam)
+ select {
+ case err := <-errChan:
+ if err.hasError {
+ log.Error().Err(err.err)
}
+ default:
+ log.Error().Err(errBadLogic)
}
+ fmt.Println("checkpoint 4")
- if identifier == "" {
- err := json.NewEncoder(writer).Encode(map[string]interface{}{
- "isSuccessful": false,
- "error": "Id parameter is not valid.",
- })
- if err != nil {
- log.Error().Err(err)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
- }
-
- return
+ var price priceChanStruct
+ select {
+ case priceCh := <-priceChan:
+ price = priceCh
+ default:
+ log.Error().Err(errBadLogic)
}
+ fmt.Println("checkpoint 5")
- ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
- defer cancel()
-
- redisResult := alertHandler.rdb.Get(ctx, identifier)
+ token := os.Getenv(telegramBotTokenEnvVar)
+ msgText := "ticker: " + ticker.Name + ":" + strconv.FormatFloat(price.price, 'f', -1, 64)
+ tokenInt, err := strconv.ParseInt(token[1:len(token)-1], 10, 64)
- redisResultString, err := redisResult.Result()
- if err != nil {
- log.Err(err)
- }
+ fmt.Println(msgText)
- var ErrorString string
if err == nil {
- ErrorString = ""
- } else {
- ErrorString = err.Error()
+ log.Error().Err(err)
}
+ sendToTg("telebot:8000", msgText, tokenInt)
+}
- writer.Header().Add("Content-Type", "application/json")
-
- err = json.NewEncoder(writer).Encode(struct {
- IsSuccessful bool `json:"isSuccessful"`
- Error string `json:"error"`
- Key string `json:"key"`
- Expr string `json:"expr"`
- }{IsSuccessful: true, Error: ErrorString, Key: identifier, Expr: redisResultString})
+type addAlertJSONType struct {
+ Name string `json:"name"`
+ Expr string `json:"expr"`
+}
- if err != nil {
- log.Error().Err(err)
- http.Error(writer, "internal server error", http.StatusInternalServerError)
- }
+type tickerJSONType struct {
+ Name string `json:"name"`
}
-func alertHandler(writer http.ResponseWriter, request *http.Request) {
+func tickerHandler(writer http.ResponseWriter, request *http.Request) {
addSecureHeaders(&writer)
- alertHandler := AlertHandler{rdb: rdb}
+ handler := Handler{rdb: rdb}
switch request.Method {
case http.MethodPost:
- alertHandler.HandleAlertPost(writer, request)
+ handler.HandleTickerPost(writer, request)
case http.MethodPut:
- alertHandler.HandleAlertPost(writer, request)
+ handler.HandleTickerPost(writer, request)
case http.MethodPatch:
- alertHandler.HandleAlertPost(writer, request)
+ handler.HandleTickerPost(writer, request)
case http.MethodDelete:
- alertHandler.HandleAlertDelete(writer, request)
+ handler.HandleTickerDelete(writer, request)
case http.MethodGet:
- alertHandler.HandleAlertGet(writer, request)
+ handler.HandleTickerGet(writer, request)
default:
http.Error(writer, "Method is not supported.", http.StatusNotFound)
}
@@ -790,6 +740,7 @@ func startServer(gracefulWait time.Duration, flagPort string) {
router.HandleFunc("/crypto/v1/price", PriceHandler)
router.HandleFunc("/crypto/v1/pair", PairHandler)
router.HandleFunc("/crypto/v1/alert", alertHandler)
+ router.HandleFunc("/crypto/v1/ticker", tickerHandler)
router.HandleFunc("/crypto/v1/robots.txt", robotsHandler)
go func() {
@@ -841,6 +792,10 @@ func main() {
"alertinterval",
alertCheckIntervalDefault,
"in seconds, the amount of time between alert checks")
+ tickerCheckInterval := flag.Int64(
+ "interval",
+ tickerCheckIntervalDefault,
+ "in seconds, the amount of time between alert checks")
flag.DurationVar(
&gracefulWait, "gracefulwait",
@@ -860,5 +815,7 @@ func main() {
go alertManager(*alertsCheckInterval)
+ go tickerManager(*tickerCheckInterval)
+
startServer(gracefulWait, *flagPort)
}
diff --git a/hived/hived_responses.go b/hived/hived_responses.go
index 72a340c..1065340 100644
--- a/hived/hived_responses.go
+++ b/hived/hived_responses.go
@@ -2,7 +2,7 @@ package main
import "github.com/go-redis/redis/v8"
-type AlertHandler struct {
+type Handler struct {
rdb *redis.Client
}
diff --git a/hived/hived_test.go b/hived/hived_test.go
index e9c45e4..22cd8dc 100644
--- a/hived/hived_test.go
+++ b/hived/hived_test.go
@@ -96,7 +96,7 @@ func TestAlertHandlerPhase1(t *testing.T) {
req.Header.Set("Content-Type", "application/json")
recorder := httptest.NewRecorder()
- alertHandler := AlertHandler{rdb: rdb}
+ alertHandler := Handler{rdb: rdb}
alertHandler.HandleAlertPost(recorder, req)
errorHandler(recorder, t, err)
@@ -128,7 +128,7 @@ func TestAlertHandlerPhase2(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, endpoint+"/alert?key=alertTest", nil)
recorder := httptest.NewRecorder()
- alertHandler := AlertHandler{rdb: rdb}
+ alertHandler := Handler{rdb: rdb}
alertHandler.HandleAlertGet(recorder, req)
errorHandler(recorder, t, err)
@@ -170,7 +170,7 @@ func TestAlertHandlerPhase3(t *testing.T) {
req.Header.Set("Content-Type", "application/json")
recorder := httptest.NewRecorder()
- alertHandler := AlertHandler{rdb: rdb}
+ alertHandler := Handler{rdb: rdb}
alertHandler.HandleAlertGet(recorder, req)
errorHandler(recorder, t, err)
}
@@ -185,7 +185,7 @@ func TestAlertHandlerPhase4(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, endpoint+"/alert?key=alertTest", nil)
recorder := httptest.NewRecorder()
- alertHandler := AlertHandler{rdb: rdb}
+ alertHandler := Handler{rdb: rdb}
alertHandler.HandleAlertGet(recorder, req)
errorHandler(recorder, t, err)
@@ -217,7 +217,7 @@ func TestAlertHandlerPhase5(t *testing.T) {
req, err := http.NewRequest(http.MethodDelete, endpoint+"/alert?key=alertTest", nil)
recorder := httptest.NewRecorder()
- alertHandler := AlertHandler{rdb: rdb}
+ alertHandler := Handler{rdb: rdb}
alertHandler.HandleAlertGet(recorder, req)
errorHandler(recorder, t, err)
@@ -250,7 +250,7 @@ func TestAlertHandlerPhase6(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, endpoint+"/alert?key=alertTest", nil)
recorder := httptest.NewRecorder()
- alertHandler := AlertHandler{rdb: rdb}
+ alertHandler := Handler{rdb: rdb}
alertHandler.HandleAlertGet(recorder, req)
errorHandler(recorder, t, err)
diff --git a/hived/tickerHandlers.go b/hived/tickerHandlers.go
new file mode 100644
index 0000000..d857eaf
--- /dev/null
+++ b/hived/tickerHandlers.go
@@ -0,0 +1,171 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/rs/zerolog/log"
+)
+
+func (tickerHandler Handler) HandleTickerGet(writer http.ResponseWriter, request *http.Request) {
+ var identifier string
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ params := request.URL.Query()
+ for key, value := range params {
+ switch key {
+ case "key":
+ identifier = value[0]
+ default:
+ log.Error().Err(errUnknownParam)
+ }
+ }
+
+ if identifier == "" {
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "Id parameter is not valid.",
+ })
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ defer cancel()
+
+ redisResult := tickerHandler.rdb.Get(ctx, identifier)
+
+ redisResultString, err := redisResult.Result()
+ if err != nil {
+ log.Err(err)
+ }
+
+ var ErrorString string
+ if err == nil {
+ ErrorString = ""
+ } else {
+ ErrorString = err.Error()
+ }
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ err = json.NewEncoder(writer).Encode(struct {
+ IsSuccessful bool `json:"isSuccessful"`
+ Error string `json:"error"`
+ Key string `json:"key"`
+ Expr string `json:"expr"`
+ }{IsSuccessful: true, Error: ErrorString, Key: identifier, Expr: redisResultString})
+
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+}
+
+func (tickerHandler Handler) HandleTickerDelete(writer http.ResponseWriter, request *http.Request) {
+ var identifier string
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ params := request.URL.Query()
+
+ for key, value := range params {
+ switch key {
+ case "key":
+ identifier = value[0]
+ default:
+ log.Error().Err(errUnknownParam)
+ }
+ }
+
+ if identifier == "" {
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "Id parameter is not valid.",
+ })
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ defer cancel()
+
+ tickerHandler.rdb.Del(ctx, identifier)
+ setKey := "alert:" + identifier
+ tickerHandler.rdb.SRem(ctx, "tickerkeys", setKey)
+ log.Printf(setKey)
+
+ err := json.NewEncoder(writer).Encode(struct {
+ IsSuccessful bool `json:"isSuccessful"`
+ Err string `json:"err"`
+ }{IsSuccessful: true, Err: ""})
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+}
+
+func (tickerHandler Handler) HandleTickerPost(writer http.ResponseWriter, request *http.Request) {
+ var bodyJSON tickerJSONType
+
+ writer.Header().Add("Content-Type", "application/json")
+
+ err := json.NewDecoder(request.Body).Decode(&bodyJSON)
+ if err != nil {
+ fmt.Println(err.Error())
+ log.Printf(err.Error())
+
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ // "error": "not all parameters are valid.",
+ "error": "XXX",
+ })
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+ }
+
+ fmt.Println(bodyJSON.Name)
+ if bodyJSON.Name == "" {
+ err := json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": false,
+ "error": "name is empty.",
+ })
+ if err != nil {
+ log.Error().Err(errFailedUnmarshall)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(request.Context(), redisContextTimeout*time.Second)
+ defer cancel()
+
+ key := "ticker:" + bodyJSON.Name
+ tickerHandler.rdb.Set(ctx, bodyJSON.Name, true, 0)
+ tickerHandler.rdb.SAdd(ctx, "tickerkeys", key)
+
+ err = json.NewEncoder(writer).Encode(map[string]interface{}{
+ "isSuccessful": true,
+ "error": "",
+ })
+
+ if err != nil {
+ log.Error().Err(err)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+ }
+}
diff --git a/test/endpoints.sh b/test/endpoints.sh
index 7f9533b..2b73614 100755
--- a/test/endpoints.sh
+++ b/test/endpoints.sh
@@ -2,11 +2,21 @@
set -e
set -x
-# sleep 5
-curl -k -X GET "https://localhost:8008/crypto/v1/price?name=CAKE&unit=USD"
-curl -k -X GET "https://localhost:8008/crypto/v1/pair?one=ETH&two=CAKE&multiplier=4.0"
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:8008/crypto/v1/alert
+curl -k -X GET "https://localhost:10008/crypto/v1/price?name=PEPE&unit=USD"
+curl -k -X GET "https://localhost:10008/crypto/v1/pair?one=ETH&two=CAKE&multiplier=4.0"
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:10008/crypto/v1/alert
-curl -k -X GET -H "Content-Type: application/json" "https://localhost:8008/crypto/v1/alert?key=alert1"
-curl -k -X DELETE -H "Content-Type: application/json" "https://localhost:8008/crypto/v1/alert?key=alert1"
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:8008/crypto/v1/alert
+# alert
+curl -k -X GET -H "Content-Type: application/json" "https://localhost:10008/crypto/v1/alert?key=alert1"
+curl -k -X DELETE -H "Content-Type: application/json" "https://localhost:10008/crypto/v1/alert?key=alert1"
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:10008/crypto/v1/alert
+
+# ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"ETH"}' https://localhost:10008/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"BTC"}' https://localhost:10008/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"XMR"}' https://localhost:10008/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"STX"}' https://localhost:10008/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"LINK"}' https://localhost:10008/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"PEPE"}' https://localhost:10008/crypto/v1/ticker
+curl -k -X GET -H "Content-Type: application/json" https://localhost:10008/crypto/v1/ticker?key=ETH
+curl -k -X DELETE -H "Content-Type: application/json" https://localhost:10008/crypto/v1/ticker?key=ETH