Skip to content

Commit 7327766

Browse files
committed
feat: add a health check handler (#146)
1 parent 217cef9 commit 7327766

File tree

7 files changed

+294
-23
lines changed

7 files changed

+294
-23
lines changed
+251
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
swagger: "2.0"
2+
info:
3+
description: "Joe Bot server API."
4+
version: "0.7.0"
5+
title: "Joe Bot"
6+
contact:
7+
8+
license:
9+
name: "Joe Bot License"
10+
url: "https://gitlab.com/postgres-ai/joe/-/blob/master/LICENSE"
11+
basePath: "/"
12+
tags:
13+
- name: "Joe Bot"
14+
description: "API Reference"
15+
externalDocs:
16+
description: "Joe Bot Docs"
17+
url: "https://postgres.ai/docs/joe-bot/what_is_joe"
18+
schemes:
19+
- "https"
20+
- "http"
21+
22+
paths:
23+
/:
24+
get:
25+
tags:
26+
- "Server API"
27+
summary: "Get the status of the instance we are working with"
28+
description: ""
29+
operationId: "healthCheck"
30+
produces:
31+
- "application/json"
32+
responses:
33+
200:
34+
description: "Successful operation"
35+
schema:
36+
$ref: "#/definitions/Instance"
37+
500:
38+
description: "Internal server error"
39+
40+
/webui/verify:
41+
post:
42+
tags:
43+
- "Web UI"
44+
summary: "Get the list of snapshots"
45+
description: ""
46+
operationId: "getSnapshots"
47+
consumes:
48+
- "application/json"
49+
produces:
50+
- "application/json"
51+
parameters:
52+
- in: body
53+
name: body
54+
description: "Challenge request"
55+
required: true
56+
schema:
57+
$ref: '#/definitions/Challenge'
58+
responses:
59+
200:
60+
description: "Successful operation"
61+
schema:
62+
$ref: "#/definitions/Challenge"
63+
400:
64+
description: "Bad request"
65+
403:
66+
description: "Forbidden"
67+
500:
68+
description: "Internal server error"
69+
security:
70+
- WebUISignature: []
71+
72+
/webui/channels:
73+
get:
74+
tags:
75+
- "Web UI"
76+
summary: "Get the list of available channels"
77+
description: ""
78+
operationId: "getChannels"
79+
consumes:
80+
- "application/json"
81+
produces:
82+
- "application/json"
83+
responses:
84+
200:
85+
description: "Successful operation"
86+
schema:
87+
type: "array"
88+
items:
89+
$ref: "#/definitions/Channel"
90+
400:
91+
description: "Bad request"
92+
403:
93+
description: "Forbidden"
94+
404:
95+
description: "Not Found"
96+
500:
97+
description: "Internal server error"
98+
security:
99+
- WebUISignature: []
100+
101+
/webui/command:
102+
post:
103+
tags:
104+
- "Web UI"
105+
summary: "Post a command to Joe Bot"
106+
description: ""
107+
operationId: "postCommand"
108+
consumes:
109+
- "application/json"
110+
produces:
111+
- "application/json"
112+
parameters:
113+
- in: body
114+
name: body
115+
description: "Bot command"
116+
required: true
117+
schema:
118+
$ref: '#/definitions/Message'
119+
responses:
120+
200:
121+
description: "Successful operation"
122+
400:
123+
description: "Bad request"
124+
403:
125+
description: "Forbidden"
126+
500:
127+
description: "Internal server error"
128+
security:
129+
- WebUISignature: []
130+
131+
132+
/slack/:
133+
post:
134+
tags:
135+
- "Slack"
136+
summary: "The endpoint to handle events from a Slack App"
137+
description: ""
138+
operationId: "handleSlackEvent"
139+
consumes:
140+
- "application/json"
141+
produces:
142+
- "application/json"
143+
parameters:
144+
- in: body
145+
required: true
146+
name: body
147+
schema:
148+
$ref: "#/definitions/SlackEvent"
149+
description: "Slack API event"
150+
responses:
151+
200:
152+
description: "Successful operation"
153+
404:
154+
description: "Not found"
155+
500:
156+
description: "Internal server error"
157+
externalDocs:
158+
description: Learn more about Slack API Events.
159+
url: https://api.slack.com/events
160+
security:
161+
- SlackSignature: []
162+
SlackRequestTimestamp: []
163+
SlackRetry: []
164+
165+
securityDefinitions:
166+
WebUISignature:
167+
type: apiKey
168+
in: header
169+
name: Verification-Signature
170+
SlackSignature:
171+
type: apiKey
172+
in: header
173+
name: X-Slack-Signature
174+
SlackRequestTimestamp:
175+
type: apiKey
176+
in: header
177+
name: X-Slack-Request-Timestamp
178+
SlackRetry:
179+
type: apiKey
180+
in: header
181+
name: X-Slack-Retry-Num
182+
183+
definitions:
184+
Instance:
185+
type: "object"
186+
properties:
187+
version:
188+
type: "string"
189+
edition:
190+
type: "string"
191+
communication_types:
192+
type: "array"
193+
items:
194+
type: "string"
195+
196+
Challenge:
197+
type: "object"
198+
properties:
199+
challenge:
200+
type: "string"
201+
description: "Challenge"
202+
203+
Channel:
204+
type: "object"
205+
properties:
206+
channel_id:
207+
type: "string"
208+
209+
Message:
210+
type: "object"
211+
properties:
212+
session_id:
213+
type: "string"
214+
command_id:
215+
type: "string"
216+
text:
217+
type: "string"
218+
channel_id:
219+
type: "string"
220+
user_id:
221+
type: "string"
222+
timestamp:
223+
type: "string"
224+
225+
SlackEvent:
226+
type: "object"
227+
properties:
228+
token:
229+
type: string
230+
team_id:
231+
type: string
232+
api_app_id:
233+
type: string
234+
event:
235+
type: object
236+
properties:
237+
type:
238+
type: string
239+
event_ts:
240+
type: string
241+
type:
242+
type: string
243+
event_id:
244+
type: string
245+
event_time:
246+
type: integer
247+
authed_user:
248+
type: array
249+
items:
250+
type: string
251+
description: "https://api.slack.com/types/event"

go.mod

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ go 1.13
55
require (
66
github.com/dustin/go-humanize v1.0.0
77
github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4
8-
github.com/ilyakaznacheev/cleanenv v1.2.1
8+
github.com/ilyakaznacheev/cleanenv v1.2.2
99
github.com/jackc/pgconn v1.5.0
1010
github.com/jackc/pgx/v4 v4.6.0
11-
github.com/jessevdk/go-flags v1.4.1-0.20181221193153-c0795c8afcf4
1211
github.com/lib/pq v1.3.0
1312
github.com/mattn/go-runewidth v0.0.8 // indirect
1413
github.com/nlopes/slack v0.6.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTM
3131
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
3232
github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4 h1:60gBOooTSmNtrqNaRvrDbi8VAne0REaek2agjnITKSw=
3333
github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
34-
github.com/ilyakaznacheev/cleanenv v1.2.1 h1:+L4jB4YPPbqia6tkJeZRDw74MkUsnb9uEjn5ceu3ygk=
35-
github.com/ilyakaznacheev/cleanenv v1.2.1/go.mod h1:53X9Yx1hcUGX8A+GKNMlYvGhuQRx3B86n4wgt3tbYVA=
34+
github.com/ilyakaznacheev/cleanenv v1.2.2 h1:VIuY3GshV+7DvTYoKbUB1hQ6IkeHwmgl5kLFhfsxlAg=
35+
github.com/ilyakaznacheev/cleanenv v1.2.2/go.mod h1:53X9Yx1hcUGX8A+GKNMlYvGhuQRx3B86n4wgt3tbYVA=
3636
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
3737
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
3838
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=

pkg/bot/bot.go

+37
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package bot
66

77
import (
88
"context"
9+
"encoding/json"
910
"fmt"
11+
"html"
1012
"net/http"
1113
"sync"
1214
"time"
@@ -39,6 +41,13 @@ type App struct {
3941
dblabInstances map[string]*dblab.Instance
4042
}
4143

44+
// HealthResponse represents a response for heath-check requests.
45+
type HealthResponse struct {
46+
Version string `json:"version"`
47+
Edition string `json:"edition"`
48+
CommunicationTypes []string `json:"communication_types"`
49+
}
50+
4251
// Creates a new application.
4352
func NewApp(cfg *config.Config, enterprise *features.Pack) *App {
4453
bot := App{
@@ -74,6 +83,8 @@ func (a *App) RunServer(ctx context.Context) error {
7483
})
7584
}
7685

86+
http.HandleFunc("/", a.healthCheck)
87+
7788
port := a.Config.App.Port
7889
log.Msg(fmt.Sprintf("Server start listening on localhost:%d", port))
7990

@@ -177,3 +188,29 @@ func (a *App) setupDBLabInstances(assistant connection.Assistant, workspace conf
177188

178189
return nil
179190
}
191+
192+
// healthCheck handles health-check requests.
193+
func (a *App) healthCheck(w http.ResponseWriter, r *http.Request) {
194+
log.Msg("Health check received:", html.EscapeString(r.URL.Path))
195+
196+
communicationTypes := make([]string, 0, len(a.Config.ChannelMapping.CommunicationTypes))
197+
198+
for typeName := range a.Config.ChannelMapping.CommunicationTypes {
199+
communicationTypes = append(communicationTypes, typeName)
200+
}
201+
202+
healthResponse := HealthResponse{
203+
Version: a.Config.App.Version,
204+
Edition: a.featurePack.Entertainer().GetEdition(),
205+
CommunicationTypes: communicationTypes,
206+
}
207+
208+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
209+
210+
if err := json.NewEncoder(w).Encode(healthResponse); err != nil {
211+
http.Error(w, err.Error(), http.StatusInternalServerError)
212+
log.Err(err)
213+
214+
return
215+
}
216+
}

pkg/connection/slack/assistant.go

-8
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ func (a *Assistant) Init() error {
9595
http.Handle(fmt.Sprintf("%s/%s", a.prefix, path), handleFunc)
9696
}
9797

98-
http.HandleFunc(fmt.Sprintf("%s/health", a.prefix), a.healthCheck)
99-
10098
return nil
10199
}
102100

@@ -185,12 +183,6 @@ func (a *Assistant) handlers() map[string]http.HandlerFunc {
185183
}
186184
}
187185

188-
func (a *Assistant) healthCheck(w http.ResponseWriter, r *http.Request) {
189-
log.Msg("Request received:", html.EscapeString(r.URL.Path))
190-
191-
w.WriteHeader(http.StatusOK)
192-
}
193-
194186
func (a *Assistant) handleEvent(w http.ResponseWriter, r *http.Request) {
195187
log.Msg("Request received:", html.EscapeString(r.URL.Path))
196188

pkg/connection/webui/assistant.go

-11
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"context"
1111
"encoding/json"
1212
"fmt"
13-
"html"
1413
"net/http"
1514
"strings"
1615
"sync"
@@ -83,8 +82,6 @@ func (a *Assistant) Init() error {
8382
http.Handle(fmt.Sprintf("%s/%s", a.prefix, path), verifier.Handler(handleFunc))
8483
}
8584

86-
http.HandleFunc(fmt.Sprintf("%s/health", a.prefix), a.healthCheck)
87-
8885
return nil
8986
}
9087

@@ -168,12 +165,6 @@ func (a *Assistant) handlers() map[string]http.HandlerFunc {
168165
}
169166
}
170167

171-
func (a *Assistant) healthCheck(w http.ResponseWriter, r *http.Request) {
172-
log.Msg("Request received:", html.EscapeString(r.URL.Path))
173-
174-
w.WriteHeader(http.StatusOK)
175-
}
176-
177168
type challengeResponse struct {
178169
Challenge string `json:"challenge"`
179170
}
@@ -251,8 +242,6 @@ func (m *Message) ToIncomingMessage() models.IncomingMessage {
251242
}
252243

253244
func (a *Assistant) commandHandler(w http.ResponseWriter, r *http.Request) {
254-
log.Msg("Request received:", html.EscapeString(r.URL.Path))
255-
256245
buf := new(bytes.Buffer)
257246
if _, err := buf.ReadFrom(r.Body); err != nil {
258247
log.Err("Failed to read the request body:", err)

0 commit comments

Comments
 (0)