Skip to content

Commit 614ae5e

Browse files
fiftincursoragent
andauthored
Add project test notification API endpoint (#3208)
* Add test notification endpoint for project alerts Co-authored-by: denguk <[email protected]> * fix(test-notif): add user list for email notification testing * feat(test-notif): add more informative error message * feat(ui): add progress to test notifications * docs: fix test endpoint --------- Co-authored-by: Cursor Agent <[email protected]>
1 parent 4066a1e commit 614ae5e

File tree

7 files changed

+153
-3
lines changed

7 files changed

+153
-3
lines changed

.dredd/hooks/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var skipTests = []string{
1919
"/api/ws > Websocket handler > 200 > application/json",
2020
"authentication > /api/auth/login > Performs Login > 204 > application/json",
2121
"authentication > /api/auth/logout > Destroys current session > 204 > application/json",
22+
"/project/{project_id}/notifications/test",
2223
//"/api/upgrade > Upgrade the server > 200 > application/json",
2324
// TODO - Skipping this while we work out how to get a 204 response from the api for testing
2425
//"/api/upgrade > Check if new updates available and fetch /info > 204 > application/json",

api-docs.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2499,4 +2499,18 @@ paths:
24992499
schema:
25002500
type: array
25012501
items:
2502-
$ref: "#/definitions/App"
2502+
$ref: "#/definitions/App"
2503+
2504+
/project/{project_id}/notifications/test:
2505+
post:
2506+
tags:
2507+
- project
2508+
summary: Send test notification
2509+
description: Sends a test notification to all enabled messengers for the project
2510+
parameters:
2511+
- $ref: "#/parameters/project_id"
2512+
responses:
2513+
409:
2514+
description: Alerts not enabled for the project
2515+
# 204:
2516+
# description: Test notification dispatched (or alerts disabled)

api/projects/project.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package projects
22

33
import (
4+
"net/http"
5+
46
"github.com/gorilla/mux"
57
"github.com/semaphoreui/semaphore/api/helpers"
68
"github.com/semaphoreui/semaphore/db"
79
"github.com/semaphoreui/semaphore/services/server"
10+
"github.com/semaphoreui/semaphore/services/tasks"
811
"github.com/semaphoreui/semaphore/util"
912
log "github.com/sirupsen/logrus"
10-
"net/http"
1113
)
1214

1315
// ProjectMiddleware ensures a project exists and loads it to the context
@@ -66,6 +68,25 @@ type ProjectController struct {
6668
ProjectService server.ProjectService
6769
}
6870

71+
// SendTestNotification triggers sending a test notification to enabled messengers for this project.
72+
func (c *ProjectController) SendTestNotification(w http.ResponseWriter, r *http.Request) {
73+
project := helpers.GetFromContext(r, "project").(db.Project)
74+
75+
// Respect project.Alert flag: if disabled, still return 204 without sending
76+
if !project.Alert {
77+
w.WriteHeader(http.StatusConflict)
78+
return
79+
}
80+
81+
err := tasks.SendProjectTestAlerts(project, helpers.Store(r))
82+
if err != nil {
83+
helpers.WriteError(w, err)
84+
return
85+
}
86+
87+
w.WriteHeader(http.StatusNoContent)
88+
}
89+
6990
func (c *ProjectController) UpdateProject(w http.ResponseWriter, r *http.Request) {
7091
project := helpers.GetFromContext(r, "project").(db.Project)
7192
var body db.Project

api/router.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ func Route(
319319
projectUserAPI.Path("/integrations").HandlerFunc(projects.GetIntegrations).Methods("GET", "HEAD")
320320
projectUserAPI.Path("/integrations").HandlerFunc(projects.AddIntegration).Methods("POST")
321321
projectUserAPI.Path("/backup").HandlerFunc(projects.GetBackup).Methods("GET", "HEAD")
322+
projectUserAPI.Path("/notifications/test").HandlerFunc(projectController.SendTestNotification).Methods("POST")
322323

323324
projectUserAPI.Path("/runners").HandlerFunc(projectRunnerController.GetRunners).Methods("GET", "HEAD")
324325
projectUserAPI.Path("/runners").HandlerFunc(projectRunnerController.AddRunner).Methods("POST")

services/tasks/alert_test_sender.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package tasks
2+
3+
import (
4+
"github.com/semaphoreui/semaphore/db"
5+
"github.com/semaphoreui/semaphore/pkg/task_logger"
6+
)
7+
8+
// SendProjectTestAlerts sends test alerts to all enabled notifiers for the given project.
9+
func SendProjectTestAlerts(project db.Project, store db.Store) (err error) {
10+
11+
projectUsers, err := store.GetProjectUsers(project.ID, db.RetrieveQueryParams{})
12+
if err != nil {
13+
return
14+
}
15+
16+
var userIDs []int
17+
for _, u := range projectUsers {
18+
userIDs = append(userIDs, u.ID)
19+
}
20+
21+
tr := &TaskRunner{
22+
Task: db.Task{
23+
ProjectID: project.ID,
24+
TemplateID: 0,
25+
Status: task_logger.TaskSuccessStatus,
26+
Message: "This is a test notification",
27+
},
28+
Template: db.Template{
29+
ID: 0,
30+
ProjectID: project.ID,
31+
Name: "Test Notification",
32+
Type: db.TemplateTask,
33+
},
34+
users: userIDs,
35+
alert: project.Alert,
36+
alertChat: project.AlertChat,
37+
pool: &TaskPool{
38+
logger: make(chan logRecord, 100),
39+
store: store,
40+
},
41+
}
42+
43+
tr.sendTelegramAlert()
44+
tr.sendSlackAlert()
45+
tr.sendRocketChatAlert()
46+
tr.sendMicrosoftTeamsAlert()
47+
tr.sendDingTalkAlert()
48+
tr.sendGotifyAlert()
49+
tr.sendMailAlert()
50+
51+
return
52+
}

web/public/swagger/api-docs.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,20 @@ paths:
15731573
items:
15741574
$ref: '#/definitions/Event'
15751575

1576+
/project/{project_id}/notifications/test:
1577+
post:
1578+
summary: Send test notification
1579+
description: Sends a test notification to all enabled messengers for the project
1580+
parameters:
1581+
- $ref: "#/parameters/project_id"
1582+
responses:
1583+
204:
1584+
description: Test notification dispatched (or alerts disabled)
1585+
401:
1586+
description: not authenticated
1587+
403:
1588+
description: forbidden
1589+
15761590
# User management
15771591
/project/{project_id}/users:
15781592
parameters:

web/src/views/project/Settings.vue

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,26 @@
3737
/>
3838
</div>
3939

40-
<div class="text-right">
40+
<div class="d-flex justify-space-between mt-4">
41+
<v-btn
42+
color="blue-grey"
43+
@click="sendTestNotification()"
44+
width="170"
45+
:disabled="testNotificationProgress"
46+
data-testid="settings-testAlerts"
47+
>Test Alerts</v-btn>
4148
<v-btn color="primary" @click="saveProject()">{{ $t('save') }}</v-btn>
4249
</div>
50+
51+
<v-progress-linear
52+
v-if="testNotificationProgress"
53+
color="blue-grey darken-1"
54+
indeterminate
55+
rounded
56+
width="170"
57+
height="36"
58+
style="margin-top: -36px; width: 170px;"
59+
></v-progress-linear>
4360
</div>
4461

4562
<h2 class="mt-8 mb-1">{{ $t('danger_zone_settings') }}</h2>
@@ -167,10 +184,40 @@ export default {
167184
deleteProjectDialog: null,
168185
backupProgress: false,
169186
clearCacheProgress: false,
187+
testNotificationProgress: false,
170188
};
171189
},
172190
173191
methods: {
192+
async sendTestNotification() {
193+
this.testNotificationProgress = true;
194+
try {
195+
await axios({
196+
method: 'post',
197+
url: `/api/project/${this.projectId}/notifications/test`,
198+
responseType: 'json',
199+
});
200+
EventBus.$emit('i-snackbar', {
201+
color: 'success',
202+
text: 'Test notification sent.',
203+
});
204+
} catch (err) {
205+
let msg;
206+
if (err.response.status === 409) {
207+
msg = 'Please allow alerts for the project and save it.';
208+
} else {
209+
msg = getErrorMessage(err);
210+
}
211+
212+
EventBus.$emit('i-snackbar', {
213+
color: 'error',
214+
text: msg,
215+
});
216+
} finally {
217+
this.testNotificationProgress = false;
218+
}
219+
},
220+
174221
showDrawer() {
175222
EventBus.$emit('i-show-drawer');
176223
},

0 commit comments

Comments
 (0)