Skip to content

Conversation

@tiankii
Copy link

@tiankii tiankii commented Dec 10, 2025

Summary by CodeRabbit

  • New Features

    • Notifications: add multi‑language message templates and message records with create/update/delete/list and status tracking.
    • Admin API: new admin GraphQL queries & mutations to manage templates and messages.
    • Public APIs: gRPC, service client wrappers and TypeScript types for template/message operations.
  • Chores

    • Database migrations added to create templates/messages, indexes, and a rollback script.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds notification templates and messages: DB migrations, Rust repositories and gRPC server handlers, protobuf and generated JS/TS bindings, gRPC client/service wrappers, GraphQL types/queries/mutations, and API wiring to create/list/update/delete templates and create/list/update messages.

Changes

Cohort / File(s) Summary
Database migrations
core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql, core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.down.sql
Adds an up migration creating msg_templates and msg_messages tables, constraints, enums/checks and indexes; adds a down migration that drops the indexes and tables (rollback).
Core notifications — Rust modules & app wiring
core/notifications/src/lib.rs, core/notifications/src/app/mod.rs
Registers msg_templates and msg_messages modules; injects MsgTemplateRepository and MsgMessageRepository into NotificationsApp; adds async app methods to create/update/delete/list templates and create/update/list messages.
Template repo (Rust)
core/notifications/src/msg_templates/mod.rs, core/notifications/src/msg_templates/repo.rs
New MsgTemplate model and MsgTemplateRepository with new, create_template, update_template, delete_template, and list_templates (supports optional pagination; uses write/read pools).
Message repo (Rust)
core/notifications/src/msg_messages/mod.rs, core/notifications/src/msg_messages/repo.rs
New MsgMessage model and MsgMessageRepository with new, create_message, update_message_status, and list_messages (supports optional pagination; uses write/read pools).
gRPC server & conversion (Rust)
core/notifications/src/grpc/server/mod.rs, core/notifications/src/grpc/server/convert.rs
Adds server-side RPC handlers for template/message create/update/delete/list, UUID validation and error mapping, and conversions mapping domain structs to proto messages (including status and timestamp mapping).
Protobuf definitions
core/notifications/proto/notifications.proto, core/api/src/services/notifications/proto/notifications.proto
Adds messages/enums (MsgTemplate, MsgMessage, MsgMessageStatus) and seven RPCs: MsgTemplateCreate/Update/Delete/TemplatesList and MsgMessageCreate/UpdateStatus/MessagesList with corresponding request/response messages.
Generated protobuf/grpc artifacts (JS/TS)
core/api/src/services/notifications/proto/notifications_pb.d.ts, core/api/src/services/notifications/proto/notifications_grpc_pb.d.ts, core/api/src/services/notifications/proto/notifications_grpc_pb.js
Adds generated TS/JS protobuf classes, enums and gRPC service/client descriptors for the new RPCs; updates client constructor export and adds method descriptors.
gRPC client wrappers (JS)
core/api/src/services/notifications/grpc-client.ts
Adds promisified client functions for each new notifications RPC and imports the corresponding request/response types.
Notifications service (JS) integration
core/api/src/services/notifications/index.ts
Adds conversion helpers between proto and domain status and exposes new service methods wrapping the gRPC calls: msgTemplateCreate/Update/Delete/TemplatesList and msgMessageCreate/UpdateStatus/MessagesList.
Frontend/domain types
core/api/src/domain/notifications/index.types.d.ts
Adds types MsgMessageStatus, MsgTemplate, MsgMessage and extends INotificationsService with create/update/delete/list methods for templates and create/update/list for messages.
GraphQL API (admin)
core/api/src/graphql/admin/mutations.ts, core/api/src/graphql/admin/queries.ts, core/api/src/graphql/admin/root/mutation/notification-*.ts, core/api/src/graphql/admin/root/query/notification-*.ts, core/api/src/graphql/admin/types/object/notification-*.ts, core/api/src/graphql/admin/schema.graphql
Adds GraphQL object types for NotificationTemplate/NotificationMessage; adds admin queries notificationTemplates/notificationMessages; adds mutations for template create/update/delete and message create/updateStatus; wires new fields into admin Query/Mutation types and resolvers with argument validation and error mapping.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as GraphQL Client / Admin UI
  participant API as GraphQL Server
  participant Svc as JS NotificationsService
  participant gRPC as gRPC Client
  participant Rust as Notifications gRPC Server
  participant DB as Postgres

  Client->>API: GraphQL mutation/query (template/message create/list/update/delete)
  API->>Svc: call msgTemplate*/msgMessage*(...)
  Svc->>gRPC: unary RPC (MsgTemplate*/MsgMessage*Request)
  gRPC->>Rust: invoke RPC handler
  Rust->>DB: repository SQL (INSERT/UPDATE/DELETE/SELECT)
  DB-->>Rust: rows
  Rust-->>gRPC: proto response (MsgTemplate/MsgMessage)
  gRPC-->>Svc: RPC result
  Svc-->>API: mapped result or error
  API-->>Client: GraphQL response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Check SQL migration correctness (enum/check, index names, down ordering).
  • Review repository queries for parameter binding, RETURNING usage, and read vs write pool selection.
  • Validate proto ↔ domain enum mappings and timestamp conversions in conversions and server handlers.
  • Confirm generated JS/TS gRPC artifacts align with client wrappers and service usage.
  • Inspect GraphQL resolvers for proper input validation and consistent error mapping.

Poem

🐇 I hopped through proto, SQL, and code,

Templates and messages on the road,
Repos snug and neat,
RPCs in a beat,
A tiny rabbit’s feature now bestowed 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: notification message templates' accurately reflects the primary objective of adding comprehensive message template and message handling functionality across the notifications system.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@tiankii tiankii marked this pull request as ready for review December 10, 2025 14:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql (1)

3-11: Redundant index on unique constraint.

In PostgreSQL, UNIQUE constraints automatically create an index on the constrained columns. The explicit index idx_msg_templates_name_language_code (lines 21–22) duplicates the index created by the UNIQUE constraint (line 10), adding maintenance overhead without benefit.

Remove the redundant index definition:

-CREATE INDEX idx_msg_templates_name_language_code
-  ON msg_templates (name, language_code);
-
 CREATE INDEX idx_msg_messages_username

Also applies to: 21-22

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a85ba1e and a183e53.

📒 Files selected for processing (2)
  • core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.down.sql (1 hunks)
  • core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql (1 hunks)
🔇 Additional comments (3)
core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql (2)

13-19: Add foreign key constraints for referential integrity.

The username and sent_by columns appear to be user identifiers but lack foreign key constraints. Without referential integrity, orphaned records could accumulate if users are deleted, and it's unclear whether these should reference a users table.

Additionally, consider whether msg_messages should reference msg_templates if messages are associated with specific templates.

Please confirm the schema design:

  • Should username and sent_by have foreign key constraints to a users table?
  • Should msg_messages include a foreign key reference to msg_templates?
  • Should msg_messages have a created_at timestamp for audit purposes?

If foreign keys are needed, consider adding them in this migration to ensure data consistency from the start.


24-28: Index choices are appropriate.

The indexes on username and status are well-chosen for filtering operations. These are not redundant and support common query patterns.

core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.down.sql (1)

3-8: Rollback logic is sound.

The down migration correctly drops indexes before tables and uses IF EXISTS for idempotency. Once the redundant index in the up migration is removed, the down migration will continue to work correctly because the IF EXISTS clause safely handles the absence of that index.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
core/notifications/src/msg_messages/repo.rs (3)

8-15: Consider using an enum for the status field.

The status field is currently a String, which allows any arbitrary value. Consider defining a MessageStatus enum to ensure type safety and prevent invalid status values at compile time.

Additionally, consider adding a created_at field to track when the message was originally created, distinct from updated_at.


23-29: Inconsistent constructor pattern across repositories.

This constructor takes references and clones them (&PgPool, &ReadPool), while NotificationHistory::new takes ownership directly (pool: PgPool). Consider standardizing the constructor pattern across all repositories for consistency.


