Skip to content

Commit 219760c

Browse files
committed
Updated webhook endpoint
1 parent 5daf8e0 commit 219760c

File tree

8 files changed

+129
-42
lines changed

8 files changed

+129
-42
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- +goose Up
2+
-- +goose StatementBegin
3+
4+
-- Rename columns in external_content_events table
5+
ALTER TABLE external_content_events RENAME COLUMN content_id TO task_id;
6+
ALTER TABLE external_content_events RENAME COLUMN reading_plan_id TO plan_id;
7+
8+
-- Add consumed_at timestamp column
9+
ALTER TABLE external_content_events ADD COLUMN consumed_at TIMESTAMPTZ;
10+
11+
-- +goose StatementEnd
12+
13+
-- +goose Down
14+
-- +goose StatementBegin
15+
16+
-- Remove consumed_at column
17+
ALTER TABLE external_content_events DROP COLUMN consumed_at;
18+
19+
-- Revert column renames
20+
ALTER TABLE external_content_events RENAME COLUMN task_id TO content_id;
21+
ALTER TABLE external_content_events RENAME COLUMN plan_id TO reading_plan_id;
22+
23+
-- +goose StatementEnd

backend/internal/database/queries/external_content_events.sql

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
-- name: CreateExternalContentEvent :one
2-
INSERT INTO external_content_events (id, person_id, content_id, reading_plan_id, source, received_at, content_progress)
3-
VALUES (@id, @personid::uuid, @contentid::text, @readingplanid::text, @source::text, @receivedat::timestamptz, @contentprogress)
4-
RETURNING id, person_id, content_id, reading_plan_id, source, received_at, content_progress;
2+
INSERT INTO external_content_events (id, person_id, task_id, plan_id, source, received_at, content_progress, consumed_at)
3+
VALUES (@id, @personid::uuid, @taskid::text, @planid::text, @source::text, @receivedat::timestamptz, @contentprogress, @consumedat::timestamptz)
4+
RETURNING id, person_id, task_id, plan_id, source, received_at, content_progress, consumed_at;
55

66
-- name: GetExternalContentEventByID :one
7-
SELECT id, person_id, content_id, reading_plan_id, source, received_at, content_progress
7+
SELECT id, person_id, task_id, plan_id, source, received_at, content_progress, consumed_at
88
FROM external_content_events
99
WHERE id = @id;
1010

1111
-- name: GetExternalContentEventsByPersonID :many
12-
SELECT id, person_id, content_id, reading_plan_id, source, received_at, content_progress
12+
SELECT id, person_id, task_id, plan_id, source, received_at, content_progress, consumed_at
1313
FROM external_content_events
1414
WHERE person_id = @personid::uuid
1515
ORDER BY received_at DESC
1616
LIMIT CASE WHEN @querylimit::int IS NULL THEN NULL ELSE @querylimit::int END
1717
OFFSET CASE WHEN @queryoffset::int IS NULL THEN 0 ELSE @queryoffset::int END;
1818

1919
-- name: GetExternalContentEventsBySource :many
20-
SELECT id, person_id, content_id, reading_plan_id, source, received_at, content_progress
20+
SELECT id, person_id, task_id, plan_id, source, received_at, content_progress, consumed_at
2121
FROM external_content_events
2222
WHERE source = @source::text
2323
ORDER BY received_at DESC

backend/internal/database/sqlc/external_content_events.sql.go

