mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
chore: implement diagnostics endpoint (resolve #225)
This commit is contained in:
parent
9e3203ac41
commit
2088987a0c
@ -33,6 +33,7 @@ const (
|
|||||||
SimpleDateTimeFormat = "2006-01-02 15:04:05"
|
SimpleDateTimeFormat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
ErrUnauthorized = "401 unauthorized"
|
ErrUnauthorized = "401 unauthorized"
|
||||||
|
ErrBadRequest = "400 bad request"
|
||||||
ErrInternalServerError = "500 internal server error"
|
ErrInternalServerError = "500 internal server error"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -203,6 +204,9 @@ func (c *Config) GetMigrationFunc(dbDialect string) models.MigrationFunc {
|
|||||||
if err := db.AutoMigrate(&models.ProjectLabel{}); err != nil && !c.Db.AutoMigrateFailSilently {
|
if err := db.AutoMigrate(&models.ProjectLabel{}); err != nil && !c.Db.AutoMigrateFailSilently {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := db.AutoMigrate(&models.Diagnostics{}); err != nil && !c.Db.AutoMigrateFailSilently {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
6
main.go
6
main.go
@ -54,6 +54,7 @@ var (
|
|||||||
projectLabelRepository repositories.IProjectLabelRepository
|
projectLabelRepository repositories.IProjectLabelRepository
|
||||||
summaryRepository repositories.ISummaryRepository
|
summaryRepository repositories.ISummaryRepository
|
||||||
keyValueRepository repositories.IKeyValueRepository
|
keyValueRepository repositories.IKeyValueRepository
|
||||||
|
diagnosticsRepository repositories.IDiagnosticsRepository
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -67,6 +68,7 @@ var (
|
|||||||
mailService services.IMailService
|
mailService services.IMailService
|
||||||
keyValueService services.IKeyValueService
|
keyValueService services.IKeyValueService
|
||||||
reportService services.IReportService
|
reportService services.IReportService
|
||||||
|
diagnosticsService services.IDiagnosticsService
|
||||||
miscService services.IMiscService
|
miscService services.IMiscService
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -143,6 +145,7 @@ func main() {
|
|||||||
projectLabelRepository = repositories.NewProjectLabelRepository(db)
|
projectLabelRepository = repositories.NewProjectLabelRepository(db)
|
||||||
summaryRepository = repositories.NewSummaryRepository(db)
|
summaryRepository = repositories.NewSummaryRepository(db)
|
||||||
keyValueRepository = repositories.NewKeyValueRepository(db)
|
keyValueRepository = repositories.NewKeyValueRepository(db)
|
||||||
|
diagnosticsRepository = repositories.NewDiagnosticsRepository(db)
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
mailService = mail.NewMailService()
|
mailService = mail.NewMailService()
|
||||||
@ -155,6 +158,7 @@ func main() {
|
|||||||
aggregationService = services.NewAggregationService(userService, summaryService, heartbeatService)
|
aggregationService = services.NewAggregationService(userService, summaryService, heartbeatService)
|
||||||
keyValueService = services.NewKeyValueService(keyValueRepository)
|
keyValueService = services.NewKeyValueService(keyValueRepository)
|
||||||
reportService = services.NewReportService(summaryService, userService, mailService)
|
reportService = services.NewReportService(summaryService, userService, mailService)
|
||||||
|
diagnosticsService = services.NewDiagnosticsService(diagnosticsRepository)
|
||||||
miscService = services.NewMiscService(userService, summaryService, keyValueService)
|
miscService = services.NewMiscService(userService, summaryService, keyValueService)
|
||||||
|
|
||||||
// Schedule background tasks
|
// Schedule background tasks
|
||||||
@ -169,6 +173,7 @@ func main() {
|
|||||||
heartbeatApiHandler := api.NewHeartbeatApiHandler(userService, heartbeatService, languageMappingService)
|
heartbeatApiHandler := api.NewHeartbeatApiHandler(userService, heartbeatService, languageMappingService)
|
||||||
summaryApiHandler := api.NewSummaryApiHandler(userService, summaryService)
|
summaryApiHandler := api.NewSummaryApiHandler(userService, summaryService)
|
||||||
metricsHandler := api.NewMetricsHandler(userService, summaryService, heartbeatService, keyValueService)
|
metricsHandler := api.NewMetricsHandler(userService, summaryService, heartbeatService, keyValueService)
|
||||||
|
diagnosticsHandler := api.NewDiagnosticsApiHandler(userService, diagnosticsService)
|
||||||
|
|
||||||
// Compat Handlers
|
// Compat Handlers
|
||||||
wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(userService, summaryService)
|
wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(userService, summaryService)
|
||||||
@ -219,6 +224,7 @@ func main() {
|
|||||||
healthApiHandler.RegisterRoutes(apiRouter)
|
healthApiHandler.RegisterRoutes(apiRouter)
|
||||||
heartbeatApiHandler.RegisterRoutes(apiRouter)
|
heartbeatApiHandler.RegisterRoutes(apiRouter)
|
||||||
metricsHandler.RegisterRoutes(apiRouter)
|
metricsHandler.RegisterRoutes(apiRouter)
|
||||||
|
diagnosticsHandler.RegisterRoutes(apiRouter)
|
||||||
wakatimeV1AllHandler.RegisterRoutes(apiRouter)
|
wakatimeV1AllHandler.RegisterRoutes(apiRouter)
|
||||||
wakatimeV1SummariesHandler.RegisterRoutes(apiRouter)
|
wakatimeV1SummariesHandler.RegisterRoutes(apiRouter)
|
||||||
wakatimeV1StatsHandler.RegisterRoutes(apiRouter)
|
wakatimeV1StatsHandler.RegisterRoutes(apiRouter)
|
||||||
|
13
models/diagnostics.go
Normal file
13
models/diagnostics.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Diagnostics struct {
|
||||||
|
ID uint `gorm:"primary_key"`
|
||||||
|
User *User `json:"-" gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
|
UserID string `json:"-" gorm:"not null; index:idx_diagnostics_user"`
|
||||||
|
Platform string `json:"platform"`
|
||||||
|
Architecture string `json:"architecture"`
|
||||||
|
Plugin string `json:"plugin"`
|
||||||
|
CliVersion string `json:"cli_version"`
|
||||||
|
Logs string `json:"logs" gorm:"type:text"`
|
||||||
|
StackTrace string `json:"stacktrace" gorm:"type:text"`
|
||||||
|
}
|
18
repositories/diagnostics.go
Normal file
18
repositories/diagnostics.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiagnosticsRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiagnosticsRepository(db *gorm.DB) *DiagnosticsRepository {
|
||||||
|
return &DiagnosticsRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DiagnosticsRepository) Insert(diagnostics *models.Diagnostics) (*models.Diagnostics, error) {
|
||||||
|
return diagnostics, r.db.Create(diagnostics).Error
|
||||||
|
}
|
@ -31,6 +31,10 @@ type IHeartbeatRepository interface {
|
|||||||
DeleteBefore(time.Time) error
|
DeleteBefore(time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IDiagnosticsRepository interface {
|
||||||
|
Insert(diagnostics *models.Diagnostics) (*models.Diagnostics, error)
|
||||||
|
}
|
||||||
|
|
||||||
type IKeyValueRepository interface {
|
type IKeyValueRepository interface {
|
||||||
GetAll() ([]*models.KeyStringValue, error)
|
GetAll() ([]*models.KeyStringValue, error)
|
||||||
GetString(string) (*models.KeyStringValue, error)
|
GetString(string) (*models.KeyStringValue, error)
|
||||||
|
71
routes/api/diagnostics.go
Normal file
71
routes/api/diagnostics.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
conf "github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/middlewares"
|
||||||
|
"github.com/muety/wakapi/services"
|
||||||
|
"github.com/muety/wakapi/utils"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiagnosticsApiHandler struct {
|
||||||
|
config *conf.Config
|
||||||
|
userSrvc services.IUserService
|
||||||
|
diagnosticsSrvc services.IDiagnosticsService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiagnosticsApiHandler(userService services.IUserService, diagnosticsService services.IDiagnosticsService) *DiagnosticsApiHandler {
|
||||||
|
return &DiagnosticsApiHandler{
|
||||||
|
config: conf.Get(),
|
||||||
|
userSrvc: userService,
|
||||||
|
diagnosticsSrvc: diagnosticsService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DiagnosticsApiHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
r := router.PathPrefix("/plugins/errors").Subrouter()
|
||||||
|
r.Use(
|
||||||
|
middlewares.NewAuthenticateMiddleware(h.userSrvc).Handler,
|
||||||
|
)
|
||||||
|
r.Path("").Methods(http.MethodPost).HandlerFunc(h.Post)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Push a new diagnostics object
|
||||||
|
// @ID post-diagnostics
|
||||||
|
// @Tags diagnostics
|
||||||
|
// @Accept json
|
||||||
|
// @Param diagnostics body models.Diagnostics true "A single diagnostics object sent by WakaTime CLI"
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Success 201
|
||||||
|
// @Router /plugins/errors [post]
|
||||||
|
func (h *DiagnosticsApiHandler) Post(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var diagnostics models.Diagnostics
|
||||||
|
|
||||||
|
user := middlewares.GetPrincipal(r)
|
||||||
|
if user == nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte(conf.ErrUnauthorized))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&diagnostics); err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(conf.ErrBadRequest))
|
||||||
|
conf.Log().Request(r).Error("failed to parse diagnostics for user %s - %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
diagnostics.UserID = user.ID
|
||||||
|
|
||||||
|
if _, err := h.diagnosticsSrvc.Create(&diagnostics); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(conf.ErrInternalServerError))
|
||||||
|
conf.Log().Request(r).Error("failed to insert diagnostics for user %s - %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.RespondJSON(w, r, http.StatusCreated, struct{}{})
|
||||||
|
}
|
@ -72,6 +72,7 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
heartbeats, err = h.tryParseSingle(r)
|
heartbeats, err = h.tryParseSingle(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conf.Log().Request(r).Error(err.Error())
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
|
23
services/diagnostics.go
Normal file
23
services/diagnostics.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiagnosticsService struct {
|
||||||
|
config *config.Config
|
||||||
|
repository repositories.IDiagnosticsRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiagnosticsService(diagnosticsRepo repositories.IDiagnosticsRepository) *DiagnosticsService {
|
||||||
|
return &DiagnosticsService{
|
||||||
|
config: config.Get(),
|
||||||
|
repository: diagnosticsRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *DiagnosticsService) Create(diagnostics *models.Diagnostics) (*models.Diagnostics, error) {
|
||||||
|
return srv.repository.Insert(diagnostics)
|
||||||
|
}
|
@ -39,6 +39,10 @@ type IHeartbeatService interface {
|
|||||||
DeleteBefore(time.Time) error
|
DeleteBefore(time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IDiagnosticsService interface {
|
||||||
|
Create(*models.Diagnostics) (*models.Diagnostics, error)
|
||||||
|
}
|
||||||
|
|
||||||
type IKeyValueService interface {
|
type IKeyValueService interface {
|
||||||
GetString(string) (*models.KeyStringValue, error)
|
GetString(string) (*models.KeyStringValue, error)
|
||||||
MustGetString(string) *models.KeyStringValue
|
MustGetString(string) *models.KeyStringValue
|
||||||
|
@ -482,6 +482,39 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/plugins/errors": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"diagnostics"
|
||||||
|
],
|
||||||
|
"summary": "Push a new diagnostics object",
|
||||||
|
"operationId": "post-diagnostics",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "A single diagnostics object sent by WakaTime CLI",
|
||||||
|
"name": "diagnostics",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Diagnostics"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/summary": {
|
"/summary": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -687,6 +720,32 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"models.Diagnostics": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"architecture": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cli_version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"logs": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"plugin": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"stacktrace": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.Heartbeat": {
|
"models.Heartbeat": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -750,6 +809,7 @@ var doc = `{
|
|||||||
"example": "2006-01-02 15:04:05.000"
|
"example": "2006-01-02 15:04:05.000"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"description": "labels are not persisted, but calculated at runtime, i.e. when summary is retrieved",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.SummaryItem"
|
"$ref": "#/definitions/models.SummaryItem"
|
||||||
|
@ -466,6 +466,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/plugins/errors": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"diagnostics"
|
||||||
|
],
|
||||||
|
"summary": "Push a new diagnostics object",
|
||||||
|
"operationId": "post-diagnostics",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "A single diagnostics object sent by WakaTime CLI",
|
||||||
|
"name": "diagnostics",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Diagnostics"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/summary": {
|
"/summary": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -671,6 +704,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"models.Diagnostics": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"architecture": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cli_version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"logs": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"plugin": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"stacktrace": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.Heartbeat": {
|
"models.Heartbeat": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -734,6 +793,7 @@
|
|||||||
"example": "2006-01-02 15:04:05.000"
|
"example": "2006-01-02 15:04:05.000"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"description": "labels are not persisted, but calculated at runtime, i.e. when summary is retrieved",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.SummaryItem"
|
"$ref": "#/definitions/models.SummaryItem"
|
||||||
|
@ -1,5 +1,22 @@
|
|||||||
basePath: /api
|
basePath: /api
|
||||||
definitions:
|
definitions:
|
||||||
|
models.Diagnostics:
|
||||||
|
properties:
|
||||||
|
architecture:
|
||||||
|
type: string
|
||||||
|
cli_version:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
logs:
|
||||||
|
type: string
|
||||||
|
platform:
|
||||||
|
type: string
|
||||||
|
plugin:
|
||||||
|
type: string
|
||||||
|
stacktrace:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
models.Heartbeat:
|
models.Heartbeat:
|
||||||
properties:
|
properties:
|
||||||
branch:
|
branch:
|
||||||
@ -44,6 +61,8 @@ definitions:
|
|||||||
format: date
|
format: date
|
||||||
type: string
|
type: string
|
||||||
labels:
|
labels:
|
||||||
|
description: labels are not persisted, but calculated at runtime, i.e. when
|
||||||
|
summary is retrieved
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/models.SummaryItem'
|
$ref: '#/definitions/models.SummaryItem'
|
||||||
type: array
|
type: array
|
||||||
@ -622,6 +641,26 @@ paths:
|
|||||||
summary: Push new heartbeats
|
summary: Push new heartbeats
|
||||||
tags:
|
tags:
|
||||||
- heartbeat
|
- heartbeat
|
||||||
|
/plugins/errors:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
operationId: post-diagnostics
|
||||||
|
parameters:
|
||||||
|
- description: A single diagnostics object sent by WakaTime CLI
|
||||||
|
in: body
|
||||||
|
name: diagnostics
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Diagnostics'
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
description: ""
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Push a new diagnostics object
|
||||||
|
tags:
|
||||||
|
- diagnostics
|
||||||
/summary:
|
/summary:
|
||||||
get:
|
get:
|
||||||
operationId: get-summary
|
operationId: get-summary
|
||||||
|
Loading…
Reference in New Issue
Block a user