Skip to content

Commit

Permalink
✨ feat: added basic Markdown #39
Browse files Browse the repository at this point in the history
  • Loading branch information
pnguyen215 committed Oct 29, 2023
1 parent 6527a92 commit d161de8
Show file tree
Hide file tree
Showing 10 changed files with 557 additions and 149 deletions.
206 changes: 106 additions & 100 deletions apix/api.go

Large diffs are not rendered by default.

53 changes: 29 additions & 24 deletions apix/api_model.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package apix

import "time"
import (
"time"

type Authentication struct {
"github.com/sivaosorg/govm/bot/telegram"
)

type AuthenticationConfig struct {
IsEnabled bool `json:"enabled" yaml:"enabled"`
Type string `json:"type,omitempty" yaml:"type"`
Token string `json:"-" yaml:"token"`
Username string `json:"username,omitempty" yaml:"username"`
Password string `json:"-" yaml:"password"`
}

type Retry struct {
type RetryConfig struct {
IsEnabled bool `json:"enabled" yaml:"enabled"`
MaxAttempts int `json:"max_attempts" yaml:"max_attempts"`
InitialInterval time.Duration `json:"initial_interval" yaml:"initial_interval"`
Expand All @@ -19,27 +23,28 @@ type Retry struct {
RetryOnStatus []int `json:"retry_on_status" yaml:"retry_on_status"`
}

type Endpoint struct {
IsEnabled bool `json:"enabled" yaml:"enabled"`
DebugMode bool `json:"debug_mode" yaml:"debug_mode"`
BaseURL string `json:"base_url" yaml:"base_url"`
Timeout time.Duration `json:"timeout" yaml:"timeout"`
Path string `json:"path" yaml:"path"`
Method string `json:"method" yaml:"method"`
Description string `json:"description" yaml:"description"`
QueryParams map[string]string `json:"query_params" yaml:"query_params"`
PathParams map[string]string `json:"path_params" yaml:"path_params"`
Headers map[string]string `json:"headers" yaml:"headers"`
Body map[string]interface{} `json:"body" yaml:"body"`
Retry Retry `json:"retry" yaml:"retry"`
Authentication Authentication `json:"authentication" yaml:"authentication"`
Repeat int `json:"repeat" yaml:"repeat"`
type EndpointConfig struct {
IsEnabled bool `json:"enabled" yaml:"enabled"`
DebugMode bool `json:"debug_mode" yaml:"debug_mode"`
BaseURL string `json:"base_url" yaml:"base_url"`
Timeout time.Duration `json:"timeout" yaml:"timeout"`
Path string `json:"path" yaml:"path"`
Method string `json:"method" yaml:"method"`
Description string `json:"description" yaml:"description"`
QueryParams map[string]string `json:"query_params" yaml:"query_params"`
PathParams map[string]string `json:"path_params" yaml:"path_params"`
Headers map[string]string `json:"headers" yaml:"headers"`
Body map[string]interface{} `json:"body" yaml:"body"`
Retry RetryConfig `json:"retry" yaml:"retry"`
Authentication AuthenticationConfig `json:"authentication" yaml:"authentication"`
Telegram telegram.TelegramConfig `json:"telegram" yaml:"telegram"`
}

type ApiRequest struct {
BaseURL string `json:"base_url" yaml:"base_url"`
Authentication Authentication `json:"authentication" yaml:"authentication"`
Headers map[string]string `json:"headers" yaml:"headers"`
Endpoints map[string]Endpoint `json:"endpoints" yaml:"endpoints"`
Retry Retry `json:"retry" yaml:"retry"`
type ApiRequestConfig struct {
BaseURL string `json:"base_url" yaml:"base_url"`
Authentication AuthenticationConfig `json:"authentication" yaml:"authentication"`
Headers map[string]string `json:"headers" yaml:"headers"`
Endpoints map[string]EndpointConfig `json:"endpoints" yaml:"endpoints"`
Retry RetryConfig `json:"retry" yaml:"retry"`
Telegram telegram.TelegramConfig `json:"telegram" yaml:"telegram"`
}
92 changes: 70 additions & 22 deletions apix/api_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,59 @@ import (
"sync"
"time"

"github.com/sivaosorg/govm/blueprint"
"github.com/sivaosorg/govm/bot/telegram"
"github.com/sivaosorg/govm/coltx"
"github.com/sivaosorg/govm/restify"
"github.com/sivaosorg/govm/timex"
"github.com/sivaosorg/govm/utils"
)

type ApiService interface {
Do(client *restify.Client, endpoint Endpoint) (*restify.Response, error)
DoAsync(client *restify.Client, endpoints ...Endpoint)
DoAsyncWith(client *restify.Client, endpoints map[string]Endpoint)
Do(client *restify.Client, endpoint EndpointConfig) (*restify.Response, error)
DoAsync(client *restify.Client, endpoints ...EndpointConfig)
DoAsyncWith(client *restify.Client, endpoints map[string]EndpointConfig)
}

type apiServiceImpl struct {
conf ApiRequest
conf ApiRequestConfig
telegramSvc telegram.TelegramService
}

func NewApiService(conf ApiRequest) ApiService {
func NewApiService(conf ApiRequestConfig) ApiService {
s := &apiServiceImpl{
conf: conf,
}
return s
}

func (s *apiServiceImpl) Do(client *restify.Client, endpoint Endpoint) (*restify.Response, error) {
func (s *apiServiceImpl) Do(client *restify.Client, endpoint EndpointConfig) (*restify.Response, error) {
return s.execute(client, endpoint)
}

func (s *apiServiceImpl) DoAsync(client *restify.Client, endpoints ...Endpoint) {
func (s *apiServiceImpl) DoAsync(client *restify.Client, endpoints ...EndpointConfig) {
if len(endpoints) == 0 {
return
}
var wg sync.WaitGroup
for _, v := range endpoints {
wg.Add(1)
go func(endpoint Endpoint) {
go func(endpoint EndpointConfig) {
defer wg.Done()
response, err := s.Do(client, endpoint)
if err != nil {
_, err := s.Do(client, endpoint)
if err == nil {
// send notification
} else {
if response.IsSuccess() {

}
if response.IsError() {

}
}
}(v)
}
wg.Wait()
}

func (s *apiServiceImpl) DoAsyncWith(client *restify.Client, endpoints map[string]Endpoint) {
func (s *apiServiceImpl) DoAsyncWith(client *restify.Client, endpoints map[string]EndpointConfig) {
if len(endpoints) == 0 {
return
}
var e []Endpoint
var e []EndpointConfig
for _, v := range endpoints {
if !v.IsEnabled {
continue
Expand All @@ -71,7 +69,7 @@ func (s *apiServiceImpl) DoAsyncWith(client *restify.Client, endpoints map[strin
s.DoAsync(client, e...)
}

func (s *apiServiceImpl) execute(client *restify.Client, endpoint Endpoint) (*restify.Response, error) {
func (s *apiServiceImpl) execute(client *restify.Client, endpoint EndpointConfig) (*restify.Response, error) {
if !endpoint.IsEnabled {
return nil, fmt.Errorf("Endpoint %v unavailable", endpoint.Method)
}
Expand All @@ -89,6 +87,10 @@ func (s *apiServiceImpl) execute(client *restify.Client, endpoint Endpoint) (*re
headers := s.conf.CombineHeaders(endpoint)
retry := s.conf.CombineRetry(endpoint)
auth := s.conf.CombineAuthentication(endpoint)
telegramConf := s.conf.CombineTelegram(endpoint)
options := telegram.NewTelegramOptionConfig().SetType(telegram.ModeMarkdown)
telegramSvc := telegram.NewTelegramService(telegramConf, *options)
s.telegramSvc = telegramSvc

client.SetHostURL(host)
client.SetHeaders(headers)
Expand Down Expand Up @@ -121,13 +123,15 @@ func (s *apiServiceImpl) execute(client *restify.Client, endpoint Endpoint) (*re
if confirm {
// send notification
// use goroutine
go s.alert(endpoint, _response, err)
}
return confirm
} else {
confirm := (_response.StatusCode() >= http.StatusBadRequest && _response.StatusCode() <= http.StatusNetworkAuthenticationRequired) || err != nil
if confirm {
// send notification
// use goroutine
go s.alert(endpoint, _response, err)
}
return confirm
}
Expand All @@ -144,11 +148,55 @@ func (s *apiServiceImpl) execute(client *restify.Client, endpoint Endpoint) (*re
}

request := client.R()
request.SetResult(&response).SetError(&err)

if endpoint.AvailableBody() {
request.SetBody(endpoint.Body)
}
response, err = request.Execute(endpoint.Method, fullURL)
return response, err
}

func (s *apiServiceImpl) alert(endpoint EndpointConfig, response *restify.Response, err error) {
var message strings.Builder
if err != nil || response.IsError() {
icon, _ := blueprint.TypeIcons[blueprint.TypeError]
message.WriteString(fmt.Sprintf("%v ", icon))
}
if err == nil && response.IsSuccess() {
icon, _ := blueprint.TypeIcons[blueprint.TypeNotification]
message.WriteString(fmt.Sprintf("%v ", icon))
}
message.WriteString("API REST HTTP\n")
message.WriteString(fmt.Sprintf("Tz: `%s` (Received At: `%v`)\n\n",
time.Now().Format(timex.DateTimeFormYearMonthDayHourMinuteSecond), response.ReceivedAt()))
if utils.IsNotEmpty(endpoint.Description) {
message.WriteString(fmt.Sprintf("Decs: `%s`\n", endpoint.Description))
}
message.WriteString(fmt.Sprintf("(`%s`) URL: `%s`\n", response.Request.Method, response.Request.URL))
message.WriteString("\n---\n")
if endpoint.AvailableHeaders() {
message.WriteString(fmt.Sprintf("Header(s): \n\t`%s`\n", coltx.MapString2Table(endpoint.Headers)))
}
if endpoint.AvailableQueryParams() {
message.WriteString(fmt.Sprintf("Query Param(s): `%s`\n", coltx.MapString2Table(endpoint.QueryParams)))
}
if endpoint.AvailablePathParams() {
message.WriteString(fmt.Sprintf("Path Param(s): `%s`\n", coltx.MapString2Table(endpoint.PathParams)))
}
if endpoint.AvailableBody() {
message.WriteString(fmt.Sprintf("Request Body: `%s`\n", utils.ToJson(endpoint.Body)))
}
if endpoint.Retry.AvailableRetryOnStatus() {
message.WriteString(fmt.Sprintf("Retry On Status: `%s`\n", utils.ToJson(endpoint.Retry.RetryOnStatus)))
}
message.WriteString("\n---\n")
message.WriteString(fmt.Sprintf("Status Code: %v\n", response.StatusCode()))
if utils.IsNotEmpty(response.String()) {
message.WriteString(fmt.Sprintf("Response: `%v`\n", response.String()))
}
message.WriteString(fmt.Sprintf("No. attempt: %v\n", response.Request.Attempt))
message.WriteString(fmt.Sprintf("Duration: `%v`\n", response.Time()))
if err != nil {
message.WriteString(fmt.Sprintf("Error(R): `%v`\n", err.Error()))
}
s.telegramSvc.SendMessage(message.String())
}
12 changes: 12 additions & 0 deletions blueprint/blueprint_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,15 @@ var CardDefault = `
<a href="{{.ButtonUrl}}">{{.ButtonText}}</a>
{{end}}
`

// CardMarkdownDefault is the Markdown template for the card
var CardMarkdownDefault = `
**{{.Icon}} {{.Title}}**
*{{.Description}}*
{{if .ImageUrl}}
![Image]({{.ImageUrl}})
{{end}}
{{if .ButtonUrl}}
[{{.ButtonText}}]({{.ButtonUrl}})
{{end}}
`
7 changes: 4 additions & 3 deletions bot/telegram/telegram_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ const (
)

const (
ModeMarkdown TelegramFormatType = "Markdown"
ModeHTML TelegramFormatType = "HTML"
ModeNone TelegramFormatType = "None"
ModeMarkdown TelegramFormatType = "Markdown"
ModeMarkdownV2 TelegramFormatType = "MarkdownV2"
ModeHTML TelegramFormatType = "HTML"
ModeNone TelegramFormatType = "None"
)

var (
Expand Down
32 changes: 32 additions & 0 deletions coltx/coltx.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"sort"
"strings"
"time"

"github.com/sivaosorg/govm/utils"
)

func Contains[T comparable](array []T, item T) bool {
Expand Down Expand Up @@ -393,3 +395,33 @@ func MergeMapsString(maps ...map[string]string) map[string]string {
}
return result
}

// MapString2Table returns a map as a table without borders.
func MapString2Table(data map[string]string) string {
var builder strings.Builder
maxKeyLength := 0
for key := range data {
if len(key) > maxKeyLength {
maxKeyLength = len(key)
}
}
for key, value := range data {
fmt.Fprintf(&builder, "%-*s %s\n", maxKeyLength, key, value)
}
return builder.String()
}

// MapToTable returns a map as a table without borders.
func Map2Table(data map[string]interface{}) string {
var builder strings.Builder
maxKeyLength := 0
for key := range data {
if len(key) > maxKeyLength {
maxKeyLength = len(key)
}
}
for key, value := range data {
fmt.Fprintf(&builder, "%-*s %s\n", maxKeyLength, key, utils.ToJson(value))
}
return builder.String()
}
Loading

0 comments on commit d161de8

Please sign in to comment.