Skip to content

Commit

Permalink
add notifications backend service
Browse files Browse the repository at this point in the history
  • Loading branch information
rudream committed Mar 1, 2024
1 parent 768e4ac commit fdf95dc
Show file tree
Hide file tree
Showing 12 changed files with 1,598 additions and 809 deletions.
127 changes: 64 additions & 63 deletions api/gen/proto/go/teleport/notifications/v1/notifications.pb.go

Large diffs are not rendered by default.

779 changes: 251 additions & 528 deletions api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions api/proto/teleport/notifications/v1/notifications.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
Expand Down Expand Up @@ -34,7 +34,7 @@ message Notification {
string sub_kind = 2;
// version is the resource version.
string version = 3;
// metadata is the notification's metadata. This contains the notification's title, description, labels, and expiry.
// metadata is the notification's metadata. This contains the notification's labels, and expiry. All custom notification metadata should be stored in labels.
teleport.header.v1.Metadata metadata = 4;
// spec is the notification specification.
NotificationSpec spec = 5;
Expand Down Expand Up @@ -146,15 +146,17 @@ message UserLastSeenNotification {
teleport.header.v1.Metadata metadata = 4;
// UserLastSeenNotificationSpec is the user last seen notification item's specification.
UserLastSeenNotificationSpec spec = 5;
// time is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified.
UserLastSeenNotificationTime time = 6;
reserved 6;
reserved "time";
// status is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified.
UserLastSeenNotificationStatus status = 7;
}

// UserLastSeenNotificationSpec is a user last seen notification specification.
message UserLastSeenNotificationSpec {}

// UserLastSeenNotificationTime is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified.
message UserLastSeenNotificationTime {
// UserLastSeenNotificationStatus is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified.
message UserLastSeenNotificationStatus {
// last_seen_time is the timestamp of the last notification that the user has seen.
google.protobuf.Timestamp last_seen_time = 1;
}
57 changes: 14 additions & 43 deletions api/proto/teleport/notifications/v1/notifications_service.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
Expand Down Expand Up @@ -31,31 +31,26 @@ service NotificationService {
rpc CreateUserNotification(CreateUserNotificationRequest) returns (Notification);
// DeleteUserNotification deletes a user-specific notification.
rpc DeleteUserNotification(DeleteUserNotificationRequest) returns (google.protobuf.Empty);
// ListUserNotifications returns a page of user-specific notifications.
rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse);

// CreateGlobalNotification creates a global notification.
rpc CreateGlobalNotification(CreateGlobalNotificationRequest) returns (GlobalNotification);
// DeleteGlobalNotification deletes a global notification.
rpc DeleteGlobalNotification(DeleteGlobalNotificationRequest) returns (google.protobuf.Empty);
// ListGlobalNotifications returns a page of global notifications.
rpc ListGlobalNotifications(ListGlobalNotificationsRequest) returns (ListGlobalNotificationsResponse);

// CreateUserNotificationState creates a user notification state which records whether a user has clicked on or dismissed a notification.
rpc CreateUserNotificationState(CreateUserNotificationStateRequest) returns (UserNotificationState);
// UpdateUserNotificationState updates a user notification state to record whether a user has clicked on or dismissed a notification.
rpc UpdateUserNotificationState(UpdateUserNotificationStateRequest) returns (UserNotificationState);
// ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them.
rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse);

// UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification.
rpc UpsertUserNotificationState(UpsertUserNotificationStateRequest) returns (UserNotificationState);
// DeleteUserNotificationState deletes a user notification state object.
rpc DeleteUserNotificationState(DeleteUserNotificationStateRequest) returns (google.protobuf.Empty);
// ListUserNotificationStates returns a page of user notification states.
// ListUserNotificationStates returns a page of a user's notification states.
rpc ListUserNotificationStates(ListUserNotificationStatesRequest) returns (ListUserNotificationStatesResponse);

// GetUserLastSeenNotification returns a user's last seen notification item.
rpc GetUserLastSeenNotification(GetUserLastSeenNotificationRequest) returns (UserLastSeenNotification);
// CreateUserLastSeenNotification creates a user's last seen notification item.
rpc CreateUserLastSeenNotification(CreateUserLastSeenNotificationRequest) returns (UserLastSeenNotification);
// UpdateUserLastSeenNotification updates a user's last seen notification item.
rpc UpdateUserLastSeenNotification(UpdateUserLastSeenNotificationRequest) returns (UserLastSeenNotification);
// UpsertUserLastSeenNotification creates or updates a user's last seen notification item.
rpc UpsertUserLastSeenNotification(UpsertUserLastSeenNotificationRequest) returns (UserLastSeenNotification);
// DeleteUserLastSeenNotification deletes a user's last seen notification item.
rpc DeleteUserLastSeenNotification(DeleteUserLastSeenNotificationRequest) returns (google.protobuf.Empty);
}
Expand All @@ -76,7 +71,7 @@ message DeleteUserNotificationRequest {
string notification_id = 2;
}

