diff --git a/Dockerfile.api b/Dockerfile.api
index 520944ec0..f34cbc4e8 100644
--- a/Dockerfile.api
+++ b/Dockerfile.api
@@ -3,9 +3,12 @@ FROM golang:1.18 as builder
COPY go.mod go.sum /go/src/github.com/moira-alert/moira/
WORKDIR /go/src/github.com/moira-alert/moira
RUN go mod download
+RUN go install github.com/swaggo/swag/cmd/swag@v1.8.12
COPY . /go/src/github.com/moira-alert/moira/
+RUN /go/bin/swag init -g api/handler/handler.go
+
ARG GO_VERSION="GoVersion"
ARG GIT_COMMIT="git_Commit"
ARG MoiraVersion="MoiraVersion"
diff --git a/Makefile b/Makefile
index 74e3a0e35..97df83ae3 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,20 @@ lint:
mock:
. ./generate_mocks.sh
+.PHONY: install-swag
+install-swag:
+ go install github.com/swaggo/swag/cmd/swag@v1.8.12
+
+.PHONY: spec
+spec:
+ echo "Generating Swagger documentation"
+ swag init -g api/handler/handler.go
+ swag fmt
+
+.PHONY: validate-spec
+validate-spec:
+ openapi-generator-cli validate -i docs/swagger.yaml
+
.PHONY: test
test:
echo 'mode: atomic' > coverage.txt && go list ./... | xargs -n1 -I{} sh -c 'go test -race -v -bench=. -covermode=atomic -coverprofile=coverage.tmp {} && tail -n +2 coverage.tmp >> coverage.txt' && rm coverage.tmp
diff --git a/api/dto/contact.go b/api/dto/contact.go
index 4f28adf2b..12e9fd1da 100644
--- a/api/dto/contact.go
+++ b/api/dto/contact.go
@@ -17,10 +17,10 @@ func (*ContactList) Render(w http.ResponseWriter, r *http.Request) error {
}
type Contact struct {
- Type string `json:"type"`
- Value string `json:"value"`
- ID string `json:"id,omitempty"`
- User string `json:"user,omitempty"`
+ Type string `json:"type" example:"mail"`
+ Value string `json:"value" example:"devops@example.com"`
+ ID string `json:"id,omitempty" example:"1dd38765-c5be-418d-81fa-7a5f879c2315"`
+ User string `json:"user,omitempty" example:""`
TeamID string `json:"team_id,omitempty"`
}
diff --git a/api/dto/events.go b/api/dto/events.go
index e24fddde3..2f704d337 100644
--- a/api/dto/events.go
+++ b/api/dto/events.go
@@ -8,9 +8,9 @@ import (
)
type EventsList struct {
- Page int64 `json:"page"`
- Size int64 `json:"size"`
- Total int64 `json:"total"`
+ Page int64 `json:"page" example:"0"`
+ Size int64 `json:"size" example:"100"`
+ Total int64 `json:"total" example:"10"`
List []moira.NotificationEvent `json:"list"`
}
diff --git a/api/dto/health.go b/api/dto/health.go
index a9fd7eb8d..db8adbfc9 100644
--- a/api/dto/health.go
+++ b/api/dto/health.go
@@ -13,8 +13,8 @@ const (
)
type NotifierState struct {
- State string `json:"state"`
- Message string `json:"message,omitempty"`
+ State string `json:"state" example:"ERROR"`
+ Message string `json:"message,omitempty" example:"Moira has been turned off for maintenance"`
}
func (*NotifierState) Render(w http.ResponseWriter, r *http.Request) error {
diff --git a/api/dto/notification.go b/api/dto/notification.go
index 0c685df32..4a6fa6ef1 100644
--- a/api/dto/notification.go
+++ b/api/dto/notification.go
@@ -8,7 +8,7 @@ import (
)
type NotificationsList struct {
- Total int64 `json:"total"`
+ Total int64 `json:"total" example:"0"`
List []*moira.ScheduledNotification `json:"list"`
}
@@ -17,7 +17,7 @@ func (*NotificationsList) Render(w http.ResponseWriter, r *http.Request) error {
}
type NotificationDeleteResponse struct {
- Result int64 `json:"result"`
+ Result int64 `json:"result" example:"0"`
}
func (*NotificationDeleteResponse) Render(w http.ResponseWriter, r *http.Request) error {
diff --git a/api/dto/pattern.go b/api/dto/pattern.go
index 6df448512..3fdd4710b 100644
--- a/api/dto/pattern.go
+++ b/api/dto/pattern.go
@@ -14,7 +14,7 @@ func (*PatternList) Render(w http.ResponseWriter, r *http.Request) error {
}
type PatternData struct {
- Metrics []string `json:"metrics"`
- Pattern string `json:"pattern"`
+ Metrics []string `json:"metrics" example:"DevOps.my_server.hdd.freespace_mbytes, DevOps.my_server.hdd.freespace_mbytes, DevOps.my_server.db.*"`
+ Pattern string `json:"pattern" example:"Devops.my_server.*"`
Triggers []TriggerModel `json:"triggers"`
}
diff --git a/api/dto/tag.go b/api/dto/tag.go
index a6f3ae497..9fd435bf4 100644
--- a/api/dto/tag.go
+++ b/api/dto/tag.go
@@ -8,7 +8,7 @@ import (
)
type TagsData struct {
- TagNames []string `json:"list"`
+ TagNames []string `json:"list" example:"cpu"`
}
func (*TagsData) Render(w http.ResponseWriter, r *http.Request) error {
@@ -16,7 +16,7 @@ func (*TagsData) Render(w http.ResponseWriter, r *http.Request) error {
}
type MessageResponse struct {
- Message string `json:"message"`
+ Message string `json:"message" example:"tag deleted"`
}
func (*MessageResponse) Render(w http.ResponseWriter, r *http.Request) error {
@@ -28,8 +28,8 @@ type TagsStatistics struct {
}
type TagStatistics struct {
- TagName string `json:"name"`
- Triggers []string `json:"triggers"`
+ TagName string `json:"name" example:"cpu"`
+ Triggers []string `json:"triggers" example:"bcba82f5-48cf-44c0-b7d6-e1d32c64a88c"`
Subscriptions []moira.SubscriptionData `json:"subscriptions"`
}
diff --git a/api/dto/target.go b/api/dto/target.go
index 135e3b52f..2c6f11afb 100644
--- a/api/dto/target.go
+++ b/api/dto/target.go
@@ -101,10 +101,10 @@ var (
)
type ProblemOfTarget struct {
- Argument string `json:"argument"`
- Type typeOfProblem `json:"type,omitempty"`
- Description string `json:"description,omitempty"`
- Position int `json:"position"`
+ Argument string `json:"argument" example:"consolidateBy"`
+ Type typeOfProblem `json:"type,omitempty" example:"warn"`
+ Description string `json:"description,omitempty" example:"This function affects only visual graph representation. It is meaningless in Moira"`
+ Position int `json:"position" example:"0"`
Problems []ProblemOfTarget `json:"problems,omitempty"`
}
@@ -123,7 +123,7 @@ func (p *ProblemOfTarget) hasError() bool {
}
type TreeOfProblems struct {
- SyntaxOk bool `json:"syntax_ok"`
+ SyntaxOk bool `json:"syntax_ok" example:"true"`
TreeOfProblems *ProblemOfTarget `json:"tree_of_problems,omitempty"`
}
diff --git a/api/dto/team.go b/api/dto/team.go
index 1bb07c7bb..53b129967 100644
--- a/api/dto/team.go
+++ b/api/dto/team.go
@@ -15,9 +15,9 @@ const (
// TeamModel is a structure that represents team entity in HTTP transfer
type TeamModel struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Description string `json:"description"`
+ ID string `json:"id" example:"d5d98eb3-ee18-4f75-9364-244f67e23b54"`
+ Name string `json:"name" example:"Infrastructure Team"`
+ Description string `json:"description" example:"Team that holds all members of infrastructure division"`
}
// NewTeamModel is a constructor function that creates a new TeamModel using moira.Team
@@ -59,7 +59,7 @@ func (t TeamModel) ToMoiraTeam() moira.Team {
// SaveTeamResponse is a structure to return team creation result in HTTP response
type SaveTeamResponse struct {
- ID string `json:"id"`
+ ID string `json:"id" example:"d5d98eb3-ee18-4f75-9364-244f67e23b54"`
}
// Render is a function that implements chi Renderer interface for SaveTeamResponse
@@ -79,7 +79,7 @@ func (UserTeams) Render(w http.ResponseWriter, r *http.Request) error {
// TeamMembers is a structure that represents a team members in HTTP transfer
type TeamMembers struct {
- Usernames []string `json:"usernames"`
+ Usernames []string `json:"usernames" example:"anonymous"`
}
// Bind is a method that implements Binder interface from chi and checks that validity of data in request
@@ -96,7 +96,7 @@ func (TeamMembers) Render(w http.ResponseWriter, r *http.Request) error {
}
type TeamSettings struct {
- TeamID string `json:"team_id"`
+ TeamID string `json:"team_id" example:"d5d98eb3-ee18-4f75-9364-244f67e23b54"`
Contacts []moira.ContactData `json:"contacts"`
Subscriptions []moira.SubscriptionData `json:"subscriptions"`
}
diff --git a/api/dto/triggers.go b/api/dto/triggers.go
index 300df70af..c573e1659 100644
--- a/api/dto/triggers.go
+++ b/api/dto/triggers.go
@@ -36,43 +36,43 @@ func (*TriggersList) Render(http.ResponseWriter, *http.Request) error {
type Trigger struct {
TriggerModel
- Throttling int64 `json:"throttling"`
+ Throttling int64 `json:"throttling" example:"0"`
}
// TriggerModel is moira.Trigger api representation
type TriggerModel struct {
// Trigger unique ID
- ID string `json:"id"`
+ ID string `json:"id" example:"292516ed-4924-4154-a62c-ebe312431fce"`
// Trigger name
- Name string `json:"name"`
+ Name string `json:"name" example:"Not enough disk space left"`
// Description string
- Desc *string `json:"desc,omitempty"`
+ Desc *string `json:"desc,omitempty" example:"check the size of /var/log"`
// Graphite-like targets: t1, t2, ...
- Targets []string `json:"targets"`
+ Targets []string `json:"targets" example:"devOps.my_server.hdd.freespace_mbytes"`
// WARN threshold
- WarnValue *float64 `json:"warn_value"`
+ WarnValue *float64 `json:"warn_value" example:"500"`
// ERROR threshold
- ErrorValue *float64 `json:"error_value"`
+ ErrorValue *float64 `json:"error_value" example:"1000"`
// Could be: rising, falling, expression
- TriggerType string `json:"trigger_type"`
+ TriggerType string `json:"trigger_type" example:"rising"`
// Set of tags to manipulate subscriptions
- Tags []string `json:"tags"`
+ Tags []string `json:"tags" example:"server,disk"`
// When there are no metrics for trigger, Moira will switch metric to TTLState state after TTL seconds
- TTLState *moira.TTLState `json:"ttl_state,omitempty"`
+ TTLState *moira.TTLState `json:"ttl_state,omitempty" example:"NODATA"`
// When there are no metrics for trigger, Moira will switch metric to TTLState state after TTL seconds
- TTL int64 `json:"ttl,omitempty"`
+ TTL int64 `json:"ttl,omitempty" example:"600"`
// Determines when Moira should monitor trigger
Schedule *moira.ScheduleData `json:"sched,omitempty"`
// Used if you need more complex logic than provided by WARN/ERROR values
- Expression string `json:"expression"`
+ Expression string `json:"expression" example:""`
// Graphite patterns for trigger
- Patterns []string `json:"patterns"`
+ Patterns []string `json:"patterns" example:""`
// Shows if trigger is remote (graphite-backend) based or stored inside Moira-Redis DB
- IsRemote bool `json:"is_remote"`
+ IsRemote bool `json:"is_remote" example:"false"`
// If true, first event NODATA → OK will be omitted
- MuteNewMetrics bool `json:"mute_new_metrics"`
+ MuteNewMetrics bool `json:"mute_new_metrics" example:"false"`
// A list of targets that have only alone metrics
- AloneMetrics map[string]bool `json:"alone_metrics"`
+ AloneMetrics map[string]bool `json:"alone_metrics" example:"t1:true"`
// Datetime when the trigger was created
CreatedAt *time.Time `json:"created_at"`
// Datetime when the trigger was updated
@@ -366,7 +366,7 @@ type TriggerCheckResponse struct {
type TriggerCheck struct {
*moira.CheckData
- TriggerID string `json:"trigger_id"`
+ TriggerID string `json:"trigger_id" example:"trigger_id"`
}
func (*TriggerCheck) Render(http.ResponseWriter, *http.Request) error {
@@ -380,7 +380,7 @@ func (*MetricsMaintenance) Bind(*http.Request) error {
}
type TriggerMaintenance struct {
- Trigger *int64 `json:"trigger"`
+ Trigger *int64 `json:"trigger" example:"1594225165"`
Metrics map[string]int64 `json:"metrics"`
}
@@ -389,7 +389,7 @@ func (*TriggerMaintenance) Bind(*http.Request) error {
}
type ThrottlingResponse struct {
- Throttling int64 `json:"throttling"`
+ Throttling int64 `json:"throttling" example:"0"`
}
func (*ThrottlingResponse) Render(http.ResponseWriter, *http.Request) error {
@@ -397,8 +397,8 @@ func (*ThrottlingResponse) Render(http.ResponseWriter, *http.Request) error {
}
type SaveTriggerResponse struct {
- ID string `json:"id"`
- Message string `json:"message"`
+ ID string `json:"id" example:"trigger_id"`
+ Message string `json:"message" example:"trigger created"`
CheckResult TriggerCheckResponse `json:"checkResult,omitempty"`
}
@@ -426,7 +426,7 @@ type TriggerDump struct {
}
type TriggersSearchResultDeleteResponse struct {
- PagerID string `json:"pager_id"`
+ PagerID string `json:"pager_id" example:"292516ed-4924-4154-a62c-ebe312431fce"`
}
func (TriggersSearchResultDeleteResponse) Render(http.ResponseWriter, *http.Request) error {
diff --git a/api/dto/user.go b/api/dto/user.go
index c03b94bd4..68d45eca2 100644
--- a/api/dto/user.go
+++ b/api/dto/user.go
@@ -18,7 +18,7 @@ func (*UserSettings) Render(w http.ResponseWriter, r *http.Request) error {
}
type User struct {
- Login string `json:"login"`
+ Login string `json:"login" example:"john"`
}
func (*User) Render(w http.ResponseWriter, r *http.Request) error {
diff --git a/api/error_response.go b/api/error_response.go
index fb4d40658..08dfec426 100644
--- a/api/error_response.go
+++ b/api/error_response.go
@@ -85,3 +85,35 @@ var ErrNotFound = &ErrorResponse{HTTPStatusCode: http.StatusNotFound, StatusText
// ErrMethodNotAllowed is default 405 router method not allowed
var ErrMethodNotAllowed = &ErrorResponse{HTTPStatusCode: http.StatusMethodNotAllowed, StatusText: "Method not allowed."}
+
+// Examples for `swaggo`:
+
+type ErrorInternalServerExample struct {
+ StatusText string `json:"status" example:"Internal Server Error"`
+ ErrorText string `json:"error" example:"server error during request handling"`
+}
+
+type ErrorInvalidRequestExample struct {
+ StatusText string `json:"status" example:"Invalid request"`
+ ErrorText string `json:"error" example:"resource with the ID does not exist"`
+}
+
+type ErrorRenderExample struct {
+ StatusText string `json:"status" example:"Error rendering response"`
+ ErrorText string `json:"error" example:"rendering error"`
+}
+
+type ErrorNotFoundExample struct {
+ StatusText string `json:"status" example:"Resource not found"`
+ ErrorText string `json:"error" example:"resource with ID '66741a8c-c2ba-4357-a2c9-ee78e0e7' does not exist"`
+}
+
+type ErrorForbiddenExample struct {
+ StatusText string `json:"status" example:"Forbidden"`
+ ErrorText string `json:"error" example:"you cannot access this resource"`
+}
+
+type ErrorRemoteServerUnavailableExample struct {
+ StatusText string `json:"status" example:"Remote server unavailable"`
+ ErrorText string `json:"error" example:"Remote server error, please contact administrator"`
+}
diff --git a/api/handler/config.go b/api/handler/config.go
index c758f2145..ea62edc2b 100644
--- a/api/handler/config.go
+++ b/api/handler/config.go
@@ -2,6 +2,24 @@ package handler
import "net/http"
+type ContactExample struct {
+ Type string `json:"type" example:"telegram"`
+ Label string `json:"label" example:"Telegram"`
+}
+
+type ConfigurationResponse struct {
+ RemoteAllowed bool `json:"remoteAllowed" example:"false"`
+ Contacts []ContactExample `json:"contacts"`
+}
+
+// nolint: gofmt,goimports
+//
+// @summary Get available configuration
+// @id get-web-config
+// @tags config
+// @produce json
+// @success 200 {object} ConfigurationResponse "Configuration fetched successfully"
+// @router /config [get]
func getWebConfig(configContent []byte) http.HandlerFunc {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
diff --git a/api/handler/contact.go b/api/handler/contact.go
index b824a9037..9c4f338b5 100644
--- a/api/handler/contact.go
+++ b/api/handler/contact.go
@@ -27,6 +27,16 @@ func contact(router chi.Router) {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Gets all Moira contacts
+// @id get-all-contacts
+// @tags contact
+// @produce json
+// @success 200 {object} dto.ContactList "Contacts fetched successfully"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact [get]
func getAllContacts(writer http.ResponseWriter, request *http.Request) {
contacts, err := controller.GetAllContacts(database)
if err != nil {
@@ -40,6 +50,19 @@ func getAllContacts(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get contact by ID
+// @id get-contact-by-id
+// @tags contact
+// @produce json
+// @param contactID path string true "Contact ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.Contact "Successfully received contact"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact/{contactID} [get]
func getContactById(writer http.ResponseWriter, request *http.Request) {
contactData := request.Context().Value(contactKey).(moira.ContactData)
@@ -56,6 +79,19 @@ func getContactById(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Creates a new contact notification for the current user
+// @id create-new-contact
+// @tags contact
+// @accept json
+// @produce json
+// @param contact body dto.Contact true "Contact data"
+// @success 200 {object} dto.Contact "Contact created successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact [put]
func createNewContact(writer http.ResponseWriter, request *http.Request) {
contact := &dto.Contact{}
if err := render.Bind(request, contact); err != nil {
@@ -90,6 +126,22 @@ func contactFilter(next http.Handler) http.Handler {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Updates an existing notification contact to the values passed in the request body
+// @id update-contact
+// @accept json
+// @produce json
+// @param contactID path string true "ID of the contact to update" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param contact body dto.Contact true "Updated contact data"
+// @success 200 {object} dto.Contact "Updated contact"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact/{contactID} [put]
+// @tags contact
func updateContact(writer http.ResponseWriter, request *http.Request) {
contactDTO := dto.Contact{}
if err := render.Bind(request, &contactDTO); err != nil {
@@ -108,6 +160,20 @@ func updateContact(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Deletes notification contact for the current user and remove the contact ID from all subscriptions
+// @id remove-contact
+// @accept json
+// @produce json
+// @tags contact
+// @param contactID path string true "ID of the contact to remove" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Contact has been deleted"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact/{contactID} [delete]
func removeContact(writer http.ResponseWriter, request *http.Request) {
contactData := request.Context().Value(contactKey).(moira.ContactData)
err := controller.RemoveContact(database, contactData.ID, contactData.User, "")
@@ -116,6 +182,19 @@ func removeContact(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Push a test notification to verify that the contact is properly set up
+// @id send-test-contact-notification
+// @accept json
+// @produce json
+// @param contactID path string true "The ID of the target contact" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Test successful"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact/{contactID}/test [post]
+// @tags contact
func sendTestContactNotification(writer http.ResponseWriter, request *http.Request) {
contactID := middleware.GetContactID(request)
err := controller.SendTestContactNotification(database, contactID)
diff --git a/api/handler/contact_events.go b/api/handler/contact_events.go
index e1192a254..7c57b409e 100644
--- a/api/handler/contact_events.go
+++ b/api/handler/contact_events.go
@@ -24,6 +24,22 @@ func contactEvents(router chi.Router) {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Get contact events by ID with time range
+// @id get-contact-events-by-id
+// @tags contact
+// @produce json
+// @param contactID path string true "Contact ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param from query string false "Start time of the time range" default(-3hour)
+// @param to query string false "End time of the time range" default(now)
+// @success 200 {object} dto.ContactEventItemList "Successfully received contact events"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /contact/{contactID}/events [get]
func getContactByIdWithEvents(writer http.ResponseWriter, request *http.Request) {
contactData := request.Context().Value(contactKey).(moira.ContactData)
fromStr := middleware.GetFromStr(request)
diff --git a/api/handler/event.go b/api/handler/event.go
index 10f162e5c..398ad96a8 100644
--- a/api/handler/event.go
+++ b/api/handler/event.go
@@ -15,6 +15,21 @@ func event(router chi.Router) {
router.Delete("/all", deleteAllEvents)
}
+// nolint: gofmt,goimports
+//
+// @summary Gets all trigger events for current page and their count
+// @id get-events-list
+// @tags event
+// @produce json
+// @param triggerID path string true "The ID of updated trigger" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param size query int false "Number of items to be displayed on one page" default(100)
+// @param p query int false "Defines the number of the displayed page. E.g, p=2 would display the 2nd page" default(0)
+// @success 200 {object} dto.EventsList "Events fetched successfully"
+// @Failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @Failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @Failure 422 {object} api.ErrorRenderExample "Render error"
+// @Failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /event/{triggerID} [get]
func getEventsList(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
size := middleware.GetSize(request)
@@ -29,6 +44,15 @@ func getEventsList(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Deletes all notification events
+// @id delete-all-events
+// @tags event
+// @produce json
+// @success 200 "Events removed successfully"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /event/all [delete]
func deleteAllEvents(writer http.ResponseWriter, request *http.Request) {
if errorResponse := controller.DeleteAllEvents(database); errorResponse != nil {
render.Render(writer, request, errorResponse) //nolint
diff --git a/api/handler/handler.go b/api/handler/handler.go
index cad6f07c9..54d7e3254 100644
--- a/api/handler/handler.go
+++ b/api/handler/handler.go
@@ -8,10 +8,13 @@ import (
"github.com/go-chi/render"
metricSource "github.com/moira-alert/moira/metric_source"
"github.com/rs/cors"
+ httpSwagger "github.com/swaggo/http-swagger"
"github.com/moira-alert/moira"
"github.com/moira-alert/moira/api"
moiramiddle "github.com/moira-alert/moira/api/middleware"
+
+ _ "github.com/moira-alert/moira/docs" // docs is generated by Swag CLI, you have to import it.
)
var database moira.Database
@@ -33,6 +36,53 @@ func NewHandler(db moira.Database, log moira.Logger, index moira.Searcher, confi
router.NotFound(notFoundHandler)
router.MethodNotAllowed(methodNotAllowedHandler)
+ // @title Moira Alert
+ // @version master
+ // @description This is an API description for [Moira Alert API](https://moira.readthedocs.io/en/latest/overview.html)
+ // @description Check us out on [Github](https://github.com/moira-alert) or look up our [guide on getting started with Moira](https://moira.readthedocs.io)
+ // @contact.name Contact Moira Team
+ // @contact.email opensource@skbkontur.com
+ // @license.name MIT
+ // @BasePath /api
+ //
+ // @tag.name contact
+ // @tag.description APIs for working with Moira contacts. For more details, see
+ //
+ // @tag.name config
+ // @tag.description View Moira's runtime configuration. For more details, see
+ //
+ // @tag.name event
+ // @tag.description APIs for interacting with notification events. See for details
+ //
+ // @tag.name health
+ // @tag.description interact with Moira states/health status. See for details
+ //
+ // @tag.name notification
+ // @tag.description manage notifications that are currently in queue. See
+ //
+ // @tag.name pattern
+ // @tag.description APIs for interacting with graphite patterns in Moira. See
+ //
+ // @tag.name subscription
+ // @tag.description APIs for managing a user's subscription(s). See to learn about Moira subscriptions
+ //
+ // @tag.name tag
+ // @tag.description APIs for managing tags (a grouping of tags and subscriptions). See
+ //
+ // @tag.name trigger
+ // @tag.description APIs for interacting with Moira triggers. See to learn about Triggers
+ //
+ // @tag.name team
+ // @tag.description APIs for interacting with Moira teams
+ //
+ // @tag.name teamSubscription
+ // @tag.description APIs for interacting with Moira subscriptions owned by certain team
+ //
+ // @tag.name teamContact
+ // @tag.description APIs for interacting with Moira contacts owned by certain team
+ //
+ // @tag.name user
+ // @tag.description APIs for interacting with Moira users
router.Route("/api", func(router chi.Router) {
router.Use(moiramiddle.DatabaseContext(database))
router.Get("/config", getWebConfig(webConfigContent))
@@ -45,12 +95,15 @@ func NewHandler(db moira.Database, log moira.Logger, index moira.Searcher, confi
router.Route("/notification", notification)
router.Route("/health", health)
router.Route("/teams", teams)
-
router.Route("/contact", func(router chi.Router) {
contact(router)
contactEvents(router)
})
+ router.Get("/swagger/*", httpSwagger.Handler(
+ httpSwagger.URL("/api/swagger/doc.json"),
+ ))
})
+
if config.EnableCORS {
return cors.AllowAll().Handler(router)
}
diff --git a/api/handler/health.go b/api/handler/health.go
index 30eb5c1f2..e8affaa7f 100644
--- a/api/handler/health.go
+++ b/api/handler/health.go
@@ -15,6 +15,16 @@ func health(router chi.Router) {
router.Put("/notifier", setNotifierState)
}
+// nolint: gofmt,goimports
+//
+// @summary Get notifier state
+// @id get-notifier-state
+// @tags health
+// @produce json
+// @success 200 {object} dto.NotifierState "Notifier state retrieved"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /health/notifier [get]
func getNotifierState(writer http.ResponseWriter, request *http.Request) {
state, err := controller.GetNotifierState(database)
if err != nil {
diff --git a/api/handler/notification.go b/api/handler/notification.go
index 1ab60441f..92d879f00 100644
--- a/api/handler/notification.go
+++ b/api/handler/notification.go
@@ -18,6 +18,19 @@ func notification(router chi.Router) {
router.Delete("/all", deleteAllNotifications)
}
+// nolint: gofmt,goimports
+//
+// @summary Gets a paginated list of notifications, all notifications are fetched if end = -1 and start = 0
+// @id get-notifications
+// @tags notification
+// @produce json
+// @param start query int false "Default Value: 0" default(0)
+// @param end query int false "Default Value: -1" default(-1)
+// @success 200 {object} dto.NotificationsList "Notifications fetched successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /notification [get]
func getNotification(writer http.ResponseWriter, request *http.Request) {
urlValues, err := url.ParseQuery(request.URL.RawQuery)
if err != nil {
@@ -46,6 +59,18 @@ func getNotification(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete a notification by id
+// @id delete-notification
+// @tags notification
+// @param id query string true "The ID of deleted notification" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @produce json
+// @success 200 {object} dto.NotificationDeleteResponse "Notification have been deleted"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /notification [delete]
func deleteNotification(writer http.ResponseWriter, request *http.Request) {
urlValues, err := url.ParseQuery(request.URL.RawQuery)
if err != nil {
diff --git a/api/handler/pattern.go b/api/handler/pattern.go
index ac2c20327..148c6b98f 100644
--- a/api/handler/pattern.go
+++ b/api/handler/pattern.go
@@ -16,6 +16,16 @@ func pattern(router chi.Router) {
router.Delete("/{pattern}", deletePattern)
}
+// nolint: gofmt,goimports
+//
+// @summary Get all patterns
+// @id get-all-patterns
+// @tags pattern
+// @produce json
+// @success 200 {object} dto.PatternList "Patterns fetched successfully"
+// @Failure 422 {object} api.ErrorRenderExample "Render error"
+// @Failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /pattern [get]
func getAllPatterns(writer http.ResponseWriter, request *http.Request) {
logger := middleware.GetLoggerEntry(request)
patternsList, err := controller.GetAllPatterns(database, logger)
@@ -28,6 +38,17 @@ func getAllPatterns(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Deletes a Moira pattern
+// @id delete-pattern
+// @tags pattern
+// @produce json
+// @param pattern path string true "Trigger pattern to operate on" default(DevOps.my_server.hdd.freespace_mbytes)
+// @success 200 "Pattern deleted successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /pattern/{pattern} [delete]
func deletePattern(writer http.ResponseWriter, request *http.Request) {
pattern := chi.URLParam(request, "pattern")
if pattern == "" {
diff --git a/api/handler/subscription.go b/api/handler/subscription.go
index 1a958e513..833abbfb5 100644
--- a/api/handler/subscription.go
+++ b/api/handler/subscription.go
@@ -26,6 +26,16 @@ func subscription(router chi.Router) {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Get all subscriptions
+// @id get-user-subscriptions
+// @tags subscription
+// @produce json
+// @success 200 {object} dto.SubscriptionList "Subscriptions fetched successfully"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /subscription [get]
func getUserSubscriptions(writer http.ResponseWriter, request *http.Request) {
userLogin := middleware.GetLogin(request)
contacts, err := controller.GetUserSubscriptions(database, userLogin)
@@ -39,6 +49,19 @@ func getUserSubscriptions(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Create a new subscription
+// @id create-subscription
+// @tags subscription
+// @accept json
+// @produce json
+// @param subscription body dto.Subscription true "Subscription data"
+// @success 200 {object} dto.Subscription "Subscription created successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /subscription [put]
func createSubscription(writer http.ResponseWriter, request *http.Request) {
subscription := &dto.Subscription{}
if err := render.Bind(request, subscription); err != nil {
@@ -78,6 +101,22 @@ func subscriptionFilter(next http.Handler) http.Handler {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Update a subscription
+// @id update-subscription
+// @tags subscription
+// @accept json
+// @produce json
+// @param subscriptionID path string true "ID of the subscription to update" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param subscription body dto.Subscription true "Updated subscription data"
+// @success 200 {object} dto.Subscription "Subscription updated successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /subscription/{subscriptionID} [put]
func updateSubscription(writer http.ResponseWriter, request *http.Request) {
subscription := &dto.Subscription{}
if err := render.Bind(request, subscription); err != nil {
@@ -109,6 +148,18 @@ func updateSubscription(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete a subscription
+// @id remove-subscription
+// @tags subscription
+// @produce json
+// @param subscriptionID path string true "ID of the subscription to remove" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Subscription deleted"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /subscription/{subscriptionID} [delete]
func removeSubscription(writer http.ResponseWriter, request *http.Request) {
subscriptionID := middleware.GetSubscriptionID(request)
if err := controller.RemoveSubscription(database, subscriptionID); err != nil {
@@ -116,6 +167,18 @@ func removeSubscription(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Send a test notification for a subscription
+// @id send-test-notification
+// @tags subscription
+// @produce json
+// @param subscriptionID path string true "ID of the subscription to send the test notification" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Test notification sent successfully"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /subscription/{subscriptionID}/test [put]
func sendTestNotification(writer http.ResponseWriter, request *http.Request) {
subscriptionID := middleware.GetSubscriptionID(request)
if err := controller.SendTestNotification(database, subscriptionID); err != nil {
diff --git a/api/handler/tag.go b/api/handler/tag.go
index 40068dee8..6faaea6ae 100644
--- a/api/handler/tag.go
+++ b/api/handler/tag.go
@@ -19,6 +19,16 @@ func tag(router chi.Router) {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Get all tags
+// @id get-all-tags
+// @tags tag
+// @produce json
+// @success 200 {object} dto.TagsData "Tags fetched successfully"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /tag [get]
func getAllTags(writer http.ResponseWriter, request *http.Request) {
tagData, err := controller.GetAllTags(database)
if err != nil {
@@ -32,6 +42,16 @@ func getAllTags(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get all tags and their subscriptions
+// @id get-all-tags-and-subscriptions
+// @tags tag
+// @produce json
+// @success 200 {object} dto.TagsStatistics "Successful"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /tag/stats [get]
func getAllTagsAndSubscriptions(writer http.ResponseWriter, request *http.Request) {
logger := middleware.GetLoggerEntry(request)
data, err := controller.GetAllTagsAndSubscriptions(database, logger)
@@ -45,6 +65,18 @@ func getAllTagsAndSubscriptions(writer http.ResponseWriter, request *http.Reques
}
}
+// nolint: gofmt,goimports
+//
+// @summary Remove a tag
+// @id remove-tag
+// @tags tag
+// @produce json
+// @param tag path string true "Name of the tag to remove" default(cpu)
+// @success 200 {object} dto.MessageResponse "Tag removed successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /tag/{tag} [delete]
func removeTag(writer http.ResponseWriter, request *http.Request) {
tagName := middleware.GetTag(request)
response, err := controller.RemoveTag(database, tagName)
diff --git a/api/handler/team.go b/api/handler/team.go
index 44c644903..bc079e6b4 100644
--- a/api/handler/team.go
+++ b/api/handler/team.go
@@ -46,6 +46,19 @@ func usersFilterForTeams(next http.Handler) http.Handler {
})
}
+// nolint: gofmt,goimports
+//
+// @summary Create a new team
+// @id create-team
+// @tags team
+// @accept json
+// @produce json
+// @param team body dto.TeamModel true "Team data"
+// @success 200 {object} dto.SaveTeamResponse "Team created successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams [post]
func createTeam(writer http.ResponseWriter, request *http.Request) {
user := middleware.GetLogin(request)
team := dto.TeamModel{}
@@ -65,6 +78,16 @@ func createTeam(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get all teams
+// @id get-all-teams
+// @tags team
+// @produce json
+// @success 200 {object} dto.UserTeams "Teams fetched successfully"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams [get]
func getAllTeams(writer http.ResponseWriter, request *http.Request) {
user := middleware.GetLogin(request)
response, err := controller.GetUserTeams(database, user)
@@ -79,6 +102,19 @@ func getAllTeams(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get a team by ID
+// @id get-team
+// @tags team
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TeamModel "Team updated successfully"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID} [get]
func getTeam(writer http.ResponseWriter, request *http.Request) {
teamID := middleware.GetTeamID(request)
@@ -94,6 +130,22 @@ func getTeam(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Update existing team
+// @id update-team
+// @tags team
+// @accept json
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param team body dto.TeamModel true "Updated team data"
+// @success 200 {object} dto.SaveTeamResponse "Team updated successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID} [patch]
func updateTeam(writer http.ResponseWriter, request *http.Request) {
team := dto.TeamModel{}
err := render.Bind(request, &team)
@@ -115,6 +167,20 @@ func updateTeam(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete a team
+// @id delete-team
+// @tags team
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.SaveTeamResponse "Team has been successfully deleted"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID} [delete]
func deleteTeam(writer http.ResponseWriter, request *http.Request) {
userLogin := middleware.GetLogin(request)
teamID := middleware.GetTeamID(request)
@@ -130,6 +196,19 @@ func deleteTeam(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get users of a team
+// @id get-team-users
+// @tags team
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TeamMembers "Users fetched successfully"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/users [get]
func getTeamUsers(writer http.ResponseWriter, request *http.Request) {
teamID := middleware.GetTeamID(request)
@@ -145,6 +224,22 @@ func getTeamUsers(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Set users of a team
+// @id set-team-users
+// @tags team
+// @accept json
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param usernames body dto.TeamMembers true "Usernames to set as team members"
+// @success 200 {object} dto.TeamMembers "Team updated successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/users [put]
func setTeamUsers(writer http.ResponseWriter, request *http.Request) {
members := dto.TeamMembers{}
err := render.Bind(request, &members)
@@ -167,6 +262,22 @@ func setTeamUsers(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Add users to a team
+// @id add-team-users
+// @tags team
+// @accept json
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param usernames body dto.TeamMembers true "Usernames to add to the team"
+// @success 200 {object} dto.TeamMembers "Team updated successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/users [post]
func addTeamUsers(writer http.ResponseWriter, request *http.Request) {
members := dto.TeamMembers{}
err := render.Bind(request, &members)
@@ -188,6 +299,21 @@ func addTeamUsers(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete a user from a team
+// @id delete-team-user
+// @tags team
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param teamUserID path string true "User login in methods related to teams manipulation" default(anonymous)
+// @success 200 {object} dto.TeamMembers "Team updated successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/users/{teamUserID} [delete]
func deleteTeamUser(writer http.ResponseWriter, request *http.Request) {
teamID := middleware.GetTeamID(request)
userID := middleware.GetTeamUserID(request)
@@ -204,6 +330,19 @@ func deleteTeamUser(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get team settings
+// @id get-team-settings
+// @tags team
+// @produce json
+// @param teamID path string true "ID of the team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TeamSettings "Team settings"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/settings [get]
func getTeamSettings(writer http.ResponseWriter, request *http.Request) {
teamID := middleware.GetTeamID(request)
teamSettings, err := controller.GetTeamSettings(database, teamID)
diff --git a/api/handler/team_contact.go b/api/handler/team_contact.go
index 70f094db2..1ed5874ab 100644
--- a/api/handler/team_contact.go
+++ b/api/handler/team_contact.go
@@ -15,6 +15,22 @@ func teamContact(router chi.Router) {
router.Post("/", createNewTeamContact)
}
+// nolint: gofmt,goimports
+//
+// @summary Create a new team contact
+// @id create-new-team-contact
+// @tags teamContact
+// @accept json
+// @produce json
+// @param teamID path string true "The ID of team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param contact body dto.Contact true "Team contact data"
+// @success 200 {object} dto.Contact "Team contact created successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/contacts [post]
func createNewTeamContact(writer http.ResponseWriter, request *http.Request) {
contact := &dto.Contact{}
if err := render.Bind(request, contact); err != nil {
diff --git a/api/handler/team_subscription.go b/api/handler/team_subscription.go
index 4d7d29939..c913fff79 100644
--- a/api/handler/team_subscription.go
+++ b/api/handler/team_subscription.go
@@ -16,6 +16,22 @@ func teamSubscription(router chi.Router) {
router.Post("/", createTeamSubscription)
}
+// nolint: gofmt,goimports
+//
+// @summary Create a new team subscription
+// @id create-new-team-subscription
+// @tags teamSubscription
+// @accept json
+// @produce json
+// @param teamID path string true "The ID of team" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param subscription body dto.Subscription true "Team subscription data"
+// @success 200 {object} dto.Subscription "Team subscription created successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 403 {object} api.ErrorForbiddenExample "Forbidden"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /teams/{teamID}/subscriptions [post]
func createTeamSubscription(writer http.ResponseWriter, request *http.Request) {
subscription := &dto.Subscription{}
if err := render.Bind(request, subscription); err != nil {
diff --git a/api/handler/trigger.go b/api/handler/trigger.go
index 924f338dd..73d9f574e 100644
--- a/api/handler/trigger.go
+++ b/api/handler/trigger.go
@@ -30,6 +30,22 @@ func trigger(router chi.Router) {
router.Get("/dump", triggerDump)
}
+// nolint: gofmt,goimports
+//
+// @summary Update existing trigger
+// @id update-trigger
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param validate query bool false "For validating targets"
+// @param body body dto.Trigger true "Trigger data"
+// @success 200 {object} dto.SaveTriggerResponse "Updated trigger"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @failure 503 {object} api.ErrorRemoteServerUnavailableExample "Remote server unavailable"
+// @router /trigger/{triggerID} [put]
func updateTrigger(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
@@ -95,6 +111,16 @@ func writeErrorSaveResponse(writer http.ResponseWriter, request *http.Request, t
render.JSON(writer, request, response)
}
+// nolint: gofmt,goimports
+//
+// @summary Remove trigger
+// @id remove-trigger
+// @tags trigger
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Successfully removed"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID} [delete]
func removeTrigger(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
err := controller.RemoveTrigger(database, triggerID)
@@ -103,6 +129,19 @@ func removeTrigger(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get an existing trigger
+// @id get-trigger
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param populated query bool false "Populated" default(false)
+// @success 200 {object} dto.Trigger "Trigger data"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID} [get]
func getTrigger(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
@@ -140,6 +179,18 @@ func checkingTemplateFilling(request *http.Request, trigger dto.Trigger) *api.Er
return nil
}
+// nolint: gofmt,goimports
+//
+// @summary Get the trigger state as at last check
+// @id get-trigger-state
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TriggerCheck "Trigger state fetched successful"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/state [get]
func getTriggerState(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
triggerState, err := controller.GetTriggerLastCheck(database, triggerID)
@@ -152,6 +203,17 @@ func getTriggerState(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get a trigger with its throttling i.e its next allowed message time
+// @id get-trigger-throttling
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.ThrottlingResponse "Trigger throttle info retrieved"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @router /trigger/{triggerID}/throttling [get]
func getTriggerThrottling(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
triggerState, err := controller.GetTriggerThrottling(database, triggerID)
@@ -164,6 +226,16 @@ func getTriggerThrottling(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Deletes throttling for a trigger
+// @id delete-trigger-throttling
+// @tags trigger
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Trigger throttling has been deleted"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/throttling [delete]
func deleteThrottling(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
err := controller.DeleteTriggerThrottling(database, triggerID)
@@ -172,6 +244,19 @@ func deleteThrottling(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Set metrics and the trigger itself to maintenance mode
+// @id set-trigger-maintenance
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param body body dto.TriggerMaintenance true "Maintenance data"
+// @success 200 "Trigger or metric have been scheduled for maintenance"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/setMaintenance [put]
func setTriggerMaintenance(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
triggerMaintenance := dto.TriggerMaintenance{}
@@ -188,6 +273,17 @@ func setTriggerMaintenance(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get trigger dump
+// @id get-trigger-dump
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TriggerDump "Trigger dump"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/dump [get]
func triggerDump(writer http.ResponseWriter, request *http.Request) {
triggerID, log := prepareTriggerContext(request)
diff --git a/api/handler/trigger_metrics.go b/api/handler/trigger_metrics.go
index d48360b29..a1f95fa4a 100644
--- a/api/handler/trigger_metrics.go
+++ b/api/handler/trigger_metrics.go
@@ -21,6 +21,21 @@ func triggerMetrics(router chi.Router) {
router.Delete("/nodata", deleteTriggerNodataMetrics)
}
+// nolint: gofmt,goimports
+//
+// @summary Get metrics associated with certain trigger
+// @id get-trigger-metrics
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param from query string false "Start time for metrics retrieval" default(-10minutes)
+// @param to query string false "End time for metrics retrieval" default(now)
+// @success 200 {object} dto.TriggerMetrics "Trigger metrics retrieved successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/metrics [get]
func getTriggerMetrics(writer http.ResponseWriter, request *http.Request) {
metricSourceProvider := middleware.GetTriggerTargetsSourceProvider(request)
triggerID := middleware.GetTriggerID(request)
@@ -49,6 +64,19 @@ func getTriggerMetrics(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete metric from last check and all trigger pattern metrics
+// @id delete-trigger-metric
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param name query string false "Name of the target metric" default(DevOps.my_server.hdd.freespace_mbytes)
+// @success 200 "Trigger metric deleted successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/metrics [delete]
func deleteTriggerMetric(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
@@ -64,6 +92,18 @@ func deleteTriggerMetric(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete all metrics from last data which are in NODATA state. It also deletes all trigger patterns of those metrics
+// @id delete-trigger-nodata-metrics
+// @tags trigger
+// @produce json
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 "Trigger nodata metrics deleted successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/metrics/nodata [delete]
func deleteTriggerNodataMetrics(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
if err := controller.DeleteTriggerNodataMetrics(database, triggerID); err != nil {
diff --git a/api/handler/trigger_render.go b/api/handler/trigger_render.go
index c2e961211..02794c759 100644
--- a/api/handler/trigger_render.go
+++ b/api/handler/trigger_render.go
@@ -18,6 +18,24 @@ import (
"github.com/moira-alert/moira/plotting"
)
+// nolint: gofmt,goimports
+//
+// @summary Render trigger metrics plot
+// @id render-trigger-metrics
+// @tags trigger
+// @produce png
+// @param triggerID path string true "Trigger ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @param target query string false "Target metric name" default(t1)
+// @param from query string false "Start time for metrics retrieval" default(-1hour)
+// @param to query string false "End time for metrics retrieval" default(now)
+// @param timezone query string false "Timezone for rendering" default(UTC)
+// @param theme query string false "Plot theme" default(light)
+// @param realtime query bool false "Fetch real-time data" default(false)
+// @success 200 "Rendered plot image successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/{triggerID}/render [get]
func renderTrigger(writer http.ResponseWriter, request *http.Request) {
sourceProvider, targetName, from, to, triggerID, fetchRealtimeData, err := getEvaluationParameters(request)
if err != nil {
diff --git a/api/handler/triggers.go b/api/handler/triggers.go
index fe61fae70..3b78433da 100644
--- a/api/handler/triggers.go
+++ b/api/handler/triggers.go
@@ -38,6 +38,16 @@ func triggers(metricSourceProvider *metricSource.SourceProvider, searcher moira.
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get all triggers
+// @id get-all-triggers
+// @tags trigger
+// @produce json
+// @success 200 {object} dto.TriggersList "Fetched all triggers"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger [get]
func getAllTriggers(writer http.ResponseWriter, request *http.Request) {
triggersList, errorResponse := controller.GetAllTriggers(database)
if errorResponse != nil {
@@ -51,7 +61,22 @@ func getAllTriggers(writer http.ResponseWriter, request *http.Request) {
}
}
-// createTrigger handler creates moira.Trigger.
+// nolint: gofmt,goimports
+// createTrigger handler creates moira.Trigger
+//
+// @summary Create a new trigger
+// @id create-trigger
+// @tags trigger
+// @accept json
+// @produce json
+// @param validate query bool false "For validating targets"
+// @param trigger body dto.Trigger true "Trigger data"
+// @success 200 {object} dto.SaveTriggerResponse "Trigger created successfully"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @failure 503 {object} api.ErrorRemoteServerUnavailableExample "Remote server unavailable"
+// @router /trigger [put]
func createTrigger(writer http.ResponseWriter, request *http.Request) {
trigger, err := getTriggerFromRequest(request)
if err != nil {
@@ -133,6 +158,18 @@ func getMetricTTLByTrigger(request *http.Request, trigger *dto.Trigger) time.Dur
return ttl
}
+// nolint: gofmt,goimports
+//
+// @summary Validates trigger target
+// @id trigger-check
+// @tags trigger
+// @accept json
+// @produce json
+// @param trigger body dto.Trigger true "Trigger data"
+// @success 200 {object} dto.TriggerCheckResponse "Validation is done, see response body for validation result"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/check [put]
func triggerCheck(writer http.ResponseWriter, request *http.Request) {
trigger := &dto.Trigger{}
response := dto.TriggerCheckResponse{}
@@ -157,6 +194,26 @@ func triggerCheck(writer http.ResponseWriter, request *http.Request) {
render.JSON(writer, request, response)
}
+// nolint: gofmt,goimports
+//
+// @summary Search triggers. Replaces the deprecated `page` path
+// @description You can also add filtering by tags, for this purpose add query parameters tags[0]=test, tags[1]=test1 and so on
+// @description For example, `/api/trigger/search?tags[0]=test&tags[1]=test1`
+// @id search-triggers
+// @tags trigger
+// @produce json
+// @param onlyProblems query boolean false "Only include problems" default(false)
+// @param text query string false "Search text" default(cpu)
+// @param p query integer false "Page number" default(0)
+// @param size query integer false "Page size" default(10)
+// @param createPager query boolean false "Create pager" default(false)
+// @param pagerID query string false "Pager ID" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TriggersList "Successfully fetched matching triggers"
+// @failure 400 {object} api.ErrorInvalidRequestExample "Bad request from client"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/search [get]
func searchTriggers(writer http.ResponseWriter, request *http.Request) {
request.ParseForm() //nolint
onlyErrors := getOnlyProblemsFlag(request)
@@ -181,6 +238,17 @@ func searchTriggers(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Delete triggers pager
+// @tags trigger
+// @produce json
+// @param pagerID path string true "Pager ID to delete" default(bcba82f5-48cf-44c0-b7d6-e1d32c64a88c)
+// @success 200 {object} dto.TriggersSearchResultDeleteResponse "Successfully deleted pager"
+// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /trigger/pagers/{pagerID} [delete]
func deletePager(writer http.ResponseWriter, request *http.Request) {
pagerID := middleware.GetPagerID(request)
diff --git a/api/handler/user.go b/api/handler/user.go
index 8432f544f..17c856698 100644
--- a/api/handler/user.go
+++ b/api/handler/user.go
@@ -16,6 +16,15 @@ func user(router chi.Router) {
router.Get("/settings", getUserSettings)
}
+// nolint: gofmt,goimports
+//
+// @summary Gets the username of the authenticated user if it is available
+// @id get-user-name
+// @tags user
+// @produce json
+// @success 200 {object} dto.User "User name fetched successfully"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @router /user [get]
func getUserName(writer http.ResponseWriter, request *http.Request) {
userLogin := middleware.GetLogin(request)
if err := render.Render(writer, request, &dto.User{Login: userLogin}); err != nil {
@@ -24,6 +33,16 @@ func getUserName(writer http.ResponseWriter, request *http.Request) {
}
}
+// nolint: gofmt,goimports
+//
+// @summary Get the user's contacts and subscriptions
+// @id get-user-settings
+// @tags user
+// @produce json
+// @success 200 {object} dto.UserSettings "Settings fetched successfully"
+// @failure 422 {object} api.ErrorRenderExample "Render error"
+// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
+// @router /user/settings [get]
func getUserSettings(writer http.ResponseWriter, request *http.Request) {
userLogin := middleware.GetLogin(request)
userSettings, err := controller.GetUserSettings(database, userLogin)
diff --git a/datatypes.go b/datatypes.go
index 2c37daad9..852cba7bd 100644
--- a/datatypes.go
+++ b/datatypes.go
@@ -42,16 +42,16 @@ const (
// NotificationEvent represents trigger state changes event
type NotificationEvent struct {
- IsTriggerEvent bool `json:"trigger_event,omitempty"`
- Timestamp int64 `json:"timestamp"`
- Metric string `json:"metric"`
- Value *float64 `json:"value,omitempty"`
+ IsTriggerEvent bool `json:"trigger_event,omitempty" example:"true"`
+ Timestamp int64 `json:"timestamp" example:"1590741878"`
+ Metric string `json:"metric" example:"carbon.agents.*.metricsReceived"`
+ Value *float64 `json:"value,omitempty" example:"70"`
Values map[string]float64 `json:"values,omitempty"`
- State State `json:"state"`
- TriggerID string `json:"trigger_id"`
+ State State `json:"state" example:"OK"`
+ TriggerID string `json:"trigger_id" example:"5ff37996-8927-4cab-8987-970e80d8e0a8"`
SubscriptionID *string `json:"sub_id,omitempty"`
ContactID string `json:"contact_id,omitempty"`
- OldState State `json:"old_state"`
+ OldState State `json:"old_state" example:"ERROR"`
Message *string `json:"msg,omitempty"`
MessageEventInfo *EventInfo `json:"event_message"`
}
@@ -70,7 +70,7 @@ type NotificationEventHistoryItem struct {
// EventInfo - a base for creating messages.
type EventInfo struct {
Maintenance *MaintenanceInfo `json:"maintenance,omitempty"`
- Interval *int64 `json:"interval,omitempty"`
+ Interval *int64 `json:"interval,omitempty" example:"0"`
}
// CreateMessage - creates a message based on EventInfo.
@@ -157,14 +157,14 @@ func NotificationEventsToTemplatingEvents(events NotificationEvents) []templatin
// TriggerData represents trigger object
type TriggerData struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Desc string `json:"desc"`
- Targets []string `json:"targets"`
- WarnValue float64 `json:"warn_value"`
- ErrorValue float64 `json:"error_value"`
- IsRemote bool `json:"is_remote"`
- Tags []string `json:"__notifier_trigger_tags"`
+ ID string `json:"id" example:"292516ed-4924-4154-a62c-ebe312431fce"`
+ Name string `json:"name" example:"Not enough disk space left"`
+ Desc string `json:"desc" example:"check the size of /var/log"`
+ Targets []string `json:"targets" example:"devOps.my_server.hdd.freespace_mbytes"`
+ WarnValue float64 `json:"warn_value" example:"5000"`
+ ErrorValue float64 `json:"error_value" example:"1000"`
+ IsRemote bool `json:"is_remote" example:"false"`
+ Tags []string `json:"__notifier_trigger_tags" example:"server,disk"`
}
// GetTriggerURI gets frontUri and returns triggerUrl, returns empty string on selfcheck and test notifications
@@ -184,47 +184,47 @@ type Team struct {
// ContactData represents contact object
type ContactData struct {
- Type string `json:"type"`
- Value string `json:"value"`
- ID string `json:"id"`
- User string `json:"user"`
+ Type string `json:"type" example:"mail"`
+ Value string `json:"value" example:"devops@example.com"`
+ ID string `json:"id" example:"1dd38765-c5be-418d-81fa-7a5f879c2315"`
+ User string `json:"user" example:""`
Team string `json:"team"`
}
// SubscriptionData represents user subscription
type SubscriptionData struct {
- Contacts []string `json:"contacts"`
- Tags []string `json:"tags"`
+ Contacts []string `json:"contacts" example:"acd2db98-1659-4a2f-b227-52d71f6e3ba1"`
+ Tags []string `json:"tags" example:"server,cpu"`
Schedule ScheduleData `json:"sched"`
Plotting PlottingData `json:"plotting"`
- ID string `json:"id"`
- Enabled bool `json:"enabled"`
- AnyTags bool `json:"any_tags"`
- IgnoreWarnings bool `json:"ignore_warnings,omitempty"`
- IgnoreRecoverings bool `json:"ignore_recoverings,omitempty"`
- ThrottlingEnabled bool `json:"throttling"`
- User string `json:"user"`
- TeamID string `json:"team_id"`
+ ID string `json:"id" example:"292516ed-4924-4154-a62c-ebe312431fce"`
+ Enabled bool `json:"enabled" example:"true"`
+ AnyTags bool `json:"any_tags" example:"false"`
+ IgnoreWarnings bool `json:"ignore_warnings,omitempty" example:"false"`
+ IgnoreRecoverings bool `json:"ignore_recoverings,omitempty" example:"false"`
+ ThrottlingEnabled bool `json:"throttling" example:"false"`
+ User string `json:"user" example:""`
+ TeamID string `json:"team_id" example:"324516ed-4924-4154-a62c-eb124234fce"`
}
// PlottingData represents plotting settings
type PlottingData struct {
- Enabled bool `json:"enabled"`
- Theme string `json:"theme"`
+ Enabled bool `json:"enabled" example:"true"`
+ Theme string `json:"theme" example:"dark"`
}
// ScheduleData represents subscription schedule
type ScheduleData struct {
Days []ScheduleDataDay `json:"days"`
- TimezoneOffset int64 `json:"tzOffset"`
- StartOffset int64 `json:"startOffset"`
- EndOffset int64 `json:"endOffset"`
+ TimezoneOffset int64 `json:"tzOffset" example:"-60"`
+ StartOffset int64 `json:"startOffset" example:"0"`
+ EndOffset int64 `json:"endOffset" example:"1439"`
}
// ScheduleDataDay represents week day of schedule
type ScheduleDataDay struct {
- Enabled bool `json:"enabled"`
- Name string `json:"name,omitempty"`
+ Enabled bool `json:"enabled" example:"true"`
+ Name string `json:"name,omitempty" example:"Mon"`
}
// ScheduledNotification represent notification object
@@ -233,9 +233,9 @@ type ScheduledNotification struct {
Trigger TriggerData `json:"trigger"`
Contact ContactData `json:"contact"`
Plotting PlottingData `json:"plotting"`
- Throttled bool `json:"throttled"`
- SendFail int `json:"send_fail"`
- Timestamp int64 `json:"timestamp"`
+ Throttled bool `json:"throttled" example:"false"`
+ SendFail int `json:"send_fail" example:"0"`
+ Timestamp int64 `json:"timestamp" example:"1594471927"`
}
// MatchedMetric represents parsed and matched metric data
@@ -266,23 +266,23 @@ const (
// Trigger represents trigger data object
type Trigger struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Desc *string `json:"desc,omitempty"`
- Targets []string `json:"targets"`
- WarnValue *float64 `json:"warn_value"`
- ErrorValue *float64 `json:"error_value"`
- TriggerType string `json:"trigger_type"`
- Tags []string `json:"tags"`
- TTLState *TTLState `json:"ttl_state,omitempty"`
- TTL int64 `json:"ttl,omitempty"`
+ ID string `json:"id" example:"292516ed-4924-4154-a62c-ebe312431fce"`
+ Name string `json:"name" example:"Not enough disk space left"`
+ Desc *string `json:"desc,omitempty" example:"check the size of /var/log"`
+ Targets []string `json:"targets" example:"devOps.my_server.hdd.freespace_mbytes"`
+ WarnValue *float64 `json:"warn_value" example:"5000"`
+ ErrorValue *float64 `json:"error_value" example:"1000"`
+ TriggerType string `json:"trigger_type" example:"rising"`
+ Tags []string `json:"tags" example:"server,disk"`
+ TTLState *TTLState `json:"ttl_state,omitempty" example:"NODATA"`
+ TTL int64 `json:"ttl,omitempty" example:"600"`
Schedule *ScheduleData `json:"sched,omitempty"`
- Expression *string `json:"expression,omitempty"`
+ Expression *string `json:"expression,omitempty" example:""`
PythonExpression *string `json:"python_expression,omitempty"`
- Patterns []string `json:"patterns"`
- IsRemote bool `json:"is_remote"`
- MuteNewMetrics bool `json:"mute_new_metrics"`
- AloneMetrics map[string]bool `json:"alone_metrics"`
+ Patterns []string `json:"patterns" example:""`
+ IsRemote bool `json:"is_remote" example:"false"`
+ MuteNewMetrics bool `json:"mute_new_metrics" example:"false"`
+ AloneMetrics map[string]bool `json:"alone_metrics" example:"t1:true"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
CreatedBy string `json:"created_by"`
@@ -292,7 +292,7 @@ type Trigger struct {
// TriggerCheck represents trigger data with last check data and check timestamp
type TriggerCheck struct {
Trigger
- Throttling int64 `json:"throttling"`
+ Throttling int64 `json:"throttling" example:"0"`
LastCheck CheckData `json:"last_check"`
Highlights map[string]string `json:"highlights"`
}
@@ -309,15 +309,15 @@ type CheckData struct {
// MetricsToTargetRelation is a map that holds relation between metric names that was alone during last
// check and targets that fetched this metric
// {"t1": "metric.name.1", "t2": "metric.name.2"}
- MetricsToTargetRelation map[string]string `json:"metrics_to_target_relation"`
- Score int64 `json:"score"`
- State State `json:"state"`
- Maintenance int64 `json:"maintenance,omitempty"`
+ MetricsToTargetRelation map[string]string `json:"metrics_to_target_relation" example:"t1:metric.name.1,t2:metric.name.2"`
+ Score int64 `json:"score" example:"100"`
+ State State `json:"state" example:"OK"`
+ Maintenance int64 `json:"maintenance,omitempty" example:"0"`
MaintenanceInfo MaintenanceInfo `json:"maintenance_info"`
- Timestamp int64 `json:"timestamp,omitempty"`
- EventTimestamp int64 `json:"event_timestamp,omitempty"`
- LastSuccessfulCheckTimestamp int64 `json:"last_successful_check_timestamp"`
- Suppressed bool `json:"suppressed,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty" example:"1590741916"`
+ EventTimestamp int64 `json:"event_timestamp,omitempty" example:"1590741878"`
+ LastSuccessfulCheckTimestamp int64 `json:"last_successful_check_timestamp" example:"1590741916"`
+ Suppressed bool `json:"suppressed,omitempty" example:"true"`
SuppressedState State `json:"suppressed_state,omitempty"`
Message string `json:"msg,omitempty"`
}
@@ -334,14 +334,14 @@ func (checkData *CheckData) RemoveMetricsToTargetRelation() {
// MetricState represents metric state data for given timestamp
type MetricState struct {
- EventTimestamp int64 `json:"event_timestamp"`
- State State `json:"state"`
- Suppressed bool `json:"suppressed"`
+ EventTimestamp int64 `json:"event_timestamp" example:"1590741878"`
+ State State `json:"state" example:"OK"`
+ Suppressed bool `json:"suppressed" example:"false"`
SuppressedState State `json:"suppressed_state,omitempty"`
- Timestamp int64 `json:"timestamp"`
- Value *float64 `json:"value,omitempty"`
+ Timestamp int64 `json:"timestamp" example:"1590741878"`
+ Value *float64 `json:"value,omitempty" example:"70"`
Values map[string]float64 `json:"values,omitempty"`
- Maintenance int64 `json:"maintenance,omitempty"`
+ Maintenance int64 `json:"maintenance,omitempty" example:"0"`
MaintenanceInfo MaintenanceInfo `json:"maintenance_info"`
// AloneMetrics map[string]string `json:"alone_metrics"` // represents a relation between name of alone metrics and their targets
}
@@ -360,9 +360,9 @@ func (metricState *MetricState) GetMaintenance() (MaintenanceInfo, int64) {
// MaintenanceInfo represents user and time set/unset maintenance
type MaintenanceInfo struct {
StartUser *string `json:"setup_user"`
- StartTime *int64 `json:"setup_time"`
+ StartTime *int64 `json:"setup_time" example:"0"`
StopUser *string `json:"remove_user"`
- StopTime *int64 `json:"remove_time"`
+ StopTime *int64 `json:"remove_time" example:"0"`
}
// Set maintanace start and stop users and times
diff --git a/docs/docs.go b/docs/docs.go
new file mode 100644
index 000000000..a8f0f0241
--- /dev/null
+++ b/docs/docs.go
@@ -0,0 +1,3 @@
+// Will be generated in `Dockerfile.api` by the `make spec` command for swagger docs. DO NOT EDIT.
+
+package docs
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 14e7107fe..09559cd88 100644
--- a/go.mod
+++ b/go.mod
@@ -47,7 +47,10 @@ require (
gopkg.in/yaml.v2 v2.4.0
)
-require github.com/mitchellh/mapstructure v1.5.0
+require (
+ github.com/mitchellh/mapstructure v1.5.0
+ github.com/swaggo/http-swagger v1.3.4
+)
require (
bitbucket.org/tebeka/strftime v0.0.0-20140926081919-2194253a23c0 // indirect
@@ -146,7 +149,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.14.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
- github.com/stretchr/testify v1.8.1 // indirect
+ github.com/stretchr/testify v1.8.4 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
@@ -158,12 +161,12 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.24.0 // indirect
- golang.org/x/crypto v0.6.0 // indirect
+ golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20200924195034-c827fd4f18b9 // indirect
golang.org/x/image v0.5.0 // indirect
- golang.org/x/net v0.8.0 // indirect
- golang.org/x/sys v0.6.0 // indirect
- golang.org/x/text v0.8.0 // indirect
+ golang.org/x/net v0.14.0 // indirect
+ golang.org/x/sys v0.11.0 // indirect
+ golang.org/x/text v0.12.0 // indirect
gonum.org/v1/gonum v0.12.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
@@ -173,13 +176,24 @@ require (
)
require (
+ github.com/BurntSushi/toml v1.3.2 // indirect
+ github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
+ github.com/go-openapi/jsonpointer v0.20.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/spec v0.20.9 // indirect
+ github.com/go-openapi/swag v0.22.4 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.11 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
+ github.com/swaggo/files v1.0.1 // indirect
+ github.com/swaggo/swag v1.8.12 // indirect
+ golang.org/x/tools v0.11.1 // indirect
)
// Have to exclude version that is incorectly retracted by authors
diff --git a/go.sum b/go.sum
index 09b73f652..b7930c20f 100644
--- a/go.sum
+++ b/go.sum
@@ -399,13 +399,16 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/JaderDias/movingmedian v0.0.0-20220813210630-d8c6b6de8835 h1:mbxQnovjDz5SvlatpxkbiMvybHH1hsSEu6OhPDLlfU8=
github.com/JaderDias/movingmedian v0.0.0-20220813210630-d8c6b6de8835/go.mod h1:zsfWLaDctbM7aV1TsQAwkVswuKQ0k7PK4rjC1VZqpbI=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
@@ -602,6 +605,21 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
+github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
+github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
+github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
@@ -793,6 +811,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -824,7 +844,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -842,6 +862,11 @@ github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuz
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8=
@@ -903,6 +928,7 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -1053,12 +1079,19 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
+github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
+github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
+github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
+github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w=
+github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
@@ -1131,8 +1164,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1178,6 +1211,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1245,8 +1279,9 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
+golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1386,8 +1421,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1409,8 +1444,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1478,6 +1513,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
+golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc=
+golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1739,6 +1776,7 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@@ -1768,6 +1806,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=