diff --git a/cmd/web/docs/docs.go b/cmd/web/docs/docs.go index bcd2ec0..0ab7f27 100644 --- a/cmd/web/docs/docs.go +++ b/cmd/web/docs/docs.go @@ -2451,7 +2451,130 @@ const docTemplate = `{ } }, "/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": { + "security": [ + { + "BearerAuth": [] + } + ], "description": "Create a new notification with the provided information", "consumes": [ "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": { "get": { "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": { "get": { "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": { "type": "object", "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": { "type": "string", "enum": [ diff --git a/cmd/web/docs/swagger.json b/cmd/web/docs/swagger.json index 375d454..b2afa29 100644 --- a/cmd/web/docs/swagger.json +++ b/cmd/web/docs/swagger.json @@ -2445,7 +2445,130 @@ } }, "/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": { + "security": [ + { + "BearerAuth": [] + } + ], "description": "Create a new notification with the provided information", "consumes": [ "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": { "get": { "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": { "get": { "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": { "type": "object", "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": { "type": "string", "enum": [ diff --git a/cmd/web/docs/swagger.yaml b/cmd/web/docs/swagger.yaml index c9085eb..4aa57d6 100644 --- a/cmd/web/docs/swagger.yaml +++ b/cmd/web/docs/swagger.yaml @@ -654,6 +654,15 @@ definitions: example: reviewed type: string 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: properties: channels: @@ -681,6 +690,40 @@ definitions: type: $ref: '#/definitions/notification.NotificationType' 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: enum: - push @@ -2726,6 +2769,80 @@ paths: tags: - Admin-Inquiries /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: consumes: - application/json @@ -2752,9 +2869,86 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/response.APIResponse' + security: + - BearerAuth: [] summary: Create a new notification tags: - 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: get: consumes: @@ -4324,6 +4518,81 @@ paths: summary: Create new inquiry tags: - 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: get: consumes: diff --git a/cmd/web/router/routes.go b/cmd/web/router/routes.go index e4e547c..cf40f46 100644 --- a/cmd/web/router/routes.go +++ b/cmd/web/router/routes.go @@ -134,8 +134,10 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler // Admin Notifications Routes notificationsGP := adminV1.Group("/notifications") { - // notificationsGP.Use(userHandler.AuthMiddleware()) + notificationsGP.Use(userHandler.AuthMiddleware()) 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 - // notificationsGP := v1.Group("/notifications") - // { - // notificationsGP.Use(customerHandler.AuthMiddleware()) - // notificationsGP.GET("", notificationHandler.GetNotifications) - // notificationsGP.GET("/stats", notificationHandler.GetNotificationStats) - // notificationsGP.POST("/mark-read", notificationHandler.MarkAsRead) - // notificationsGP.GET("/:id", notificationHandler.GetNotificationByID) - // } + notificationsGP := v1.Group("/notifications") + { + notificationsGP.Use(customerHandler.AuthMiddleware()) + notificationsGP.GET("", notificationHandler.CustomerNotifications) + } } diff --git a/internal/notification/form.go b/internal/notification/form.go index ae59705..113c89e 100644 --- a/internal/notification/form.go +++ b/internal/notification/form.go @@ -1,5 +1,7 @@ package notification +import "tm/pkg/response" + // CreateRequest represents the request to create a notification type NotificationRequest struct { Recipient []string `json:"recipient"` @@ -16,11 +18,31 @@ type NotificationRequest struct { // SearchForm represents the form for searching notifications type SearchForm struct { - Search *string `query:"q" valid:"optional"` - Status NotificationStatus `query:"status" valid:"optional"` - - SortBy *string `query:"sort_by" valid:"optional"` - SortOrder *string `query:"sort_order" valid:"optional"` - Limit *int `query:"limit" valid:"optional"` - Offset *int `query:"offset" valid:"optional"` + Status string `query:"status" valid:"optional"` + Method string `query:"method" valid:"optional"` + EventType string `query:"event_type" valid:"optional"` + Type string `query:"type" valid:"optional"` + Recipient string `query:"recipient" 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"` } diff --git a/internal/notification/handler.go b/internal/notification/handler.go index 18decb8..00f5b01 100644 --- a/internal/notification/handler.go +++ b/internal/notification/handler.go @@ -4,6 +4,7 @@ import ( "github.com/asaskevich/govalidator" "github.com/labstack/echo/v4" + "tm/internal/user" "tm/pkg/logger" "tm/pkg/response" ) @@ -32,6 +33,7 @@ func NewHandler(service Service, logger logger.Logger) *NotificationHandler { // @Success 201 {object} response.APIResponse "Notification created successfully" // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse +// @Security BearerAuth // @Router /admin/v1/notifications [post] func (h *NotificationHandler) Send(c echo.Context) error { var req NotificationRequest @@ -49,3 +51,151 @@ func (h *NotificationHandler) Send(c echo.Context) error { 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") +} diff --git a/internal/notification/service.go b/internal/notification/service.go index 385b0e3..7e2e9c3 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -14,6 +14,7 @@ import ( // NotificationService defines the interface for notification business logic type Service interface { 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) 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 } +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, ¬ification.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: ¬ification.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) { recipients := make([]notificationRecipient, 0) offset := 0 diff --git a/pkg/notification/USAGE.md b/pkg/notification/USAGE.md index ef00148..99e91cd 100644 --- a/pkg/notification/USAGE.md +++ b/pkg/notification/USAGE.md @@ -68,10 +68,82 @@ requests := []*notification.NotificationRequest{...} batchResponse, err := sdk.SendBatch(ctx, requests) ``` +### Getting Notifications + +#### Get All Notifications +```go +// Get all notifications +response, err := sdk.GetNotifications(ctx, ¬ification.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 := ¬ification.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 + +### Send Notification Response - **Success**: `{"status": "Notification sent"}` - **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 ```go if err != nil { diff --git a/pkg/notification/client.go b/pkg/notification/client.go index 46cf1c3..a4b02a1 100644 --- a/pkg/notification/client.go +++ b/pkg/notification/client.go @@ -197,6 +197,113 @@ func (c *Client) sendRequest(ctx context.Context, req *NotificationRequest) (*No return ¬ificationResp, 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, ¬ificationsResp); 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 ¬ificationsResp, 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 func (c *Client) shouldRetry(err error) bool { switch e := err.(type) { diff --git a/pkg/notification/entities.go b/pkg/notification/entities.go index 61a8581..c5ba13a 100644 --- a/pkg/notification/entities.go +++ b/pkg/notification/entities.go @@ -116,3 +116,49 @@ func (nm *NotificationMethods) GetMethodValue(eventType EventType) string { func (nr *NotificationRequest) IsImmediate() bool { 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"` +} diff --git a/pkg/notification/interfaces.go b/pkg/notification/interfaces.go index 26ebf21..627f411 100644 --- a/pkg/notification/interfaces.go +++ b/pkg/notification/interfaces.go @@ -21,6 +21,9 @@ type NotificationService interface { // SendOTP sends an OTP notification 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(ctx context.Context) error diff --git a/pkg/notification/sdk.go b/pkg/notification/sdk.go index 616f4dc..0ff12fd 100644 --- a/pkg/notification/sdk.go +++ b/pkg/notification/sdk.go @@ -74,6 +74,11 @@ func (s *SDK) SendOTP(ctx context.Context, model Model) (*NotificationResponse, 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 func (s *SDK) NewBuilder() Builder { return s.service.(*Service).NewBuilder() @@ -115,6 +120,35 @@ func (s *SDK) QuickOTP(ctx context.Context, model Model) error { 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 // BatchRequest represents a batch notification request diff --git a/pkg/notification/service.go b/pkg/notification/service.go index 70d35f8..d506db0 100644 --- a/pkg/notification/service.go +++ b/pkg/notification/service.go @@ -104,6 +104,11 @@ func (s *Service) SendOTP(ctx context.Context, model Model) (*NotificationRespon 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 func (s *Service) Health(ctx context.Context) error { // Create a simple test request to check service availability