feat: notifications in the app and, optionally, by e-mail
Derived from events and kept per person: whoever a ticket is assigned to, whoever reports or works on it when it moves, fails QA or gets a comment, and whoever joins a project. Never about what you did yourself, once per event. They arrive live through a subscription to a bell in the header, and are copied by e-mail to whoever turns it on when SMTP_URL is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4707,6 +4707,363 @@
|
||||
],
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/me/notifications": {
|
||||
"get": {
|
||||
"operationId": "NotificationsController_mine",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "unreadOnly",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Only unread ones",
|
||||
"schema": {
|
||||
"default": false,
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Object"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
"default": 30,
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MyNotificationsDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing or rejected Pireon token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Not a member of the project (or not allowed)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"pireon-oauth": []
|
||||
},
|
||||
{
|
||||
"pireon": []
|
||||
}
|
||||
],
|
||||
"summary": "My notifications, newest first",
|
||||
"tags": [
|
||||
"Users"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/me/notifications/read": {
|
||||
"post": {
|
||||
"operationId": "NotificationsController_markRead",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MarkNotificationsReadDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Done"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing or rejected Pireon token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Not a member of the project (or not allowed)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Someone changed it at the same time: retry",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Business rule (e.g. a transition the workflow forbids)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"pireon-oauth": []
|
||||
},
|
||||
{
|
||||
"pireon": []
|
||||
}
|
||||
],
|
||||
"summary": "Mark notifications as read (all of them without `ids`)",
|
||||
"tags": [
|
||||
"Users"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/me/notification-settings": {
|
||||
"get": {
|
||||
"operationId": "NotificationsController_settings",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NotificationSettingsResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing or rejected Pireon token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Not a member of the project (or not allowed)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"pireon-oauth": []
|
||||
},
|
||||
{
|
||||
"pireon": []
|
||||
}
|
||||
],
|
||||
"summary": "Whether I get copies by e-mail",
|
||||
"tags": [
|
||||
"Users"
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"operationId": "NotificationsController_updateSettings",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NotificationSettingsDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NotificationSettingsResponseDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing or rejected Pireon token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Not a member of the project (or not allowed)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Someone changed it at the same time: retry",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Business rule (e.g. a transition the workflow forbids)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiErrorDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"pireon-oauth": []
|
||||
},
|
||||
{
|
||||
"pireon": []
|
||||
}
|
||||
],
|
||||
"summary": "Turn e-mail copies on or off",
|
||||
"tags": [
|
||||
"Users"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
@@ -6714,6 +7071,145 @@
|
||||
"description": "Publish even with unfinished tickets"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Object": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"NotificationKind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ASSIGNED",
|
||||
"STATUS_CHANGED",
|
||||
"QA_FAILED",
|
||||
"COMMENTED",
|
||||
"MENTIONED",
|
||||
"WATCHED",
|
||||
"ADDED_TO_PROJECT",
|
||||
"REMINDER"
|
||||
]
|
||||
},
|
||||
"NotificationDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NotificationKind"
|
||||
}
|
||||
]
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"projectKey": {
|
||||
"type": "string",
|
||||
"example": "WEB"
|
||||
},
|
||||
"issueId": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"format": "uuid"
|
||||
},
|
||||
"issueKey": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"example": "WEB-12"
|
||||
},
|
||||
"actor": {
|
||||
"$ref": "#/components/schemas/UserRefDto"
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"example": "Ana te asignó WEB-12: Login roto en Safari"
|
||||
},
|
||||
"readAt": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"format": "date-time",
|
||||
"description": "Null: unread"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"kind",
|
||||
"projectId",
|
||||
"projectKey",
|
||||
"issueId",
|
||||
"issueKey",
|
||||
"actor",
|
||||
"summary",
|
||||
"readAt",
|
||||
"createdAt"
|
||||
]
|
||||
},
|
||||
"MyNotificationsDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NotificationDto"
|
||||
}
|
||||
},
|
||||
"unread": {
|
||||
"type": "number",
|
||||
"description": "All my unread ones, not just these"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"items",
|
||||
"unread"
|
||||
]
|
||||
},
|
||||
"MarkNotificationsReadDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "Omit to mark all of them",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"NotificationSettingsResponseDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "boolean",
|
||||
"description": "Copies by e-mail"
|
||||
},
|
||||
"emailAvailable": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this installation can send e-mail"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"email",
|
||||
"emailAvailable"
|
||||
]
|
||||
},
|
||||
"NotificationSettingsDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "boolean",
|
||||
"description": "Copies by e-mail"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"email"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user