Basic impl added
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package notifier.v1;
|
||||
|
||||
option go_package = "github.com/igodwin/notifier/api/grpc/pb";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// NotifierService handles notification operations
|
||||
service NotifierService {
|
||||
// SendNotification sends a single notification
|
||||
rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse);
|
||||
|
||||
// SendBatchNotifications sends multiple notifications
|
||||
rpc SendBatchNotifications(SendBatchNotificationsRequest) returns (SendBatchNotificationsResponse);
|
||||
|
||||
// GetNotification retrieves a notification by ID
|
||||
rpc GetNotification(GetNotificationRequest) returns (GetNotificationResponse);
|
||||
|
||||
// ListNotifications retrieves notifications matching a filter
|
||||
rpc ListNotifications(ListNotificationsRequest) returns (ListNotificationsResponse);
|
||||
|
||||
// CancelNotification cancels a pending notification
|
||||
rpc CancelNotification(CancelNotificationRequest) returns (CancelNotificationResponse);
|
||||
|
||||
// RetryNotification retries a failed notification
|
||||
rpc RetryNotification(RetryNotificationRequest) returns (RetryNotificationResponse);
|
||||
|
||||
// GetStats returns notification statistics
|
||||
rpc GetStats(GetStatsRequest) returns (GetStatsResponse);
|
||||
|
||||
// HealthCheck verifies the service is operational
|
||||
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
|
||||
}
|
||||
|
||||
// NotificationType defines the channel for notification delivery
|
||||
enum NotificationType {
|
||||
NOTIFICATION_TYPE_UNSPECIFIED = 0;
|
||||
NOTIFICATION_TYPE_EMAIL = 1;
|
||||
NOTIFICATION_TYPE_SLACK = 2;
|
||||
NOTIFICATION_TYPE_NTFY = 3;
|
||||
NOTIFICATION_TYPE_STDOUT = 4;
|
||||
}
|
||||
|
||||
// Priority defines the urgency level
|
||||
enum Priority {
|
||||
PRIORITY_UNSPECIFIED = 0;
|
||||
PRIORITY_LOW = 1;
|
||||
PRIORITY_NORMAL = 2;
|
||||
PRIORITY_HIGH = 3;
|
||||
PRIORITY_CRITICAL = 4;
|
||||
}
|
||||
|
||||
// NotificationStatus represents the state of a notification
|
||||
enum NotificationStatus {
|
||||
NOTIFICATION_STATUS_UNSPECIFIED = 0;
|
||||
NOTIFICATION_STATUS_PENDING = 1;
|
||||
NOTIFICATION_STATUS_QUEUED = 2;
|
||||
NOTIFICATION_STATUS_PROCESSING = 3;
|
||||
NOTIFICATION_STATUS_SENT = 4;
|
||||
NOTIFICATION_STATUS_FAILED = 5;
|
||||
NOTIFICATION_STATUS_RETRYING = 6;
|
||||
}
|
||||
|
||||
// Notification represents a notification message
|
||||
message Notification {
|
||||
string id = 1;
|
||||
NotificationType type = 2;
|
||||
Priority priority = 3;
|
||||
NotificationStatus status = 4;
|
||||
string subject = 5;
|
||||
string body = 6;
|
||||
repeated string recipients = 7;
|
||||
map<string, string> metadata = 8;
|
||||
google.protobuf.Timestamp created_at = 9;
|
||||
google.protobuf.Timestamp scheduled_for = 10;
|
||||
google.protobuf.Timestamp sent_at = 11;
|
||||
int32 retry_count = 12;
|
||||
int32 max_retries = 13;
|
||||
string last_error = 14;
|
||||
}
|
||||
|
||||
// NotificationResult represents the outcome of sending a notification
|
||||
message NotificationResult {
|
||||
string notification_id = 1;
|
||||
bool success = 2;
|
||||
string message = 3;
|
||||
string error = 4;
|
||||
google.protobuf.Timestamp sent_at = 5;
|
||||
map<string, string> provider_response = 6;
|
||||
}
|
||||
|
||||
// SendNotificationRequest sends a single notification
|
||||
message SendNotificationRequest {
|
||||
NotificationType type = 1;
|
||||
Priority priority = 2;
|
||||
string subject = 3;
|
||||
string body = 4;
|
||||
repeated string recipients = 5;
|
||||
map<string, string> metadata = 6;
|
||||
google.protobuf.Timestamp scheduled_for = 7;
|
||||
int32 max_retries = 8;
|
||||
}
|
||||
|
||||
// SendNotificationResponse returns the result of sending a notification
|
||||
message SendNotificationResponse {
|
||||
NotificationResult result = 1;
|
||||
}
|
||||
|
||||
// SendBatchNotificationsRequest sends multiple notifications
|
||||
message SendBatchNotificationsRequest {
|
||||
repeated SendNotificationRequest notifications = 1;
|
||||
}
|
||||
|
||||
// SendBatchNotificationsResponse returns the results of sending multiple notifications
|
||||
message SendBatchNotificationsResponse {
|
||||
repeated NotificationResult results = 1;
|
||||
}
|
||||
|
||||
// GetNotificationRequest retrieves a notification by ID
|
||||
message GetNotificationRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
// GetNotificationResponse returns a notification
|
||||
message GetNotificationResponse {
|
||||
Notification notification = 1;
|
||||
}
|
||||
|
||||
// NotificationFilter is used for querying notifications
|
||||
message NotificationFilter {
|
||||
repeated string ids = 1;
|
||||
repeated NotificationType types = 2;
|
||||
repeated NotificationStatus statuses = 3;
|
||||
repeated string recipients = 4;
|
||||
google.protobuf.Timestamp created_after = 5;
|
||||
google.protobuf.Timestamp created_before = 6;
|
||||
int32 limit = 7;
|
||||
int32 offset = 8;
|
||||
}
|
||||
|
||||
// ListNotificationsRequest retrieves notifications matching a filter
|
||||
message ListNotificationsRequest {
|
||||
NotificationFilter filter = 1;
|
||||
}
|
||||
|
||||
// ListNotificationsResponse returns a list of notifications
|
||||
message ListNotificationsResponse {
|
||||
repeated Notification notifications = 1;
|
||||
int64 total = 2;
|
||||
}
|
||||
|
||||
// CancelNotificationRequest cancels a pending notification
|
||||
message CancelNotificationRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
// CancelNotificationResponse returns the result of canceling a notification
|
||||
message CancelNotificationResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
// RetryNotificationRequest retries a failed notification
|
||||
message RetryNotificationRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
// RetryNotificationResponse returns the result of retrying a notification
|
||||
message RetryNotificationResponse {
|
||||
NotificationResult result = 1;
|
||||
}
|
||||
|
||||
// GetStatsRequest requests notification statistics
|
||||
message GetStatsRequest {}
|
||||
|
||||
// GetStatsResponse returns notification statistics
|
||||
message GetStatsResponse {
|
||||
int64 total_sent = 1;
|
||||
int64 total_failed = 2;
|
||||
int64 total_pending = 3;
|
||||
int64 total_queued = 4;
|
||||
map<string, int64> by_type = 5;
|
||||
map<string, int64> by_status = 6;
|
||||
double average_latency_ms = 7;
|
||||
}
|
||||
|
||||
// HealthCheckRequest requests health status
|
||||
message HealthCheckRequest {}
|
||||
|
||||
// HealthCheckResponse returns health status
|
||||
message HealthCheckResponse {
|
||||
bool healthy = 1;
|
||||
string status = 2;
|
||||
map<string, string> components = 3;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,407 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v6.33.0
|
||||
// source: api/grpc/notifier.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
NotifierService_SendNotification_FullMethodName = "/notifier.v1.NotifierService/SendNotification"
|
||||
NotifierService_SendBatchNotifications_FullMethodName = "/notifier.v1.NotifierService/SendBatchNotifications"
|
||||
NotifierService_GetNotification_FullMethodName = "/notifier.v1.NotifierService/GetNotification"
|
||||
NotifierService_ListNotifications_FullMethodName = "/notifier.v1.NotifierService/ListNotifications"
|
||||
NotifierService_CancelNotification_FullMethodName = "/notifier.v1.NotifierService/CancelNotification"
|
||||
NotifierService_RetryNotification_FullMethodName = "/notifier.v1.NotifierService/RetryNotification"
|
||||
NotifierService_GetStats_FullMethodName = "/notifier.v1.NotifierService/GetStats"
|
||||
NotifierService_HealthCheck_FullMethodName = "/notifier.v1.NotifierService/HealthCheck"
|
||||
)
|
||||
|
||||
// NotifierServiceClient is the client API for NotifierService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// NotifierService handles notification operations
|
||||
type NotifierServiceClient interface {
|
||||
// SendNotification sends a single notification
|
||||
SendNotification(ctx context.Context, in *SendNotificationRequest, opts ...grpc.CallOption) (*SendNotificationResponse, error)
|
||||
// SendBatchNotifications sends multiple notifications
|
||||
SendBatchNotifications(ctx context.Context, in *SendBatchNotificationsRequest, opts ...grpc.CallOption) (*SendBatchNotificationsResponse, error)
|
||||
// GetNotification retrieves a notification by ID
|
||||
GetNotification(ctx context.Context, in *GetNotificationRequest, opts ...grpc.CallOption) (*GetNotificationResponse, error)
|
||||
// ListNotifications retrieves notifications matching a filter
|
||||
ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error)
|
||||
// CancelNotification cancels a pending notification
|
||||
CancelNotification(ctx context.Context, in *CancelNotificationRequest, opts ...grpc.CallOption) (*CancelNotificationResponse, error)
|
||||
// RetryNotification retries a failed notification
|
||||
RetryNotification(ctx context.Context, in *RetryNotificationRequest, opts ...grpc.CallOption) (*RetryNotificationResponse, error)
|
||||
// GetStats returns notification statistics
|
||||
GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
|
||||
// HealthCheck verifies the service is operational
|
||||
HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error)
|
||||
}
|
||||
|
||||
type notifierServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewNotifierServiceClient(cc grpc.ClientConnInterface) NotifierServiceClient {
|
||||
return ¬ifierServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) SendNotification(ctx context.Context, in *SendNotificationRequest, opts ...grpc.CallOption) (*SendNotificationResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SendNotificationResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_SendNotification_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) SendBatchNotifications(ctx context.Context, in *SendBatchNotificationsRequest, opts ...grpc.CallOption) (*SendBatchNotificationsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SendBatchNotificationsResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_SendBatchNotifications_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) GetNotification(ctx context.Context, in *GetNotificationRequest, opts ...grpc.CallOption) (*GetNotificationResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetNotificationResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_GetNotification_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListNotificationsResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_ListNotifications_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) CancelNotification(ctx context.Context, in *CancelNotificationRequest, opts ...grpc.CallOption) (*CancelNotificationResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CancelNotificationResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_CancelNotification_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) RetryNotification(ctx context.Context, in *RetryNotificationRequest, opts ...grpc.CallOption) (*RetryNotificationResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RetryNotificationResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_RetryNotification_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetStatsResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_GetStats_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierServiceClient) HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(HealthCheckResponse)
|
||||
err := c.cc.Invoke(ctx, NotifierService_HealthCheck_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NotifierServiceServer is the server API for NotifierService service.
|
||||
// All implementations must embed UnimplementedNotifierServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// NotifierService handles notification operations
|
||||
type NotifierServiceServer interface {
|
||||
// SendNotification sends a single notification
|
||||
SendNotification(context.Context, *SendNotificationRequest) (*SendNotificationResponse, error)
|
||||
// SendBatchNotifications sends multiple notifications
|
||||
SendBatchNotifications(context.Context, *SendBatchNotificationsRequest) (*SendBatchNotificationsResponse, error)
|
||||
// GetNotification retrieves a notification by ID
|
||||
GetNotification(context.Context, *GetNotificationRequest) (*GetNotificationResponse, error)
|
||||
// ListNotifications retrieves notifications matching a filter
|
||||
ListNotifications(context.Context, *ListNotificationsRequest) (*ListNotificationsResponse, error)
|
||||
// CancelNotification cancels a pending notification
|
||||
CancelNotification(context.Context, *CancelNotificationRequest) (*CancelNotificationResponse, error)
|
||||
// RetryNotification retries a failed notification
|
||||
RetryNotification(context.Context, *RetryNotificationRequest) (*RetryNotificationResponse, error)
|
||||
// GetStats returns notification statistics
|
||||
GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
||||
// HealthCheck verifies the service is operational
|
||||
HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error)
|
||||
mustEmbedUnimplementedNotifierServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedNotifierServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedNotifierServiceServer struct{}
|
||||
|
||||
func (UnimplementedNotifierServiceServer) SendNotification(context.Context, *SendNotificationRequest) (*SendNotificationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SendNotification not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) SendBatchNotifications(context.Context, *SendBatchNotificationsRequest) (*SendBatchNotificationsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SendBatchNotifications not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) GetNotification(context.Context, *GetNotificationRequest) (*GetNotificationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetNotification not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) ListNotifications(context.Context, *ListNotificationsRequest) (*ListNotificationsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListNotifications not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) CancelNotification(context.Context, *CancelNotificationRequest) (*CancelNotificationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CancelNotification not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) RetryNotification(context.Context, *RetryNotificationRequest) (*RetryNotificationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RetryNotification not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method HealthCheck not implemented")
|
||||
}
|
||||
func (UnimplementedNotifierServiceServer) mustEmbedUnimplementedNotifierServiceServer() {}
|
||||
func (UnimplementedNotifierServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeNotifierServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to NotifierServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeNotifierServiceServer interface {
|
||||
mustEmbedUnimplementedNotifierServiceServer()
|
||||
}
|
||||
|
||||
func RegisterNotifierServiceServer(s grpc.ServiceRegistrar, srv NotifierServiceServer) {
|
||||
// If the following call pancis, it indicates UnimplementedNotifierServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&NotifierService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _NotifierService_SendNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendNotificationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).SendNotification(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_SendNotification_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).SendNotification(ctx, req.(*SendNotificationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_SendBatchNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendBatchNotificationsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).SendBatchNotifications(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_SendBatchNotifications_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).SendBatchNotifications(ctx, req.(*SendBatchNotificationsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_GetNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetNotificationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).GetNotification(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_GetNotification_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).GetNotification(ctx, req.(*GetNotificationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_ListNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListNotificationsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).ListNotifications(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_ListNotifications_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).ListNotifications(ctx, req.(*ListNotificationsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_CancelNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CancelNotificationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).CancelNotification(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_CancelNotification_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).CancelNotification(ctx, req.(*CancelNotificationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_RetryNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RetryNotificationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).RetryNotification(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_RetryNotification_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).RetryNotification(ctx, req.(*RetryNotificationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetStatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).GetStats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_GetStats_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).GetStats(ctx, req.(*GetStatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotifierService_HealthCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HealthCheckRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServiceServer).HealthCheck(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NotifierService_HealthCheck_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServiceServer).HealthCheck(ctx, req.(*HealthCheckRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// NotifierService_ServiceDesc is the grpc.ServiceDesc for NotifierService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var NotifierService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "notifier.v1.NotifierService",
|
||||
HandlerType: (*NotifierServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "SendNotification",
|
||||
Handler: _NotifierService_SendNotification_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SendBatchNotifications",
|
||||
Handler: _NotifierService_SendBatchNotifications_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetNotification",
|
||||
Handler: _NotifierService_GetNotification_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListNotifications",
|
||||
Handler: _NotifierService_ListNotifications_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CancelNotification",
|
||||
Handler: _NotifierService_CancelNotification_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RetryNotification",
|
||||
Handler: _NotifierService_RetryNotification_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStats",
|
||||
Handler: _NotifierService_GetStats_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "HealthCheck",
|
||||
Handler: _NotifierService_HealthCheck_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "api/grpc/notifier.proto",
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/igodwin/notifier/internal/domain"
|
||||
)
|
||||
|
||||
// Handler handles REST API requests
|
||||
type Handler struct {
|
||||
service domain.NotificationService
|
||||
}
|
||||
|
||||
// NewHandler creates a new REST handler
|
||||
func NewHandler(service domain.NotificationService) *Handler {
|
||||
return &Handler{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
// SendNotification handles POST /api/v1/notifications
|
||||
func (h *Handler) SendNotification(w http.ResponseWriter, r *http.Request) {
|
||||
var req SendNotificationRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "invalid request body", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if err := req.Validate(); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "validation failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to domain notification
|
||||
notification := req.ToNotification()
|
||||
|
||||
// Send notification
|
||||
result, err := h.service.Send(r.Context(), notification)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to send notification", err)
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusAccepted, SendNotificationResponse{
|
||||
Result: NotificationResultFromDomain(result),
|
||||
})
|
||||
}
|
||||
|
||||
// SendBatchNotifications handles POST /api/v1/notifications/batch
|
||||
func (h *Handler) SendBatchNotifications(w http.ResponseWriter, r *http.Request) {
|
||||
var req SendBatchNotificationsRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "invalid request body", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate and convert to domain notifications
|
||||
notifications := make([]*domain.Notification, 0, len(req.Notifications))
|
||||
for _, notifReq := range req.Notifications {
|
||||
if err := notifReq.Validate(); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "validation failed", err)
|
||||
return
|
||||
}
|
||||
notifications = append(notifications, notifReq.ToNotification())
|
||||
}
|
||||
|
||||
// Send batch
|
||||
results, err := h.service.SendBatch(r.Context(), notifications)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to send batch notifications", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert results
|
||||
apiResults := make([]NotificationResult, 0, len(results))
|
||||
for _, result := range results {
|
||||
apiResults = append(apiResults, NotificationResultFromDomain(result))
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusAccepted, SendBatchNotificationsResponse{
|
||||
Results: apiResults,
|
||||
})
|
||||
}
|
||||
|
||||
// GetNotification handles GET /api/v1/notifications/{id}
|
||||
func (h *Handler) GetNotification(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
notification, err := h.service.GetNotification(r.Context(), id)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusNotFound, "notification not found", err)
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, NotificationFromDomain(notification))
|
||||
}
|
||||
|
||||
// ListNotifications handles GET /api/v1/notifications
|
||||
func (h *Handler) ListNotifications(w http.ResponseWriter, r *http.Request) {
|
||||
filter := parseNotificationFilter(r)
|
||||
|
||||
notifications, err := h.service.ListNotifications(r.Context(), filter)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to list notifications", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to API format
|
||||
apiNotifications := make([]Notification, 0, len(notifications))
|
||||
for _, notif := range notifications {
|
||||
apiNotifications = append(apiNotifications, NotificationFromDomain(notif))
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, ListNotificationsResponse{
|
||||
Notifications: apiNotifications,
|
||||
Total: int64(len(apiNotifications)),
|
||||
})
|
||||
}
|
||||
|
||||
// CancelNotification handles DELETE /api/v1/notifications/{id}
|
||||
func (h *Handler) CancelNotification(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
if err := h.service.CancelNotification(r.Context(), id); err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to cancel notification", err)
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "notification canceled successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// RetryNotification handles POST /api/v1/notifications/{id}/retry
|
||||
func (h *Handler) RetryNotification(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
result, err := h.service.RetryNotification(r.Context(), id)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to retry notification", err)
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, RetryNotificationResponse{
|
||||
Result: NotificationResultFromDomain(result),
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats handles GET /api/v1/stats
|
||||
func (h *Handler) GetStats(w http.ResponseWriter, r *http.Request) {
|
||||
stats, err := h.service.GetStats(r.Context())
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "failed to get stats", err)
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, stats)
|
||||
}
|
||||
|
||||
// HealthCheck handles GET /health
|
||||
func (h *Handler) HealthCheck(w http.ResponseWriter, r *http.Request) {
|
||||
respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"status": "healthy",
|
||||
"service": "notifier",
|
||||
"time": time.Now().UTC(),
|
||||
})
|
||||
}
|
||||
|
||||
// parseNotificationFilter parses query parameters into a NotificationFilter
|
||||
func parseNotificationFilter(r *http.Request) *domain.NotificationFilter {
|
||||
query := r.URL.Query()
|
||||
filter := &domain.NotificationFilter{}
|
||||
|
||||
// Parse limit
|
||||
if limitStr := query.Get("limit"); limitStr != "" {
|
||||
if limit, err := strconv.Atoi(limitStr); err == nil {
|
||||
filter.Limit = limit
|
||||
}
|
||||
}
|
||||
|
||||
// Parse offset
|
||||
if offsetStr := query.Get("offset"); offsetStr != "" {
|
||||
if offset, err := strconv.Atoi(offsetStr); err == nil {
|
||||
filter.Offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
// Parse types
|
||||
if types := query["type"]; len(types) > 0 {
|
||||
for _, t := range types {
|
||||
filter.Types = append(filter.Types, domain.NotificationType(t))
|
||||
}
|
||||
}
|
||||
|
||||
// Parse statuses
|
||||
if statuses := query["status"]; len(statuses) > 0 {
|
||||
for _, s := range statuses {
|
||||
filter.Statuses = append(filter.Statuses, domain.NotificationStatus(s))
|
||||
}
|
||||
}
|
||||
|
||||
// Parse recipients
|
||||
if recipients := query["recipient"]; len(recipients) > 0 {
|
||||
filter.Recipients = recipients
|
||||
}
|
||||
|
||||
return filter
|
||||
}
|
||||
|
||||
// respondJSON sends a JSON response
|
||||
func respondJSON(w http.ResponseWriter, status int, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
if err := json.NewEncoder(w).Encode(data); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// respondError sends an error response
|
||||
func respondError(w http.ResponseWriter, status int, message string, err error) {
|
||||
errMsg := message
|
||||
if err != nil {
|
||||
errMsg = message + ": " + err.Error()
|
||||
}
|
||||
|
||||
respondJSON(w, status, map[string]interface{}{
|
||||
"error": message,
|
||||
"details": errMsg,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/igodwin/notifier/internal/domain"
|
||||
)
|
||||
|
||||
// NewRouter creates a new HTTP router with all routes configured
|
||||
func NewRouter(service domain.NotificationService) *mux.Router {
|
||||
handler := NewHandler(service)
|
||||
router := mux.NewRouter()
|
||||
|
||||
// API v1 routes
|
||||
v1 := router.PathPrefix("/api/v1").Subrouter()
|
||||
|
||||
// Notification routes
|
||||
v1.HandleFunc("/notifications", handler.SendNotification).Methods(http.MethodPost)
|
||||
v1.HandleFunc("/notifications/batch", handler.SendBatchNotifications).Methods(http.MethodPost)
|
||||
v1.HandleFunc("/notifications", handler.ListNotifications).Methods(http.MethodGet)
|
||||
v1.HandleFunc("/notifications/{id}", handler.GetNotification).Methods(http.MethodGet)
|
||||
v1.HandleFunc("/notifications/{id}", handler.CancelNotification).Methods(http.MethodDelete)
|
||||
v1.HandleFunc("/notifications/{id}/retry", handler.RetryNotification).Methods(http.MethodPost)
|
||||
|
||||
// Stats route
|
||||
v1.HandleFunc("/stats", handler.GetStats).Methods(http.MethodGet)
|
||||
|
||||
// Health check route
|
||||
router.HandleFunc("/health", handler.HealthCheck).Methods(http.MethodGet)
|
||||
|
||||
// Middleware
|
||||
router.Use(loggingMiddleware)
|
||||
router.Use(corsMiddleware)
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
// loggingMiddleware logs incoming requests
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// You can add structured logging here
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// corsMiddleware adds CORS headers
|
||||
func corsMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/igodwin/notifier/internal/domain"
|
||||
)
|
||||
|
||||
// SendNotificationRequest is the REST API request for sending a notification
|
||||
type SendNotificationRequest struct {
|
||||
Type string `json:"type"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
ScheduledFor *time.Time `json:"scheduled_for,omitempty"`
|
||||
MaxRetries int `json:"max_retries,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates the request
|
||||
func (r *SendNotificationRequest) Validate() error {
|
||||
if r.Type == "" {
|
||||
return fmt.Errorf("type is required")
|
||||
}
|
||||
|
||||
if len(r.Recipients) == 0 {
|
||||
return fmt.Errorf("at least one recipient is required")
|
||||
}
|
||||
|
||||
if r.Body == "" {
|
||||
return fmt.Errorf("body is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToNotification converts the request to a domain notification
|
||||
func (r *SendNotificationRequest) ToNotification() *domain.Notification {
|
||||
maxRetries := r.MaxRetries
|
||||
if maxRetries == 0 {
|
||||
maxRetries = 3 // Default
|
||||
}
|
||||
|
||||
return &domain.Notification{
|
||||
ID: uuid.New().String(),
|
||||
Type: domain.NotificationType(r.Type),
|
||||
Priority: domain.Priority(r.Priority),
|
||||
Status: domain.StatusPending,
|
||||
Subject: r.Subject,
|
||||
Body: r.Body,
|
||||
Recipients: r.Recipients,
|
||||
Metadata: r.Metadata,
|
||||
CreatedAt: time.Now(),
|
||||
ScheduledFor: r.ScheduledFor,
|
||||
MaxRetries: maxRetries,
|
||||
RetryCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// SendNotificationResponse is the REST API response for sending a notification
|
||||
type SendNotificationResponse struct {
|
||||
Result NotificationResult `json:"result"`
|
||||
}
|
||||
|
||||
// SendBatchNotificationsRequest is the REST API request for sending multiple notifications
|
||||
type SendBatchNotificationsRequest struct {
|
||||
Notifications []SendNotificationRequest `json:"notifications"`
|
||||
}
|
||||
|
||||
// SendBatchNotificationsResponse is the REST API response for sending multiple notifications
|
||||
type SendBatchNotificationsResponse struct {
|
||||
Results []NotificationResult `json:"results"`
|
||||
}
|
||||
|
||||
// Notification represents a notification in the REST API
|
||||
type Notification struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Priority int `json:"priority"`
|
||||
Status string `json:"status"`
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ScheduledFor *time.Time `json:"scheduled_for,omitempty"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
MaxRetries int `json:"max_retries"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
}
|
||||
|
||||
// NotificationFromDomain converts a domain notification to API format
|
||||
func NotificationFromDomain(n *domain.Notification) Notification {
|
||||
return Notification{
|
||||
ID: n.ID,
|
||||
Type: string(n.Type),
|
||||
Priority: int(n.Priority),
|
||||
Status: string(n.Status),
|
||||
Subject: n.Subject,
|
||||
Body: n.Body,
|
||||
Recipients: n.Recipients,
|
||||
Metadata: n.Metadata,
|
||||
CreatedAt: n.CreatedAt,
|
||||
ScheduledFor: n.ScheduledFor,
|
||||
SentAt: n.SentAt,
|
||||
RetryCount: n.RetryCount,
|
||||
MaxRetries: n.MaxRetries,
|
||||
LastError: n.LastError,
|
||||
}
|
||||
}
|
||||
|
||||
// NotificationResult represents the result of a notification operation
|
||||
type NotificationResult struct {
|
||||
NotificationID string `json:"notification_id"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
SentAt time.Time `json:"sent_at"`
|
||||
ProviderResponse map[string]interface{} `json:"provider_response,omitempty"`
|
||||
}
|
||||
|
||||
// NotificationResultFromDomain converts a domain result to API format
|
||||
func NotificationResultFromDomain(r *domain.NotificationResult) NotificationResult {
|
||||
return NotificationResult{
|
||||
NotificationID: r.NotificationID,
|
||||
Success: r.Success,
|
||||
Message: r.Message,
|
||||
Error: r.Error,
|
||||
SentAt: r.SentAt,
|
||||
ProviderResponse: r.ProviderResponse,
|
||||
}
|
||||
}
|
||||
|
||||
// ListNotificationsResponse is the REST API response for listing notifications
|
||||
type ListNotificationsResponse struct {
|
||||
Notifications []Notification `json:"notifications"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// RetryNotificationResponse is the REST API response for retrying a notification
|
||||
type RetryNotificationResponse struct {
|
||||
Result NotificationResult `json:"result"`
|
||||
}
|
||||
Reference in New Issue
Block a user