71: Implement Discord webhook notification delivery
This commit is contained in:
@@ -61,12 +61,46 @@ An SMTP destination SHALL contain both an `ΩEmailAddressΩ` and a fixed `ΩEmai
|
||||
- **WHEN** a valid message is pushed through a valid SMTP binding
|
||||
- **THEN** the service reports that SMTP delivery is not implemented
|
||||
|
||||
### Requirement: Discord delivery remains unimplemented
|
||||
The Discord stub SHALL implement `BoundNotificationService`, accept `ΩNotificationMessageΩ` directly, and SHALL NOT implement webhook delivery as part of this capability.
|
||||
### Requirement: Discord webhook delivery
|
||||
The Discord notification service SHALL remain a `BoundNotificationService` that is fully configured at construction from a non-null structurally valid canonical Discord incoming-webhook `ΩRestEndpointΩ` and a non-null positive `Duration`. It SHALL make no construction-time network request, follow no HTTP redirects, and SHALL NOT expose a raw `URL` or raw `String` constructor.
|
||||
|
||||
#### Scenario: Valid Discord delivery remains unimplemented
|
||||
- **WHEN** a valid message is pushed through the configured Discord service
|
||||
- **THEN** the service reports that Discord delivery is not implemented without performing webhook delivery
|
||||
The endpoint SHALL be an absolute HTTPS URI with a case-insensitive `discord.com` host, no user-info, fragment, query, or non-HTTPS port, and either an unversioned `/api/webhooks/{id}/{token}` path or a versioned `/api/v{version}/webhooks/{id}/{token}` path using a non-discontinued Discord API version from 6 through 10, with non-blank ID and token segments. Constructor validation diagnostics SHALL NOT disclose the endpoint or token.
|
||||
|
||||
Every public `push(...)` entry point SHALL reject a null, empty, blank, or over-2,000-character `ΩNotificationMessageΩ` with `IllegalArgumentException` before JSON serialization, request construction, or transport work. For every valid message, the service SHALL synchronously send exactly one POST to the configured endpoint with `wait=true`, the configured request timeout, JSON UTF-8 content and accept headers, a `DiscordBot (url, version)` User-Agent identifying Nenjim, and a safely serialized JSON object whose `content` preserves the message exactly. The webhook token SHALL be the only authentication mechanism.
|
||||
|
||||
Every 2xx HTTP response SHALL return normally. Every non-2xx response, including redirects and 429 responses, and every communication or request-preparation failure SHALL cause `IOException` without retrying, waiting, or sending another request. Response failures SHALL include the HTTP status and useful response body when available, without disclosing the endpoint or token. When synchronous HTTP delivery is interrupted, the service SHALL restore the thread interrupt flag and throw an `IOException` retaining the `InterruptedException` as its cause.
|
||||
|
||||
#### Scenario: Fully configured construction
|
||||
- **WHEN** a caller constructs the service with a canonical Discord webhook endpoint and positive timeout
|
||||
- **THEN** it receives a bound notification service without a network request
|
||||
|
||||
#### Scenario: Invalid endpoint or timeout is rejected
|
||||
- **WHEN** a caller supplies a null or structurally invalid endpoint, or a null, zero, or negative timeout
|
||||
- **THEN** construction throws `IllegalArgumentException` without disclosing the endpoint token
|
||||
|
||||
#### Scenario: Invalid notification message is rejected before delivery
|
||||
- **WHEN** a caller pushes a null, empty, blank, or over-2,000-character message
|
||||
- **THEN** the service throws `IllegalArgumentException` before serialization or HTTP work
|
||||
|
||||
#### Scenario: Valid message is synchronously confirmed
|
||||
- **WHEN** a caller pushes a valid notification message and Discord returns a 2xx response
|
||||
- **THEN** the service sends one synchronous JSON POST with `wait=true` and returns normally
|
||||
|
||||
#### Scenario: Discord rejects delivery
|
||||
- **WHEN** Discord returns a non-2xx response, including 429 or a redirect
|
||||
- **THEN** the service throws `IOException` containing the status and does not retry or follow the redirect
|
||||
|
||||
#### Scenario: Delivery communication fails
|
||||
- **WHEN** preparing or sending the webhook request fails
|
||||
- **THEN** the service throws `IOException` without exposing the webhook endpoint or token
|
||||
|
||||
#### Scenario: Delivery is interrupted
|
||||
- **WHEN** synchronous webhook delivery is interrupted
|
||||
- **THEN** the service restores the thread interrupt status and throws `IOException` with the interruption as its cause
|
||||
|
||||
#### Scenario: Webhook secret remains protected
|
||||
- **WHEN** validation or delivery reports an error
|
||||
- **THEN** no log or exception message includes the complete webhook endpoint or token
|
||||
|
||||
### Requirement: Obsolete notification API is removed
|
||||
The notification framework SHALL reside exclusively under `com.r35157.service.notification`. It SHALL NOT retain compatibility aliases under `com.r35157.libs.notification`, notifier-named public interfaces or implementations, the `NotificationMessage` marker, or transport-specific message wrapper types.
|
||||
|
||||
Reference in New Issue
Block a user