From 444c7531f275c3cd45614d94f9b100c5068ecdc3 Mon Sep 17 00:00:00 2001 From: Krisztian Gacsal Date: Thu, 15 Aug 2024 14:59:40 +0200 Subject: [PATCH] refactor(api): notification events sending Allow sending notification events for testing rules only. --- api/api.gen.go | 410 +++++------ api/client/go/client.gen.go | 673 +++++++++--------- api/openapi.yaml | 97 +-- internal/ent/db/migrate/schema.go | 3 +- internal/ent/db/mutation.go | 80 ++- internal/ent/db/notificationevent.go | 13 + .../db/notificationevent/notificationevent.go | 13 + internal/ent/db/notificationevent/where.go | 10 + internal/ent/db/notificationevent_create.go | 88 ++- internal/ent/db/notificationevent_update.go | 44 ++ internal/ent/db/predicate/predicate.go | 11 + internal/ent/db/runtime.go | 3 + internal/ent/schema/notification.go | 30 + internal/notification/annotations.go | 8 + internal/notification/event.go | 3 + internal/notification/httpdriver/event.go | 66 -- internal/notification/httpdriver/handler.go | 2 +- internal/notification/httpdriver/rule.go | 69 ++ internal/notification/internal/rule.go | 90 +++ .../notification/repository/repository.go | 3 +- internal/server/router/notification.go | 22 +- openmeter/notification/httpdriver/alias.go | 14 +- 22 files changed, 1053 insertions(+), 699 deletions(-) create mode 100644 internal/notification/annotations.go create mode 100644 internal/notification/internal/rule.go diff --git a/api/api.gen.go b/api/api.gen.go index 9c3936ae9..ed4473260 100644 --- a/api/api.gen.go +++ b/api/api.gen.go @@ -1034,6 +1034,9 @@ type NotificationChannelsResponse struct { // // The `payload` field contains the actual message sent to the notification channel. type NotificationEvent struct { + // Annotations List of annotations managed by the system. + Annotations *map[string]string `json:"annotations,omitempty"` + // CreatedAt Timestamp when the notification event was created. CreatedAt time.Time `json:"createdAt"` @@ -1091,16 +1094,6 @@ type NotificationEventCommonPayload struct { Type NotificationEventType `json:"type"` } -// NotificationEventCreateRequest Request for creating new notification event with specific type and payload. -type NotificationEventCreateRequest struct { - // Payload The actual payload sent to channel as part of the notification event. - Payload NotificationEventPayload `json:"payload"` - RuleId string `json:"ruleId"` - - // Type The type of the notification event. - Type NotificationEventType `json:"type"` -} - // NotificationEventDeliveryStatus defines model for NotificationEventDeliveryStatus. type NotificationEventDeliveryStatus struct { // Channel Limited representation of notification channel which includes only the channel identifier and its type. @@ -1900,9 +1893,6 @@ type CreateNotificationChannelJSONRequestBody = NotificationChannelCreateRequest // UpdateNotificationChannelJSONRequestBody defines body for UpdateNotificationChannel for application/json ContentType. type UpdateNotificationChannelJSONRequestBody = NotificationChannelCreateRequest -// CreateNotificationEventJSONRequestBody defines body for CreateNotificationEvent for application/json ContentType. -type CreateNotificationEventJSONRequestBody = NotificationEventCreateRequest - // CreateNotificationRuleJSONRequestBody defines body for CreateNotificationRule for application/json ContentType. type CreateNotificationRuleJSONRequestBody = NotificationRuleCreateRequest @@ -2920,9 +2910,6 @@ type ServerInterface interface { // List notification evens // (GET /api/v1/notification/events) ListNotificationEvents(w http.ResponseWriter, r *http.Request, params ListNotificationEventsParams) - // Create a notification event - // (POST /api/v1/notification/events) - CreateNotificationEvent(w http.ResponseWriter, r *http.Request) // Get notification event // (GET /api/v1/notification/events/{eventId}) GetNotificationEvent(w http.ResponseWriter, r *http.Request, eventId EventId) @@ -2941,6 +2928,9 @@ type ServerInterface interface { // Update a notification rule // (PUT /api/v1/notification/rules/{ruleId}) UpdateNotificationRule(w http.ResponseWriter, r *http.Request, ruleId RuleId) + // Test notification rule + // (POST /api/v1/notification/rules/{ruleId}/test) + TestNotificationRule(w http.ResponseWriter, r *http.Request, ruleId RuleId) // Receive Svix operational events // (POST /api/v1/notification/webhook/svix) ReceiveSvixOperationalEvent(w http.ResponseWriter, r *http.Request) @@ -3142,12 +3132,6 @@ func (_ Unimplemented) ListNotificationEvents(w http.ResponseWriter, r *http.Req w.WriteHeader(http.StatusNotImplemented) } -// Create a notification event -// (POST /api/v1/notification/events) -func (_ Unimplemented) CreateNotificationEvent(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - // Get notification event // (GET /api/v1/notification/events/{eventId}) func (_ Unimplemented) GetNotificationEvent(w http.ResponseWriter, r *http.Request, eventId EventId) { @@ -3184,6 +3168,12 @@ func (_ Unimplemented) UpdateNotificationRule(w http.ResponseWriter, r *http.Req w.WriteHeader(http.StatusNotImplemented) } +// Test notification rule +// (POST /api/v1/notification/rules/{ruleId}/test) +func (_ Unimplemented) TestNotificationRule(w http.ResponseWriter, r *http.Request, ruleId RuleId) { + w.WriteHeader(http.StatusNotImplemented) +} + // Receive Svix operational events // (POST /api/v1/notification/webhook/svix) func (_ Unimplemented) ReceiveSvixOperationalEvent(w http.ResponseWriter, r *http.Request) { @@ -4298,25 +4288,6 @@ func (siw *ServerInterfaceWrapper) ListNotificationEvents(w http.ResponseWriter, handler.ServeHTTP(w, r.WithContext(ctx)) } -// CreateNotificationEvent operation middleware -func (siw *ServerInterfaceWrapper) CreateNotificationEvent(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) - - ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.CreateNotificationEvent(w, r) - })) - - for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { - handler = siw.HandlerMiddlewares[i](handler) - } - - handler.ServeHTTP(w, r.WithContext(ctx)) -} - // GetNotificationEvent operation middleware func (siw *ServerInterfaceWrapper) GetNotificationEvent(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -4544,6 +4515,36 @@ func (siw *ServerInterfaceWrapper) UpdateNotificationRule(w http.ResponseWriter, handler.ServeHTTP(w, r.WithContext(ctx)) } +// TestNotificationRule operation middleware +func (siw *ServerInterfaceWrapper) TestNotificationRule(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "ruleId" ------------- + var ruleId RuleId + + err = runtime.BindStyledParameterWithOptions("simple", "ruleId", chi.URLParam(r, "ruleId"), &ruleId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "ruleId", Err: err}) + return + } + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.TestNotificationRule(w, r, ruleId) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // ReceiveSvixOperationalEvent operation middleware func (siw *ServerInterfaceWrapper) ReceiveSvixOperationalEvent(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5449,9 +5450,6 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/notification/events", wrapper.ListNotificationEvents) }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/api/v1/notification/events", wrapper.CreateNotificationEvent) - }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/notification/events/{eventId}", wrapper.GetNotificationEvent) }) @@ -5470,6 +5468,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Put(options.BaseURL+"/api/v1/notification/rules/{ruleId}", wrapper.UpdateNotificationRule) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/api/v1/notification/rules/{ruleId}/test", wrapper.TestNotificationRule) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/api/v1/notification/webhook/svix", wrapper.ReceiveSvixOperationalEvent) }) @@ -5669,166 +5670,167 @@ var swaggerSpec = []string{ "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", + "FxZVAv/0/tP/FwAA//+yFlpZzJ0BAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/client/go/client.gen.go b/api/client/go/client.gen.go index ffbb0dff5..e1c420094 100644 --- a/api/client/go/client.gen.go +++ b/api/client/go/client.gen.go @@ -1034,6 +1034,9 @@ type NotificationChannelsResponse struct { // // The `payload` field contains the actual message sent to the notification channel. type NotificationEvent struct { + // Annotations List of annotations managed by the system. + Annotations *map[string]string `json:"annotations,omitempty"` + // CreatedAt Timestamp when the notification event was created. CreatedAt time.Time `json:"createdAt"` @@ -1091,16 +1094,6 @@ type NotificationEventCommonPayload struct { Type NotificationEventType `json:"type"` } -// NotificationEventCreateRequest Request for creating new notification event with specific type and payload. -type NotificationEventCreateRequest struct { - // Payload The actual payload sent to channel as part of the notification event. - Payload NotificationEventPayload `json:"payload"` - RuleId string `json:"ruleId"` - - // Type The type of the notification event. - Type NotificationEventType `json:"type"` -} - // NotificationEventDeliveryStatus defines model for NotificationEventDeliveryStatus. type NotificationEventDeliveryStatus struct { // Channel Limited representation of notification channel which includes only the channel identifier and its type. @@ -1900,9 +1893,6 @@ type CreateNotificationChannelJSONRequestBody = NotificationChannelCreateRequest // UpdateNotificationChannelJSONRequestBody defines body for UpdateNotificationChannel for application/json ContentType. type UpdateNotificationChannelJSONRequestBody = NotificationChannelCreateRequest -// CreateNotificationEventJSONRequestBody defines body for CreateNotificationEvent for application/json ContentType. -type CreateNotificationEventJSONRequestBody = NotificationEventCreateRequest - // CreateNotificationRuleJSONRequestBody defines body for CreateNotificationRule for application/json ContentType. type CreateNotificationRuleJSONRequestBody = NotificationRuleCreateRequest @@ -3003,11 +2993,6 @@ type ClientInterface interface { // ListNotificationEvents request ListNotificationEvents(ctx context.Context, params *ListNotificationEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreateNotificationEventWithBody request with any body - CreateNotificationEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - CreateNotificationEvent(ctx context.Context, body CreateNotificationEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetNotificationEvent request GetNotificationEvent(ctx context.Context, eventId EventId, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3030,6 +3015,9 @@ type ClientInterface interface { UpdateNotificationRule(ctx context.Context, ruleId RuleId, body UpdateNotificationRuleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // TestNotificationRule request + TestNotificationRule(ctx context.Context, ruleId RuleId, reqEditors ...RequestEditorFn) (*http.Response, error) + // ReceiveSvixOperationalEventWithBody request with any body ReceiveSvixOperationalEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3452,30 +3440,6 @@ func (c *Client) ListNotificationEvents(ctx context.Context, params *ListNotific return c.Client.Do(req) } -func (c *Client) CreateNotificationEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateNotificationEventRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) CreateNotificationEvent(ctx context.Context, body CreateNotificationEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateNotificationEventRequest(c.Server, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - func (c *Client) GetNotificationEvent(ctx context.Context, eventId EventId, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetNotificationEventRequest(c.Server, eventId) if err != nil { @@ -3572,6 +3536,18 @@ func (c *Client) UpdateNotificationRule(ctx context.Context, ruleId RuleId, body return c.Client.Do(req) } +func (c *Client) TestNotificationRule(ctx context.Context, ruleId RuleId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewTestNotificationRuleRequest(c.Server, ruleId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) ReceiveSvixOperationalEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewReceiveSvixOperationalEventRequestWithBody(c.Server, contentType, body) if err != nil { @@ -5512,46 +5488,6 @@ func NewListNotificationEventsRequest(server string, params *ListNotificationEve return req, nil } -// NewCreateNotificationEventRequest calls the generic CreateNotificationEvent builder with application/json body -func NewCreateNotificationEventRequest(server string, body CreateNotificationEventJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewCreateNotificationEventRequestWithBody(server, "application/json", bodyReader) -} - -// NewCreateNotificationEventRequestWithBody generates requests for CreateNotificationEvent with any type of body -func NewCreateNotificationEventRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/api/v1/notification/events") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - // NewGetNotificationEventRequest generates requests for GetNotificationEvent func NewGetNotificationEventRequest(server string, eventId EventId) (*http.Request, error) { var err error @@ -5902,6 +5838,40 @@ func NewUpdateNotificationRuleRequestWithBody(server string, ruleId RuleId, cont return req, nil } +// NewTestNotificationRuleRequest generates requests for TestNotificationRule +func NewTestNotificationRuleRequest(server string, ruleId RuleId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "ruleId", runtime.ParamLocationPath, ruleId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/notification/rules/%s/test", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewReceiveSvixOperationalEventRequest calls the generic ReceiveSvixOperationalEvent builder with application/json body func NewReceiveSvixOperationalEventRequest(server string, body ReceiveSvixOperationalEventJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -7062,11 +7032,6 @@ type ClientWithResponsesInterface interface { // ListNotificationEventsWithResponse request ListNotificationEventsWithResponse(ctx context.Context, params *ListNotificationEventsParams, reqEditors ...RequestEditorFn) (*ListNotificationEventsResponse, error) - // CreateNotificationEventWithBodyWithResponse request with any body - CreateNotificationEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateNotificationEventResponse, error) - - CreateNotificationEventWithResponse(ctx context.Context, body CreateNotificationEventJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateNotificationEventResponse, error) - // GetNotificationEventWithResponse request GetNotificationEventWithResponse(ctx context.Context, eventId EventId, reqEditors ...RequestEditorFn) (*GetNotificationEventResponse, error) @@ -7089,6 +7054,9 @@ type ClientWithResponsesInterface interface { UpdateNotificationRuleWithResponse(ctx context.Context, ruleId RuleId, body UpdateNotificationRuleJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateNotificationRuleResponse, error) + // TestNotificationRuleWithResponse request + TestNotificationRuleWithResponse(ctx context.Context, ruleId RuleId, reqEditors ...RequestEditorFn) (*TestNotificationRuleResponse, error) + // ReceiveSvixOperationalEventWithBodyWithResponse request with any body ReceiveSvixOperationalEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ReceiveSvixOperationalEventResponse, error) @@ -7736,35 +7704,6 @@ func (r ListNotificationEventsResponse) StatusCode() int { return 0 } -type CreateNotificationEventResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *struct { - union json.RawMessage - } - ApplicationproblemJSON400 *BadRequestProblemResponse - ApplicationproblemJSON401 *UnauthorizedProblemResponse - ApplicationproblemJSON409 *ConflictProblemResponse - ApplicationproblemJSON501 *NotImplementedProblemResponse - ApplicationproblemJSONDefault *UnexpectedProblemResponse -} - -// Status returns HTTPResponse.Status -func (r CreateNotificationEventResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r CreateNotificationEventResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - type GetNotificationEventResponse struct { Body []byte HTTPResponse *http.Response @@ -7917,6 +7856,35 @@ func (r UpdateNotificationRuleResponse) StatusCode() int { return 0 } +type TestNotificationRuleResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *struct { + union json.RawMessage + } + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSON409 *ConflictProblemResponse + ApplicationproblemJSON501 *NotImplementedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r TestNotificationRuleResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r TestNotificationRuleResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type ReceiveSvixOperationalEventResponse struct { Body []byte HTTPResponse *http.Response @@ -8658,23 +8626,6 @@ func (c *ClientWithResponses) ListNotificationEventsWithResponse(ctx context.Con return ParseListNotificationEventsResponse(rsp) } -// CreateNotificationEventWithBodyWithResponse request with arbitrary body returning *CreateNotificationEventResponse -func (c *ClientWithResponses) CreateNotificationEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateNotificationEventResponse, error) { - rsp, err := c.CreateNotificationEventWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseCreateNotificationEventResponse(rsp) -} - -func (c *ClientWithResponses) CreateNotificationEventWithResponse(ctx context.Context, body CreateNotificationEventJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateNotificationEventResponse, error) { - rsp, err := c.CreateNotificationEvent(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseCreateNotificationEventResponse(rsp) -} - // GetNotificationEventWithResponse request returning *GetNotificationEventResponse func (c *ClientWithResponses) GetNotificationEventWithResponse(ctx context.Context, eventId EventId, reqEditors ...RequestEditorFn) (*GetNotificationEventResponse, error) { rsp, err := c.GetNotificationEvent(ctx, eventId, reqEditors...) @@ -8745,6 +8696,15 @@ func (c *ClientWithResponses) UpdateNotificationRuleWithResponse(ctx context.Con return ParseUpdateNotificationRuleResponse(rsp) } +// TestNotificationRuleWithResponse request returning *TestNotificationRuleResponse +func (c *ClientWithResponses) TestNotificationRuleWithResponse(ctx context.Context, ruleId RuleId, reqEditors ...RequestEditorFn) (*TestNotificationRuleResponse, error) { + rsp, err := c.TestNotificationRule(ctx, ruleId, reqEditors...) + if err != nil { + return nil, err + } + return ParseTestNotificationRuleResponse(rsp) +} + // ReceiveSvixOperationalEventWithBodyWithResponse request with arbitrary body returning *ReceiveSvixOperationalEventResponse func (c *ClientWithResponses) ReceiveSvixOperationalEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ReceiveSvixOperationalEventResponse, error) { rsp, err := c.ReceiveSvixOperationalEventWithBody(ctx, contentType, body, reqEditors...) @@ -10026,69 +9986,6 @@ func ParseListNotificationEventsResponse(rsp *http.Response) (*ListNotificationE return response, nil } -// ParseCreateNotificationEventResponse parses an HTTP response from a CreateNotificationEventWithResponse call -func ParseCreateNotificationEventResponse(rsp *http.Response) (*CreateNotificationEventResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &CreateNotificationEventResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest struct { - union json.RawMessage - } - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestProblemResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.ApplicationproblemJSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: - var dest UnauthorizedProblemResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.ApplicationproblemJSON401 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ConflictProblemResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.ApplicationproblemJSON409 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 501: - var dest NotImplementedProblemResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.ApplicationproblemJSON501 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: - var dest UnexpectedProblemResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.ApplicationproblemJSONDefault = &dest - - } - - return response, nil -} - // ParseGetNotificationEventResponse parses an HTTP response from a GetNotificationEventWithResponse call func ParseGetNotificationEventResponse(rsp *http.Response) (*GetNotificationEventResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -10373,6 +10270,69 @@ func ParseUpdateNotificationRuleResponse(rsp *http.Response) (*UpdateNotificatio return response, nil } +// ParseTestNotificationRuleResponse parses an HTTP response from a TestNotificationRuleWithResponse call +func ParseTestNotificationRuleResponse(rsp *http.Response) (*TestNotificationRuleResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &TestNotificationRuleResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest struct { + union json.RawMessage + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest ConflictProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 501: + var dest NotImplementedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON501 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + // ParseReceiveSvixOperationalEventResponse parses an HTTP response from a ReceiveSvixOperationalEventWithResponse call func ParseReceiveSvixOperationalEventResponse(rsp *http.Response) (*ReceiveSvixOperationalEventResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -11460,166 +11420,167 @@ var swaggerSpec = []string{ "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", + "FxZVAv/0/tP/FwAA//+yFlpZzJ0BAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/openapi.yaml b/api/openapi.yaml index 0ea95a7a6..12a30ae20 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1599,6 +1599,35 @@ paths: default: $ref: "#/components/responses/UnexpectedProblemResponse" + /api/v1/notification/rules/{ruleId}/test: + post: + operationId: testNotificationRule + summary: Test notification rule + description: | + Test a notification rule by sending a test event with random data. + tags: + - Notification (Experimental) + parameters: + - $ref: "#/components/parameters/ruleId" + responses: + "201": + description: Test notification event sent. + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/NotificationEvent" + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + "409": + $ref: "#/components/responses/ConflictProblemResponse" + "501": + $ref: "#/components/responses/NotImplementedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" + # Notifications - Events /api/v1/notification/events: @@ -1640,38 +1669,6 @@ paths: $ref: "#/components/responses/UnauthorizedProblemResponse" default: $ref: "#/components/responses/UnexpectedProblemResponse" - post: - operationId: createNotificationEvent - summary: Create a notification event - description: | - Create a new notification event. - tags: - - Notifications (Experimental) - requestBody: - description: The notification event to create. - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NotificationEventCreateRequest" - responses: - "201": - description: Notification event created. - content: - application/json: - schema: - oneOf: - - $ref: "#/components/schemas/NotificationEvent" - "400": - $ref: "#/components/responses/BadRequestProblemResponse" - "401": - $ref: "#/components/responses/UnauthorizedProblemResponse" - "409": - $ref: "#/components/responses/ConflictProblemResponse" - "501": - $ref: "#/components/responses/NotImplementedProblemResponse" - default: - $ref: "#/components/responses/UnexpectedProblemResponse" /api/v1/notification/events/{eventId}: get: @@ -3252,20 +3249,20 @@ components: - "01ARZ3NDEKTSV4RRFFQ69G5FAV" NotificationRuleBalanceThresholdValue: - description: Threshold value with multiple supported types. - type: object - required: - - value - - type - properties: - value: - type: number - format: double - type: - type: string - enum: - - NUMBER - - PERCENT + description: Threshold value with multiple supported types. + type: object + required: + - value + - type + properties: + value: + type: number + format: double + type: + type: string + enum: + - NUMBER + - PERCENT NotificationRuleBalanceThreshold: description: | @@ -3440,6 +3437,14 @@ components: $ref: "#/components/schemas/NotificationEventDeliveryStatus" payload: $ref: "#/components/schemas/NotificationEventPayload" + annotations: + description: | + List of annotations managed by the system. + type: object + additionalProperties: + type: string + example: + test-event: "true" NotificationEventPayload: description: The actual payload sent to channel as part of the notification event. diff --git a/internal/ent/db/migrate/schema.go b/internal/ent/db/migrate/schema.go index 064f075b7..13ca274ff 100644 --- a/internal/ent/db/migrate/schema.go +++ b/internal/ent/db/migrate/schema.go @@ -246,6 +246,7 @@ var ( {Name: "created_at", Type: field.TypeTime}, {Name: "type", Type: field.TypeEnum, Enums: []string{"entitlements.balance.threshold"}}, {Name: "payload", Type: field.TypeString, SchemaType: map[string]string{"postgres": "jsonb"}}, + {Name: "annotations", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "rule_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, } // NotificationEventsTable holds the schema information for the "notification_events" table. @@ -256,7 +257,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "notification_events_notification_rules_events", - Columns: []*schema.Column{NotificationEventsColumns[5]}, + Columns: []*schema.Column{NotificationEventsColumns[6]}, RefColumns: []*schema.Column{NotificationRulesColumns[0]}, OnDelete: schema.NoAction, }, diff --git a/internal/ent/db/mutation.go b/internal/ent/db/mutation.go index 7dc7c2e9a..c50784c5a 100644 --- a/internal/ent/db/mutation.go +++ b/internal/ent/db/mutation.go @@ -6196,6 +6196,7 @@ type NotificationEventMutation struct { created_at *time.Time _type *notification.EventType payload *string + annotations *map[string]interface{} clearedFields map[string]struct{} delivery_statuses map[string]struct{} removeddelivery_statuses map[string]struct{} @@ -6491,6 +6492,55 @@ func (m *NotificationEventMutation) ResetPayload() { m.payload = nil } +// SetAnnotations sets the "annotations" field. +func (m *NotificationEventMutation) SetAnnotations(value map[string]interface{}) { + m.annotations = &value +} + +// Annotations returns the value of the "annotations" field in the mutation. +func (m *NotificationEventMutation) Annotations() (r map[string]interface{}, exists bool) { + v := m.annotations + if v == nil { + return + } + return *v, true +} + +// OldAnnotations returns the old "annotations" field's value of the NotificationEvent entity. +// If the NotificationEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationEventMutation) OldAnnotations(ctx context.Context) (v map[string]interface{}, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAnnotations requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) + } + return oldValue.Annotations, nil +} + +// ClearAnnotations clears the value of the "annotations" field. +func (m *NotificationEventMutation) ClearAnnotations() { + m.annotations = nil + m.clearedFields[notificationevent.FieldAnnotations] = struct{}{} +} + +// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. +func (m *NotificationEventMutation) AnnotationsCleared() bool { + _, ok := m.clearedFields[notificationevent.FieldAnnotations] + return ok +} + +// ResetAnnotations resets all changes to the "annotations" field. +func (m *NotificationEventMutation) ResetAnnotations() { + m.annotations = nil + delete(m.clearedFields, notificationevent.FieldAnnotations) +} + // AddDeliveryStatusIDs adds the "delivery_statuses" edge to the NotificationEventDeliveryStatus entity by ids. func (m *NotificationEventMutation) AddDeliveryStatusIDs(ids ...string) { if m.delivery_statuses == nil { @@ -6619,7 +6669,7 @@ func (m *NotificationEventMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *NotificationEventMutation) Fields() []string { - fields := make([]string, 0, 5) + fields := make([]string, 0, 6) if m.namespace != nil { fields = append(fields, notificationevent.FieldNamespace) } @@ -6635,6 +6685,9 @@ func (m *NotificationEventMutation) Fields() []string { if m.payload != nil { fields = append(fields, notificationevent.FieldPayload) } + if m.annotations != nil { + fields = append(fields, notificationevent.FieldAnnotations) + } return fields } @@ -6653,6 +6706,8 @@ func (m *NotificationEventMutation) Field(name string) (ent.Value, bool) { return m.RuleID() case notificationevent.FieldPayload: return m.Payload() + case notificationevent.FieldAnnotations: + return m.Annotations() } return nil, false } @@ -6672,6 +6727,8 @@ func (m *NotificationEventMutation) OldField(ctx context.Context, name string) ( return m.OldRuleID(ctx) case notificationevent.FieldPayload: return m.OldPayload(ctx) + case notificationevent.FieldAnnotations: + return m.OldAnnotations(ctx) } return nil, fmt.Errorf("unknown NotificationEvent field %s", name) } @@ -6716,6 +6773,13 @@ func (m *NotificationEventMutation) SetField(name string, value ent.Value) error } m.SetPayload(v) return nil + case notificationevent.FieldAnnotations: + v, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAnnotations(v) + return nil } return fmt.Errorf("unknown NotificationEvent field %s", name) } @@ -6745,7 +6809,11 @@ func (m *NotificationEventMutation) AddField(name string, value ent.Value) error // ClearedFields returns all nullable fields that were cleared during this // mutation. func (m *NotificationEventMutation) ClearedFields() []string { - return nil + var fields []string + if m.FieldCleared(notificationevent.FieldAnnotations) { + fields = append(fields, notificationevent.FieldAnnotations) + } + return fields } // FieldCleared returns a boolean indicating if a field with the given name was @@ -6758,6 +6826,11 @@ func (m *NotificationEventMutation) FieldCleared(name string) bool { // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. func (m *NotificationEventMutation) ClearField(name string) error { + switch name { + case notificationevent.FieldAnnotations: + m.ClearAnnotations() + return nil + } return fmt.Errorf("unknown NotificationEvent nullable field %s", name) } @@ -6780,6 +6853,9 @@ func (m *NotificationEventMutation) ResetField(name string) error { case notificationevent.FieldPayload: m.ResetPayload() return nil + case notificationevent.FieldAnnotations: + m.ResetAnnotations() + return nil } return fmt.Errorf("unknown NotificationEvent field %s", name) } diff --git a/internal/ent/db/notificationevent.go b/internal/ent/db/notificationevent.go index 3c8f7e6ef..7704685fb 100644 --- a/internal/ent/db/notificationevent.go +++ b/internal/ent/db/notificationevent.go @@ -29,6 +29,8 @@ type NotificationEvent struct { RuleID string `json:"rule_id,omitempty"` // Payload holds the value of the "payload" field. Payload string `json:"payload,omitempty"` + // Annotations holds the value of the "annotations" field. + Annotations map[string]interface{} `json:"annotations,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the NotificationEventQuery when eager-loading is set. Edges NotificationEventEdges `json:"edges"` @@ -75,6 +77,8 @@ func (*NotificationEvent) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullString) case notificationevent.FieldCreatedAt: values[i] = new(sql.NullTime) + case notificationevent.FieldAnnotations: + values[i] = notificationevent.ValueScanner.Annotations.ScanValue() default: values[i] = new(sql.UnknownType) } @@ -126,6 +130,12 @@ func (ne *NotificationEvent) assignValues(columns []string, values []any) error } else if value.Valid { ne.Payload = value.String } + case notificationevent.FieldAnnotations: + if value, err := notificationevent.ValueScanner.Annotations.FromValue(values[i]); err != nil { + return err + } else { + ne.Annotations = value + } default: ne.selectValues.Set(columns[i], values[i]) } @@ -186,6 +196,9 @@ func (ne *NotificationEvent) String() string { builder.WriteString(", ") builder.WriteString("payload=") builder.WriteString(ne.Payload) + builder.WriteString(", ") + builder.WriteString("annotations=") + builder.WriteString(fmt.Sprintf("%v", ne.Annotations)) builder.WriteByte(')') return builder.String() } diff --git a/internal/ent/db/notificationevent/notificationevent.go b/internal/ent/db/notificationevent/notificationevent.go index c37dbd6ce..5d2d79779 100644 --- a/internal/ent/db/notificationevent/notificationevent.go +++ b/internal/ent/db/notificationevent/notificationevent.go @@ -8,6 +8,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" "github.com/openmeterio/openmeter/internal/notification" ) @@ -26,6 +27,8 @@ const ( FieldRuleID = "rule_id" // FieldPayload holds the string denoting the payload field in the database. FieldPayload = "payload" + // FieldAnnotations holds the string denoting the annotations field in the database. + FieldAnnotations = "annotations" // EdgeDeliveryStatuses holds the string denoting the delivery_statuses edge name in mutations. EdgeDeliveryStatuses = "delivery_statuses" // EdgeRules holds the string denoting the rules edge name in mutations. @@ -54,6 +57,7 @@ var Columns = []string{ FieldType, FieldRuleID, FieldPayload, + FieldAnnotations, } var ( @@ -79,6 +83,10 @@ var ( DefaultCreatedAt func() time.Time // DefaultID holds the default value on creation for the "id" field. DefaultID func() string + // ValueScanner of all NotificationEvent fields. + ValueScanner struct { + Annotations field.TypeValueScanner[map[string]interface{}] + } ) // TypeValidator is a validator for the "type" field enum values. It is called by the builders before save. @@ -124,6 +132,11 @@ func ByPayload(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldPayload, opts...).ToFunc() } +// ByAnnotations orders the results by the annotations field. +func ByAnnotations(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAnnotations, opts...).ToFunc() +} + // ByDeliveryStatusesCount orders the results by delivery_statuses count. func ByDeliveryStatusesCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/internal/ent/db/notificationevent/where.go b/internal/ent/db/notificationevent/where.go index 291837037..7204c1a9c 100644 --- a/internal/ent/db/notificationevent/where.go +++ b/internal/ent/db/notificationevent/where.go @@ -351,6 +351,16 @@ func PayloadContainsFold(v string) predicate.NotificationEvent { return predicate.NotificationEvent(sql.FieldContainsFold(FieldPayload, v)) } +// AnnotationsIsNil applies the IsNil predicate on the "annotations" field. +func AnnotationsIsNil() predicate.NotificationEvent { + return predicate.NotificationEvent(sql.FieldIsNull(FieldAnnotations)) +} + +// AnnotationsNotNil applies the NotNil predicate on the "annotations" field. +func AnnotationsNotNil() predicate.NotificationEvent { + return predicate.NotificationEvent(sql.FieldNotNull(FieldAnnotations)) +} + // HasDeliveryStatuses applies the HasEdge predicate on the "delivery_statuses" edge. func HasDeliveryStatuses() predicate.NotificationEvent { return predicate.NotificationEvent(func(s *sql.Selector) { diff --git a/internal/ent/db/notificationevent_create.go b/internal/ent/db/notificationevent_create.go index 78c53a221..ec9508840 100644 --- a/internal/ent/db/notificationevent_create.go +++ b/internal/ent/db/notificationevent_create.go @@ -64,6 +64,12 @@ func (nec *NotificationEventCreate) SetPayload(s string) *NotificationEventCreat return nec } +// SetAnnotations sets the "annotations" field. +func (nec *NotificationEventCreate) SetAnnotations(m map[string]interface{}) *NotificationEventCreate { + nec.mutation.SetAnnotations(m) + return nec +} + // SetID sets the "id" field. func (nec *NotificationEventCreate) SetID(s string) *NotificationEventCreate { nec.mutation.SetID(s) @@ -186,7 +192,10 @@ func (nec *NotificationEventCreate) sqlSave(ctx context.Context) (*NotificationE if err := nec.check(); err != nil { return nil, err } - _node, _spec := nec.createSpec() + _node, _spec, err := nec.createSpec() + if err != nil { + return nil, err + } if err := sqlgraph.CreateNode(ctx, nec.driver, _spec); err != nil { if sqlgraph.IsConstraintError(err) { err = &ConstraintError{msg: err.Error(), wrap: err} @@ -205,7 +214,7 @@ func (nec *NotificationEventCreate) sqlSave(ctx context.Context) (*NotificationE return _node, nil } -func (nec *NotificationEventCreate) createSpec() (*NotificationEvent, *sqlgraph.CreateSpec) { +func (nec *NotificationEventCreate) createSpec() (*NotificationEvent, *sqlgraph.CreateSpec, error) { var ( _node = &NotificationEvent{config: nec.config} _spec = sqlgraph.NewCreateSpec(notificationevent.Table, sqlgraph.NewFieldSpec(notificationevent.FieldID, field.TypeString)) @@ -231,6 +240,14 @@ func (nec *NotificationEventCreate) createSpec() (*NotificationEvent, *sqlgraph. _spec.SetField(notificationevent.FieldPayload, field.TypeString, value) _node.Payload = value } + if value, ok := nec.mutation.Annotations(); ok { + vv, err := notificationevent.ValueScanner.Annotations.Value(value) + if err != nil { + return nil, nil, err + } + _spec.SetField(notificationevent.FieldAnnotations, field.TypeString, vv) + _node.Annotations = value + } if nodes := nec.mutation.DeliveryStatusesIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, @@ -264,7 +281,7 @@ func (nec *NotificationEventCreate) createSpec() (*NotificationEvent, *sqlgraph. _node.RuleID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } - return _node, _spec + return _node, _spec, nil } // OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause @@ -328,6 +345,24 @@ func (u *NotificationEventUpsert) UpdatePayload() *NotificationEventUpsert { return u } +// SetAnnotations sets the "annotations" field. +func (u *NotificationEventUpsert) SetAnnotations(v map[string]interface{}) *NotificationEventUpsert { + u.Set(notificationevent.FieldAnnotations, v) + return u +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *NotificationEventUpsert) UpdateAnnotations() *NotificationEventUpsert { + u.SetExcluded(notificationevent.FieldAnnotations) + return u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *NotificationEventUpsert) ClearAnnotations() *NotificationEventUpsert { + u.SetNull(notificationevent.FieldAnnotations) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -402,6 +437,27 @@ func (u *NotificationEventUpsertOne) UpdatePayload() *NotificationEventUpsertOne }) } +// SetAnnotations sets the "annotations" field. +func (u *NotificationEventUpsertOne) SetAnnotations(v map[string]interface{}) *NotificationEventUpsertOne { + return u.Update(func(s *NotificationEventUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *NotificationEventUpsertOne) UpdateAnnotations() *NotificationEventUpsertOne { + return u.Update(func(s *NotificationEventUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *NotificationEventUpsertOne) ClearAnnotations() *NotificationEventUpsertOne { + return u.Update(func(s *NotificationEventUpsert) { + s.ClearAnnotations() + }) +} + // Exec executes the query. func (u *NotificationEventUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -470,7 +526,10 @@ func (necb *NotificationEventCreateBulk) Save(ctx context.Context) ([]*Notificat } builder.mutation = mutation var err error - nodes[i], specs[i] = builder.createSpec() + nodes[i], specs[i], err = builder.createSpec() + if err != nil { + return nil, err + } if i < len(mutators)-1 { _, err = mutators[i+1].Mutate(root, necb.builders[i+1].mutation) } else { @@ -643,6 +702,27 @@ func (u *NotificationEventUpsertBulk) UpdatePayload() *NotificationEventUpsertBu }) } +// SetAnnotations sets the "annotations" field. +func (u *NotificationEventUpsertBulk) SetAnnotations(v map[string]interface{}) *NotificationEventUpsertBulk { + return u.Update(func(s *NotificationEventUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *NotificationEventUpsertBulk) UpdateAnnotations() *NotificationEventUpsertBulk { + return u.Update(func(s *NotificationEventUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *NotificationEventUpsertBulk) ClearAnnotations() *NotificationEventUpsertBulk { + return u.Update(func(s *NotificationEventUpsert) { + s.ClearAnnotations() + }) +} + // Exec executes the query. func (u *NotificationEventUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/internal/ent/db/notificationevent_update.go b/internal/ent/db/notificationevent_update.go index f5aa70522..06778ca6f 100644 --- a/internal/ent/db/notificationevent_update.go +++ b/internal/ent/db/notificationevent_update.go @@ -42,6 +42,18 @@ func (neu *NotificationEventUpdate) SetNillablePayload(s *string) *NotificationE return neu } +// SetAnnotations sets the "annotations" field. +func (neu *NotificationEventUpdate) SetAnnotations(m map[string]interface{}) *NotificationEventUpdate { + neu.mutation.SetAnnotations(m) + return neu +} + +// ClearAnnotations clears the value of the "annotations" field. +func (neu *NotificationEventUpdate) ClearAnnotations() *NotificationEventUpdate { + neu.mutation.ClearAnnotations() + return neu +} + // AddDeliveryStatusIDs adds the "delivery_statuses" edge to the NotificationEventDeliveryStatus entity by IDs. func (neu *NotificationEventUpdate) AddDeliveryStatusIDs(ids ...string) *NotificationEventUpdate { neu.mutation.AddDeliveryStatusIDs(ids...) @@ -133,6 +145,16 @@ func (neu *NotificationEventUpdate) sqlSave(ctx context.Context) (n int, err err if value, ok := neu.mutation.Payload(); ok { _spec.SetField(notificationevent.FieldPayload, field.TypeString, value) } + if value, ok := neu.mutation.Annotations(); ok { + vv, err := notificationevent.ValueScanner.Annotations.Value(value) + if err != nil { + return 0, err + } + _spec.SetField(notificationevent.FieldAnnotations, field.TypeString, vv) + } + if neu.mutation.AnnotationsCleared() { + _spec.ClearField(notificationevent.FieldAnnotations, field.TypeString) + } if neu.mutation.DeliveryStatusesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, @@ -212,6 +234,18 @@ func (neuo *NotificationEventUpdateOne) SetNillablePayload(s *string) *Notificat return neuo } +// SetAnnotations sets the "annotations" field. +func (neuo *NotificationEventUpdateOne) SetAnnotations(m map[string]interface{}) *NotificationEventUpdateOne { + neuo.mutation.SetAnnotations(m) + return neuo +} + +// ClearAnnotations clears the value of the "annotations" field. +func (neuo *NotificationEventUpdateOne) ClearAnnotations() *NotificationEventUpdateOne { + neuo.mutation.ClearAnnotations() + return neuo +} + // AddDeliveryStatusIDs adds the "delivery_statuses" edge to the NotificationEventDeliveryStatus entity by IDs. func (neuo *NotificationEventUpdateOne) AddDeliveryStatusIDs(ids ...string) *NotificationEventUpdateOne { neuo.mutation.AddDeliveryStatusIDs(ids...) @@ -333,6 +367,16 @@ func (neuo *NotificationEventUpdateOne) sqlSave(ctx context.Context) (_node *Not if value, ok := neuo.mutation.Payload(); ok { _spec.SetField(notificationevent.FieldPayload, field.TypeString, value) } + if value, ok := neuo.mutation.Annotations(); ok { + vv, err := notificationevent.ValueScanner.Annotations.Value(value) + if err != nil { + return nil, err + } + _spec.SetField(notificationevent.FieldAnnotations, field.TypeString, vv) + } + if neuo.mutation.AnnotationsCleared() { + _spec.ClearField(notificationevent.FieldAnnotations, field.TypeString) + } if neuo.mutation.DeliveryStatusesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, diff --git a/internal/ent/db/predicate/predicate.go b/internal/ent/db/predicate/predicate.go index 7cad3065b..ffb66f344 100644 --- a/internal/ent/db/predicate/predicate.go +++ b/internal/ent/db/predicate/predicate.go @@ -35,6 +35,17 @@ func NotificationChannelOrErr(p NotificationChannel, err error) NotificationChan // NotificationEvent is the predicate function for notificationevent builders. type NotificationEvent func(*sql.Selector) +// NotificationEventOrErr calls the predicate only if the error is not nit. +func NotificationEventOrErr(p NotificationEvent, err error) NotificationEvent { + return func(s *sql.Selector) { + if err != nil { + s.AddError(err) + return + } + p(s) + } +} + // NotificationEventDeliveryStatus is the predicate function for notificationeventdeliverystatus builders. type NotificationEventDeliveryStatus func(*sql.Selector) diff --git a/internal/ent/db/runtime.go b/internal/ent/db/runtime.go index c1246dc72..351ab52c2 100644 --- a/internal/ent/db/runtime.go +++ b/internal/ent/db/runtime.go @@ -197,6 +197,9 @@ func init() { notificationeventDescCreatedAt := notificationeventFields[0].Descriptor() // notificationevent.DefaultCreatedAt holds the default value on creation for the created_at field. notificationevent.DefaultCreatedAt = notificationeventDescCreatedAt.Default.(func() time.Time) + // notificationeventDescAnnotations is the schema descriptor for annotations field. + notificationeventDescAnnotations := notificationeventFields[4].Descriptor() + notificationevent.ValueScanner.Annotations = notificationeventDescAnnotations.ValueScanner.(field.TypeValueScanner[map[string]interface{}]) // notificationeventDescID is the schema descriptor for id field. notificationeventDescID := notificationeventMixinFields0[0].Descriptor() // notificationevent.DefaultID holds the default value on creation for the id field. diff --git a/internal/ent/schema/notification.go b/internal/ent/schema/notification.go index 935c60825..b1112123c 100644 --- a/internal/ent/schema/notification.go +++ b/internal/ent/schema/notification.go @@ -138,6 +138,13 @@ func (NotificationEvent) Fields() []ent.Field { SchemaType(map[string]string{ dialect.Postgres: "jsonb", }), + field.String("annotations"). + GoType(notification.Annotations{}). + ValueScanner(AnnotationsValueScanner). + SchemaType(map[string]string{ + dialect.Postgres: "jsonb", + }). + Optional(), } } @@ -327,3 +334,26 @@ var RuleConfigValueScanner = field.ValueScannerFunc[notification.RuleConfig, *sq return ruleConfig, nil }, } + +var AnnotationsValueScanner = field.ValueScannerFunc[notification.Annotations, *sql.NullString]{ + V: func(annotations notification.Annotations) (driver.Value, error) { + b, err := json.Marshal(annotations) + if err != nil { + return nil, err + } + + return string(b), nil + }, + S: func(ns *sql.NullString) (notification.Annotations, error) { + var annotations notification.Annotations + if ns == nil || !ns.Valid { + return annotations, nil + } + + if err := json.Unmarshal([]byte(ns.String), &annotations); err != nil { + return nil, err + } + + return annotations, nil + }, +} diff --git a/internal/notification/annotations.go b/internal/notification/annotations.go new file mode 100644 index 000000000..71dc75f6e --- /dev/null +++ b/internal/notification/annotations.go @@ -0,0 +1,8 @@ +package notification + +const ( + // AnnotationRuleTestEvent indicates that the event is generated as part of testing a notification rule + AnnotationRuleTestEvent = "notification.rule.test" +) + +type Annotations = map[string]interface{} diff --git a/internal/notification/event.go b/internal/notification/event.go index 1a37bc43a..1fd5499e1 100644 --- a/internal/notification/event.go +++ b/internal/notification/event.go @@ -21,6 +21,7 @@ var _ payloadObjectMapper = (*Event)(nil) type Event struct { models.NamespacedModel + Annotations // ID is the unique identifier for Event. ID string `json:"id"` @@ -96,6 +97,7 @@ func (e Event) AsNotificationEvent() (api.NotificationEvent, error) { switch e.Type { case EventTypeBalanceThreshold: + event.Type = api.EntitlementsBalanceThreshold err = event.Payload.FromNotificationEventBalanceThresholdPayload(e.AsNotificationEventBalanceThresholdPayload()) if err != nil { return event, ValidationError{ @@ -297,6 +299,7 @@ var _ validator = (*CreateEventInput)(nil) type CreateEventInput struct { models.NamespacedModel + Annotations `json:"annotations,omitempty"` // Type of the notification Event (e.g. entitlements.balance.threshold) Type EventType `json:"type"` diff --git a/internal/notification/httpdriver/event.go b/internal/notification/httpdriver/event.go index 902ee4d79..17af8d03e 100644 --- a/internal/notification/httpdriver/event.go +++ b/internal/notification/httpdriver/event.go @@ -119,69 +119,3 @@ func (h *handler) GetEvent() GetEventHandler { )..., ) } - -type ( - CreateEventRequest = notification.CreateEventInput - CreateEventResponse = api.NotificationEvent - CreateEventHandler httptransport.Handler[CreateEventRequest, CreateEventResponse] -) - -func (h *handler) CreateEvent() CreateEventHandler { - return httptransport.NewHandler( - func(ctx context.Context, r *http.Request) (CreateEventRequest, error) { - body := api.NotificationEventCreateRequest{} - if err := commonhttp.JSONRequestBodyDecoder(r, &body); err != nil { - return CreateEventRequest{}, fmt.Errorf("field to decode create channel request: %w", err) - } - - ns, err := h.resolveNamespace(ctx) - if err != nil { - return CreateEventRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) - } - - payloadValue, err := body.Payload.ValueByDiscriminator() - if err != nil { - return CreateEventRequest{}, notification.ValidationError{ - Err: err, - } - } - - payload := notification.EventPayload{} - switch v := payloadValue.(type) { - case api.NotificationEventBalanceThresholdPayload: - payload = payload.FromNotificationEventBalanceThresholdPayload(v) - if err != nil { - return CreateEventRequest{}, fmt.Errorf("failed to unmarshal payload as BalanceThresholdPayload: %w", err) - } - - default: - return CreateEventRequest{}, fmt.Errorf("unknown event type: %s", body.Type) - } - - req := CreateEventRequest{ - NamespacedModel: models.NamespacedModel{ - Namespace: ns, - }, - Type: notification.EventType(body.Type), - Payload: payload, - RuleID: body.RuleId, - } - - return req, nil - }, - func(ctx context.Context, request CreateEventRequest) (CreateEventResponse, error) { - event, err := h.service.CreateEvent(ctx, request) - if err != nil { - return CreateEventResponse{}, fmt.Errorf("failed to get event: %w", err) - } - - return event.AsNotificationEvent() - }, - commonhttp.JSONResponseEncoderWithStatus[CreateEventResponse](http.StatusOK), - httptransport.AppendOptions( - h.options, - httptransport.WithOperationName("createNotificationEvent"), - httptransport.WithErrorEncoder(errorEncoder()), - )..., - ) -} diff --git a/internal/notification/httpdriver/handler.go b/internal/notification/httpdriver/handler.go index 45736ebd7..45b78fa03 100644 --- a/internal/notification/httpdriver/handler.go +++ b/internal/notification/httpdriver/handler.go @@ -31,12 +31,12 @@ type RuleHandler interface { DeleteRule() DeleteRuleHandler GetRule() GetRuleHandler UpdateRule() UpdateRuleHandler + TestRule() TestRuleHandler } type EventHandler interface { ListEvents() ListEventsHandler GetEvent() GetEventHandler - CreateEvent() CreateEventHandler } var _ Handler = (*handler)(nil) diff --git a/internal/notification/httpdriver/rule.go b/internal/notification/httpdriver/rule.go index 6d0cecb31..b54c234a8 100644 --- a/internal/notification/httpdriver/rule.go +++ b/internal/notification/httpdriver/rule.go @@ -7,6 +7,7 @@ import ( "github.com/openmeterio/openmeter/api" "github.com/openmeterio/openmeter/internal/notification" + "github.com/openmeterio/openmeter/internal/notification/internal" "github.com/openmeterio/openmeter/pkg/defaultx" "github.com/openmeterio/openmeter/pkg/framework/commonhttp" "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" @@ -270,3 +271,71 @@ func (h *handler) GetRule() GetRuleHandler { )..., ) } + +type TestRuleRequest struct { + models.NamespacedModel + + // RuleID defines the notification Rule that generated this Event. + RuleID string `json:"ruleId"` +} + +type ( + TestRuleResponse = api.NotificationEvent + TestRuleHandler httptransport.HandlerWithArgs[TestRuleRequest, TestRuleResponse, api.RuleId] +) + +func (h *handler) TestRule() TestRuleHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, ruleID api.RuleId) (TestRuleRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return TestRuleRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) + } + + req := TestRuleRequest{ + NamespacedModel: models.NamespacedModel{ + Namespace: ns, + }, + RuleID: ruleID, + } + + return req, nil + }, + func(ctx context.Context, request TestRuleRequest) (TestRuleResponse, error) { + rule, err := h.service.GetRule(ctx, notification.GetRuleInput{ + Namespace: request.NamespacedModel.Namespace, + ID: request.RuleID, + }) + if err != nil { + return TestRuleResponse{}, fmt.Errorf("failed to get rule: %w", err) + } + + var payload notification.EventPayload + switch rule.Type { + case notification.RuleTypeBalanceThreshold: + payload = internal.NewTestEventPayload(notification.EventTypeBalanceThreshold) + } + + event, err := h.service.CreateEvent(ctx, notification.CreateEventInput{ + NamespacedModel: request.NamespacedModel, + Type: notification.EventType(rule.Type), + Payload: payload, + RuleID: rule.ID, + Annotations: notification.Annotations{ + notification.AnnotationRuleTestEvent: true, + }, + }) + if err != nil { + return TestRuleResponse{}, fmt.Errorf("failed to create test event: %w", err) + } + + return event.AsNotificationEvent() + }, + commonhttp.JSONResponseEncoderWithStatus[TestRuleResponse](http.StatusOK), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("createNotificationEvent"), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} diff --git a/internal/notification/internal/rule.go b/internal/notification/internal/rule.go new file mode 100644 index 000000000..3461333b3 --- /dev/null +++ b/internal/notification/internal/rule.go @@ -0,0 +1,90 @@ +package internal + +import ( + "time" + + "github.com/openmeterio/openmeter/api" + "github.com/openmeterio/openmeter/internal/notification" + "github.com/openmeterio/openmeter/pkg/convert" +) + +var ( + now = time.Now() + createdAt = now.Add(-24 * time.Hour) + updatedAt = now.Add(-12 * time.Hour) + from = now.Add(-24 * time.Hour) + to = now.Add(-12 * time.Hour) +) + +func NewTestEventPayload(eventType notification.EventType) notification.EventPayload { + return notification.EventPayload{ + EventPayloadMeta: notification.EventPayloadMeta{ + Type: eventType, + }, + BalanceThreshold: notification.BalanceThresholdPayload{ + Entitlement: api.EntitlementMetered{ + CreatedAt: convert.ToPointer(createdAt), + CurrentUsagePeriod: api.Period{ + From: from, + To: to, + }, + DeletedAt: nil, + FeatureId: "01J5AVN2T6S0RDGJHVNN0BW3F5", + FeatureKey: "test-feature-1", + Id: convert.ToPointer("01J5AVNM7H1PT65RDFWGXXPT47"), + IsSoftLimit: convert.ToPointer(false), + IsUnlimited: convert.ToPointer(true), + IssueAfterReset: convert.ToPointer(10.0), + IssueAfterResetPriority: convert.ToPointer(5), + LastReset: time.Time{}, + MeasureUsageFrom: time.Time{}, + Metadata: &map[string]string{ + "test-metadata-key": "test-metadata-value", + }, + PreserveOverageAtReset: convert.ToPointer(true), + SubjectKey: "test-subject-1", + Type: api.EntitlementMeteredTypeMetered, + UpdatedAt: convert.ToPointer(updatedAt), + UsagePeriod: api.RecurringPeriod{ + Anchor: from, + Interval: api.RecurringPeriodEnumDAY, + }, + }, + Feature: api.Feature{ + ArchivedAt: nil, + CreatedAt: convert.ToPointer(createdAt), + DeletedAt: nil, + Id: convert.ToPointer("01J5AVN2T6S0RDGJHVNN0BW3F5"), + Key: "test-feature-1", + Metadata: &map[string]string{ + "test-metadata-key": "test-metadata-value", + }, + MeterGroupByFilters: nil, + MeterSlug: convert.ToPointer("test-meter-1"), + Name: "test-meter-1", + UpdatedAt: convert.ToPointer(updatedAt), + }, + Subject: api.Subject{ + CurrentPeriodEnd: convert.ToPointer(from), + CurrentPeriodStart: convert.ToPointer(to), + DisplayName: convert.ToPointer("Test Subject 1"), + Id: convert.ToPointer("01J5AW0ZD6T8624PCK0Q5TYX71"), + Key: "test-subject-1", + Metadata: &map[string]interface{}{ + "test-metadata-key": "test-metadata-value", + }, + StripeCustomerId: convert.ToPointer("01J5AW2XS6DYHH7E9PNJSQJ341"), + }, + Threshold: api.NotificationRuleBalanceThresholdValue{ + Type: api.PERCENT, + Value: 50, + }, + Value: api.EntitlementValue{ + Balance: convert.ToPointer(10_000.0), + HasAccess: convert.ToPointer(true), + Overage: convert.ToPointer(99.0), + Usage: convert.ToPointer(5_001.0), + }, + }, + } +} diff --git a/internal/notification/repository/repository.go b/internal/notification/repository/repository.go index 2941126ba..c0de28afe 100644 --- a/internal/notification/repository/repository.go +++ b/internal/notification/repository/repository.go @@ -581,7 +581,8 @@ func (r repository) CreateEvent(ctx context.Context, params notification.CreateE SetType(params.Type). SetNamespace(params.Namespace). SetRuleID(params.RuleID). - SetPayload(string(payloadJSON)) + SetPayload(string(payloadJSON)). + SetAnnotations(params.Annotations) eventRow, err := query.Save(ctx) if err != nil { diff --git a/internal/server/router/notification.go b/internal/server/router/notification.go index 9617782f4..86ea0fd2e 100644 --- a/internal/server/router/notification.go +++ b/internal/server/router/notification.go @@ -83,17 +83,6 @@ func (a *Router) GetNotificationEvent(w http.ResponseWriter, r *http.Request, ev a.notificationHandler.GetEvent().With(eventID).ServeHTTP(w, r) } -// Create notification event -// (POST /api/v1/notification/events) -func (a *Router) CreateNotificationEvent(w http.ResponseWriter, r *http.Request) { - if !a.config.NotificationEnabled { - unimplemented.CreateNotificationEvent(w, r) - return - } - - a.notificationHandler.CreateEvent().ServeHTTP(w, r) -} - // List notification rules // (GET /api/v1/notification/rules) func (a *Router) ListNotificationRules(w http.ResponseWriter, r *http.Request, params api.ListNotificationRulesParams) { @@ -149,6 +138,17 @@ func (a *Router) UpdateNotificationRule(w http.ResponseWriter, r *http.Request, a.notificationHandler.UpdateRule().With(ruleID).ServeHTTP(w, r) } +// Test notification rule +// (POST /api/v1/notification/rules/{ruleId}/test) +func (a *Router) TestNotificationRule(w http.ResponseWriter, r *http.Request, ruleID api.RuleId) { + if !a.config.NotificationEnabled { + unimplemented.TestNotificationRule(w, r, ruleID) + return + } + + a.notificationHandler.TestRule().With(ruleID).ServeHTTP(w, r) +} + // Receive Svix operational events // (POST /api/v1/notification/webhook/svix) func (a *Router) ReceiveSvixOperationalEvent(w http.ResponseWriter, r *http.Request) { diff --git a/openmeter/notification/httpdriver/alias.go b/openmeter/notification/httpdriver/alias.go index 374109308..d821cc51d 100644 --- a/openmeter/notification/httpdriver/alias.go +++ b/openmeter/notification/httpdriver/alias.go @@ -16,7 +16,7 @@ type ( UpdateRuleHandler = httpdriver.UpdateRuleHandler ListEventsHandler = httpdriver.ListEventsHandler GetEventHandler = httpdriver.GetEventHandler - CreateEventHandler = httpdriver.CreateEventHandler + TestRuleHandler = httpdriver.TestRuleHandler ) type ( @@ -43,15 +43,15 @@ type ( GetRuleResponse = httpdriver.GetRuleResponse UpdateRuleRequest = httpdriver.UpdateRuleRequest UpdateRuleResponse = httpdriver.UpdateRuleResponse + TestRuleRequest = httpdriver.TestRuleRequest + TestRuleResponse = httpdriver.TestRuleResponse ) type ( - ListEventsRequest = httpdriver.ListEventsRequest - ListEventsResponse = httpdriver.ListEventsResponse - GetEventRequest = httpdriver.GetEventRequest - GetEventResponse = httpdriver.GetEventResponse - CreateEventRequest = httpdriver.CreateEventRequest - CreateEventResponse = httpdriver.CreateEventResponse + ListEventsRequest = httpdriver.ListEventsRequest + ListEventsResponse = httpdriver.ListEventsResponse + GetEventRequest = httpdriver.GetEventRequest + GetEventResponse = httpdriver.GetEventResponse ) var NewHandler = httpdriver.New