diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 17836561..428b2ba2 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -4502,6 +4502,121 @@ const docTemplate = `{ } } }, + "/organizations/{organizationId}/policy-notifications": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotifications", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-notifications/{policyNotificationId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "policyNotificationId", + "name": "policyNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse" + } + } + } + } + }, "/organizations/{organizationId}/policy-statistics": { "get": { "security": [ @@ -12875,6 +12990,28 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse": { + "type": "object", + "properties": { + "policyNotification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policyNotifications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse": { "type": "object", "properties": { @@ -13842,6 +13979,53 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse": { + "type": "object", + "properties": { + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" + }, + "createdAt": { + "type": "string" + }, + "grafanaUrl": { + "type": "string" + }, + "id": { + "type": "string" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "rawData": { + "type": "string" + }, + "read": { + "type": "boolean" + }, + "severity": { + "type": "string" + }, + "status": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.PolicyParameter": { "type": "object", "properties": { @@ -15080,6 +15264,9 @@ const docTemplate = `{ "rawData": { "type": "string" }, + "read": { + "type": "boolean" + }, "severity": { "type": "string" }, diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 78268f7b..379290e4 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -4496,6 +4496,121 @@ } } }, + "/organizations/{organizationId}/policy-notifications": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotifications", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-notifications/{policyNotificationId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "policyNotificationId", + "name": "policyNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse" + } + } + } + } + }, "/organizations/{organizationId}/policy-statistics": { "get": { "security": [ @@ -12869,6 +12984,28 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse": { + "type": "object", + "properties": { + "policyNotification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policyNotifications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse": { "type": "object", "properties": { @@ -13836,6 +13973,53 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse": { + "type": "object", + "properties": { + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" + }, + "createdAt": { + "type": "string" + }, + "grafanaUrl": { + "type": "string" + }, + "id": { + "type": "string" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "rawData": { + "type": "string" + }, + "read": { + "type": "boolean" + }, + "severity": { + "type": "string" + }, + "status": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.PolicyParameter": { "type": "object", "properties": { @@ -15074,6 +15258,9 @@ "rawData": { "type": "string" }, + "read": { + "type": "boolean" + }, "severity": { "type": "string" }, diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index 49f7ed45..947fbb88 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -1851,6 +1851,20 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' type: array type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse: + properties: + policyNotification: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + policyNotifications: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse' + type: array + type: object github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse: properties: policy: @@ -2482,6 +2496,37 @@ definitions: warn: type: integer type: object + github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse: + properties: + cluster: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse' + createdAt: + type: string + grafanaUrl: + type: string + id: + type: string + messageActionProposal: + type: string + messageContent: + type: string + messageTitle: + type: string + notificationType: + type: string + organizationId: + type: string + rawData: + type: string + read: + type: boolean + severity: + type: string + status: + type: string + updatedAt: + type: string + type: object github_com_openinfradev_tks-api_pkg_domain.PolicyParameter: properties: name: @@ -3313,6 +3358,8 @@ definitions: type: integer rawData: type: string + read: + type: boolean severity: type: string status: @@ -7134,6 +7181,80 @@ paths: summary: '[ExistsPolicyName] 정책 아름 존재 여부 확인' tags: - Policy + /organizations/{organizationId}/policy-notifications: + get: + consumes: + - application/json + description: Get PolicyNotifications + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse' + security: + - JWT: [] + summary: Get PolicyNotifications + tags: + - PolicyNotifications + /organizations/{organizationId}/policy-notifications/{policyNotificationId}: + get: + consumes: + - application/json + description: Get PolicyNotification + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: policyNotificationId + in: path + name: policyNotificationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse' + security: + - JWT: [] + summary: Get PolicyNotification + tags: + - PolicyNotifications /organizations/{organizationId}/policy-statistics: get: consumes: diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 3d21b9ec..7ca962bf 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -155,6 +155,9 @@ const ( UpdateSystemNotification CreateSystemNotificationAction + // PolicyNotification + GetPolicyNotifications + // Stack GetStacks // 스택관리/조회 CreateStack // 스택관리/생성 diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index 3fb31f0f..3aa787fb 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -479,6 +479,10 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "CreateSystemNotificationAction", Group: "SystemNotification", }, + GetPolicyNotifications: { + Name: "GetPolicyNotifications", + Group: "PolicyNotification", + }, GetStacks: { Name: "GetStacks", Group: "Stack", @@ -1160,6 +1164,8 @@ func (e Endpoint) String() string { return "UpdateSystemNotification" case CreateSystemNotificationAction: return "CreateSystemNotificationAction" + case GetPolicyNotifications: + return "GetPolicyNotifications" case GetStacks: return "GetStacks" case CreateStack: @@ -1624,6 +1630,8 @@ func GetEndpoint(name string) Endpoint { return UpdateSystemNotification case "CreateSystemNotificationAction": return CreateSystemNotificationAction + case "GetPolicyNotifications": + return GetPolicyNotifications case "GetStacks": return GetStacks case "CreateStack": diff --git a/internal/delivery/http/cloud-account.go b/internal/delivery/http/cloud-account.go index 25e144c3..7aa18a57 100644 --- a/internal/delivery/http/cloud-account.go +++ b/internal/delivery/http/cloud-account.go @@ -3,6 +3,7 @@ package http import ( "fmt" "net/http" + "strconv" "github.com/google/uuid" "github.com/gorilla/mux" @@ -98,6 +99,15 @@ func (h *CloudAccountHandler) GetCloudAccounts(w http.ResponseWriter, r *http.Re urlParams := r.URL.Query() pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.CloudAccountStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } + } + cloudAccounts, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/delivery/http/policy-notification.go b/internal/delivery/http/policy-notification.go new file mode 100644 index 00000000..40122206 --- /dev/null +++ b/internal/delivery/http/policy-notification.go @@ -0,0 +1,123 @@ +package http + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" +) + +type PolicyNotificationHandler struct { + usecase usecase.ISystemNotificationUsecase +} + +func NewPolicyNotificationHandler(h usecase.Usecase) *PolicyNotificationHandler { + return &PolicyNotificationHandler{ + usecase: h.SystemNotification, + } +} + +// GetPolicyNotification godoc +// +// @Tags PolicyNotifications +// @Summary Get PolicyNotifications +// @Description Get PolicyNotifications +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetPolicyNotificationsResponse +// @Router /organizations/{organizationId}/policy-notifications [get] +// @Security JWT +func (h *PolicyNotificationHandler) GetPolicyNotifications(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.SystemNotificationRuleStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } + } + + policyNotifications, err := h.usecase.FetchPolicyNotifications(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetPolicyNotificationsResponse + out.PolicyNotifications = make([]domain.PolicyNotificationResponse, len(policyNotifications)) + for i, policyNotification := range policyNotifications { + if err := serializer.Map(r.Context(), policyNotification, &out.PolicyNotifications[i]); err != nil { + log.Info(r.Context(), err) + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyNotification godoc +// +// @Tags PolicyNotifications +// @Summary Get PolicyNotification +// @Description Get PolicyNotification +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param policyNotificationId path string true "policyNotificationId" +// @Success 200 {object} domain.GetPolicyNotificationResponse +// @Router /organizations/{organizationId}/policy-notifications/{policyNotificationId} [get] +// @Security JWT +func (h *PolicyNotificationHandler) GetPolicyNotification(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["policyNotificationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyNotificationId"), "", "")) + return + } + + policyNotificationId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "", "")) + return + } + + policyNotification, err := h.usecase.Get(r.Context(), policyNotificationId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetPolicyNotificationResponse + if err := serializer.Map(r.Context(), policyNotification, &out.PolicyNotification); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/system-notification-rule.go b/internal/delivery/http/system-notification-rule.go index 0f97dabe..a4f4cfa0 100644 --- a/internal/delivery/http/system-notification-rule.go +++ b/internal/delivery/http/system-notification-rule.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "github.com/google/uuid" "github.com/gorilla/mux" @@ -98,6 +99,15 @@ func (h *SystemNotificationRuleHandler) GetSystemNotificationRules(w http.Respon urlParams := r.URL.Query() pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.SystemNotificationRuleStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } + } + systemNotificationRules, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/delivery/http/system-notification.go b/internal/delivery/http/system-notification.go index 48deeea8..1df9eea4 100644 --- a/internal/delivery/http/system-notification.go +++ b/internal/delivery/http/system-notification.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "strconv" "github.com/google/uuid" "github.com/gorilla/mux" @@ -99,25 +100,16 @@ func (h *SystemNotificationHandler) GetSystemNotifications(w http.ResponseWriter urlParams := r.URL.Query() pg := pagination.NewPagination(&urlParams) - // convert status for i, filter := range pg.GetFilters() { if filter.Column == "status" { for j, value := range filter.Values { - switch value { - case "CREATED": - pg.GetFilters()[i].Values[j] = "0" - case "INPROGRESS": - pg.GetFilters()[i].Values[j] = "1" - case "CLOSED": - pg.GetFilters()[i].Values[j] = "2" - case "ERROR": - pg.GetFilters()[i].Values[j] = "3" - } + var s domain.SystemNotificationActionStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) } } } - systemNotifications, err := h.usecase.Fetch(r.Context(), organizationId, pg) + systemNotifications, err := h.usecase.FetchSystemNotifications(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) return diff --git a/internal/model/system-notification.go b/internal/model/system-notification.go index ca707c70..c33fadf5 100644 --- a/internal/model/system-notification.go +++ b/internal/model/system-notification.go @@ -36,6 +36,8 @@ type SystemNotification struct { Summary string RawData datatypes.JSON Status domain.SystemNotificationActionStatus `gorm:"index"` + Read bool `gorm:"-:all"` + Readers []User `gorm:"many2many:system_notification_users;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT"` } type SystemNotificationAction struct { diff --git a/internal/pagination/pagination.go b/internal/pagination/pagination.go index 43feb12f..4b82ed84 100644 --- a/internal/pagination/pagination.go +++ b/internal/pagination/pagination.go @@ -87,6 +87,15 @@ func (p *Pagination) GetFilters() []Filter { return p.Filters } +func (p *Pagination) GetFilter(key string) *Filter { + for _, filter := range p.Filters { + if filter.Column == key { + return &filter + } + } + return nil +} + func (p *Pagination) AddFilter(f Filter) { p.Filters = append(p.Filters, f) } diff --git a/internal/repository/system-notification.go b/internal/repository/system-notification.go index 2a810edd..7d69a831 100644 --- a/internal/repository/system-notification.go +++ b/internal/repository/system-notification.go @@ -2,28 +2,32 @@ package repository import ( "context" + "fmt" "time" "github.com/google/uuid" "gorm.io/gorm" "gorm.io/gorm/clause" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" ) // Interfaces type ISystemNotificationRepository interface { Get(ctx context.Context, systemNotificationId uuid.UUID) (model.SystemNotification, error) GetByName(ctx context.Context, organizationId string, name string) (model.SystemNotification, error) - Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) FetchPodRestart(ctx context.Context, organizationId string, start time.Time, end time.Time) ([]model.SystemNotification, error) Create(ctx context.Context, dto model.SystemNotification) (systemNotificationId uuid.UUID, err error) Update(ctx context.Context, dto model.SystemNotification) (err error) Delete(ctx context.Context, dto model.SystemNotification) (err error) - CreateSystemNotificationAction(ctx context.Context, dto model.SystemNotificationAction) (systemNotificationActionId uuid.UUID, err error) + UpdateRead(ctx context.Context, systemNotificationId uuid.UUID, user model.User) (err error) } type SystemNotificationRepository struct { @@ -42,6 +46,7 @@ func (r *SystemNotificationRepository) Get(ctx context.Context, systemNotificati if res.Error != nil { return model.SystemNotification{}, res.Error } + return } @@ -53,19 +58,70 @@ func (r *SystemNotificationRepository) GetByName(ctx context.Context, organizati return } -func (r *SystemNotificationRepository) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotification, err error) { +func (r *SystemNotificationRepository) FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return out, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + if pg == nil { pg = pagination.NewPagination(nil) } - _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.SystemNotification{}). + db := r.db.WithContext(ctx).Model(&model.SystemNotification{}). Preload("SystemNotificationActions", func(db *gorm.DB) *gorm.DB { return db.Order("created_at ASC") }).Preload("SystemNotificationActions.Taker"). Preload("Cluster", "status = 2"). Preload("Organization"). Joins("join clusters on clusters.id = system_notifications.cluster_id AND clusters.status = 2"). - Where("system_notifications.organization_id = ?", organizationId), &out) + Where("system_notifications.organization_id = ? AND system_notifications.notification_type = 'SYSTEM_NOTIFICATION'", organizationId) + + readFilter := pg.GetFilter("read") + if readFilter != nil { + if readFilter.Values[0] == "true" { + db.Joins("join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()) + } else { + db.Joins("left outer join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()). + Where("system_notification_users.user_id is null") + } + } + + _, res := pg.Fetch(db, &out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationRepository) FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return out, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + if pg == nil { + pg = pagination.NewPagination(nil) + } + + db := r.db.WithContext(ctx).Model(&model.SystemNotification{}). + Preload("Cluster", "status = 2"). + Preload("Organization"). + Joins("join clusters on clusters.id = system_notifications.cluster_id AND clusters.status = 2"). + Where("system_notifications.organization_id = ? AND system_notifications.notification_type = 'POLICY_NOTIFICATION'", organizationId) + + readFilter := pg.GetFilter("read") + if readFilter != nil { + if readFilter.Values[0] == "true" { + db.Joins("join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()) + } else { + db.Joins("left outer join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()). + Where("system_notification_users.user_id is null") + } + } + + _, res := pg.Fetch(db, &out) if res.Error != nil { return nil, res.Error @@ -147,3 +203,18 @@ func (r *SystemNotificationRepository) CreateSystemNotificationAction(ctx contex return systemNotification.ID, nil } + +func (r *SystemNotificationRepository) UpdateRead(ctx context.Context, systemNotificationId uuid.UUID, user model.User) (err error) { + var systemNotification = model.SystemNotification{} + res := r.db.WithContext(ctx).First(&systemNotification, "id = ?", systemNotificationId) + if res.Error != nil { + return res.Error + } + + users := []model.User{user} + err = r.db.WithContext(ctx).Model(&systemNotification).Association("Readers").Append(users) + if err != nil { + return err + } + return nil +} diff --git a/internal/route/route.go b/internal/route/route.go index 63aab31f..cc71e4da 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -217,15 +217,6 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/{dashboardKey}", customMiddleware.Handle(internalApi.GetDashboard, http.HandlerFunc(dashboardHandler.GetDashboard))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/{dashboardKey}", customMiddleware.Handle(internalApi.UpdateDashboard, http.HandlerFunc(dashboardHandler.UpdateDashboard))).Methods(http.MethodPut) - systemNotificationHandler := delivery.NewSystemNotificationHandler(usecaseFactory) - r.HandleFunc(SYSTEM_API_PREFIX+SYSTEM_API_VERSION+"/system-notifications", systemNotificationHandler.CreateSystemNotification).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications", customMiddleware.Handle(internalApi.GetSystemNotifications, http.HandlerFunc(systemNotificationHandler.GetSystemNotifications))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.GetSystemNotification, http.HandlerFunc(systemNotificationHandler.GetSystemNotification))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.DeleteSystemNotification, http.HandlerFunc(systemNotificationHandler.DeleteSystemNotification))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.UpdateSystemNotification, http.HandlerFunc(systemNotificationHandler.UpdateSystemNotification))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}/actions", customMiddleware.Handle(internalApi.CreateSystemNotificationAction, http.HandlerFunc(systemNotificationHandler.CreateSystemNotificationAction))).Methods(http.MethodPost) - r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", systemNotificationHandler.CreateSystemNotification).Methods(http.MethodPost) - systemNotificationTemplateHandler := delivery.NewSystemNotificationTemplateHandler(usecaseFactory) r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates", customMiddleware.Handle(internalApi.Admin_CreateSystemNotificationTemplate, http.HandlerFunc(systemNotificationTemplateHandler.CreateSystemNotificationTemplate))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates", customMiddleware.Handle(internalApi.Admin_GetSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.GetSystemNotificationTemplates))).Methods(http.MethodGet) @@ -247,6 +238,18 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.UpdateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.UpdateSystemNotificationRule))).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.DeleteSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.DeleteSystemNotificationRule))).Methods(http.MethodDelete) + systemNotificationHandler := delivery.NewSystemNotificationHandler(usecaseFactory) + r.HandleFunc(SYSTEM_API_PREFIX+SYSTEM_API_VERSION+"/system-notifications", systemNotificationHandler.CreateSystemNotification).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications", customMiddleware.Handle(internalApi.GetSystemNotifications, http.HandlerFunc(systemNotificationHandler.GetSystemNotifications))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.GetSystemNotification, http.HandlerFunc(systemNotificationHandler.GetSystemNotification))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.DeleteSystemNotification, http.HandlerFunc(systemNotificationHandler.DeleteSystemNotification))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.UpdateSystemNotification, http.HandlerFunc(systemNotificationHandler.UpdateSystemNotification))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}/actions", customMiddleware.Handle(internalApi.CreateSystemNotificationAction, http.HandlerFunc(systemNotificationHandler.CreateSystemNotificationAction))).Methods(http.MethodPost) + r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", systemNotificationHandler.CreateSystemNotification).Methods(http.MethodPost) + + policyNotificationHandler := delivery.NewPolicyNotificationHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-notifications", customMiddleware.Handle(internalApi.GetSystemNotifications, http.HandlerFunc(policyNotificationHandler.GetPolicyNotifications))).Methods(http.MethodGet) + stackHandler := delivery.NewStackHandler(usecaseFactory) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", customMiddleware.Handle(internalApi.GetStacks, http.HandlerFunc(stackHandler.GetStacks))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", customMiddleware.Handle(internalApi.CreateStack, http.HandlerFunc(stackHandler.CreateStack))).Methods(http.MethodPost) diff --git a/internal/usecase/system-notification.go b/internal/usecase/system-notification.go index 0d3f984e..30b389be 100644 --- a/internal/usecase/system-notification.go +++ b/internal/usecase/system-notification.go @@ -23,7 +23,8 @@ import ( type ISystemNotificationUsecase interface { Get(ctx context.Context, systemNotificationId uuid.UUID) (model.SystemNotification, error) GetByName(ctx context.Context, organizationId string, name string) (model.SystemNotification, error) - Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) Create(ctx context.Context, dto domain.CreateSystemNotificationRequest) (err error) Update(ctx context.Context, dto model.SystemNotification) error Delete(ctx context.Context, dto model.SystemNotification) error @@ -37,6 +38,7 @@ type SystemNotificationUsecase struct { organizationRepo repository.IOrganizationRepository appGroupRepo repository.IAppGroupRepository systemNotificationRuleRepo repository.ISystemNotificationRuleRepository + userRepo repository.IUserRepository } func NewSystemNotificationUsecase(r repository.Repository) ISystemNotificationUsecase { @@ -46,6 +48,7 @@ func NewSystemNotificationUsecase(r repository.Repository) ISystemNotificationUs appGroupRepo: r.AppGroup, organizationRepo: r.Organization, systemNotificationRuleRepo: r.SystemNotificationRule, + userRepo: r.User, } } @@ -165,11 +168,24 @@ func (u *SystemNotificationUsecase) Update(ctx context.Context, dto model.System } func (u *SystemNotificationUsecase) Get(ctx context.Context, systemNotificationId uuid.UUID) (systemNotification model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return systemNotification, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + systemNotification, err = u.repo.Get(ctx, systemNotificationId) if err != nil { return systemNotification, err } - u.makeAdditionalInfo(&systemNotification) + u.makeAdditionalInfo(&systemNotification, userInfo.GetUserId()) + + user, err := u.userRepo.GetByUuid(ctx, userInfo.GetUserId()) + if err == nil { + err = u.repo.UpdateRead(ctx, systemNotificationId, user) + if err != nil { + return systemNotification, err + } + } return } @@ -185,14 +201,37 @@ func (u *SystemNotificationUsecase) GetByName(ctx context.Context, organizationI return } -func (u *SystemNotificationUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (systemNotifications []model.SystemNotification, err error) { - systemNotifications, err = u.repo.Fetch(ctx, organizationId, pg) +func (u *SystemNotificationUsecase) FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (systemNotifications []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return systemNotifications, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + systemNotifications, err = u.repo.FetchSystemNotifications(ctx, organizationId, pg) + if err != nil { + return nil, err + } + + for i := range systemNotifications { + u.makeAdditionalInfo(&systemNotifications[i], userInfo.GetUserId()) + } + + return systemNotifications, nil +} + +func (u *SystemNotificationUsecase) FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (systemNotifications []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return systemNotifications, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + systemNotifications, err = u.repo.FetchPolicyNotifications(ctx, organizationId, pg) if err != nil { return nil, err } for i := range systemNotifications { - u.makeAdditionalInfo(&systemNotifications[i]) + u.makeAdditionalInfo(&systemNotifications[i], userInfo.GetUserId()) } return systemNotifications, nil @@ -256,7 +295,8 @@ func (u *SystemNotificationUsecase) getOrganizationFromCluster(clusters *[]model return "", fmt.Errorf("No martched organization %s", strId) } -func (u *SystemNotificationUsecase) makeAdditionalInfo(systemNotification *model.SystemNotification) { +func (u *SystemNotificationUsecase) makeAdditionalInfo(systemNotification *model.SystemNotification, userId uuid.UUID) { + systemNotification.FiredAt = &systemNotification.CreatedAt //systemNotification.Status = model.SystemNotificationActionStatus_CREATED @@ -273,6 +313,14 @@ func (u *SystemNotificationUsecase) makeAdditionalInfo(systemNotification *model systemNotification.TakedSec = int((systemNotification.SystemNotificationActions[0].CreatedAt).Sub(systemNotification.CreatedAt).Seconds()) //systemNotification.Status = systemNotification.SystemNotificationActions[len(systemNotification.SystemNotificationActions)-1].Status } + + systemNotification.Read = false + for _, v := range systemNotification.Readers { + if v.ID == userId { + systemNotification.Read = true + break + } + } } func (u *SystemNotificationUsecase) makeGrafanaUrl(ctx context.Context, primaryCluster model.Cluster, systemNotification domain.SystemNotificationRequest, clusterId domain.ClusterId) (url string) { diff --git a/pkg/domain/policy-notification.go b/pkg/domain/policy-notification.go new file mode 100644 index 00000000..50467266 --- /dev/null +++ b/pkg/domain/policy-notification.go @@ -0,0 +1,31 @@ +package domain + +import ( + "time" +) + +type PolicyNotificationResponse struct { + ID string `json:"id"` + OrganizationId string `json:"organizationId"` + Severity string `json:"severity"` + MessageTitle string `json:"messageTitle"` + MessageContent string `json:"messageContent"` + MessageActionProposal string `json:"messageActionProposal"` + Cluster SimpleClusterResponse `json:"cluster"` + GrafanaUrl string `json:"grafanaUrl"` + Status string `json:"status"` + RawData string `json:"rawData"` + NotificationType string `json:"notificationType"` + Read bool `json:"read"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetPolicyNotificationsResponse struct { + PolicyNotifications []PolicyNotificationResponse `json:"policyNotifications"` + Pagination PaginationResponse `json:"pagination"` +} + +type GetPolicyNotificationResponse struct { + PolicyNotification PolicyNotificationResponse `json:"policyNotification"` +} diff --git a/pkg/domain/stack.go b/pkg/domain/stack.go index 37f9de81..8e4c8c79 100644 --- a/pkg/domain/stack.go +++ b/pkg/domain/stack.go @@ -66,7 +66,7 @@ func (m StackStatus) FromString(s string) StackStatus { } const MAX_STEP_CLUSTER_CREATE = 26 -const MAX_STEP_CLUSTER_REMOVE = 14 +const MAX_STEP_CLUSTER_REMOVE = 16 const MAX_STEP_LMA_CREATE_PRIMARY = 39 const MAX_STEP_LMA_CREATE_MEMBER = 29 const MAX_STEP_LMA_REMOVE = 12 diff --git a/pkg/domain/system-notification.go b/pkg/domain/system-notification.go index f95868ef..8c2f63f1 100644 --- a/pkg/domain/system-notification.go +++ b/pkg/domain/system-notification.go @@ -98,6 +98,7 @@ type SystemNotificationResponse struct { LastTaker SimpleUserResponse `json:"lastTaker"` RawData string `json:"rawData"` NotificationType string `json:"notificationType"` + Read bool `json:"read"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` }