1
0
mirror of https://github.com/muety/wakapi.git synced 2023-08-10 21:12:56 +03:00

fix: single heartbeat endpoint (resolve #212)

docs: swagger docs for all available heartbeat endpoints
This commit is contained in:
Ferdinand Mütsch
2021-06-21 21:53:47 +02:00
parent 466f2e1786
commit 9f1266957b
5 changed files with 580 additions and 15 deletions

View File

@ -1,6 +1,7 @@
package api
import (
"bytes"
"encoding/json"
"github.com/gorilla/mux"
conf "github.com/muety/wakapi/config"
@ -9,6 +10,7 @@ import (
routeutils "github.com/muety/wakapi/routes/utils"
"github.com/muety/wakapi/services"
"github.com/muety/wakapi/utils"
"io/ioutil"
"net/http"
"github.com/muety/wakapi/models"
@ -40,16 +42,20 @@ func (h *HeartbeatApiHandler) RegisterRoutes(router *mux.Router) {
middlewares.NewAuthenticateMiddleware(h.userSrvc).Handler,
customMiddleware.NewWakatimeRelayMiddleware().Handler,
)
r.PathPrefix("/heartbeat").Methods(http.MethodPost).HandlerFunc(h.Post)
r.PathPrefix("/v1/users/{user}/heartbeats").Methods(http.MethodPost).HandlerFunc(h.Post)
r.PathPrefix("/compat/wakatime/v1/users/{user}/heartbeats").Methods(http.MethodPost).HandlerFunc(h.Post)
// see https://github.com/muety/wakapi/issues/203
r.Path("/heartbeat").Methods(http.MethodPost).HandlerFunc(h.Post)
r.Path("/heartbeats").Methods(http.MethodPost).HandlerFunc(h.Post)
r.Path("/v1/users/{user}/heartbeats").Methods(http.MethodPost).HandlerFunc(h.Post)
r.Path("/v1/users/{user}/heartbeats.bulk").Methods(http.MethodPost).HandlerFunc(h.Post)
r.Path("/compat/wakatime/v1/users/{user}/heartbeats").Methods(http.MethodPost).HandlerFunc(h.Post)
r.Path("/compat/wakatime/v1/users/{user}/heartbeats.bulk").Methods(http.MethodPost).HandlerFunc(h.Post)
}
// @Summary Push a new heartbeat
// @ID post-heartbeat
// @Tags heartbeat
// @Accept json
// @Param heartbeat body models.Heartbeat true "A heartbeat"
// @Param heartbeat body models.Heartbeat true "A single heartbeat"
// @Security ApiKeyAuth
// @Success 201
// @Router /heartbeat [post]
@ -60,16 +66,19 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
}
var heartbeats []*models.Heartbeat
heartbeats, err = h.tryParseBulk(r)
if err != nil {
heartbeats, err = h.tryParseSingle(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
}
opSys, editor, _ := utils.ParseUserAgent(r.Header.Get("User-Agent"))
machineName := r.Header.Get("X-Machine-Name")
dec := json.NewDecoder(r.Body)
if err := dec.Decode(&heartbeats); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
for _, hb := range heartbeats {
hb.OperatingSystem = opSys
hb.Editor = editor
@ -103,12 +112,46 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
}
}
defer func() {}()
utils.RespondJSON(w, r, http.StatusCreated, constructSuccessResponse(len(heartbeats)))
}
func (h *HeartbeatApiHandler) tryParseBulk(r *http.Request) ([]*models.Heartbeat, error) {
var heartbeats []*models.Heartbeat
body, _ := ioutil.ReadAll(r.Body)
r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
dec := json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(body)))
if err := dec.Decode(&heartbeats); err != nil {
return nil, err
}
return heartbeats, nil
}
func (h *HeartbeatApiHandler) tryParseSingle(r *http.Request) ([]*models.Heartbeat, error) {
var heartbeat models.Heartbeat
body, _ := ioutil.ReadAll(r.Body)
r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
dec := json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(body)))
if err := dec.Decode(&heartbeat); err != nil {
return nil, err
}
return []*models.Heartbeat{&heartbeat}, nil
}
// construct weird response format (see https://github.com/wakatime/wakatime/blob/2e636d389bf5da4e998e05d5285a96ce2c181e3d/wakatime/api.py#L288)
// to make the cli consider all heartbeats to having been successfully saved
// response looks like: { "responses": [ [ { "data": {...} }, 201 ], ... ] }
// update: this was probably a temporary bug at wakatime, responses actually looks like so: https://pastr.de/p/nyf6kj2e6843fbw4xkj4h4pj
// TODO: adapt response format some time
func constructSuccessResponse(n int) *heartbeatResponseVm {
responses := make([][]interface{}, n)
@ -123,3 +166,55 @@ func constructSuccessResponse(n int) *heartbeatResponseVm {
Responses: responses,
}
}
// Only for Swagger
// @Summary Push a new heartbeat
// @ID post-heartbeat-2
// @Tags heartbeat
// @Accept json
// @Param heartbeat body models.Heartbeat true "A single heartbeat"
// @Security ApiKeyAuth
// @Success 201
// @Router /v1/users/{user}/heartbeats [post]
func (h *HeartbeatApiHandler) postAlias1() {}
// @Summary Push a new heartbeat
// @ID post-heartbeat-3
// @Tags heartbeat
// @Accept json
// @Param heartbeat body models.Heartbeat true "A single heartbeat"
// @Security ApiKeyAuth
// @Success 201
// @Router /compat/wakatime/v1/users/{user}/heartbeats [post]
func (h *HeartbeatApiHandler) postAlias2() {}
// @Summary Push new heartbeats
// @ID post-heartbeat-4
// @Tags heartbeat
// @Accept json
// @Param heartbeat body []models.Heartbeat true "Multiple heartbeats"
// @Security ApiKeyAuth
// @Success 201
// @Router /heartbeats [post]
func (h *HeartbeatApiHandler) postAlias3() {}
// @Summary Push new heartbeats
// @ID post-heartbeat-5
// @Tags heartbeat
// @Accept json
// @Param heartbeat body []models.Heartbeat true "Multiple heartbeats"
// @Security ApiKeyAuth
// @Success 201
// @Router /v1/users/{user}/heartbeats.bulk [post]
func (h *HeartbeatApiHandler) postAlias4() {}
// @Summary Push new heartbeats
// @ID post-heartbeat-6
// @Tags heartbeat
// @Accept json
// @Param heartbeat body []models.Heartbeat true "Multiple heartbeats"
// @Security ApiKeyAuth
// @Success 201
// @Router /compat/wakatime/v1/users/{user}/heartbeats.bulk [post]
func (h *HeartbeatApiHandler) postAlias5() {}