Files
galaxy-game/notification/api/intents-asyncapi.yaml
T
2026-04-22 08:49:45 +02:00

557 lines
16 KiB
YAML

asyncapi: 3.1.0
info:
title: Notification Service Intent Contract
version: 1.0.0
description: |
Stable Redis Streams contract for normalized notification intents
published by upstream services toward Notification Service.
channels:
intents:
address: notification:intents
messages:
notificationIntent:
$ref: '#/components/messages/NotificationIntent'
operations:
publishNotificationIntent:
action: send
summary: Publish one normalized notification intent.
channel:
$ref: '#/channels/intents'
messages:
- $ref: '#/channels/intents/messages/notificationIntent'
components:
messages:
NotificationIntent:
name: NotificationIntent
title: Notification intent
summary: One normalized notification request published into Notification Service.
payload:
$ref: '#/components/schemas/NotificationIntentEnvelope'
examples:
- name: gameTurnReady
summary: User-targeted game-turn notification.
payload:
notification_type: game.turn.ready
producer: game_master
audience_kind: user
recipient_user_ids_json: '["user-1","user-2"]'
idempotency_key: game-master:game-123:turn-54
occurred_at_ms: "1775121700000"
request_id: request-123
trace_id: trace-123
payload_json: '{"game_id":"game-123","game_name":"Nebula Clash","turn_number":54}'
- name: geoReviewRecommended
summary: Administrator email notification.
payload:
notification_type: geo.review_recommended
producer: geoprofile
audience_kind: admin_email
idempotency_key: geoprofile:user-123:review-true:1775121700001
occurred_at_ms: "1775121700001"
payload_json: '{"user_id":"user-123","user_email":"pilot@example.com","observed_country":"DE","usual_connection_country":"PL","review_reason":"country_mismatch"}'
- name: lobbyApplicationSubmittedPublic
summary: Public-game application notification sent to configured admins.
payload:
notification_type: lobby.application.submitted
producer: game_lobby
audience_kind: admin_email
idempotency_key: game-lobby:game-456:application-submitted:user-42
occurred_at_ms: "1775121700002"
payload_json: '{"game_id":"game-456","game_name":"Orion Front","applicant_user_id":"user-42","applicant_name":"Nova Pilot"}'
schemas:
NotificationIntentEnvelope:
type: object
additionalProperties: false
description: |
Stable producer-to-notification envelope for one normalized
notification intent.
Duplicate handling is scoped by `(producer, idempotency_key)`.
A replay with the same normalized content is a successful duplicate.
A replay with different normalized content is a conflict.
`request_id` and `trace_id` are observability-only metadata and do not
participate in idempotency fingerprinting.
required:
- notification_type
- producer
- audience_kind
- idempotency_key
- occurred_at_ms
- payload_json
properties:
notification_type:
type: string
enum:
- geo.review_recommended
- game.turn.ready
- game.finished
- game.generation_failed
- lobby.runtime_paused_after_start
- lobby.application.submitted
- lobby.membership.approved
- lobby.membership.rejected
- lobby.invite.created
- lobby.invite.redeemed
- lobby.invite.expired
description: |
Exact v1 notification type catalog. `lobby.invite.revoked`
deliberately remains outside the supported catalog because it
produces no notification.
producer:
type: string
enum:
- geoprofile
- game_master
- game_lobby
description: |
Stable producer identifier. The exact producer value is frozen per
`notification_type` by the v1 catalog.
audience_kind:
type: string
enum:
- user
- admin_email
description: |
Delivery audience selector.
`user` targets concrete `user_id` values from the producer.
`admin_email` targets configured administrator email lists.
recipient_user_ids_json:
type: string
description: |
JSON-encoded array of unique stable `user_id` values.
Required for `audience_kind=user`. Forbidden for
`audience_kind=admin_email`.
`Notification Service` treats the recipient set as unordered for
idempotency purposes: duplicate `user_id` values are invalid and
element order does not change normalized content.
contentMediaType: application/json
contentSchema:
type: array
minItems: 1
uniqueItems: true
items:
type: string
minLength: 1
idempotency_key:
type: string
minLength: 1
description: |
Producer-owned idempotency key scoped together with `producer`.
occurred_at_ms:
type: string
pattern: '^[0-9]+$'
description: Milliseconds since Unix epoch as a base-10 string.
request_id:
type: string
description: Optional observability request identifier.
trace_id:
type: string
description: Optional observability trace identifier.
payload_json:
type: string
description: |
JSON-encoded type-specific payload. Payload normalization ignores
insignificant whitespace and object key order, while array order
remains significant. Required payload fields are frozen per
`notification_type`.
contentMediaType: application/json
contentSchema:
type: object
additionalProperties: true
allOf:
- if:
properties:
audience_kind:
const: user
required:
- audience_kind
then:
required:
- recipient_user_ids_json
- if:
properties:
audience_kind:
const: admin_email
required:
- audience_kind
then:
not:
required:
- recipient_user_ids_json
- if:
properties:
notification_type:
const: geo.review_recommended
required:
- notification_type
then:
properties:
producer:
const: geoprofile
audience_kind:
const: admin_email
payload_json:
contentSchema:
$ref: '#/components/schemas/GeoReviewRecommendedPayload'
- if:
properties:
notification_type:
const: game.turn.ready
required:
- notification_type
then:
properties:
producer:
const: game_master
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/GameTurnReadyPayload'
- if:
properties:
notification_type:
const: game.finished
required:
- notification_type
then:
properties:
producer:
const: game_master
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/GameFinishedPayload'
- if:
properties:
notification_type:
const: game.generation_failed
required:
- notification_type
then:
properties:
producer:
const: game_master
audience_kind:
const: admin_email
payload_json:
contentSchema:
$ref: '#/components/schemas/GameGenerationFailedPayload'
- if:
properties:
notification_type:
const: lobby.runtime_paused_after_start
required:
- notification_type
then:
properties:
producer:
const: game_lobby
audience_kind:
const: admin_email
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyRuntimePausedAfterStartPayload'
- if:
properties:
notification_type:
const: lobby.application.submitted
required:
- notification_type
then:
properties:
producer:
const: game_lobby
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyApplicationSubmittedPayload'
oneOf:
- properties:
audience_kind:
const: user
required:
- audience_kind
- properties:
audience_kind:
const: admin_email
required:
- audience_kind
- if:
properties:
notification_type:
const: lobby.membership.approved
required:
- notification_type
then:
properties:
producer:
const: game_lobby
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyMembershipApprovedPayload'
- if:
properties:
notification_type:
const: lobby.membership.rejected
required:
- notification_type
then:
properties:
producer:
const: game_lobby
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyMembershipRejectedPayload'
- if:
properties:
notification_type:
const: lobby.invite.created
required:
- notification_type
then:
properties:
producer:
const: game_lobby
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyInviteCreatedPayload'
- if:
properties:
notification_type:
const: lobby.invite.redeemed
required:
- notification_type
then:
properties:
producer:
const: game_lobby
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyInviteRedeemedPayload'
- if:
properties:
notification_type:
const: lobby.invite.expired
required:
- notification_type
then:
properties:
producer:
const: game_lobby
audience_kind:
const: user
payload_json:
contentSchema:
$ref: '#/components/schemas/LobbyInviteExpiredPayload'
GeoReviewRecommendedPayload:
type: object
additionalProperties: true
required:
- user_id
- user_email
- observed_country
- usual_connection_country
- review_reason
properties:
user_id:
type: string
minLength: 1
user_email:
type: string
minLength: 1
observed_country:
type: string
minLength: 1
usual_connection_country:
type: string
minLength: 1
review_reason:
type: string
minLength: 1
GameTurnReadyPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- turn_number
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
turn_number:
type: integer
minimum: 1
GameFinishedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- final_turn_number
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
final_turn_number:
type: integer
minimum: 1
GameGenerationFailedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- failure_reason
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
failure_reason:
type: string
minLength: 1
LobbyRuntimePausedAfterStartPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
LobbyApplicationSubmittedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- applicant_user_id
- applicant_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
applicant_user_id:
type: string
minLength: 1
applicant_name:
type: string
minLength: 1
LobbyMembershipApprovedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
LobbyMembershipRejectedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
LobbyInviteCreatedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- inviter_user_id
- inviter_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
inviter_user_id:
type: string
minLength: 1
inviter_name:
type: string
minLength: 1
LobbyInviteRedeemedPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- invitee_user_id
- invitee_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
invitee_user_id:
type: string
minLength: 1
invitee_name:
type: string
minLength: 1
LobbyInviteExpiredPayload:
type: object
additionalProperties: true
required:
- game_id
- game_name
- invitee_user_id
- invitee_name
properties:
game_id:
type: string
minLength: 1
game_name:
type: string
minLength: 1
invitee_user_id:
type: string
minLength: 1
invitee_name:
type: string
minLength: 1