Enhance Notification Management with New Endpoints and Response Structures

- Added new endpoints for retrieving notifications for both admins and users, improving the flexibility of the notification system.
- Implemented query parameters for filtering notifications by status, method, event type, type, and recipient, enhancing usability.
- Introduced new response structures for notifications, including pagination information, to provide better data handling in API responses.
- Updated API documentation with Swagger comments for the new endpoints and response formats, ensuring clarity for API consumers.
- Refactored notification handling logic to support the new features, promoting a more robust notification management system.
This commit is contained in:
n.nakhostin
2025-09-20 17:41:21 +03:30
parent ab6eb3b3ed
commit 19cd346b1c
13 changed files with 1637 additions and 16 deletions
+428
View File
@@ -2451,7 +2451,130 @@ const docTemplate = `{
} }
}, },
"/admin/v1/notifications": { "/admin/v1/notifications": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get notifications",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Admin-Notification"
],
"summary": "Get notifications",
"parameters": [
{
"type": "string",
"description": "Status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Method filter",
"name": "method",
"in": "query"
},
{
"type": "string",
"description": "Event type filter",
"name": "event_type",
"in": "query"
},
{
"type": "string",
"description": "Type filter",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Recipient filter",
"name": "recipient",
"in": "query"
},
{
"type": "integer",
"description": "Limit results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset results",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Sort field",
"name": "sort_by",
"in": "query"
},
{
"type": "string",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal_notification.NotificationListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": { "post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Create a new notification with the provided information", "description": "Create a new notification with the provided information",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -2496,6 +2619,126 @@ const docTemplate = `{
} }
} }
}, },
"/admin/v1/notifications/my": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get notifications for the admin",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Admin-Notification"
],
"summary": "Get notifications for the admin",
"parameters": [
{
"type": "string",
"description": "Status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Method filter",
"name": "method",
"in": "query"
},
{
"type": "string",
"description": "Event type filter",
"name": "event_type",
"in": "query"
},
{
"type": "string",
"description": "Type filter",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Recipient filter",
"name": "recipient",
"in": "query"
},
{
"type": "integer",
"description": "Limit results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset results",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Sort field",
"name": "sort_by",
"in": "query"
},
{
"type": "string",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal_notification.NotificationListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/profile": { "/admin/v1/profile": {
"get": { "get": {
"security": [ "security": [
@@ -5014,6 +5257,126 @@ const docTemplate = `{
} }
} }
}, },
"/api/v1/notifications/my": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get notifications for the user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Notification"
],
"summary": "Get notifications for the user",
"parameters": [
{
"type": "string",
"description": "Status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Method filter",
"name": "method",
"in": "query"
},
{
"type": "string",
"description": "Event type filter",
"name": "event_type",
"in": "query"
},
{
"type": "string",
"description": "Type filter",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Recipient filter",
"name": "recipient",
"in": "query"
},
{
"type": "integer",
"description": "Limit results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset results",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Sort field",
"name": "sort_by",
"in": "query"
},
{
"type": "string",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal_notification.NotificationListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/api/v1/profile": { "/api/v1/profile": {
"get": { "get": {
"security": [ "security": [
@@ -7205,6 +7568,20 @@ const docTemplate = `{
} }
} }
}, },
"internal_notification.NotificationListResponse": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/definitions/response.Meta"
},
"notifications": {
"type": "array",
"items": {
"$ref": "#/definitions/internal_notification.NotificationResponse"
}
}
}
},
"internal_notification.NotificationRequest": { "internal_notification.NotificationRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -7246,6 +7623,57 @@ const docTemplate = `{
} }
} }
}, },
"internal_notification.NotificationResponse": {
"type": "object",
"properties": {
"created_at": {
"type": "integer"
},
"description": {
"type": "string"
},
"event_type": {
"type": "string"
},
"id": {
"type": "string"
},
"link": {
"type": "string"
},
"metadata": {
"type": "object",
"additionalProperties": {}
},
"methods": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"priority": {
"type": "string"
},
"schedule_at": {
"type": "integer"
},
"status": {
"type": "string"
},
"title": {
"type": "string"
},
"type": {
"type": "string"
},
"updated_at": {
"type": "integer"
},
"user_id": {
"type": "string"
}
}
},
"notification.DeliveryChannel": { "notification.DeliveryChannel": {
"type": "string", "type": "string",
"enum": [ "enum": [
+428
View File
@@ -2445,7 +2445,130 @@
} }
}, },
"/admin/v1/notifications": { "/admin/v1/notifications": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get notifications",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Admin-Notification"
],
"summary": "Get notifications",
"parameters": [
{
"type": "string",
"description": "Status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Method filter",
"name": "method",
"in": "query"
},
{
"type": "string",
"description": "Event type filter",
"name": "event_type",
"in": "query"
},
{
"type": "string",
"description": "Type filter",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Recipient filter",
"name": "recipient",
"in": "query"
},
{
"type": "integer",
"description": "Limit results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset results",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Sort field",
"name": "sort_by",
"in": "query"
},
{
"type": "string",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal_notification.NotificationListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": { "post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Create a new notification with the provided information", "description": "Create a new notification with the provided information",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -2490,6 +2613,126 @@
} }
} }
}, },
"/admin/v1/notifications/my": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get notifications for the admin",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Admin-Notification"
],
"summary": "Get notifications for the admin",
"parameters": [
{
"type": "string",
"description": "Status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Method filter",
"name": "method",
"in": "query"
},
{
"type": "string",
"description": "Event type filter",
"name": "event_type",
"in": "query"
},
{
"type": "string",
"description": "Type filter",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Recipient filter",
"name": "recipient",
"in": "query"
},
{
"type": "integer",
"description": "Limit results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset results",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Sort field",
"name": "sort_by",
"in": "query"
},
{
"type": "string",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal_notification.NotificationListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/profile": { "/admin/v1/profile": {
"get": { "get": {
"security": [ "security": [
@@ -5008,6 +5251,126 @@
} }
} }
}, },
"/api/v1/notifications/my": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get notifications for the user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Notification"
],
"summary": "Get notifications for the user",
"parameters": [
{
"type": "string",
"description": "Status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Method filter",
"name": "method",
"in": "query"
},
{
"type": "string",
"description": "Event type filter",
"name": "event_type",
"in": "query"
},
{
"type": "string",
"description": "Type filter",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Recipient filter",
"name": "recipient",
"in": "query"
},
{
"type": "integer",
"description": "Limit results",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset results",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Sort field",
"name": "sort_by",
"in": "query"
},
{
"type": "string",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/internal_notification.NotificationListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/api/v1/profile": { "/api/v1/profile": {
"get": { "get": {
"security": [ "security": [
@@ -7199,6 +7562,20 @@
} }
} }
}, },
"internal_notification.NotificationListResponse": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/definitions/response.Meta"
},
"notifications": {
"type": "array",
"items": {
"$ref": "#/definitions/internal_notification.NotificationResponse"
}
}
}
},
"internal_notification.NotificationRequest": { "internal_notification.NotificationRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -7240,6 +7617,57 @@
} }
} }
}, },
"internal_notification.NotificationResponse": {
"type": "object",
"properties": {
"created_at": {
"type": "integer"
},
"description": {
"type": "string"
},
"event_type": {
"type": "string"
},
"id": {
"type": "string"
},
"link": {
"type": "string"
},
"metadata": {
"type": "object",
"additionalProperties": {}
},
"methods": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"priority": {
"type": "string"
},
"schedule_at": {
"type": "integer"
},
"status": {
"type": "string"
},
"title": {
"type": "string"
},
"type": {
"type": "string"
},
"updated_at": {
"type": "integer"
},
"user_id": {
"type": "string"
}
}
},
"notification.DeliveryChannel": { "notification.DeliveryChannel": {
"type": "string", "type": "string",
"enum": [ "enum": [
+269
View File
@@ -654,6 +654,15 @@ definitions:
example: reviewed example: reviewed
type: string type: string
type: object type: object
internal_notification.NotificationListResponse:
properties:
meta:
$ref: '#/definitions/response.Meta'
notifications:
items:
$ref: '#/definitions/internal_notification.NotificationResponse'
type: array
type: object
internal_notification.NotificationRequest: internal_notification.NotificationRequest:
properties: properties:
channels: channels:
@@ -681,6 +690,40 @@ definitions:
type: type:
$ref: '#/definitions/notification.NotificationType' $ref: '#/definitions/notification.NotificationType'
type: object type: object
internal_notification.NotificationResponse:
properties:
created_at:
type: integer
description:
type: string
event_type:
type: string
id:
type: string
link:
type: string
metadata:
additionalProperties: {}
type: object
methods:
additionalProperties:
type: string
type: object
priority:
type: string
schedule_at:
type: integer
status:
type: string
title:
type: string
type:
type: string
updated_at:
type: integer
user_id:
type: string
type: object
notification.DeliveryChannel: notification.DeliveryChannel:
enum: enum:
- push - push
@@ -2726,6 +2769,80 @@ paths:
tags: tags:
- Admin-Inquiries - Admin-Inquiries
/admin/v1/notifications: /admin/v1/notifications:
get:
consumes:
- application/json
description: Get notifications
parameters:
- description: Status filter
in: query
name: status
type: string
- description: Method filter
in: query
name: method
type: string
- description: Event type filter
in: query
name: event_type
type: string
- description: Type filter
in: query
name: type
type: string
- description: Recipient filter
in: query
name: recipient
type: string
- description: Limit results
in: query
name: limit
type: integer
- description: Offset results
in: query
name: offset
type: integer
- description: Sort field
in: query
name: sort_by
type: string
- description: Sort order
in: query
name: sort_order
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/internal_notification.NotificationListResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"403":
description: Forbidden
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- BearerAuth: []
summary: Get notifications
tags:
- Admin-Notification
post: post:
consumes: consumes:
- application/json - application/json
@@ -2752,9 +2869,86 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/response.APIResponse' $ref: '#/definitions/response.APIResponse'
security:
- BearerAuth: []
summary: Create a new notification summary: Create a new notification
tags: tags:
- Admin-Notification - Admin-Notification
/admin/v1/notifications/my:
get:
consumes:
- application/json
description: Get notifications for the admin
parameters:
- description: Status filter
in: query
name: status
type: string
- description: Method filter
in: query
name: method
type: string
- description: Event type filter
in: query
name: event_type
type: string
- description: Type filter
in: query
name: type
type: string
- description: Recipient filter
in: query
name: recipient
type: string
- description: Limit results
in: query
name: limit
type: integer
- description: Offset results
in: query
name: offset
type: integer
- description: Sort field
in: query
name: sort_by
type: string
- description: Sort order
in: query
name: sort_order
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/internal_notification.NotificationListResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"403":
description: Forbidden
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- BearerAuth: []
summary: Get notifications for the admin
tags:
- Admin-Notification
/admin/v1/profile: /admin/v1/profile:
get: get:
consumes: consumes:
@@ -4324,6 +4518,81 @@ paths:
summary: Create new inquiry summary: Create new inquiry
tags: tags:
- Inquiries - Inquiries
/api/v1/notifications/my:
get:
consumes:
- application/json
description: Get notifications for the user
parameters:
- description: Status filter
in: query
name: status
type: string
- description: Method filter
in: query
name: method
type: string
- description: Event type filter
in: query
name: event_type
type: string
- description: Type filter
in: query
name: type
type: string
- description: Recipient filter
in: query
name: recipient
type: string
- description: Limit results
in: query
name: limit
type: integer
- description: Offset results
in: query
name: offset
type: integer
- description: Sort field
in: query
name: sort_by
type: string
- description: Sort order
in: query
name: sort_order
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/internal_notification.NotificationListResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"403":
description: Forbidden
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- BearerAuth: []
summary: Get notifications for the user
tags:
- Notification
/api/v1/profile: /api/v1/profile:
get: get:
consumes: consumes:
+8 -9
View File
@@ -134,8 +134,10 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler
// Admin Notifications Routes // Admin Notifications Routes
notificationsGP := adminV1.Group("/notifications") notificationsGP := adminV1.Group("/notifications")
{ {
// notificationsGP.Use(userHandler.AuthMiddleware()) notificationsGP.Use(userHandler.AuthMiddleware())
notificationsGP.POST("", notificationHandler.Send) notificationsGP.POST("", notificationHandler.Send)
notificationsGP.GET("", notificationHandler.GetNotifications)
notificationsGP.GET("/my", notificationHandler.AdminNotifications)
} }
} }
@@ -209,12 +211,9 @@ func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tende
} }
// Public Notifications Routes // Public Notifications Routes
// notificationsGP := v1.Group("/notifications") notificationsGP := v1.Group("/notifications")
// { {
// notificationsGP.Use(customerHandler.AuthMiddleware()) notificationsGP.Use(customerHandler.AuthMiddleware())
// notificationsGP.GET("", notificationHandler.GetNotifications) notificationsGP.GET("", notificationHandler.CustomerNotifications)
// notificationsGP.GET("/stats", notificationHandler.GetNotificationStats) }
// notificationsGP.POST("/mark-read", notificationHandler.MarkAsRead)
// notificationsGP.GET("/:id", notificationHandler.GetNotificationByID)
// }
} }
+29 -7
View File
@@ -1,5 +1,7 @@
package notification package notification
import "tm/pkg/response"
// CreateRequest represents the request to create a notification // CreateRequest represents the request to create a notification
type NotificationRequest struct { type NotificationRequest struct {
Recipient []string `json:"recipient"` Recipient []string `json:"recipient"`
@@ -16,11 +18,31 @@ type NotificationRequest struct {
// SearchForm represents the form for searching notifications // SearchForm represents the form for searching notifications
type SearchForm struct { type SearchForm struct {
Search *string `query:"q" valid:"optional"` Status string `query:"status" valid:"optional"`
Status NotificationStatus `query:"status" valid:"optional"` Method string `query:"method" valid:"optional"`
EventType string `query:"event_type" valid:"optional"`
SortBy *string `query:"sort_by" valid:"optional"` Type string `query:"type" valid:"optional"`
SortOrder *string `query:"sort_order" valid:"optional"` Recipient string `query:"recipient" valid:"optional"`
Limit *int `query:"limit" valid:"optional"` }
Offset *int `query:"offset" valid:"optional"`
type NotificationListResponse struct {
Notifications []*NotificationResponse `json:"notifications"`
Meta *response.Meta `json:"meta"`
}
type NotificationResponse struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Title string `json:"title"`
Description string `json:"description"`
Link *string `json:"link"`
Priority string `json:"priority"`
Type string `json:"type"`
EventType string `json:"event_type"`
Status string `json:"status"`
Methods map[string]string `json:"methods"`
Metadata map[string]any `json:"metadata"`
ScheduleAt int64 `json:"schedule_at"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
} }
+150
View File
@@ -4,6 +4,7 @@ import (
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"tm/internal/user"
"tm/pkg/logger" "tm/pkg/logger"
"tm/pkg/response" "tm/pkg/response"
) )
@@ -32,6 +33,7 @@ func NewHandler(service Service, logger logger.Logger) *NotificationHandler {
// @Success 201 {object} response.APIResponse "Notification created successfully" // @Success 201 {object} response.APIResponse "Notification created successfully"
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Security BearerAuth
// @Router /admin/v1/notifications [post] // @Router /admin/v1/notifications [post]
func (h *NotificationHandler) Send(c echo.Context) error { func (h *NotificationHandler) Send(c echo.Context) error {
var req NotificationRequest var req NotificationRequest
@@ -49,3 +51,151 @@ func (h *NotificationHandler) Send(c echo.Context) error {
return response.Created(c, nil, "Notification sent successfully") return response.Created(c, nil, "Notification sent successfully")
} }
// GetNotifications gets notifications
// @Summary Get notifications
// @Description Get notifications
// @Tags Admin-Notification
// @Accept json
// @Produce json
// @Param status query string false "Status filter"
// @Param method query string false "Method filter"
// @Param event_type query string false "Event type filter"
// @Param type query string false "Type filter"
// @Param recipient query string false "Recipient filter"
// @Param limit query int false "Limit results"
// @Param offset query int false "Offset results"
// @Param sort_by query string false "Sort field"
// @Param sort_order query string false "Sort order"
// @Success 200 {object} response.APIResponse{data=NotificationListResponse}
// @Failure 401 {object} response.APIResponse
// @Failure 403 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security BearerAuth
// @Router /admin/v1/notifications [get]
func (h *NotificationHandler) GetNotifications(c echo.Context) error {
form, err := response.Parse[SearchForm](c)
if err != nil {
return response.BadRequest(c, "Invalid request format", err.Error())
}
// Validate form
if _, err := govalidator.ValidateStruct(form); err != nil {
return response.ValidationError(c, err.Error(), "")
}
pagination := response.NewPagination(c)
// Call service
notifications, err := h.service.GetNotifications(c.Request().Context(), form, pagination)
if err != nil {
return response.InternalServerError(c, "Failed to get notifications")
}
return response.Success(c, notifications, "Notifications retrieved successfully")
}
// MyAdminNotifications gets notifications for the admin
// @Summary Get notifications for the admin
// @Description Get notifications for the admin
// @Tags Admin-Notification
// @Accept json
// @Produce json
// @Param status query string false "Status filter"
// @Param method query string false "Method filter"
// @Param event_type query string false "Event type filter"
// @Param type query string false "Type filter"
// @Param recipient query string false "Recipient filter"
// @Param limit query int false "Limit results"
// @Param offset query int false "Offset results"
// @Param sort_by query string false "Sort field"
// @Param sort_order query string false "Sort order"
// @Success 200 {object} response.APIResponse{data=NotificationListResponse}
// @Failure 401 {object} response.APIResponse
// @Failure 403 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security BearerAuth
// @Router /admin/v1/notifications/my [get]
func (h *NotificationHandler) AdminNotifications(c echo.Context) error {
userID, err := user.GetUserIDFromContext(c)
if err != nil {
return response.BadRequest(c, "User ID required", "User must be associated with a user")
}
form, err := response.Parse[SearchForm](c)
if err != nil {
return response.BadRequest(c, "Invalid request format", err.Error())
}
// Validate form
if _, err := govalidator.ValidateStruct(form); err != nil {
return response.ValidationError(c, err.Error(), "")
}
form.Recipient = userID
form.Status = "sent"
pagination := response.NewPagination(c)
// Call service
notifications, err := h.service.GetNotifications(c.Request().Context(), form, pagination)
if err != nil {
return response.InternalServerError(c, "Failed to get notifications")
}
return response.Success(c, notifications, "Notifications retrieved successfully")
}
// MyNotifications gets notifications for the user
// @Summary Get notifications for the user
// @Description Get notifications for the user
// @Tags Notification
// @Accept json
// @Produce json
// @Param status query string false "Status filter"
// @Param method query string false "Method filter"
// @Param event_type query string false "Event type filter"
// @Param type query string false "Type filter"
// @Param recipient query string false "Recipient filter"
// @Param limit query int false "Limit results"
// @Param offset query int false "Offset results"
// @Param sort_by query string false "Sort field"
// @Param sort_order query string false "Sort order"
// @Success 200 {object} response.APIResponse{data=NotificationListResponse}
// @Failure 401 {object} response.APIResponse
// @Failure 403 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security BearerAuth
// @Router /api/v1/notifications/my [get]
func (h *NotificationHandler) CustomerNotifications(c echo.Context) error {
userID, err := user.GetUserIDFromContext(c)
if err != nil {
return response.BadRequest(c, "User ID required", "User must be associated with a user")
}
form, err := response.Parse[SearchForm](c)
if err != nil {
return response.BadRequest(c, "Invalid request format", err.Error())
}
// Validate form
if _, err := govalidator.ValidateStruct(form); err != nil {
return response.ValidationError(c, err.Error(), "")
}
form.Recipient = userID
form.Status = "sent"
pagination := response.NewPagination(c)
// Call service
notifications, err := h.service.GetNotifications(c.Request().Context(), form, pagination)
if err != nil {
return response.InternalServerError(c, "Failed to get notifications")
}
return response.Success(c, notifications, "Notifications retrieved successfully")
}
+58
View File
@@ -14,6 +14,7 @@ import (
// NotificationService defines the interface for notification business logic // NotificationService defines the interface for notification business logic
type Service interface { type Service interface {
Send(ctx context.Context, req *NotificationRequest) error Send(ctx context.Context, req *NotificationRequest) error
GetNotifications(ctx context.Context, req *SearchForm, pagination *response.Pagination) (*NotificationListResponse, error)
getUsers(ctx context.Context, userIDs []string) ([]notificationRecipient, error) getUsers(ctx context.Context, userIDs []string) ([]notificationRecipient, error)
getCustomers(ctx context.Context, values []string, target string) ([]notificationRecipient, error) getCustomers(ctx context.Context, values []string, target string) ([]notificationRecipient, error)
} }
@@ -138,6 +139,63 @@ func (s *notificationService) Send(ctx context.Context, req *NotificationRequest
return nil return nil
} }
func (s *notificationService) GetNotifications(ctx context.Context, req *SearchForm, pagination *response.Pagination) (*NotificationListResponse, error) {
s.logger.Info("Getting notifications", map[string]interface{}{
"status": req.Status,
"method": req.Method,
"event_type": req.EventType,
"type": req.Type,
"recipient": req.Recipient,
"limit": pagination.Limit,
"offset": pagination.Offset,
})
notifications, err := s.sdk.GetNotifications(ctx, &notification.GetNotificationsRequest{
Status: req.Status,
Method: req.Method,
EventType: req.EventType,
Type: req.Type,
UserID: req.Recipient,
PerPage: pagination.Limit,
Page: pagination.Offset / pagination.Limit,
})
if err != nil {
s.logger.Error("Failed to get notifications", map[string]interface{}{
"error": err.Error(),
})
return nil, err
}
notificationsResponse := make([]*NotificationResponse, 0)
for _, notification := range notifications.Data {
notificationsResponse = append(notificationsResponse, &NotificationResponse{
ID: notification.ID,
UserID: notification.UserID,
Title: notification.Title,
Description: notification.Message,
Link: &notification.Link,
Priority: notification.Priority,
Type: notification.Type,
EventType: notification.EventType,
Status: notification.Status,
Methods: notification.Methods,
Metadata: notification.Metadata,
ScheduleAt: notification.ScheduledAt,
})
}
return &NotificationListResponse{
Notifications: notificationsResponse,
Meta: &response.Meta{
Total: notifications.Pagination.Total,
Limit: notifications.Pagination.PerPage,
Offset: pagination.Offset,
Page: notifications.Pagination.CurrentPage,
Pages: notifications.Pagination.TotalPages,
},
}, nil
}
func (s *notificationService) getUsers(ctx context.Context, userIDs []string) ([]notificationRecipient, error) { func (s *notificationService) getUsers(ctx context.Context, userIDs []string) ([]notificationRecipient, error) {
recipients := make([]notificationRecipient, 0) recipients := make([]notificationRecipient, 0)
offset := 0 offset := 0
+72
View File
@@ -68,10 +68,82 @@ requests := []*notification.NotificationRequest{...}
batchResponse, err := sdk.SendBatch(ctx, requests) batchResponse, err := sdk.SendBatch(ctx, requests)
``` ```
### Getting Notifications
#### Get All Notifications
```go
// Get all notifications
response, err := sdk.GetNotifications(ctx, &notification.GetNotificationsRequest{})
```
#### Get Notifications with Filters
```go
// Get notifications for specific user
response, err := sdk.GetNotificationsByUser(ctx, "user_id_here", 1, 10)
// Get notifications by status
response, err := sdk.GetNotificationsByStatus(ctx, "sent", 1, 10)
// Get notifications with custom filters
req := &notification.GetNotificationsRequest{
Status: "sent",
Method: "email",
EventType: "EMAIL",
Type: "info",
UserID: "user_id_here",
Page: 1,
PerPage: 10,
}
response, err := sdk.GetNotifications(ctx, req)
```
#### Quick Get Notifications
```go
// Quick method for basic filtering
response, err := sdk.QuickGetNotifications(ctx, "user_id_here", "sent")
```
## API Response ## API Response
### Send Notification Response
- **Success**: `{"status": "Notification sent"}` - **Success**: `{"status": "Notification sent"}`
- **Error**: `{"error": "error message"}` - **Error**: `{"error": "error message"}`
### Get Notifications Response
```json
{
"data": [
{
"id": "68ceaaec99c78402b5fec555",
"user_id": "689a0aca36bf9aae890c30c1",
"title": "CHECK",
"message": "Check Send Notification Channels",
"link": "",
"image": "",
"type": "info",
"priority": "low",
"event_type": "EMAIL",
"created_at": "2025-09-20T13:23:56.664Z",
"updated_at": "0001-01-01T00:00:00Z",
"methods": {
"email": "nakhostin.nima1998@gmail.com"
},
"metadata": null,
"status": "sent",
"scheduled_at": 0,
"is_scheduled": false
}
],
"pagination": {
"total": 3,
"count": 3,
"per_page": 10,
"current_page": 1,
"total_pages": 1
}
}
```
## Error Handling ## Error Handling
```go ```go
if err != nil { if err != nil {
+107
View File
@@ -197,6 +197,113 @@ func (c *Client) sendRequest(ctx context.Context, req *NotificationRequest) (*No
return &notificationResp, nil return &notificationResp, nil
} }
// GetNotifications retrieves a list of notifications from the notification service
func (c *Client) GetNotifications(ctx context.Context, req *GetNotificationsRequest) (*NotificationListResponse, error) {
if c.config.EnableLogging && c.logger != nil {
c.logger.Debug("Getting notifications", map[string]interface{}{
"status": req.Status,
"method": req.Method,
"event_type": req.EventType,
"type": req.Type,
"user_id": req.UserID,
"base_url": c.config.BaseURL,
})
}
// Build query parameters
queryParams := c.buildQueryParams(req)
// Create HTTP request
url := fmt.Sprintf("%s/api/v1/notifications?%s", c.config.BaseURL, queryParams)
httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
// Set headers
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("User-Agent", c.config.UserAgent)
// Send request
resp, err := c.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()
// Read response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
// Handle non-2xx status codes
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
// Try to parse error response
var errorResp NotificationErrorResponse
if json.Unmarshal(body, &errorResp) == nil && errorResp.Error != "" {
return nil, MapHTTPError(resp.StatusCode, errorResp.Error)
}
return nil, MapHTTPError(resp.StatusCode, string(body))
}
// Parse success response
var notificationsResp NotificationListResponse
if err := json.Unmarshal(body, &notificationsResp); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
if c.config.EnableLogging && c.logger != nil {
c.logger.Info("Notifications retrieved successfully", map[string]interface{}{
"count": len(notificationsResp.Data),
"total": notificationsResp.Pagination.Total,
"page": notificationsResp.Pagination.CurrentPage,
"total_pages": notificationsResp.Pagination.TotalPages,
})
}
return &notificationsResp, nil
}
// buildQueryParams builds query parameters from the request
func (c *Client) buildQueryParams(req *GetNotificationsRequest) string {
params := make([]string, 0)
if req.Status != "" {
params = append(params, fmt.Sprintf("status=%s", req.Status))
}
if req.Method != "" {
params = append(params, fmt.Sprintf("method=%s", req.Method))
}
if req.EventType != "" {
params = append(params, fmt.Sprintf("event_type=%s", req.EventType))
}
if req.Type != "" {
params = append(params, fmt.Sprintf("type=%s", req.Type))
}
if req.UserID != "" {
params = append(params, fmt.Sprintf("user_id=%s", req.UserID))
}
if req.Page > 0 {
params = append(params, fmt.Sprintf("page=%d", req.Page))
}
if req.PerPage > 0 {
params = append(params, fmt.Sprintf("per_page=%d", req.PerPage))
}
// Join parameters with &
if len(params) == 0 {
return ""
}
result := params[0]
for i := 1; i < len(params); i++ {
result += "&" + params[i]
}
return result
}
// shouldRetry determines if a request should be retried based on the error // shouldRetry determines if a request should be retried based on the error
func (c *Client) shouldRetry(err error) bool { func (c *Client) shouldRetry(err error) bool {
switch e := err.(type) { switch e := err.(type) {
+46
View File
@@ -116,3 +116,49 @@ func (nm *NotificationMethods) GetMethodValue(eventType EventType) string {
func (nr *NotificationRequest) IsImmediate() bool { func (nr *NotificationRequest) IsImmediate() bool {
return nr.ScheduledAt == 0 return nr.ScheduledAt == 0
} }
// NotificationItem represents a single notification in the list response
type NotificationItem struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Title string `json:"title"`
Message string `json:"message"`
Link string `json:"link"`
Image string `json:"image"`
Type string `json:"type"`
Priority string `json:"priority"`
EventType string `json:"event_type"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Methods map[string]string `json:"methods"`
Metadata map[string]any `json:"metadata"`
Status string `json:"status"`
ScheduledAt int64 `json:"scheduled_at"`
IsScheduled bool `json:"is_scheduled"`
}
// NotificationListResponse represents the response from getting notifications list
type NotificationListResponse struct {
Data []NotificationItem `json:"data"`
Pagination PaginationInfo `json:"pagination"`
}
// PaginationInfo represents pagination information in responses
type PaginationInfo struct {
Total int `json:"total"`
Count int `json:"count"`
PerPage int `json:"per_page"`
CurrentPage int `json:"current_page"`
TotalPages int `json:"total_pages"`
}
// GetNotificationsRequest represents the request parameters for getting notifications
type GetNotificationsRequest struct {
Status string `json:"status,omitempty" valid:"optional"`
Method string `json:"method,omitempty" valid:"optional"`
EventType string `json:"event_type,omitempty" valid:"optional"`
Type string `json:"type,omitempty" valid:"optional"`
UserID string `json:"user_id,omitempty" valid:"optional"`
Page int `json:"page,omitempty" valid:"optional"`
PerPage int `json:"per_page,omitempty" valid:"optional"`
}
+3
View File
@@ -21,6 +21,9 @@ type NotificationService interface {
// SendOTP sends an OTP notification // SendOTP sends an OTP notification
SendOTP(ctx context.Context, model Model) (*NotificationResponse, error) SendOTP(ctx context.Context, model Model) (*NotificationResponse, error)
// GetNotifications retrieves a list of notifications
GetNotifications(ctx context.Context, req *GetNotificationsRequest) (*NotificationListResponse, error)
// Health checks if the notification service is available // Health checks if the notification service is available
Health(ctx context.Context) error Health(ctx context.Context) error
+34
View File
@@ -74,6 +74,11 @@ func (s *SDK) SendOTP(ctx context.Context, model Model) (*NotificationResponse,
return s.service.SendOTP(ctx, model) return s.service.SendOTP(ctx, model)
} }
// GetNotifications retrieves a list of notifications from the notification service
func (s *SDK) GetNotifications(ctx context.Context, req *GetNotificationsRequest) (*NotificationListResponse, error) {
return s.service.GetNotifications(ctx, req)
}
// NewBuilder creates a new notification builder for fluent API usage // NewBuilder creates a new notification builder for fluent API usage
func (s *SDK) NewBuilder() Builder { func (s *SDK) NewBuilder() Builder {
return s.service.(*Service).NewBuilder() return s.service.(*Service).NewBuilder()
@@ -115,6 +120,35 @@ func (s *SDK) QuickOTP(ctx context.Context, model Model) error {
return err return err
} }
// QuickGetNotifications retrieves notifications with simple parameters
func (s *SDK) QuickGetNotifications(ctx context.Context, userID, status string) (*NotificationListResponse, error) {
req := &GetNotificationsRequest{
UserID: userID,
Status: status,
}
return s.GetNotifications(ctx, req)
}
// GetNotificationsByUser retrieves notifications for a specific user
func (s *SDK) GetNotificationsByUser(ctx context.Context, userID string, page, perPage int) (*NotificationListResponse, error) {
req := &GetNotificationsRequest{
UserID: userID,
Page: page,
PerPage: perPage,
}
return s.GetNotifications(ctx, req)
}
// GetNotificationsByStatus retrieves notifications by status
func (s *SDK) GetNotificationsByStatus(ctx context.Context, status string, page, perPage int) (*NotificationListResponse, error) {
req := &GetNotificationsRequest{
Status: status,
Page: page,
PerPage: perPage,
}
return s.GetNotifications(ctx, req)
}
// Batch operations for sending multiple notifications // Batch operations for sending multiple notifications
// BatchRequest represents a batch notification request // BatchRequest represents a batch notification request
+5
View File
@@ -104,6 +104,11 @@ func (s *Service) SendOTP(ctx context.Context, model Model) (*NotificationRespon
return s.client.SendNotification(ctx, req) return s.client.SendNotification(ctx, req)
} }
// GetNotifications retrieves a list of notifications from the notification service
func (s *Service) GetNotifications(ctx context.Context, req *GetNotificationsRequest) (*NotificationListResponse, error) {
return s.client.GetNotifications(ctx, req)
}
// Health checks if the notification service is available // Health checks if the notification service is available
func (s *Service) Health(ctx context.Context) error { func (s *Service) Health(ctx context.Context) error {
// Create a simple test request to check service availability // Create a simple test request to check service availability