// ListUserNotificationsRequest is the request for listing a user's user-specific notifications.
// ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them.
message ListUserNotificationsRequest {
// username is the username of the user the notifications to list are for.
string username = 1;
Expand Down Expand Up @@ -106,24 +101,8 @@ message DeleteGlobalNotificationRequest {
string notification_id = 1;
}

// ListGlobalNotificationsRequest is the request for listing global notifications.
message ListGlobalNotificationsRequest {
// page_size is the size of the page to return.
int32 page_size = 1;
// page_token is the next_page_token value returned from a previous ListGlobalNotifications request, if any.
string page_token = 2;
}

// ListGlobalNotificationsResponse is the response from listing global notifications.
message ListGlobalNotificationsResponse {
// global_notifications is the global notification items returned.
repeated GlobalNotification global_notifications = 1;
// next_page_token is the token to retrieve the next page of results, this will be empty if there are no more results.
string next_page_token = 2;
}

// CreateUserNotificationStateRequest is the request for creating a user notification state.
message CreateUserNotificationStateRequest {
// UpsertUserNotificationStateRequest is the request for creating or updating a user notification state.
message UpsertUserNotificationStateRequest {
// username is the username of the user.
string username = 1;
// user_notification_state is the user notification state to create.
Expand Down Expand Up @@ -166,16 +145,8 @@ message GetUserLastSeenNotificationRequest {
string username = 1;
}

// CreateUserLastSeenNotification is the request for creating a user's last seen notification item.
message CreateUserLastSeenNotificationRequest {
// username is the username of the user.
string username = 1;
// user_notification_state is the user last seen notification item to create.
UserLastSeenNotification user_last_seen_notification = 2;
}

// UpdateUserLastSeenNotificationRequest is the request for updating a user's last seen notification.
message UpdateUserLastSeenNotificationRequest {
// UpsertUserLastSeenNotificationRequest is the request for creating or updating a user's last seen notification.
message UpsertUserLastSeenNotificationRequest {
// username is the username of the user.
string username = 1;
// user_notification_state is the udpated user last seen notification item.
Expand Down
9 changes: 9 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,15 @@ const (
// KindSecurityReportCostLimiter const limiter
KindSecurityReportCostLimiter = "security_report_cost_limiter"

// KindNotification is a notification resource.
KindNotification = "notification"
// KindGlobalNotification is a global notification resource.
KindGlobalNotification = "global_notification"
// KindUserLastSeenNotification is a resource which stores the timestamp of a user's last seen notification.
KindUserLastSeenNotification = "user_last_seen_notification"
// KindUserNotificationState is a resource which tracks whether a user has clicked on or dismissed a notification.
KindUserNotificationState = "user_notification_state"

// V7 is the seventh version of resources.
V7 = "v7"

Expand Down
25 changes: 25 additions & 0 deletions lib/services/local/generic/generic_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package generic

import (
"context"
"strings"

"github.com/gravitational/trace"

Expand Down Expand Up @@ -62,6 +63,24 @@ type ServiceWrapper[T types.ResourceMetadata] struct {
service *Service[resourceMetadataAdapter[T]]
}

// WithPrefix will return a service wrapper with the given parts appended to the backend prefix.
func (s ServiceWrapper[T]) WithPrefix(parts ...string) *ServiceWrapper[T] {
if len(parts) == 0 {
return &s
}

return &ServiceWrapper[T]{
service: &Service[resourceMetadataAdapter[T]]{
backend: s.service.backend,
resourceKind: s.service.resourceKind,
pageLimit: s.service.pageLimit,
backendPrefix: strings.Join(append([]string{s.service.backendPrefix}, parts...), string(backend.Separator)),
marshalFunc: s.service.marshalFunc,
unmarshalFunc: s.service.unmarshalFunc,
},
}
}

// UpsertResource upserts a resource.
func (s ServiceWrapper[T]) UpsertResource(ctx context.Context, resource T) (T, error) {
adapter, err := s.service.UpsertResource(ctx, newResourceMetadataAdapter(resource))
Expand Down Expand Up @@ -91,6 +110,12 @@ func (s ServiceWrapper[T]) DeleteResource(ctx context.Context, name string) erro
return trace.Wrap(s.service.DeleteResource(ctx, name))
}

// DeleteAllResources removes all resources.
func (s ServiceWrapper[T]) DeleteAllResources(ctx context.Context) error {
startKey := backend.ExactKey(s.service.backendPrefix)
return trace.Wrap(s.service.backend.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)))
}

// ListResources returns a paginated list of resources.
func (s ServiceWrapper[T]) ListResources(ctx context.Context, pageSize int, pageToken string) ([]T, string, error) {
adapters, nextToken, err := s.service.ListResources(ctx, pageSize, pageToken)
Expand Down
28 changes: 28 additions & 0 deletions lib/services/local/generic/generic_wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,31 @@ func TestGenericWrapperCRUD(t *testing.T) {
err = service.DeleteResource(ctx, "doesnotexist")
require.True(t, trace.IsNotFound(err))
}

// TestGenericWrapperWithPrefix tests the withPrefix method of the generic service wrapper.
func TestGenericWrapperWithPrefix(t *testing.T) {
ctx := context.Background()

memBackend, err := memory.New(memory.Config{
Context: ctx,
Clock: clockwork.NewFakeClock(),
})
require.NoError(t, err)

const initialBackendPrefix = "initial_prefix"
const additionalBackendPrefix = "additional_prefix"

service, err := NewServiceWrapper[*testResource153](memBackend,
"generic resource",
initialBackendPrefix,
marshalResource153,
unmarshalResource153)
require.NoError(t, err)

// Verify that the service's backend prefix matches the initial backend prefix.
require.Equal(t, initialBackendPrefix, service.service.backendPrefix)

// Verify that withPrefix appends the the additional prefix.
serviceWithPrefix := service.WithPrefix(additionalBackendPrefix)
require.Equal(t, "initial_prefix/additional_prefix", serviceWithPrefix.service.backendPrefix)
}
Loading

0 comments on commit fdf95dc

Please sign in to comment.