73-135: Reduce code duplication in query construction.

The method has four nearly identical branches that differ only in the presence of LIMIT/OFFSET clauses. Consider consolidating this using dynamic query building or a query builder pattern.

Example refactor using dynamic SQL construction:

pub async fn list_messages(
    &self,
    limit: Option<i64>,
    offset: Option<i64>,
) -> Result<Vec<MsgMessage>, sqlx::Error> {
    let mut query = String::from(
        "SELECT id, username, status, sent_by, updated_at 
         FROM msg_messages 
         ORDER BY updated_at DESC"
    );
    
    let mut bindings = vec![];
    
    if let Some(lim) = limit {
        query.push_str(&format!(" LIMIT ${}", bindings.len() + 1));
        bindings.push(lim);
    }
    
    if let Some(off) = offset {
        query.push_str(&format!(" OFFSET ${}", bindings.len() + 1));
        bindings.push(off);
    }
    
    let mut q = sqlx::query_as::<_, MsgMessage>(&query);
    for binding in bindings {
        q = q.bind(binding);
    }
    
    Ok(q.fetch_all(self.read_pool.inner()).await?)
}
core/notifications/src/msg_templates/repo.rs (2)

6-14: Consider stronger typing for language_code.

The language_code field is a String, which allows any value. Consider using a validated type or enum to ensure only valid language codes are stored.


101-163: Reduce code duplication in query construction.

This method has the same four-branch duplication pattern as list_messages in the msg_messages repository. Consider applying the same dynamic query building approach to consolidate the logic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a183e53 and 6812106.

📒 Files selected for processing (6)
  • core/notifications/src/app/mod.rs (5 hunks)
  • core/notifications/src/lib.rs (1 hunks)
  • core/notifications/src/msg_messages/mod.rs (1 hunks)
  • core/notifications/src/msg_messages/repo.rs (1 hunks)
  • core/notifications/src/msg_templates/mod.rs (1 hunks)
  • core/notifications/src/msg_templates/repo.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
core/notifications/src/app/mod.rs (4)
core/notifications/src/msg_templates/repo.rs (1)
  • new (23-28)
core/notifications/src/msg_messages/repo.rs (1)
  • new (24-29)
core/notifications/src/history/repo.rs (1)
  • new (15-17)