Lines changed: 24 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/internal/database/sqlc/models.go

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/internal/handlers/webhook.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ type WebhookHandler struct {
1919

2020
// ContentEventRequest represents the incoming webhook payload for content events
2121
type ContentEventRequest struct {
22-
PersonID string `json:"person_id" binding:"required"`
23-
ContentID string `json:"content_id" binding:"required"`
24-
ReadingPlanID *string `json:"reading_plan_id"`
25-
ContentProgress *float64 `json:"content_progress"`
22+
PersonID string `json:"person_id" binding:"required"`
23+
TaskID string `json:"task_id" binding:"required"`
24+
PlanID *string `json:"plan_id"`
25+
Timestamp time.Time `json:"timestamp" binding:"required"`
26+
ContentProgress *float64 `json:"content_progress"`
2627
}
2728

2829
// HandleContentEvent handles POST requests for external content completion events
@@ -81,7 +82,7 @@ func (h *WebhookHandler) HandleContentEvent(c *gin.Context) {
8182
slog.Info("webhook: creating content event",
8283
"event_id", eventID,
8384
"person_id", req.PersonID,
84-
"content_id", req.ContentID,
85+
"task_id", req.TaskID,
8586
"source", sourceStr,
8687
)
8788

@@ -93,21 +94,30 @@ func (h *WebhookHandler) HandleContentEvent(c *gin.Context) {
9394
return
9495
}
9596

96-
// Prepare reading_plan_id (nullable)
97-
readingPlanID := ""
98-
if req.ReadingPlanID != nil {
99-
readingPlanID = *req.ReadingPlanID
97+
// Prepare plan_id (nullable)
98+
planID := ""
99+
if req.PlanID != nil {
100+
planID = *req.PlanID
101+
}
102+
103+
// Convert consumed_at timestamp to pgtype.Timestamptz
104+
var consumedAtPg pgtype.Timestamptz
105+
if err := consumedAtPg.Scan(req.Timestamp); err != nil {
106+
slog.Error("webhook: failed to convert consumed_at timestamp", "error", err)
107+
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
108+
return
100109
}
101110

102111
// Insert event into database
103112
event, err := h.DB.Queries.CreateExternalContentEvent(ctx, sqlc.CreateExternalContentEventParams{
104113
ID: eventID,
105114
Personid: personPgUUID,
106-
Contentid: req.ContentID,
107-
Readingplanid: readingPlanID,
115+
Taskid: req.TaskID,
116+
Planid: planID,
108117
Source: sourceStr,
109118
Receivedat: receivedAtPg,
110119
Contentprogress: contentProgress,
120+
Consumedat: consumedAtPg,
111121
})
112122
if err != nil {
113123
slog.Error("webhook: failed to create content event",

docs/external-content-events-api.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ Submit a new content completion event.
4040
```json
4141
{
4242
"person_id": "550e8400-e29b-41d4-a716-446655440000",
43-
"content_id": "article-123",
44-
"reading_plan_id": "plan-456",
43+
"task_id": "article-123",
44+
"plan_id": "plan-456",
45+
"timestamp": "2025-01-15T14:30:00Z",
4546
"content_progress": 0.75
4647
}
4748
```
@@ -51,8 +52,9 @@ Submit a new content completion event.
5152
| Field | Type | Required | Description |
5253
|-------|------|----------|-------------|
5354
| person_id | string (UUID) | Yes | The Brunstad TV person_id (UUID format) |
54-
| content_id | string | Yes | External content identifier |
55-
| reading_plan_id | string | No | External reading plan identifier (optional) |
55+
| task_id | string | Yes | External task identifier |
56+
| plan_id | string | No | External plan identifier (optional) |
57+
| timestamp | string (ISO8601) | Yes | When the content was consumed (RFC3339/ISO8601 format) |
5658
| content_progress | float | No | Content completion progress (0.01 to 1.1, where 1.0 = 100%). Values outside this range are ignored and stored as NULL. |
5759

5860
#### Success Response
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
type: folder
2+
model: folder
3+
id: fl_ContentEvents
4+
createdAt: 2025-12-03T10:15:00
5+
updatedAt: 2025-12-03T10:15:00
6+
workspaceId: wk_XKsmbMdFg7
7+
folderId: null
8+
authentication: {}
9+
authenticationType: null
10+
description: ''
11+
headers: []
12+
name: Webhooks
13+
sortPriority: 3000.0
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
type: http_request
2+
model: http_request
3+
id: rq_ContentEvent
4+
createdAt: 2025-12-03T10:15:00
5+
updatedAt: 2025-12-03T09:18:19.268239
6+
workspaceId: wk_XKsmbMdFg7
7+
folderId: fl_ContentEvents
8+
authentication:
9+
key: Bearer
10+
token: example-api-key-12345
11+
authenticationType: bearer
12+
body:
13+
text: |
14+
{
15+
"person_id": "550e8400-e29b-41d4-a716-446655440000",
16+
"task_id": "article-123",
17+
"plan_id": "plan-456",
18+
"timestamp": "2025-01-15T14:30:00Z",
19+
"content_progress": 0.75
20+
}
21+
bodyType: application/json
22+
description: Submit a content completion event from an external system
23+
headers:
24+
- enabled: true
25+
name: Content-Type
26+
value: application/json
27+
id: hdr_ContentType
28+
method: POST
29+
name: Submit Content Event
30+
sortPriority: 0.0
31+
url: ${[ HOST ]}/api/v1/content-events
32+
urlParameters: []

0 commit comments

Comments
 (0)