-
Notifications
You must be signed in to change notification settings - Fork 5
Feat/ecurrency transaction messages #523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThis PR introduces messaging capabilities and eVault integration to the eCurrency platform. It adds new Message and UserEVaultMapping entities, implements MessageService and PlatformEVaultService for data management, integrates message handling into WebhookController, adds transaction notification dispatch, and extends database schema with corresponding migration and mapping configurations. Changes
Sequence Diagram(s)sequenceDiagram
participant Web3 as Web3 Adapter
participant WH as WebhookController
participant MS as MessageService
participant DB as Database
participant Sub as Subscriber
participant DS as Downstream
Web3->>WH: webhook event<br/>(tableName: messages)
activate WH
WH->>MS: extract sender & group<br/>from payload
WH->>MS: createMessage() or<br/>updateMessage()
activate MS
MS->>DB: validate sender exists
MS->>DB: validate group exists
MS->>DB: save Message entity
MS-->>WH: Message (with ID mapping)
deactivate MS
WH->>DB: store local→global<br/>ID mapping
deactivate WH
Sub->>DB: listen for INSERT<br/>on messages
activate Sub
Sub->>MS: enrichMessageEntity()
MS->>DB: load Message with<br/>sender & group relations
MS-->>Sub: fully hydrated Message
Sub->>DS: emit enriched<br/>message payload
deactivate Sub
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🧹 Nitpick comments (14)
platforms/eCurrency-api/src/services/GroupService.ts (3)
58-77: Performance concern: fallback loads all private groups into memory.For non-DM groups, this query fetches every private group and filters in-memory. This will degrade as the number of private groups grows.
Consider applying the same subquery approach used for DMs:
- // Fallback to general search for other group sizes - const groups = await this.groupRepository - .createQueryBuilder("group") - .leftJoinAndSelect("group.members", "members") - .where("group.isPrivate = :isPrivate", { isPrivate: true }) - .getMany(); + // Use subquery approach for any group size + const groups = await this.groupRepository + .createQueryBuilder("group") + .leftJoinAndSelect("group.members", "members") + .where("group.isPrivate = :isPrivate", { isPrivate: true }) + .andWhere((qb) => { + const subQuery = qb.subQuery() + .select("gm.group_id") + .from("group_members", "gm") + .where("gm.user_id IN (:...memberIds)", { memberIds: sortedMemberIds }) + .groupBy("gm.group_id") + .having("COUNT(DISTINCT gm.user_id) = :memberCount", { + memberCount: sortedMemberIds.length + }) + .getQuery(); + return "group.id IN " + subQuery; + }) + .getMany();
119-124: Handle emptyadminIdsto avoid potential query issues.When
adminIdsis empty (the default),In([])can produce unexpected SQL behavior in some TypeORM versions. The subsequent length check would also incorrectly throw "One or more admins not found" since0 !== 0is false, but this is fragile.+ let admins: User[] = []; + if (adminIds.length > 0) { - const admins = await this.userRepository.findBy({ - id: In(adminIds), - }); - if (admins.length !== adminIds.length) { - throw new Error("One or more admins not found"); - } + admins = await this.userRepository.findBy({ + id: In(adminIds), + }); + if (admins.length !== adminIds.length) { + throw new Error("One or more admins not found"); + } + }
104-104: Simplify redundant condition.
name.startsWith("eCurrency Chat")is a subset ofname.includes("eCurrency Chat"), making thestartsWithcheck redundant.- if (isPrivate && (name.startsWith("eCurrency Chat") || name.includes("eCurrency Chat")) && memberIds.length === 2) { + if (isPrivate && name.includes("eCurrency Chat") && memberIds.length === 2) {platforms/eCurrency-api/src/database/entities/Message.ts (1)
12-40: LGTM! Entity structure is well-designed.The Message entity properly handles system messages with nullable sender, and the timestamps and archival flag are appropriately configured.
One minor suggestion: consider adding an explicit
@JoinColumn()on thegrouprelation for clarity, though TypeORM will infer it correctly.@ManyToOne(() => Group, (group) => group.messages) + @JoinColumn({ name: "groupId" }) group!: Group;Don't forget to import
JoinColumnfromtypeormif you apply this.platforms/eCurrency-api/src/services/LedgerService.ts (1)
131-147: Good defensive pattern with try/catch, but consider service reuse.The try/catch ensures notification failures don't break transfers—this is the right approach for a non-critical side effect.
However, instantiating
TransactionNotificationServiceon every transfer is inefficient since it creates multiple service instances (UserService, GroupService, MessageService) each time.Consider injecting or caching the service:
export class LedgerService { ledgerRepository: Repository<Ledger>; currencyRepository: Repository<Currency>; + private notificationService: TransactionNotificationService; constructor() { this.ledgerRepository = AppDataSource.getRepository(Ledger); this.currencyRepository = AppDataSource.getRepository(Currency); + this.notificationService = new TransactionNotificationService(); }Then in
transfer:- const notificationService = new TransactionNotificationService(); - await notificationService.sendTransactionNotifications( + await this.notificationService.sendTransactionNotifications(platforms/eCurrency-api/src/web3adapter/watchers/subscriber.ts (2)
60-97: Consider consolidating enrichment logic.
enrichMessageEntityduplicates relation-loading logic that could be handled by extendingenrichEntity. Currently,afterInsertcallsenrichEntityfirst (lines 105-109) and thenenrichMessageEntity(line 114) for messages, resulting in multiple database queries for the same entity.Consider either:
- Extending
enrichEntityto handle Message-specific relations- Or skipping
enrichEntityfor messages and only callingenrichMessageEntity
112-117: Consider using entity metadata instead of hardcoded table name.Using
event.metadata.tableName === "messages"works but is fragile if the table name changes. Consider comparing against the entity class instead:- if (event.metadata.tableName === "messages" && entity) { + if (event.metadata.target === Message && entity) {This requires importing the
Messageentity but provides type safety.platforms/eCurrency-api/src/services/TransactionNotificationService.ts (2)
335-355: Sequential notification sending can be parallelized.Notifications to sender and receiver users are sent sequentially. For group accounts with multiple admins, this compounds delays. Consider using
Promise.allSettledto parallelize:- for (const senderUser of senderUsers) { - console.log(`📤 Sending transaction notification to sender: ${senderUser.id} (${senderAccountType}:${senderId})`); - await this.sendNotificationToUser(senderUser.id, { - ...transactionDetails, - isSender: true, - accountId: senderId, - accountType: senderAccountType, - }); - } - - for (const receiverUser of receiverUsers) { - console.log(`📥 Sending transaction notification to receiver: ${receiverUser.id} (${receiverAccountType}:${receiverId})`); - await this.sendNotificationToUser(receiverUser.id, { - ...transactionDetails, - isSender: false, - accountId: receiverId, - accountType: receiverAccountType, - }); - } + const notificationPromises = [ + ...senderUsers.map(user => + this.sendNotificationToUser(user.id, { ...transactionDetails, isSender: true, accountId: senderId, accountType: senderAccountType }) + ), + ...receiverUsers.map(user => + this.sendNotificationToUser(user.id, { ...transactionDetails, isSender: false, accountId: receiverId, accountType: receiverAccountType }) + ) + ]; + await Promise.allSettled(notificationPromises);
227-233: Consider specifying timezone for transaction timestamps.
toLocaleStringwithout atimeZoneoption uses the server's local timezone, which may confuse users in different timezones. For financial transaction records, consider:const formattedTime = timestamp.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', - minute: '2-digit' + minute: '2-digit', + timeZone: 'UTC', + timeZoneName: 'short' });platforms/eCurrency-api/src/database/entities/UserEVaultMapping.ts (1)
26-27: Type safety concern: Consider defining an interface for userProfileData.Using
anyforuserProfileDatabypasses TypeScript's type checking. While common for JSONB columns, it can lead to runtime errors if the stored structure doesn't match expectations.Consider defining an interface:
interface UserProfileData { platformName?: string; displayName?: string; description?: string; version?: string; [key: string]: any; // Allow additional properties }Then update the column:
- @Column({ type: "jsonb", nullable: true }) - userProfileData!: any; // Store the UserProfile data + @Column({ type: "jsonb", nullable: true }) + userProfileData!: UserProfileData | null;platforms/eCurrency-api/src/controllers/WebhookController.ts (1)
188-188: Consider structured logging for better observability.The code uses multiple
console.logstatements for debugging. Consider using a structured logging library (e.g., winston, pino) for better log management, filtering, and correlation.Example with a structured logger:
// At the top of the file import logger from '../utils/logger'; // Then replace console.log calls: logger.info('Processing message', { localData: local.data, isSystemMessage, hasGroup: !!group, hasSender: !!sender });Also applies to: 209-209, 220-220, 223-223, 239-239, 242-242, 258-258, 264-264
platforms/eCurrency-api/src/services/PlatformEVaultService.ts (3)
189-257: Review retry strategy for production readiness.The retry logic attempts up to 20 times with exponential backoff (max ~20 seconds between attempts). This could take several minutes to fail, potentially blocking application startup.
Consider:
- Making
maxRetriesconfigurable via environment variable- Adding a total timeout limit in addition to retry count
- Documenting the expected startup behavior if eVault is unavailable
private async createPlatformProfileInEVault( w3id: string, uri: string, maxRetries = parseInt(process.env.EVAULT_MAX_RETRIES || '20', 10), ): Promise<string> { // ... existing code }
222-222: Document the hardcoded ontology ID.Lines 222 and 310 use the hardcoded UUID
"550e8400-e29b-41d4-a716-446655440000"as the UserProfile ontology identifier. Consider adding a constant with documentation explaining what this ID represents.// At the top of the file const USER_PROFILE_ONTOLOGY_ID = "550e8400-e29b-41d4-a716-446655440000"; // Then use it: input: { ontology: USER_PROFILE_ONTOLOGY_ID, payload: platformProfile, acl: ["*"], },Also applies to: 310-310
86-97: Validate required environment variables for production.The code provides localhost fallbacks for critical URLs and uses a demo verification code. This may mask configuration errors in production environments.
Consider validating environment variables at startup:
private validateEnvironment(): void { const required = ['PUBLIC_REGISTRY_URL', 'PUBLIC_PROVISIONER_URL']; const missing = required.filter(key => !process.env[key]); if (missing.length > 0 && process.env.NODE_ENV === 'production') { throw new Error(`Missing required environment variables: ${missing.join(', ')}`); } if (!process.env.DEMO_VERIFICATION_CODE && process.env.NODE_ENV === 'production') { console.warn('Using demo verification code in production - this should be configured properly'); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
platforms/eCurrency-api/package.json(1 hunks)platforms/eCurrency-api/src/controllers/WebhookController.ts(2 hunks)platforms/eCurrency-api/src/database/data-source.ts(2 hunks)platforms/eCurrency-api/src/database/entities/Group.ts(2 hunks)platforms/eCurrency-api/src/database/entities/Message.ts(1 hunks)platforms/eCurrency-api/src/database/entities/UserEVaultMapping.ts(1 hunks)platforms/eCurrency-api/src/database/migrations/1765208128946-migration.ts(1 hunks)platforms/eCurrency-api/src/index.ts(2 hunks)platforms/eCurrency-api/src/services/GroupService.ts(2 hunks)platforms/eCurrency-api/src/services/LedgerService.ts(2 hunks)platforms/eCurrency-api/src/services/MessageService.ts(1 hunks)platforms/eCurrency-api/src/services/PlatformEVaultService.ts(1 hunks)platforms/eCurrency-api/src/services/TransactionNotificationService.ts(1 hunks)platforms/eCurrency-api/src/web3adapter/mappings/message.mapping.json(1 hunks)platforms/eCurrency-api/src/web3adapter/watchers/subscriber.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-20T12:00:02.322Z
Learnt from: coodos
Repo: MetaState-Prototype-Project/prototype PR: 453
File: platforms/eReputation-api/src/controllers/WebhookController.ts:6-7
Timestamp: 2025-11-20T12:00:02.322Z
Learning: In platforms/eReputation-api/src/controllers/WebhookController.ts, the eager instantiation of VotingReputationService in the constructor (which requires OPENAI_API_KEY at startup) is intentional behavior and works as intended.
Applied to files:
platforms/eCurrency-api/src/controllers/WebhookController.ts
🧬 Code graph analysis (7)
platforms/eCurrency-api/src/services/LedgerService.ts (1)
platforms/eCurrency-api/src/services/TransactionNotificationService.ts (1)
TransactionNotificationService(11-362)
platforms/eCurrency-api/src/database/entities/UserEVaultMapping.ts (1)
platforms/dreamsync-api/src/database/entities/UserEVaultMapping.ts (1)
UserEVaultMapping(10-34)
platforms/eCurrency-api/src/web3adapter/watchers/subscriber.ts (1)
platforms/eCurrency-api/src/database/data-source.ts (1)
AppDataSource(27-27)
platforms/eCurrency-api/src/services/GroupService.ts (1)
platforms/dreamSync/shared/schema.ts (1)
groups(120-134)
platforms/eCurrency-api/src/controllers/WebhookController.ts (3)
platforms/eCurrency-api/src/services/UserService.ts (1)
UserService(5-66)platforms/eCurrency-api/src/services/GroupService.ts (1)
GroupService(6-178)platforms/eCurrency-api/src/services/MessageService.ts (1)
MessageService(6-134)
platforms/eCurrency-api/src/index.ts (1)
platforms/eCurrency-api/src/services/PlatformEVaultService.ts (1)
PlatformEVaultService(41-320)
platforms/eCurrency-api/src/services/PlatformEVaultService.ts (1)
platforms/eCurrency-api/src/database/data-source.ts (1)
AppDataSource(27-27)
⏰ 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). (1)
- GitHub Check: build
🔇 Additional comments (9)
platforms/eCurrency-api/src/services/GroupService.ts (1)
102-110: Potential race condition in DM duplication guard.Two concurrent requests could both pass
findGroupByMembersbefore either creates the group, resulting in duplicate DMs. Consider adding a database-level unique constraint or using a transaction with a lock.The current approach relies on application-level checking which is susceptible to TOCTOU races under concurrent load. Verify if this is acceptable for your use case or if stronger guarantees are needed.
platforms/eCurrency-api/src/database/entities/Group.ts (1)
73-75: LGTM!The
@OneToManyrelation is correctly configured with the inverse side referencingmessage.group, properly establishing the bidirectional relationship with the Message entity.platforms/eCurrency-api/src/database/data-source.ts (1)
9-10: LGTM!New entities
MessageandUserEVaultMappingare correctly imported and registered with the TypeORM data source.Also applies to: 21-21
platforms/eCurrency-api/src/web3adapter/watchers/subscriber.ts (1)
381-382: LGTM!Message relations correctly defined for entity loading.
platforms/eCurrency-api/src/index.ts (1)
28-44: Verify silent failure is acceptable for production.The eVault initialization catches errors and logs them without stopping the server. While this prevents startup failures from blocking the application, it means:
- Transaction notifications will silently fail if eVault initialization failed
- There's no health check or retry mechanism for failed initialization
Consider adding:
- A health check status for eVault initialization state (line 82-83 already has service checks)
- A background retry mechanism for failed initialization
platforms/eCurrency-api/src/services/TransactionNotificationService.ts (1)
17-21: LGTM on service instantiation.Constructor properly initializes required service dependencies. The pattern is consistent with other services in the codebase.
platforms/eCurrency-api/src/controllers/WebhookController.ts (1)
4-4: LGTM! Message service integration follows established patterns.The imports and service initialization are consistent with existing UserService and GroupService patterns in this controller.
Also applies to: 7-8, 14-14, 20-20
platforms/eCurrency-api/src/database/migrations/1765208128946-migration.ts (1)
9-10: Consider CASCADE behavior for message deletions.The foreign keys use
ON DELETE NO ACTION, which means deleting a user or group will fail if they have associated messages. Consider ifON DELETE CASCADEorON DELETE SET NULLwould be more appropriate for your use case.Verify the desired behavior:
- Should messages be deleted when a user/group is deleted? Use
ON DELETE CASCADE- Should messages be orphaned (sender/group set to NULL)? Use
ON DELETE SET NULL- Should deletions be prevented? Keep
ON DELETE NO ACTIONplatforms/eCurrency-api/src/services/PlatformEVaultService.ts (1)
105-105: Hardcoded placeholder public key may cause issues.Line 105 uses a hardcoded zero address
"0x00000000000000000000000000000000000000"as the public key. This appears to be a placeholder and may not be appropriate for production use.Verify whether this is:
- Intentional for the eCurrency platform's design
- A TODO that needs implementation
- Should be derived from actual cryptographic keys
If this is a placeholder, consider adding a TODO comment or throwing an error if a proper key configuration is missing.
| // For system messages, ensure the prefix is preserved | ||
| if (isSystemMessage && !(local.data.text as string).startsWith('$$system-message$$')) { | ||
| message.text = `$$system-message$$ ${local.data.text as string}`; | ||
| } else { | ||
| message.text = local.data.text as string; | ||
| } | ||
| message.sender = sender || undefined; | ||
| message.group = group; | ||
| message.isSystemMessage = isSystemMessage as boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential data loss: sender field may be unintentionally cleared.
On line 233, setting message.sender = sender || undefined will set sender to undefined if the sender lookup fails, even if the message previously had a valid sender. This could unintentionally clear the sender field during updates.
Consider preserving the existing sender if lookup fails:
if (isSystemMessage && !(local.data.text as string).startsWith('$$system-message$$')) {
message.text = `$$system-message$$ ${local.data.text as string}`;
} else {
message.text = local.data.text as string;
}
- message.sender = sender || undefined;
+ // Only update sender if explicitly provided (preserve existing sender if lookup fails)
+ if (local.data.sender) {
+ message.sender = sender || undefined;
+ }
message.group = group;
message.isSystemMessage = isSystemMessage as boolean;📝 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.
| // For system messages, ensure the prefix is preserved | |
| if (isSystemMessage && !(local.data.text as string).startsWith('$$system-message$$')) { | |
| message.text = `$$system-message$$ ${local.data.text as string}`; | |
| } else { | |
| message.text = local.data.text as string; | |
| } | |
| message.sender = sender || undefined; | |
| message.group = group; | |
| message.isSystemMessage = isSystemMessage as boolean; | |
| // For system messages, ensure the prefix is preserved | |
| if (isSystemMessage && !(local.data.text as string).startsWith('$$system-message$$')) { | |
| message.text = `$$system-message$$ ${local.data.text as string}`; | |
| } else { | |
| message.text = local.data.text as string; | |
| } | |
| // Only update sender if explicitly provided (preserve existing sender if lookup fails) | |
| if (local.data.sender) { | |
| message.sender = sender || undefined; | |
| } | |
| message.group = group; | |
| message.isSystemMessage = isSystemMessage as boolean; |
🤖 Prompt for AI Agents
In platforms/eCurrency-api/src/controllers/WebhookController.ts around lines 227
to 235, the code currently assigns message.sender = sender || undefined which
will clear an existing sender when the lookup returns a falsy value; change the
logic to only overwrite message.sender when sender is a valid value (e.g.,
non-null/defined) so that if the lookup fails the existing message.sender is
preserved, and ensure types are respected (cast or guard as needed) when
conditionally assigning.
platforms/eCurrency-api/src/database/migrations/1765208128946-migration.ts
Show resolved
Hide resolved
| private async resolveEndpoint(w3id: string): Promise<string> { | ||
| try { | ||
| const registryUrl = | ||
| process.env.PUBLIC_REGISTRY_URL || "http://localhost:3000"; | ||
| const response = await axios.get( | ||
| new URL(`resolve?w3id=${w3id}`, registryUrl).toString(), | ||
| ); | ||
| return new URL("/graphql", response.data.uri).toString(); | ||
| } catch (error) { | ||
| console.error("Error resolving eVault endpoint:", error); | ||
| throw new Error("Failed to resolve eVault endpoint"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add timeout configuration for external API calls.
The code makes HTTP requests to external services (registry, eVault) without timeout configuration. This can cause the application to hang if services are unresponsive.
Apply this diff to add timeouts:
private async resolveEndpoint(w3id: string): Promise<string> {
try {
const registryUrl =
process.env.PUBLIC_REGISTRY_URL || "http://localhost:3000";
const response = await axios.get(
new URL(`resolve?w3id=${w3id}`, registryUrl).toString(),
+ { timeout: 5000 } // 5 second timeout
);
return new URL("/graphql", response.data.uri).toString();
} catch (error) {Also consider adding a timeout to the GraphQL client:
this.client = new GraphQLClient(this.endpoint, {
headers: {
"X-ENAME": w3id,
},
+ timeout: 10000, // 10 second timeout
});Also applies to: 172-184
| async updatePlatformProfile( | ||
| updates: Partial<PlatformProfile>, | ||
| ): Promise<void> { | ||
| const mapping = await this.getPlatformEVaultMapping(); | ||
| if (!mapping) { | ||
| throw new Error("Platform eVault mapping not found"); | ||
| } | ||
|
|
||
| const client = await this.ensureClient(mapping.evaultW3id); | ||
|
|
||
| // Get current profile data | ||
| const currentData = mapping.userProfileData as PlatformProfile; | ||
| const updatedData = { | ||
| ...currentData, | ||
| ...updates, | ||
| updatedAt: new Date().toISOString(), | ||
| }; | ||
|
|
||
| // Update in eVault | ||
| await client.request<MetaEnvelopeResponse>(STORE_META_ENVELOPE, { | ||
| input: { | ||
| ontology: "550e8400-e29b-41d4-a716-446655440000", | ||
| payload: updatedData, | ||
| acl: ["*"], | ||
| }, | ||
| }); | ||
|
|
||
| // Update local mapping | ||
| mapping.userProfileData = updatedData; | ||
| await AppDataSource.getRepository(UserEVaultMapping).save(mapping); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential data consistency issue between eVault and local database.
The method updates the eVault (line 308) and then the local database (line 318) without transaction semantics. If the local save fails after the eVault update succeeds, the systems will be inconsistent.
Consider adding error handling to detect and log this scenario:
// Update in eVault
- await client.request<MetaEnvelopeResponse>(STORE_META_ENVELOPE, {
+ try {
+ await client.request<MetaEnvelopeResponse>(STORE_META_ENVELOPE, {
- input: {
- ontology: "550e8400-e29b-41d4-a716-446655440000",
- payload: updatedData,
- acl: ["*"],
- },
- });
+ input: {
+ ontology: "550e8400-e29b-41d4-a716-446655440000",
+ payload: updatedData,
+ acl: ["*"],
+ },
+ });
+ } catch (error) {
+ console.error("Failed to update platform profile in eVault:", error);
+ throw error;
+ }
// Update local mapping
mapping.userProfileData = updatedData;
- await AppDataSource.getRepository(UserEVaultMapping).save(mapping);
+ try {
+ await AppDataSource.getRepository(UserEVaultMapping).save(mapping);
+ } catch (error) {
+ console.error("CRITICAL: eVault updated but local DB save failed. Manual reconciliation needed:", error);
+ throw error;
+ }
}| { | ||
| "tableName": "messages", | ||
| "schemaId": "550e8400-e29b-41d4-a716-446655440004", | ||
| "ownerEnamePath": "groups(group.ename)||users(group.members[].ename)", | ||
| "ownedJunctionTables": [], | ||
| "localToUniversalMap": { | ||
| "text": "content", | ||
| "sender": "users(sender.id),senderId", | ||
| "group": "groups(group.id),chatId", | ||
| "isSystemMessage": "isSystemMessage", | ||
| "createdAt": "createdAt", | ||
| "updatedAt": "updatedAt", | ||
| "isArchived": "isArchived" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find platforms/eCurrency-api/src/web3adapter/mappings/ -name "*.json" -type fRepository: MetaState-Prototype-Project/prototype
Length of output: 425
🏁 Script executed:
for file in platforms/eCurrency-api/src/web3adapter/mappings/{user,group,currency}.mapping.json; do
echo "=== $file ==="
cat "$file"
echo ""
doneRepository: MetaState-Prototype-Project/prototype
Length of output: 2245
🏁 Script executed:
cat platforms/eCurrency-api/src/web3adapter/mappings/ledger.mapping.jsonRepository: MetaState-Prototype-Project/prototype
Length of output: 558
Duplicate schemaId detected—must be corrected before merging.
The message mapping uses schemaId 550e8400-e29b-41d4-a716-446655440004, which is already assigned to the currency mapping. Each schema must have a unique identifier. Update this to an unused UUID, such as 550e8400-e29b-41d4-a716-446655440002 to maintain the sequence consistency across mappings.
The localToUniversalMap and field mappings are otherwise correct and consistent with the existing mapping structure.
🤖 Prompt for AI Agents
In platforms/eCurrency-api/src/web3adapter/mappings/message.mapping.json around
lines 1 to 15, the schemaId currently duplicates another mapping
(550e8400-e29b-41d4-a716-446655440004); replace it with a unique UUID (for
example 550e8400-e29b-41d4-a716-446655440002) so each mapping has a distinct
schemaId, ensuring no collisions across mappings; leave the rest of the file and
localToUniversalMap unchanged.
Description of change
send messages on eCurrency sent or received
Issue Number
closes #519
Type of change
How the change has been tested
Change checklist
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.