core/notifications/src/user_notification_settings/repo.rs (1)
  • new (17-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: execute via bats
  • GitHub Check: execute via buck2
  • GitHub Check: Migrate Mongodb
  • GitHub Check: Check SDLs
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
core/notifications/src/lib.rs (1)

10-11: LGTM!

The module declarations follow the established pattern and correctly integrate the new msg_templates and msg_messages modules into the crate structure.

core/notifications/src/msg_templates/mod.rs (1)

1-3: LGTM!

The module structure follows Rust best practices by declaring the repo submodule and re-exporting the public types for cleaner imports.

core/notifications/src/msg_messages/mod.rs (1)

1-3: LGTM!

Consistent with the msg_templates module structure, correctly exposing MsgMessage and MsgMessageRepository.

core/notifications/src/msg_messages/repo.rs (2)

31-50: LGTM!

The create_message method correctly uses parameterized queries and the RETURNING clause to retrieve the inserted row.


52-71: Verify error handling for non-existent message IDs.

The fetch_one call returns a RowNotFound error if the message ID doesn't exist. Confirm that callers properly handle this error case, or consider defining a custom error type (e.g., MessageNotFound) for clearer error semantics.

core/notifications/src/msg_templates/repo.rs (2)

30-53: LGTM!

The create_template method correctly uses parameterized queries and returns the newly created template.


55-99: Verify error handling for non-existent template IDs.

Both update_template and delete_template use fetch_one, which returns a RowNotFound error if the template ID doesn't exist. Ensure that callers handle this appropriately—either by explicitly catching sqlx::Error::RowNotFound or by mapping to a domain-specific error type before returning to the API layer.

core/notifications/src/app/mod.rs (9)

13-14: LGTM!

The imports correctly bring in the new repository types needed for the messaging feature.


27-28: LGTM!

The repository fields are correctly added to the NotificationsApp struct, following the established pattern for other repositories.


45-46: LGTM!

The repositories are correctly instantiated with the pool and read_pool, consistent with other repository initialization in this method.


68-69: LGTM!

The repositories are properly included in the NotificationsApp construction.


392-406: LGTM!

The method correctly delegates to the repository and handles the result appropriately.


408-423: LGTM!

The update method properly forwards all parameters to the repository.


425-432: LGTM!

The delete method correctly delegates to the repository and returns the deleted ID.


448-460: LGTM!

The message creation method correctly delegates to the repository.


462-473: LGTM!

The status update method properly forwards the parameters to the repository.

Comment on lines 434 to 446
#[instrument(name = "app.msg_templates_list", skip(self), err)]
pub async fn list_msg_templates(
&self,
_language_code: Option<String>,
limit: Option<i64>,
offset: Option<i64>,
) -> Result<Vec<crate::msg_templates::MsgTemplate>, ApplicationError> {
let templates = self
.msg_template_repository
.list_templates(limit, offset)
.await?;
Ok(templates)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove unused parameter or implement filtering.

The _language_code parameter is accepted but never used. This creates a misleading API where callers might expect filtering by language code, but no filtering occurs. Either:

  1. Remove the parameter entirely until filtering is implemented, or
  2. Implement the filtering logic in the repository layer

The underscore prefix indicates the parameter is intentionally unused, but this suggests incomplete functionality rather than a deliberate design choice.

If filtering should be implemented, the repository's list_templates method would need to be updated to accept and apply a language_code filter in the SQL WHERE clause.

🤖 Prompt for AI Agents
In core/notifications/src/app/mod.rs around lines 434 to 446, the _language_code
parameter is accepted but unused; either remove the parameter from the function
signature and any callers (and update the instrument attribute if needed) or
implement language filtering by: adding a language_code Option<String> argument
to the repository's list_templates method, propagate that parameter through this
method, update the repository signature and its SQL/query logic to include a
WHERE clause that filters by language_code when provided, and adjust any
unit/integration tests and call sites to pass or omit the language_code
accordingly.

Comment on lines 475 to 488
#[instrument(name = "app.msg_messages_list", skip(self), err)]
pub async fn list_msg_messages(
&self,
_username: Option<String>,
_status: Option<String>,
limit: Option<i64>,
offset: Option<i64>,
) -> Result<Vec<crate::msg_messages::MsgMessage>, ApplicationError> {
let messages = self
.msg_message_repository
.list_messages(limit, offset)
.await?;
Ok(messages)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove unused parameters or implement filtering.

The _username and _status parameters are accepted but never used. This creates a misleading API where callers expect filtering capabilities that don't exist. Either:

  1. Remove these parameters until filtering is implemented, or
  2. Implement the filtering logic in the repository layer

The underscore prefixes indicate intentionally unused parameters, suggesting this is incomplete functionality that should be addressed before merging.

To implement filtering, the repository's list_messages method would need to accept these parameters and apply them in SQL WHERE clauses.

🤖 Prompt for AI Agents
In core/notifications/src/app/mod.rs around lines 475-488 the parameters
_username and _status are accepted but unused, creating a misleading API; either
remove them from the function signature (and update any callers/tests and the
instrument macro to reflect the new signature) or implement filtering by
extending the repository API to accept Option<String> username and status, pass
those values through from this method to msg_message_repository.list_messages,
and update the repository SQL/ORM query to apply WHERE clauses when the options
are Some(...) (plus adjust tests).

@github-actions github-actions bot added the core label Dec 11, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/api/src/services/notifications/proto/notifications.proto (1)

17-448: Remove duplicate proto definition or establish single source of truth.

The file core/api/src/services/notifications/proto/notifications.proto is identical to core/notifications/proto/notifications.proto. Maintaining duplicate proto definitions creates maintenance burden and risk of divergence. Consolidate to a single source and use proto imports if cross-service definitions are needed.

However, note: there is a real type inconsistency in the status field across message definitions that applies to both files—MsgMessage.status uses the enum type MsgMessageStatus, while MsgMessageCreateRequest.status and MsgMessageUpdateStatusRequest.status use the string type. This inconsistency should be resolved by standardizing on a single type.

🧹 Nitpick comments (8)
core/notifications/src/grpc/server/convert.rs (1)

363-379: Consider logging unknown status values instead of silently defaulting to Pending.

The fallback to Pending for unrecognized status strings (Line 369) could mask data integrity issues. If an unexpected status value appears, it will be silently converted to Pending without any indication of the problem.

+use tracing::warn;
+
 impl From<crate::msg_messages::MsgMessage> for proto::MsgMessage {
     fn from(message: crate::msg_messages::MsgMessage) -> Self {
         let status = match message.status.as_str() {
             "pending" => proto::MsgMessageStatus::Pending,
             "accepted" => proto::MsgMessageStatus::Accepted,
             "revoked" => proto::MsgMessageStatus::Revoked,
-            _ => proto::MsgMessageStatus::Pending,
+            unknown => {
+                warn!(status = %unknown, "Unknown MsgMessage status, defaulting to Pending");
+                proto::MsgMessageStatus::Pending
+            }
         };
core/api/src/graphql/admin/root/mutation/notification-template-delete.ts (1)

32-34: Inconsistent error handling for input validation errors.

The input validation error returns a raw object with only message, while service errors at line 41 use mapAndParseErrorForGqlResponse which provides message, path, and code. This inconsistency may cause issues for clients expecting a uniform error structure.

Consider aligning the error format or using a consistent helper function for all error responses.

core/api/src/graphql/admin/root/mutation/notification-message-update-status.ts (2)

10-10: Consider using an enum type for status instead of a raw string.

Using GT.NonNull(GT.String) allows any arbitrary string value. If there's a defined set of valid message statuses (e.g., "pending", "sent", "delivered"), consider defining a GraphQL enum type to provide:

  • Input validation at the GraphQL layer
  • Self-documenting API with available options
  • Better type safety

34-36: Inconsistent error handling for input validation.

Same pattern as in notification-template-delete.ts - the error object is missing path and code fields that mapAndParseErrorForGqlResponse provides at line 46.

core/api/src/graphql/admin/root/mutation/notification-template-create.ts (1)

41-43: Inconsistent error handling for input validation.

Same pattern as other mutations - the error object at line 42 lacks path and code fields present in mapAndParseErrorForGqlResponse output at line 56. Consider standardizing the error format across all error responses.

core/api/src/graphql/admin/root/mutation/notification-message-create.ts (1)

9-11: Consider adding validation for the status field.

The status field accepts any string value, but the domain type MsgMessageStatus only allows "pending", "accepted", or "revoked". Invalid values will be silently converted to "pending" by the service layer (see line 81 in core/api/src/services/notifications/index.ts). Consider either:

  1. Creating a GraphQL enum type for MsgMessageStatus and using it here for compile-time validation, or
  2. Adding runtime validation in the resolver to reject invalid status values with a clear error message.

Example with GraphQL enum:

+const MsgMessageStatusEnum = GT.Enum({
+  name: "MsgMessageStatus",
+  values: {
+    PENDING: { value: "pending" },
+    ACCEPTED: { value: "accepted" },
+    REVOKED: { value: "revoked" },
+  },
+})
+
 const NotificationMessageCreateInput = GT.Input({
   name: "NotificationMessageCreateInput",
   fields: () => ({
     username: { type: GT.NonNull(GT.String) },
-    status: { type: GT.NonNull(GT.String) },
+    status: { type: GT.NonNull(MsgMessageStatusEnum) },
     sentBy: { type: GT.NonNull(GT.String) },
   }),
 })
core/api/src/graphql/admin/root/mutation/notification-template-update.ts (1)

43-49: Consider consistent error handling pattern.

The error handling for id and languageCode validation (lines 43-49) returns errors with just a message field, while service errors (line 63) use mapAndParseErrorForGqlResponse which includes code and path fields. This inconsistency in error structure may complicate client-side error handling.

Consider wrapping validation errors through a similar mapper for consistency:

 if (id instanceof Error) {
-  return { errors: [{ message: id.message }], success: false }
+  return { errors: [mapAndParseErrorForGqlResponse(id)], success: false }
 }

 if (languageCode instanceof Error) {
-  return { errors: [{ message: languageCode.message }], success: false }
+  return { errors: [mapAndParseErrorForGqlResponse(languageCode)], success: false }
 }
core/notifications/proto/notifications.proto (1)

356-360: Follow protobuf enum naming conventions.

The enum values should be prefixed with the enum name, and the zero value should represent an unspecified/unknown state for forward compatibility.

Apply this diff to align with protobuf style guide:

 enum MsgMessageStatus {
-  PENDING = 0;
-  ACCEPTED = 1;
-  REVOKED = 2;
+  MSG_MESSAGE_STATUS_UNSPECIFIED = 0;
+  MSG_MESSAGE_STATUS_PENDING = 1;
+  MSG_MESSAGE_STATUS_ACCEPTED = 2;
+  MSG_MESSAGE_STATUS_REVOKED = 3;
 }

As per static analysis hints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6812106 and 669a4cc.

📒 Files selected for processing (21)
  • core/api/src/domain/notifications/index.types.d.ts (2 hunks)
  • core/api/src/graphql/admin/mutations.ts (2 hunks)
  • core/api/src/graphql/admin/queries.ts (2 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-message-create.ts (1 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-message-update-status.ts (1 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-template-create.ts (1 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-template-delete.ts (1 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-template-update.ts (1 hunks)
  • core/api/src/graphql/admin/root/query/notification-messages.ts (1 hunks)
  • core/api/src/graphql/admin/root/query/notification-templates.ts (1 hunks)
  • core/api/src/graphql/admin/types/object/notification-message.ts (1 hunks)
  • core/api/src/graphql/admin/types/object/notification-template.ts (1 hunks)
  • core/api/src/services/notifications/grpc-client.ts (2 hunks)
  • core/api/src/services/notifications/index.ts (4 hunks)
  • core/api/src/services/notifications/proto/notifications.proto (2 hunks)
  • core/api/src/services/notifications/proto/notifications_grpc_pb.d.ts (5 hunks)
  • core/api/src/services/notifications/proto/notifications_grpc_pb.js (2 hunks)
  • core/api/src/services/notifications/proto/notifications_pb.d.ts (2 hunks)
  • core/notifications/proto/notifications.proto (2 hunks)
  • core/notifications/src/grpc/server/convert.rs (1 hunks)
  • core/notifications/src/grpc/server/mod.rs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
core/notifications/src/grpc/server/convert.rs (1)
core/api/src/services/notifications/proto/notifications_pb.d.ts (2)
  • MsgTemplate (1031-1053)
  • MsgMessage (1066-1086)
core/api/src/services/notifications/proto/notifications_grpc_pb.d.ts (1)
core/api/src/services/notifications/proto/notifications_pb.d.ts (14)
  • MsgTemplateCreateRequest (1098-1118)
  • MsgTemplateCreateResponse (1130-1145)
  • MsgTemplateUpdateRequest (1153-1175)
  • MsgTemplateUpdateResponse (1188-1203)
  • MsgTemplateDeleteRequest (1211-1223)
  • MsgTemplateDeleteResponse (1231-1243)
  • MsgTemplatesListRequest (1251-1267)
  • MsgTemplatesListResponse (1277-1291)
  • MsgMessageCreateRequest (1299-1315)
  • MsgMessageCreateResponse (1325-1340)
  • MsgMessageUpdateStatusRequest (1348-1362)
  • MsgMessageUpdateStatusResponse (1371-1386)
  • MsgMessagesListRequest (1394-1408)
  • MsgMessagesListResponse (1417-1431)
core/api/src/graphql/admin/root/mutation/notification-message-create.ts (2)
core/api/src/services/notifications/index.ts (1)
  • NotificationsService (93-971)
core/api/src/graphql/error-map.ts (1)
  • mapAndParseErrorForGqlResponse (917-924)
core/api/src/graphql/admin/root/query/notification-messages.ts (2)
core/api/src/services/notifications/index.ts (1)
  • NotificationsService (93-971)
core/api/src/graphql/error-map.ts (1)
  • mapAndParseErrorForGqlResponse (917-924)
core/api/src/graphql/admin/root/query/notification-templates.ts (2)
core/api/src/services/notifications/index.ts (1)
  • NotificationsService (93-971)
core/api/src/graphql/error-map.ts (1)
  • mapAndParseErrorForGqlResponse (917-924)
core/api/src/domain/notifications/index.types.d.ts (3)
core/api/src/services/notifications/proto/notifications_pb.d.ts (2)
  • MsgTemplate (1031-1053)
  • MsgMessage (1066-1086)
core/api/src/services/notifications/grpc-client.ts (7)
  • msgTemplateCreate (124-128)
  • msgTemplateUpdate (130-134)
  • msgTemplateDelete (136-140)
  • msgTemplatesList (142-146)
  • msgMessageCreate (148-152)
  • msgMessageUpdateStatus (154-158)
  • msgMessagesList (160-164)
core/api/src/domain/notifications/errors.ts (1)
  • NotificationsServiceError (9-9)
core/api/src/services/notifications/proto/notifications_grpc_pb.js (1)
core/api/src/services/notifications/proto/notifications_grpc_pb.d.ts (1)
  • NotificationsServiceService (194-194)
core/notifications/src/grpc/server/mod.rs (2)
core/notifications/src/app/mod.rs (5)
  • msg_template_create (393-406)
  • msg_template_update (409-423)
  • msg_template_delete (426-432)
  • msg_message_create (449-460)
  • msg_message_update_status (463-473)
core/notifications/src/grpc/server/convert.rs (15)
  • from (10-20)
  • from (24-28)
  • from (32-41)
  • from (45-62)
  • from (66-79)
  • from (83-88)
  • from (92-97)
  • from (101-122)
  • from (126-150)
  • from (178-183)
  • from (187-229)
  • from (233-245)
  • from (291-347)
  • from (351-360)
  • from (364-379)
core/api/src/services/notifications/grpc-client.ts (1)
core/api/src/services/notifications/proto/notifications_pb.d.ts (14)
  • MsgTemplateCreateRequest (1098-1118)
  • MsgTemplateCreateResponse (1130-1145)
  • MsgTemplateUpdateRequest (1153-1175)
  • MsgTemplateUpdateResponse (1188-1203)
  • MsgTemplateDeleteRequest (1211-1223)
  • MsgTemplateDeleteResponse (1231-1243)
  • MsgTemplatesListRequest (1251-1267)
  • MsgTemplatesListResponse (1277-1291)
  • MsgMessageCreateRequest (1299-1315)
  • MsgMessageCreateResponse (1325-1340)
  • MsgMessageUpdateStatusRequest (1348-1362)
  • MsgMessageUpdateStatusResponse (1371-1386)
  • MsgMessagesListRequest (1394-1408)
  • MsgMessagesListResponse (1417-1431)
🪛 Buf (1.61.0)
core/notifications/proto/notifications.proto

357-357: Enum value name "PENDING" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


357-357: Enum zero value name "PENDING" should be suffixed with "_UNSPECIFIED".

(ENUM_ZERO_VALUE_SUFFIX)


358-358: Enum value name "ACCEPTED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


359-359: Enum value name "REVOKED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)

core/api/src/services/notifications/proto/notifications.proto

357-357: Enum value name "PENDING" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


357-357: Enum zero value name "PENDING" should be suffixed with "_UNSPECIFIED".

(ENUM_ZERO_VALUE_SUFFIX)


358-358: Enum value name "ACCEPTED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


359-359: Enum value name "REVOKED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Migrate Mongodb
  • GitHub Check: Check SDLs
  • GitHub Check: execute via bats
  • GitHub Check: execute via buck2
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (25)
core/api/src/graphql/admin/types/object/notification-message.ts (1)

1-31: LGTM!

The GraphQL object type definition is clean and correctly defines all required fields with appropriate types. The Timestamp scalar usage for updatedAt aligns with the proto definition.

core/api/src/services/notifications/proto/notifications_grpc_pb.js (1)

1-605: Generated code - no review required.

This file is auto-generated from protobuf definitions (as indicated by the header comment). The new RPC definitions and serialization handlers follow the established patterns consistently.

core/api/src/graphql/admin/queries.ts (1)

16-17: LGTM!

The new notification queries are properly imported and added to the authenticated query fields, following the established patterns for admin queries.

Also applies to: 39-40

core/api/src/graphql/admin/types/object/notification-template.ts (1)

1-35: LGTM!

The GraphQL object type correctly defines all template fields with appropriate types. Using the Language scalar for languageCode ensures proper validation of language codes.

core/notifications/src/grpc/server/convert.rs (1)

350-361: LGTM!

The MsgTemplate conversion is straightforward with clean field mapping.

core/api/src/graphql/admin/root/mutation/notification-template-delete.ts (1)

36-48: LGTM!

The service call and success response handling follow the expected patterns for admin mutations.

core/api/src/graphql/admin/root/query/notification-templates.ts (2)

25-27: Throwing a plain object instead of an Error instance.

mapAndParseErrorForGqlResponse returns a plain IError object { message, path, code }, not an Error instance. Throwing this directly may cause unexpected behavior in GraphQL error handling or logging since stack traces won't be captured.

Consider wrapping in a proper Error or using GraphQL's error utilities:

     if (args.languageCode instanceof Error) {
-      throw mapAndParseErrorForGqlResponse(args.languageCode)
+      const mapped = mapAndParseErrorForGqlResponse(args.languageCode)
+      throw new Error(mapped.message)
     }

Alternatively, verify this is the intended pattern used elsewhere in the codebase for query resolvers.


37-47: LGTM!

The service integration and response handling are implemented correctly. The pagination parameters are properly forwarded to the service layer.

core/api/src/graphql/admin/root/mutation/notification-message-update-status.ts (1)

38-52: LGTM!

Service instantiation, method call, and response handling follow the established mutation patterns correctly.

core/api/src/graphql/admin/root/query/notification-messages.ts (2)

22-28: Same pattern concern: throwing plain objects.

Same observation as in notification-templates.ts - mapAndParseErrorForGqlResponse returns a plain object, not an Error instance. Verify this is the intended pattern for query error handling.


30-39: LGTM!

The service call and response handling are correctly implemented with proper error checking and pagination support.

core/api/src/graphql/admin/root/mutation/notification-template-create.ts (1)

45-62: LGTM!

The service call correctly forwards all template fields, and the error/success response handling follows the established mutation patterns.

core/api/src/services/notifications/proto/notifications_grpc_pb.d.ts (1)

22-28: LGTM! Generated gRPC definitions.

The TypeScript declarations for the new gRPC methods are correctly generated and follow the standard gRPC patterns. All seven new RPCs (template and message operations) are properly defined with their request/response types.

Also applies to: 130-192, 208-214, 251-271, 309-329

core/api/src/domain/notifications/index.types.d.ts (1)

84-101: LGTM! Domain types properly structured.

The MsgMessageStatus, MsgTemplate, and MsgMessage types are well-defined. The service methods accept status: MsgMessageStatus | string which provides flexibility while offering type hints for the expected values ("pending", "accepted", "revoked"). The service layer handles conversion and defaults invalid values appropriately.

Also applies to: 186-200

core/notifications/src/grpc/server/mod.rs (2)

101-126: LGTM! Template handlers are well-implemented.

The template CRUD handlers properly:

  • Parse and validate UUIDs with clear error messages (lines 136-137, 167-168)
  • Handle optional language_code by checking for empty strings (lines 191-195)
  • Apply tracing instrumentation
  • Convert domain objects to proto types

Also applies to: 128-157, 159-181, 183-211


213-232: LGTM! Message handlers follow consistent patterns.

The message handlers are well-structured with:

  • Proper UUID validation for updates (lines 242-243)
  • Consistent error handling and tracing
  • Clean conversion from domain to proto types

Note: Line 271 passes None for the first two filter parameters in list_msg_messages. These appear to be reserved for future username/status filtering but aren't currently exposed through the API.

Also applies to: 234-256, 258-280

core/api/src/services/notifications/index.ts (3)

72-91: LGTM! Status conversion functions are correctly implemented.

The bidirectional conversion between MsgMessageStatus domain type and proto enum properly handles all three states (pending, accepted, revoked) with a safe default to pending for unknown values.


737-767: LGTM! Template and message service methods are well-structured.

All methods follow consistent patterns:

  • Proper request construction with proto types
  • Correct handling of optional parameters (languageCode, limit, offset)
  • Proper error handling via handleCommonNotificationErrors
  • Clean mapping of responses to domain types

The template and message list methods correctly handle pagination parameters by checking if they're provided before setting them on the request.

Also applies to: 769-802, 804-822, 824-857, 908-937


859-883: Fix proto definition for MsgMessageCreateRequest and MsgMessageUpdateStatusRequest status field type.

The proto definition defines MsgMessageCreateRequest.status and MsgMessageUpdateStatusRequest.status as string type, while msgMessageStatusToProto() returns a ProtoMsgMessageStatus enum and MsgMessage.status is typed as MsgMessageStatus enum. The double-cast as unknown as string at lines 871 and 897 is necessary to work around this mismatch. Update the proto definitions to use MsgMessageStatus status = 2 instead of string status = 2 in both request messages to align with the actual data type and eliminate the unsafe cast.

core/api/src/graphql/admin/mutations.ts (1)

12-16: LGTM! Mutations properly wired.

The new notification template and message mutations are correctly imported and integrated into the authed mutations object, following the established pattern.

Also applies to: 31-35

core/api/src/services/notifications/grpc-client.ts (1)

30-43: LGTM! gRPC client methods properly promisified.

All seven new client methods (msgTemplateCreate, msgTemplateUpdate, msgTemplateDelete, msgTemplatesList, msgMessageCreate, msgMessageUpdateStatus, msgMessagesList) follow the consistent promisification pattern and are correctly typed with their request/response pairs.

Also applies to: 124-164

core/notifications/proto/notifications.proto (3)

17-23: LGTM! RPC declarations follow standard gRPC patterns.

The new RPC methods for template and message management follow consistent naming conventions and standard CRUD patterns.


362-369: LGTM! MsgTemplate message structure is appropriate.

The template fields provide sufficient data for localized notification templates.


371-377: Verify missing fields in MsgMessage.

The message structure lacks:

  1. A created_at timestamp (only updated_at is present)
  2. A template_id field to link messages to templates

Ensure these omissions are intentional and align with the feature requirements.

core/api/src/services/notifications/proto/notifications_pb.d.ts (1)

1031-1573: Generated file reflects proto definition issues.

This TypeScript declaration file is generated from the proto definitions. The type inconsistencies observed here (e.g., status as string in lines 1302, 1351 vs. MsgMessageStatus enum in line 1092) reflect issues in the source proto files.

Fix the proto definitions in core/notifications/proto/notifications.proto and core/api/src/services/notifications/proto/notifications.proto, then regenerate this file.

Comment on lines 422 to 426
message MsgMessageCreateRequest {
string username = 1;
string status = 2;
string sent_by = 3;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix type inconsistency for status field.

The status field is defined as string here but as MsgMessageStatus enum in MsgMessage (line 374). This inconsistency will cause type mismatches in serialization and validation.

Apply this diff:

 message MsgMessageCreateRequest {
   string username = 1;
-  string status = 2;
+  MsgMessageStatus status = 2;
   string sent_by = 3;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
message MsgMessageCreateRequest {
string username = 1;
string status = 2;
string sent_by = 3;
}
message MsgMessageCreateRequest {
string username = 1;
MsgMessageStatus status = 2;
string sent_by = 3;
}
🤖 Prompt for AI Agents
In core/notifications/proto/notifications.proto around lines 422-426, the
MsgMessageCreateRequest.message has status typed as string which conflicts with
MsgMessageStatus enum used by MsgMessage (around line 374); change the field
type from string to MsgMessageStatus (keeping field number 2) so the request
uses the same enum type, then regenerate protobuf artifacts/stubs to pick up the
type change.

Comment on lines +432 to +435
message MsgMessageUpdateStatusRequest {
string id = 1;
string status = 2;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix type inconsistency for status field.

Same issue as MsgMessageCreateRequest: the status field should be MsgMessageStatus enum, not string.

Apply this diff:

 message MsgMessageUpdateStatusRequest {
   string id = 1;
-  string status = 2;
+  MsgMessageStatus status = 2;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
message MsgMessageUpdateStatusRequest {
string id = 1;
string status = 2;
}
message MsgMessageUpdateStatusRequest {
string id = 1;
MsgMessageStatus status = 2;
}
🤖 Prompt for AI Agents
In core/notifications/proto/notifications.proto around lines 432 to 435, the
MsgMessageUpdateStatusRequest currently defines status as a string; change the
field type to the MsgMessageStatus enum (e.g., "MsgMessageStatus status = 2;")
so it matches MsgMessageCreateRequest; ensure the MsgMessageStatus enum is
available/imported in this file or the same proto and update any
references/clients/IDL consumers if necessary to use the enum type instead of a
string.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
core/api/src/services/notifications/proto/notifications.proto (1)

17-23: Proto surface is coherent; revisit status typing and Buf enum naming, and keep both protos in sync.

The new RPCs and message types here look consistent with the server-side proto and with the higher-level TS types.

Two follow-ups (same concerns as on the server proto):

  1. status as string vs MsgMessageStatus enum

    MsgMessage.status uses MsgMessageStatus, but both MsgMessageCreateRequest.status and MsgMessageUpdateStatusRequest.status are plain string. That’s legal but bypasses schema-level validation and IDE support.

    If you prefer stronger typing (and to keep request/response aligned), consider switching these fields to the enum:

     message MsgMessageCreateRequest {
       string username = 1;
  • string status = 2;
    
  • MsgMessageStatus status = 2;
    string sent_by = 3;
    

    }

    message MsgMessageUpdateStatusRequest {
    string id = 1;

  • string status = 2;
    
  • MsgMessageStatus status = 2;
    
    }
    
    If the intent is to allow arbitrary statuses, adding a short comment to that effect would help future readers.
    
    
  1. Buf enum naming hints

    Buf is flagging MsgMessageStatus values (PENDING, ACCEPTED, REVOKED) for not following the configured prefix/zero-value conventions. If Buf lint is enforced, you may need either to:

    • Rename the enum values, e.g.:

      -enum MsgMessageStatus {
      -  PENDING = 0;
      -  ACCEPTED = 1;
      -  REVOKED = 2;
      -}
      +enum MsgMessageStatus {
      +  MSG_MESSAGE_STATUS_UNSPECIFIED = 0;
      +  MSG_MESSAGE_STATUS_PENDING = 1;
      +  MSG_MESSAGE_STATUS_ACCEPTED = 2;
      +  MSG_MESSAGE_STATUS_REVOKED = 3;
      +}

      (and update generated consumers), or

    • Relax/override the Buf lint rule for this enum to match the rest of the file’s style.

Any change you make here should be mirrored in core/notifications/proto/notifications.proto and followed by regenerating the TS/JS bindings.

Also applies to: 355-457

core/notifications/proto/notifications.proto (1)

17-23: Server proto matches the API proto; align status typing and decide on Buf enum naming.

This file mirrors the API proto, so the same two points apply as already raised in earlier review comments:

  1. status field type in create/update requests

    Prior feedback already suggested using MsgMessageStatus instead of string for MsgMessageCreateRequest.status and MsgMessageUpdateStatusRequest.status. If you want proto-level validation and consistent typing with MsgMessage.status, the diff would be:

     message MsgMessageCreateRequest {
       string username = 1;
  • string status = 2;
    
  • MsgMessageStatus status = 2;
    string sent_by = 3;
    

    }

    message MsgMessageUpdateStatusRequest {
    string id = 1;

  • string status = 2;
    
  • MsgMessageStatus status = 2;
    
    }
    
    If you intentionally kept these as `string` for flexibility, consider documenting that choice so it’s clear this is not an oversight, and ensure the Rust app layer validates/normalizes the values before persisting.
    
    
  1. Buf enum naming for MsgMessageStatus

    Buf is warning about the value names and zero value. As with the API proto, you’ll either need to rename the enum values to satisfy your lint rules or adjust the Buf config/exception list. Any renaming here must also be reflected in:

    • core/api/src/services/notifications/proto/notifications.proto
    • Generated bindings (Rust/TS)
    • Any domain/DB mapping that depends on these constants.

Also applies to: 355-457

🧹 Nitpick comments (8)
core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql (1)

16-22: Add index for updated_at to match query ORDER BY

msg_messages currently has indexes on username and status, but list_messages orders by updated_at DESC. For growing tables this will require sorting the whole result set. Consider adding an index on updated_at (or a composite index combining your usual filters with updated_at) to keep pagination performant.

Also applies to: 27-31

core/api/src/services/notifications/proto/notifications_pb.d.ts (1)

1075-1105: Align message status typing between requests, entity, and DB

MsgMessage.status is strongly typed as MsgMessageStatus, but both MsgMessageCreateRequest.status and MsgMessageUpdateStatusRequest.status are plain string, while the DB column has a CHECK constraint restricting values to 'pending' | 'accepted' | 'revoked'.

This makes it easy for clients to send invalid values that only fail at the DB layer. If feasible, consider:

  • Changing the proto for the request messages to use MsgMessageStatus as well (and regenerating), or
  • At least constraining the higher-level API (e.g., GraphQL) to an enum that maps to MsgMessageStatus, so only valid statuses reach the backend.

This would keep the status domain consistent across layers.

Also applies to: 1326-1350, 1375-1396, 1596-1600

core/notifications/src/grpc/server/convert.rs (1)

350-382: Tighten status mapping in From<MsgMessage> for proto::MsgMessage

The mapping from message.status to proto::MsgMessageStatus covers "pending" | "accepted" | "revoked" and falls back to Pending on anything else:

let status = match message.status.as_str() {
    "pending" => proto::MsgMessageStatus::Pending,
    "accepted" => proto::MsgMessageStatus::Accepted,
    "revoked" => proto::MsgMessageStatus::Revoked,
    _ => proto::MsgMessageStatus::Pending,
};

Given the DB CHECK constraint, unknown values shouldn’t occur today, but silently coercing them to Pending would hide data issues if new statuses are introduced or invariants are broken elsewhere. Consider logging and/or treating unknown values as an error instead of defaulting, so mismatches are visible during development and ops.

(Assuming the proto updated_at field is “seconds since epoch,” using updated_at.timestamp() is appropriate.)

core/api/src/graphql/admin/root/mutation/notification-template-create.ts (1)

7-82: Create mutation matches DB/service contract; only a minor error-shape nit

The input shape and nullability line up with the msg_templates schema and the msgTemplateCreate service, and the resolver wiring/complexity look good. For invalid languageCode, you currently return { message } directly instead of using mapAndParseErrorForGqlResponse, so parse failures won’t carry a code/path like service errors do. That’s a small inconsistency but not a blocker; consider using a consistent error shape if you care about clients inspecting error codes.

core/notifications/src/msg_messages/repo.rs (2)

7-14: Use a typed status enum instead of raw String

MsgMessage.status is a String, but the msg_messages.status column is constrained to 'pending' | 'accepted' | 'revoked', and the proto layer exposes a MsgMessageStatus enum. Using a raw string here means invalid statuses can be constructed all the way down to the DB before being rejected.

Consider introducing a small Rust enum for message status (with FromStr/Display or similar) and using that in MsgMessage and the repository APIs. That will enforce valid values at the type level and keep the DB constraint as a final safeguard rather than the first line of defense.

Also applies to: 30-70


72-134: Defensively handle non-positive limit/offset in list_messages

list_messages takes Option<i64> for limit/offset and passes any provided values straight into LIMIT/OFFSET. If upstream ever supplies negative values, Postgres will error. It’s minor, but you may want to:

  • Validate these in the app layer, or
  • Treat limit <= 0 / offset < 0 as None here before building the query.

That avoids surprising SQL errors from malformed client input.

core/notifications/src/grpc/server/mod.rs (1)

13-13: New template/message RPC handlers are consistent; consider pagination bounds.

The new msg_template_* and msg_message_* RPCs follow existing patterns (tracing, into_inner, UUID parsing, Status::from mapping, and From conversions), and the UUID handling plus invalid-argument mapping look good.

One thing to consider: msg_templates_list and msg_messages_list pass limit through directly when > 0. If the app layer doesn’t already clamp this, it might be worth enforcing a reasonable maximum here to avoid accidentally unbounded queries from a misbehaving client.

Also applies to: 101-287

core/api/src/domain/notifications/index.types.d.ts (1)

84-105: Types match proto well; consider tightening status typing and clarifying defaults.

The MsgTemplate / MsgMessage shapes and the new INotificationsService methods line up with the proto definitions and generated bindings, which is good.

Two minor points:

  • Both msgMessageCreate and msgMessageUpdateStatus accept status as MsgMessageStatus | string. That gives up most of the benefit of defining MsgMessageStatus; if you don’t expect arbitrary values here, consider narrowing to MsgMessageStatus (and possibly status?: MsgMessageStatus for msgMessageCreate) to get better compile-time checks.
  • msgMessageCreate makes status optional, but the proto request field is a required string. Please double-check that the service layer sets a sensible default (e.g. "pending") rather than letting an empty string fall through to gRPC.

Example tightening (if desired):

-type MsgMessageStatus = "pending" | "accepted" | "revoked"
+type MsgMessageStatus = "pending" | "accepted" | "revoked"

   msgMessageCreate(args: {
     username: string
-    status?: MsgMessageStatus | string
+    status?: MsgMessageStatus
     sentBy: string
   }): Promise<true | NotificationsServiceError>

   msgMessageUpdateStatus(args: {
     id: string
-    status: MsgMessageStatus | string
+    status: MsgMessageStatus
   }): Promise<true | NotificationsServiceError>

Also applies to: 164-209

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 669a4cc and 1c25b72.

📒 Files selected for processing (15)
  • core/api/src/domain/notifications/index.types.d.ts (2 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-message-create.ts (1 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-template-create.ts (1 hunks)
  • core/api/src/graphql/admin/root/mutation/notification-template-update.ts (1 hunks)
  • core/api/src/graphql/admin/types/object/notification-template.ts (1 hunks)
  • core/api/src/services/notifications/index.ts (3 hunks)
  • core/api/src/services/notifications/proto/notifications.proto (2 hunks)
  • core/api/src/services/notifications/proto/notifications_pb.d.ts (2 hunks)
  • core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql (1 hunks)
  • core/notifications/proto/notifications.proto (2 hunks)
  • core/notifications/src/app/mod.rs (5 hunks)
  • core/notifications/src/grpc/server/convert.rs (1 hunks)
  • core/notifications/src/grpc/server/mod.rs (2 hunks)
  • core/notifications/src/msg_messages/repo.rs (1 hunks)
  • core/notifications/src/msg_templates/repo.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • core/api/src/graphql/admin/types/object/notification-template.ts
  • core/notifications/src/app/mod.rs
  • core/api/src/services/notifications/index.ts
🧰 Additional context used
🧬 Code graph analysis (4)
core/api/src/graphql/admin/root/mutation/notification-template-update.ts (2)
core/api/src/services/notifications/index.ts (1)
  • NotificationsService (71-989)
core/api/src/graphql/error-map.ts (1)
  • mapAndParseErrorForGqlResponse (917-924)
core/api/src/graphql/admin/root/mutation/notification-template-create.ts (2)
core/api/src/services/notifications/index.ts (1)
  • NotificationsService (71-989)
core/api/src/graphql/error-map.ts (1)
  • mapAndParseErrorForGqlResponse (917-924)
core/notifications/src/msg_templates/repo.rs (1)
core/notifications/src/msg_messages/repo.rs (7)
  • sqlx (36-36)
  • sqlx (56-56)
  • sqlx (79-79)
  • sqlx (94-94)
  • sqlx (108-108)
  • sqlx (122-122)
  • new (23-28)
core/api/src/domain/notifications/index.types.d.ts (3)
core/api/src/services/notifications/proto/notifications_pb.d.ts (2)
  • MsgTemplate (1031-1059)
  • MsgMessage (1075-1095)
core/api/src/services/notifications/grpc-client.ts (7)
  • msgTemplateCreate (124-128)
  • msgTemplateUpdate (130-134)
  • msgTemplateDelete (136-140)
  • msgTemplatesList (142-146)
  • msgMessageCreate (148-152)
  • msgMessageUpdateStatus (154-158)
  • msgMessagesList (160-164)
core/api/src/domain/notifications/errors.ts (1)
  • NotificationsServiceError (9-9)
🪛 Buf (1.61.0)
core/api/src/services/notifications/proto/notifications.proto

357-357: Enum value name "PENDING" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


357-357: Enum zero value name "PENDING" should be suffixed with "_UNSPECIFIED".

(ENUM_ZERO_VALUE_SUFFIX)


358-358: Enum value name "ACCEPTED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


359-359: Enum value name "REVOKED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)

core/notifications/proto/notifications.proto

357-357: Enum value name "PENDING" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


357-357: Enum zero value name "PENDING" should be suffixed with "_UNSPECIFIED".

(ENUM_ZERO_VALUE_SUFFIX)


358-358: Enum value name "ACCEPTED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)


359-359: Enum value name "REVOKED" should be prefixed with "MSG_MESSAGE_STATUS_".

(ENUM_VALUE_PREFIX)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build and test core
  • GitHub Check: Migrate Mongodb
  • GitHub Check: Check SDLs
  • GitHub Check: execute via bats
  • GitHub Check: execute via buck2
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
core/api/src/graphql/admin/root/mutation/notification-template-update.ts (1)

7-90: Update mutation wiring looks consistent and correct

Input fields, nullability, and the resolver’s call into NotificationsService.msgTemplateUpdate all mirror the create mutation and the underlying repository/DB schema. Error handling for id/languageCode and service errors is consistent with the existing GraphQL patterns. No changes requested.

Comment on lines 6 to 52
const NotificationMessageCreateInput = GT.Input({
name: "NotificationMessageCreateInput",
fields: () => ({
username: { type: GT.NonNull(GT.String) },
status: { type: GT.String },
sentBy: { type: GT.NonNull(GT.String) },
}),
})

const NotificationMessageCreateMutation = GT.Field<
null,
GraphQLAdminContext,
{
input: {
username: string
status?: string
sentBy: string
}
}
>({
extensions: {
complexity: 40,
},
type: GT.NonNull(SuccessPayload),
args: {
input: { type: GT.NonNull(NotificationMessageCreateInput) },
},
resolve: async (_, args) => {
const { username, status, sentBy } = args.input
const notificationsService = NotificationsService()

const res = await notificationsService.msgMessageCreate({
username,
status,
sentBy,
})

if (res instanceof Error) {
return { errors: [mapAndParseErrorForGqlResponse(res)], success: false }
}

return {
errors: [],
success: true,
}
},
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Constrain status input to valid values and define a clear default

NotificationMessageCreateInput exposes status as an optional String, and the resolver forwards it directly into msgMessageCreate. Downstream:

  • The DB column msg_messages.status is NOT NULL with a CHECK on 'pending' | 'accepted' | 'revoked'.
  • The proto MsgMessage uses a MsgMessageStatus enum for the stored status.

Without validation here, callers can send arbitrary strings (e.g., "Pending" or "foo") and only see a DB error. When status is omitted, behavior also depends on how msgMessageCreate interprets the missing field vs the DB default.

I’d recommend either:

  • Making status a GraphQL enum aligned with the allowed values (and the proto enum), optionally defaulting to "pending" when omitted, or
  • At minimum, validating status against the allowed set before calling the service and returning a clear GraphQL error if it’s invalid.

This keeps invariants at the API edge and avoids opaque DB constraint failures.

🤖 Prompt for AI Agents
In core/api/src/graphql/admin/root/mutation/notification-message-create.ts
around lines 6 to 52, the NotificationMessageCreateInput currently exposes
status as an optional free-form String which allows invalid values and relies on
downstream DB/proto constraints; change the input to use a GraphQL enum that
mirrors the MsgMessageStatus values (pending, accepted, revoked) and make the
field either non-null with a default of "pending" or keep it optional but
validate in the resolver; in the resolver, coerce or default missing status to
"pending", and if a provided status is not one of the enum values return a clear
GraphQL error (mapped via mapAndParseErrorForGqlResponse or a similar error
payload) instead of forwarding invalid strings to
notificationsService.msgMessageCreate.

Comment on lines 3 to 14
CREATE TABLE msg_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR NOT NULL,
language_code VARCHAR NOT NULL,
icon_name VARCHAR NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
should_send_push BOOLEAN NOT NULL DEFAULT FALSE,
should_add_to_history BOOLEAN NOT NULL DEFAULT FALSE,
should_add_to_bulletin BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE (name, language_code)
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid redundant index on (name, language_code)

UNIQUE (name, language_code) on msg_templates already creates a B‑tree index on those columns; the extra idx_msg_templates_name_language_code index on the same pair is redundant and adds write overhead. Consider dropping the explicit index and relying on the unique constraint (and updating the down migration accordingly).

Also applies to: 24-25

🤖 Prompt for AI Agents
In
core/notifications/migrations/20251209165958_create_msg_templates_and_msg_messages.up.sql
around lines 3-14 (and similarly for lines 24-25), the explicit B-tree index on
(name, language_code) is redundant because the UNIQUE (name, language_code)
already creates that index; remove the explicit
idx_msg_templates_name_language_code index creation from the up migration and
update the down migration to stop dropping that index (instead drop only the
table or the unique constraint as appropriate), ensuring no duplicate index is
created and write overhead is reduced.

Comment on lines 146 to 243
pub async fn list_templates(
&self,
limit: Option<i64>,
offset: Option<i64>,
) -> Result<Vec<MsgTemplate>, sqlx::Error> {
match (limit, offset) {
(Some(limit), Some(offset)) => {
let templates = sqlx::query_as::<_, MsgTemplate>(
r#"
SELECT
id,
name,
language_code,
icon_name,
title,
body,
should_send_push,
should_add_to_history,
should_add_to_bulletin
FROM msg_templates
ORDER BY name, language_code
LIMIT $1 OFFSET $2
"#,
)
.bind(limit)
.bind(offset)
.fetch_all(self.read_pool.inner())
.await?;
Ok(templates)
}
(Some(limit), None) => {
let templates = sqlx::query_as::<_, MsgTemplate>(
r#"
SELECT
id,
name,
language_code,
icon_name,
title,
body,
should_send_push,
should_add_to_history,
should_add_to_bulletin
FROM msg_templates
ORDER BY name, language_code
LIMIT $1
"#,
)
.bind(limit)
.fetch_all(self.read_pool.inner())
.await?;
Ok(templates)
}
(None, Some(offset)) => {
let templates = sqlx::query_as::<_, MsgTemplate>(
r#"
SELECT
id,
name,
language_code,
icon_name,
title,
body,
should_send_push,
should_add_to_history,
should_add_to_bulletin
FROM msg_templates
ORDER BY name, language_code
OFFSET $1
"#,
)
.bind(offset)
.fetch_all(self.read_pool.inner())
.await?;
Ok(templates)
}
(None, None) => {
let templates = sqlx::query_as::<_, MsgTemplate>(
r#"
SELECT
id,
name,
language_code,
icon_name,
title,
body,
should_send_push,
should_add_to_history,
should_add_to_bulletin
FROM msg_templates
ORDER BY name, language_code
"#,
)
.fetch_all(self.read_pool.inner())
.await?;
Ok(templates)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

list_templates ignores language_code even though the API surface supports it

The repository’s list_templates only takes (limit, offset) and selects all rows ordered by name, language_code. Meanwhile, the proto MsgTemplatesListRequest includes languageCode, and the DB has an index on (name, language_code).

If callers expect to list templates for a specific language, this will currently be ignored. Either:

  • Thread language_code into this method and add a WHERE language_code = $X (using the existing index), or
  • Remove/ignore languageCode from the RPC/GraphQL surface to avoid a misleading parameter.

Right now this mismatch is easy to overlook and can cause confusing behavior.

🤖 Prompt for AI Agents
In core/notifications/src/msg_templates/repo.rs around lines 146–243 the
list_templates implementation ignores language_code from the API surface; update
the method signature to accept an Option<String> language_code, add a WHERE
clause (e.g. WHERE language_code = $N) to the SQL when language_code is Some and
bind that parameter (preserve limit/offset binds and ordering to use the
existing (name, language_code) index), and update callers to pass through the
proto MsgTemplatesListRequest.languageCode (or alternatively remove languageCode
from the RPC schema if you choose to drop support) so the DB query matches the
API surface.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
core/api/src/graphql/admin/schema.graphql (2)

427-483: Add GraphQL descriptions for new types and inputs.

The new types and inputs lack documentation strings. Other types in the schema have descriptions (e.g., AuditedAccount, BTCWallet, Merchant) that help API consumers understand purpose and usage. Add descriptions to:

  • NotificationMessage
  • NotificationMessageCreateInput
  • NotificationMessageUpdateStatusInput
  • NotificationTemplate
  • NotificationTemplateCreateInput
  • NotificationTemplateDeleteInput
  • NotificationTemplateUpdateInput

547-548: Align pagination pattern with existing cursor-based approach.

The notificationMessages and notificationTemplates queries use limit and offset pagination, but other paginated queries in the schema (e.g., invoices, transactions) use cursor-based pagination with after, before, first, last arguments. Adopting the cursor-based approach would provide consistency and better support for real-world pagination scenarios.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c25b72 and 9902f08.

📒 Files selected for processing (1)
  • core/api/src/graphql/admin/schema.graphql (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build and test core
  • GitHub Check: Migrate Mongodb
  • GitHub Check: Check SDLs
  • GitHub Check: execute via bats
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: execute via buck2

type NotificationMessage {
id: ID!
sentBy: String!
status: String!
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use enums for status field instead of String for type safety.

The status field in NotificationMessage is typed as String!, but the schema consistently uses enums for status-like fields (see AccountStatus, InvoicePaymentStatus, LnPaymentStatus, TxStatus). Additionally, status is optional (String) in the create input but required in the output type, creating an inconsistency.

Consider defining a NotificationStatus enum (with appropriate values) and using it across these fields:

  • NotificationMessage.status: NotificationStatus!
  • NotificationMessageCreateInput.status: NotificationStatus (or make it required)
  • NotificationMessageUpdateStatusInput.status: NotificationStatus!

Also applies to: 437-437, 443-443


type NotificationTemplate {
body: String!
iconName: String!
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use NotificationIcon enum instead of String for iconName field.

The schema already defines a comprehensive NotificationIcon enum (lines 373–425) with 30+ icon values. The iconName field should reference this enum for type safety and validation, rather than accepting arbitrary strings.

Apply this diff:

  type NotificationTemplate {
    body: String!
-   iconName: String!
+   iconName: NotificationIcon!
    id: ID!
    languageCode: Language!
    name: String!
    shouldAddToBulletin: Boolean!
    shouldAddToHistory: Boolean!
    shouldSendPush: Boolean!
    title: String!
  }

  input NotificationTemplateCreateInput {
    body: String!
-   iconName: String!
+   iconName: NotificationIcon!
    languageCode: Language!
    name: String!
    shouldAddToBulletin: Boolean!
    shouldAddToHistory: Boolean!
    shouldSendPush: Boolean!
    title: String!
  }

  input NotificationTemplateUpdateInput {
    body: String!
-   iconName: String!
+   iconName: NotificationIcon!
    id: ID!
    languageCode: Language!
    name: String!
    shouldAddToBulletin: Boolean!
    shouldAddToHistory: Boolean!
    shouldSendPush: Boolean!
    title: String!
  }

Also applies to: 460-460, 475-475

🤖 Prompt for AI Agents
In core/api/src/graphql/admin/schema.graphql at lines 448, 460 and 475, replace
the iconName field type from String! to the existing NotificationIcon! enum;
update each field declaration to use NotificationIcon! (e.g. "iconName:
NotificationIcon!") so schema validation and type-safety use the predefined enum
instead of arbitrary strings. Ensure no additional imports are needed since
NotificationIcon is defined in the same file and run schema validation to
confirm there are no remaining String usages for iconName elsewhere.

@littledino2112
Copy link

@tiankii Below are my comments/questions:

  • This PR has the scaffolding only (added database tables and new admin mutations). There's no integration with actual message delivery yet.
  • What do the statuses mean? It's unclear on the pending → accepted / revoked workflow:
    • When a message is marked accepted, what should happen? Should it trigger sending the notification?
    • What about revoked - is that for cancelling before it's sent, or recalling after? If the latter, how do you recall a sent push notification?

@tiankii
Copy link
Author

tiankii commented Dec 12, 2025

@tiankii Below are my comments/questions:

  • This PR has the scaffolding only (added database tables and new admin mutations). There's no integration with actual message delivery yet.

You will find code related to integration (message delivery) in the Frontend/Integration PR:

Frontend/Integrations: Card invitations #353

@tiankii
Copy link
Author

tiankii commented Dec 12, 2025

@tiankii Below are my comments/questions:

  • What do the statuses mean? It's unclear on the pending → accepted / revoked workflow:

    • When a message is marked accepted, what should happen? Should it trigger sending the notification?
    • What about revoked - is that for cancelling before it's sent, or recalling after? If the latter, how do you recall a sent push notification?

The code for the methods related to the Invitation flow (Invitation sent, KYC Passed, KYC Fail, Invitation Revoked) is still in progress.

@littledino2112
Copy link

@tiankii Below are my comments/questions:

  • This PR has the scaffolding only (added database tables and new admin mutations). There's no integration with actual message delivery yet.

You will find code related to integration (message delivery) in the Frontend/Integration PR:

Frontend/Integrations: Card invitations #353

I understand. You still use the MarketingNotification mutation for message delivery. The added tables are for persisting delivered messages and templates.

@littledino2112
Copy link

@tiankii Below are my comments/questions:

  • What do the statuses mean? It's unclear on the pending → accepted / revoked workflow:

    • When a message is marked accepted, what should happen? Should it trigger sending the notification?
    • What about revoked - is that for cancelling before it's sent, or recalling after? If the latter, how do you recall a sent push notification?

The code for the methods related to the Invitation flow (Invitation sent, KYC Passed, KYC Fail, Invitation Revoked) is still in progress.

You haven't addressed my questions though. I understand it's in progress but how do you intend for them to work?

@tiankii
Copy link
Author

tiankii commented Dec 17, 2025

@tiankii Below are my comments/questions:

  • What do the statuses mean? It's unclear on the pending → accepted / revoked workflow:

    • When a message is marked accepted, what should happen? Should it trigger sending the notification?
    • What about revoked - is that for cancelling before it's sent, or recalling after? If the latter, how do you recall a sent push notification?

The code for the methods related to the Invitation flow (Invitation sent, KYC Passed, KYC Fail, Invitation Revoked) is still in progress.

You haven't addressed my questions though. I understand it's in progress but how do you intend for them to work?

We have created a document that maps the integration events across the components.

https://docs.google.com/document/d/1LqlChHAUpYg0iKY69L1fvoLLzRuiAlivj3kaHtDbwu4/edit?usp=sharing

We have identified and added placeholders on the Admin Panel, Mobile App, and Blink KYC components, where we will do API calls to "Card Platform".

We need the "Card backend service" deployed and accessible on the blink-backend project to start coding and testing the integration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants