Skip to content

feat: add telegram analytics apis#423

Merged
Behzad-rabiei merged 3 commits intomainfrom
420-telegram-metric-apis
Dec 26, 2024
Merged

feat: add telegram analytics apis#423
Behzad-rabiei merged 3 commits intomainfrom
420-telegram-metric-apis

Conversation

@Behzad-rabiei
Copy link
Member

@Behzad-rabiei Behzad-rabiei commented Dec 26, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for Telegram as a new platform in the application.
    • Introduced multiple API endpoints for Telegram member activity, including heatmap charts, line graphs, and various member composition tables.
  • Documentation

    • Updated API documentation to reflect the inclusion of Telegram alongside existing platforms.
  • Bug Fixes

    • Improved error handling in new Telegram-related services and controllers.
  • Chores

    • Added new validation schemas for Telegram-related API requests.
    • Corrected a minor typo in the documentation for the disengaged members composition table endpoint.

@coderabbitai
Copy link

coderabbitai bot commented Dec 26, 2024

Warning

Rate limit exceeded

@Behzad-rabiei has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9662107 and 2825d62.

📒 Files selected for processing (4)
  • src/constants/neo4j.constant.ts (2 hunks)
  • src/services/telegram/heatmap.service.ts (1 hunks)
  • src/services/telegram/memberActivity.service.ts (1 hunks)
  • src/services/telegram/members.service.ts (1 hunks)

Walkthrough

This pull request introduces comprehensive support for Telegram as a new platform in the existing data analytics and member activity tracking system. The changes span multiple files across the project, adding new controllers, services, routes, validations, and documentation to enable Telegram-specific data visualization and analysis. The implementation follows the existing architectural patterns used for other platforms like Discord and Discourse, ensuring consistency and extensibility.

Changes

File Change Summary
src/constants/neo4j.constant.ts Added Telegram platform entry to SUPPORTED_NEO4J_PLATFORMS and NEO4J_PLATFORM_INFO
src/controllers/index.ts Imported and exported telegramController
src/controllers/telegram.controller.ts New controller with functions for Telegram-specific data visualization
src/docs/memberActivity.doc.yml Updated endpoint summaries to include Telegram
src/docs/telegram.doc.yml Added new API documentation for Telegram-specific endpoints
src/routes/v1/index.ts Added Telegram route to router
src/routes/v1/telegram.route.ts New router with Telegram-specific routes
src/services/index.ts Imported and exported telegramService
src/services/telegram/* New services for Telegram heatmaps, member activity, and member queries
src/validations/index.ts Imported and exported telegramValidation
src/validations/telegram.validation.ts New validation schemas for Telegram-related requests

Sequence Diagram

sequenceDiagram
    participant Client
    participant TelegramRoute
    participant TelegramController
    participant TelegramService
    participant DatabaseManager

    Client->>TelegramRoute: POST /telegram/heatmaps/chart
    TelegramRoute->>TelegramController: heatmapChart()
    TelegramController->>TelegramService: getHeatmapChart()
    TelegramService->>DatabaseManager: Query MongoDB
    DatabaseManager-->>TelegramService: Return heatmap data
    TelegramService-->>TelegramController: Process data
    TelegramController-->>Client: Send heatmap response
Loading

Poem

🐰 Telegram joins our data dance,
Rabbit hops with analytics' glance,
Charts and graphs now brightly shine,
Member insights, a new design!
Hop hop hooray for platform's grace! 📊🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (13)
src/services/telegram/members.service.ts (2)

6-7: Use consistent logger labels for better maintainability
The logger child is labeled "DiscourseMemberService", but this file is dedicated to Telegram. Consider renaming it to "TelegramMemberService" or something similar to avoid confusion.


106-107: Avoid excessive console logging in production code
Replace or remove console.log(1000, totalResults, matchStage) to maintain cleaner logs, or switch to the logger for consistency.

src/controllers/telegram.controller.ts (1)

53-54: Remove or replace debug console statements
The console.log('memberActivity', memberActivity) and console.log('members', members) statements might clutter production logs. Prefer using a logger with an appropriate log level if these are necessary for debugging.

Also applies to: 61-62

src/services/telegram/heatmap.service.ts (1)

6-7: Use consistent logger naming
The logger is labeled "DiscourseHeatmapService", but this file appears to focus on Telegram. Consider updating this field to avoid confusion.

src/services/telegram/memberActivity.service.ts (4)

13-13: Align logger names with Telegram
Currently labeled "DiscourseMemberActivityService". Rename it to "TelegramMemberActivityService" or similar for clarity.


157-158: Inaccurate error message
The error message says “Failed to get discourse members interaction network graph” while this code is for Telegram. Update for consistency.


231-231: Rename parameter for clarity
(discourseMember: any, ...) should likely be (telegramMember: any, ...) to avoid confusion with the Discourse platform.


232-260: Consider narrower typing and naming
Many references use generic or Discourse-related names (e.g., all_new_disengaged, discourseMember), which can be misleading. Create specialized type definitions or rename them for clarity and maintainability.

src/routes/v1/telegram.route.ts (2)

22-27: /member-activity/:platformId/members-interactions-network-graph endpoint
Matches the naming conventions for member activity routes. Confirm that the response format is documented for improved maintainability.


41-46: /member-activity/:platformId/disengaged-members-composition-table endpoint
Excellent coverage of Telegram member data. Confirm that the controller handles potentially large data sets with pagination or partial retrieval if necessary.

src/validations/telegram.validation.ts (1)

84-109: Good usage of array with valid string constraints.
Spelling for 'all_disengaged_were_consistently_active' is consistent here. Ensure this is consistent across documentation.

src/docs/telegram.doc.yml (1)

167-167: Remove trailing spaces.
Trailing whitespace may cause linting or merge pipeline failures. Consider removing them to satisfy yamllint.

-  summary: Get data for ...
+  summary: Get data for ...

Also applies to: 232-232, 336-336, 385-385, 434-434, 513-513, 658-658, 803-803

src/docs/memberActivity.doc.yml (1)

167-167: Remove trailing spaces.
As flagged by static analysis, remove trailing spaces on these lines.

-      summary: Get data for ...
+      summary: Get data for ...

Also applies to: 232-232, 336-336, 385-385, 434-434, 513-513, 658-658, 803-803

🧰 Tools
🪛 yamllint (1.35.1)

[error] 167-167: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8d417a and 61f3e2a.

📒 Files selected for processing (14)
  • src/constants/neo4j.constant.ts (1 hunks)
  • src/controllers/index.ts (2 hunks)
  • src/controllers/telegram.controller.ts (1 hunks)
  • src/docs/memberActivity.doc.yml (8 hunks)
  • src/docs/telegram.doc.yml (1 hunks)
  • src/routes/v1/index.ts (2 hunks)
  • src/routes/v1/telegram.route.ts (1 hunks)
  • src/services/index.ts (2 hunks)
  • src/services/telegram/heatmap.service.ts (1 hunks)
  • src/services/telegram/index.ts (1 hunks)
  • src/services/telegram/memberActivity.service.ts (1 hunks)
  • src/services/telegram/members.service.ts (1 hunks)
  • src/validations/index.ts (2 hunks)
  • src/validations/telegram.validation.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/services/telegram/index.ts
🧰 Additional context used
🪛 yamllint (1.35.1)
src/docs/memberActivity.doc.yml

[error] 167-167: trailing spaces

(trailing-spaces)


[error] 232-232: trailing spaces

(trailing-spaces)


[error] 336-336: trailing spaces

(trailing-spaces)


[error] 385-385: trailing spaces

(trailing-spaces)


[error] 434-434: trailing spaces

(trailing-spaces)


[error] 513-513: trailing spaces

(trailing-spaces)


[error] 658-658: trailing spaces

(trailing-spaces)


[error] 803-803: trailing spaces

(trailing-spaces)

🔇 Additional comments (23)
src/validations/index.ts (2)

12-12: Import statement looks good and consistent with existing validations.
The new telegramValidation import follows the same naming pattern used elsewhere in this file and does not raise any immediate concerns.


25-25: Addition of telegramValidation to the export list is correct.
Exporting it in this manner ensures that downstream modules can properly access the newly introduced Telegram validation schemas.

src/services/telegram/members.service.ts (1)

157-163: Consider more specific error handling feedback
When errors occur, returning a default set of pagination values might mask important issues. Consider returning an explicit error message or status to inform the caller of the failure cause.

src/controllers/telegram.controller.ts (1)

44-45: Validate merged request data
Merging both req.query and req.body into filter is convenient, but ensure that the validation schema properly handles all merged fields. Otherwise, unexpected keys might pass through unnoticed.

src/services/telegram/heatmap.service.ts (1)

51-52: Validate array lengths when unwinding
In the aggregation pipeline, it's assumed chat_messages and replier arrays have equal length (line 52 sums them by index). If the lengths differ, the code might reference undefined values. Verify array lengths or implement safeguards.

src/controllers/index.ts (2)

13-13: New import for telegramController
Well-aligned with the existing controller import pattern. Ensure that all references to telegramController are in place.


28-28: Exporting telegramController
This export is correct and integrates the Telegram functionality with the rest of the controllers. No issues.

src/services/index.ts (2)

19-19: New import for telegramService
Consistent naming alignment with other services. Confirm that telegramService is well-tested and thoroughly documented.


39-39: Exporting telegramService
Good. This ensures the service is accessible throughout the application.

src/routes/v1/index.ts (2)

15-15: New import for telegramRoute
In line with the route import conventions. Verify that the route file is properly tested.


72-75: Added /telegram route
Well-structured route definition. Make sure to confirm with QA or staging tests that the new endpoints are functioning securely, especially for role-based access.

src/routes/v1/telegram.route.ts (7)

1-3: Controllers and validations imported correctly
This maintains consistency with the codebase structure, which uses separate validations for each new platform.


5-5: Auth & validate middlewares
Appropriate usage of middlewares for authentication and validation. Ensure all possible error outcomes (such as invalid platform ID) are handled gracefully in the controller.


9-14: /heatmaps/:platformId/heatmap-chart endpoint
Route naming and structure are aligned with other platform routes. Confirm that the admin/view permission is sufficient for all required use-cases.


15-20: /heatmaps/:platformId/line-graph endpoint
Fetches line graph data. Consistent with the existing pattern. Confirm the time complexity if the dataset is large to ensure performance is adequate.


29-34: /member-activity/:platformId/active-members-composition-table endpoint
Implementation is straightforward. Recommend verifying that the platformId is validated in telegramValidation to avoid data injection issues.


35-40: /member-activity/:platformId/active-members-onboarding-table endpoint
Adheres to typical structure for data table endpoints. Ensure tested for edge cases, such as no active members at all.


48-48: Exporting default router
Good final step to expose the new routes.

src/validations/telegram.validation.ts (5)

1-2: Imports look good.
They are consistent with the rest of the codebase.


29-35: Consider making platformId required.
All other schemas enforce a required platformId; make it consistent here unless optional is truly intended.


37-42: Platform ID requirement consistency check.
Same note as above: consider .required() for platformId, or confirm if optional usage is deliberate.


65-82: Schema alignment looks correct.
The structure matches your usage patterns. The optional query parameters are logically grouped.


111-118: Exports are straightforward and organized.
The aggregated export object is well-structured and consistent with typical usage patterns.

Comment on lines 83 to 91
if (ngu) {
matchStage.$or = [
{ 'options.username': { $regex: ngu, $options: 'i' } },
{ 'options.first_name': { $regex: ngu, $options: 'i' } },
{ 'options.last_name': { $regex: ngu, $options: 'i' } },
];
console.log('ngu',matchStage)

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible logic bug overwriting $or conditions
When ngu is provided, matchStage.$or is assigned. Later, if memberActivityDocument exists, matchStage.$or is assigned again at lines 96-99, overwriting the previous condition. This leads to losing the ngu search criteria. Consider merging them instead.

+ // Merge existing $or conditions instead of overwriting
if (memberActivityDocument) {
  const date = new Date(memberActivityDocument.date);
  date.setHours(23, 59, 59, 999);
- matchStage.$or = [
-   { joined_at: null },
-   { joined_at: { $lte: date } }
- ];
+ const existingOr = matchStage.$or ? matchStage.$or : [];
+ matchStage.$or = [
+   ...existingOr,
+   { joined_at: null },
+   { joined_at: { $lte: date } },
+ ];
}

Also applies to: 96-99

Comment on lines +13 to +16
[PlatformNames.Telegram]: {
platform: 'TelegramPlatform',
member: 'TelegramMember',
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Include Telegram in SUPPORTED_NEO4J_PLATFORMS
You’ve added Telegram to NEO4J_PLATFORM_INFO, but SUPPORTED_NEO4J_PLATFORMS remains [Discord, Discourse]. If you intend to fully support Telegram, add it to SUPPORTED_NEO4J_PLATFORMS as well.

[
'all_new_disengaged',
'all_disengaged_were_newly_active',
'all_disengaged_were_consistenly_active',
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Typographical error in the enum value.
'all_disengaged_were_consistenly_active' is misspelled. For consistency with code, use 'all_disengaged_were_consistently_active'.

-      enum:
-        ...
-          'all_disengaged_were_consistenly_active',
+      enum:
+        ...
+          'all_disengaged_were_consistently_active',

Committable suggestion skipped: line range outside the PR's diff.

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: 1

🧹 Nitpick comments (6)
src/controllers/telegram.controller.ts (1)

43-131: Consider extracting common table logic

The table-related functions (activeMembersCompositionTable, activeMembersOnboardingTable, disengagedMembersCompositionTable) share significant code duplication. Consider extracting the common logic into a reusable function.

Example refactor:

+async function processTableData(
+  req: IAuthAndPlatform,
+  activityCompositionFields: string[],
+  activityCompositionType: string
+) {
+  const filter = pick({ ...req.query, ...req.body }, ['activityComposition', 'ngu']);
+  const options = pick(req.query, ['sortBy', 'limit', 'page']);
+  const platformConnection = await DatabaseManager.getInstance().getPlatformDb(req.platform?.id);
+  const memberActivity = await telegramService.memberActivityService.getLastDocumentForTablesUsage(
+    platformConnection,
+    activityCompositionFields
+  );
+  const members = await telegramService.membersService.queryMembersForTables(
+    platformConnection,
+    filter,
+    options,
+    memberActivity,
+    activityCompositionType
+  );
+  if (members) {
+    members.results.forEach((member) => {
+      member.ngu = telegramService.membersService.getNgu(member);
+      member.activityComposition = telegramService.memberActivityService.getActivityComposition(
+        member,
+        memberActivity,
+        filter.activityComposition
+      );
+    });
+  }
+  return members;
+}

 const activeMembersCompositionTable = catchAsync(async function (req: IAuthAndPlatform, res: Response) {
-  const filter = pick({ ...req.query, ...req.body }, ['activityComposition', 'ngu']);
-  const options = pick(req.query, ['sortBy', 'limit', 'page']);
-  // ... existing code ...
+  const fields = telegramService.memberActivityService.getActivityCompositionOfActiveMembersComposition();
+  const members = await processTableData(req, fields, activityCompostionsTypes.activeMembersComposition);
   res.send(members);
 });
src/services/telegram/members.service.ts (2)

20-24: Add defensive checks for user object

The function should handle cases where user or user.options is undefined.

 function getNgu(user: any): string {
+  if (!user?.options) return '';
   const { firstName, lastName, username } = user;
   const combinedName = [firstName, lastName].filter(Boolean).join(" ");
   return combinedName || username || '';
 }

34-196: Consider simplifying matchStage building logic

The current implementation builds matchStage through multiple conditional blocks. Consider using a more declarative approach with array-based conditions.

function buildMatchStage(filter: Filter, memberActivity: any, memberActivityDocument: any) {
  const conditions = [];
  
  // Activity composition condition
  if (filter.activityComposition?.length > 0) {
    conditions.push(buildActivityCompositionCondition(filter.activityComposition, memberActivity));
  }
  
  // NGU condition
  if (filter.ngu) {
    conditions.push({
      $or: [
        { 'options.username': { $regex: filter.ngu, $options: 'i' } },
        { 'options.first_name': { $regex: filter.ngu, $options: 'i' } },
        { 'options.last_name': { $regex: filter.ngu, $options: 'i' } },
      ]
    });
  }
  
  // Joined at condition
  if (memberActivityDocument) {
    const date = new Date(memberActivityDocument.date);
    date.setHours(23, 59, 59, 999);
    conditions.push({
      $or: [
        { joined_at: null },
        { joined_at: { $lte: date } },
      ]
    });
  }
  
  return conditions.length > 0 ? { $and: conditions } : {};
}
src/services/telegram/memberActivity.service.ts (1)

229-261: Fix parameter naming and spelling errors

  1. The parameter name discourseMember should be telegramMember since this is the Telegram service.
  2. There's a typo in the activity message "Were consistenly active".
-function getActivityComposition(discourseMember: any, memberActivity: any, activityComposition: Array<string>) {
+function getActivityComposition(telegramMember: any, memberActivity: any, activityComposition: Array<string>) {
   // ...
-    { key: 'all_disengaged_were_consistently_active', message: 'Were consistenly active' },
+    { key: 'all_disengaged_were_consistently_active', message: 'Were consistently active' },
   // ...
   if (
     memberActivity[activityType.key] &&
-    memberActivity[activityType.key].includes(discourseMember.id)
+    memberActivity[activityType.key].includes(telegramMember.id)
   ) {
src/docs/telegram.doc.yml (1)

126-228: Update examples to be Telegram-specific.

The response schema contains Discord-specific examples:

  • roleId example shows "discordRoleId1"

Consider updating the examples to be Telegram-specific or platform-neutral:

-                              example: 'discordRoleId1'
+                              example: 'telegramRoleId1'
src/docs/memberActivity.doc.yml (1)

167-167: Remove trailing spaces in summaries.

The following lines contain trailing spaces:

  • Line 167: "Get data for inactive members line graph - Discord, Discourse and Telegram "
  • Line 232: "Get data for active members interactions graph - Discord, Discourse and Telegram "
  • Line 336: "Get data for fragmentation score - Discord, Discourse and Telegram "
  • Line 385: "Get data for decentralisation score - Discord, Discourse and Telegram "
  • Line 434: "Get data for active members onboarding line graph - Discord, Discourse and Telegram "
  • Line 513: "Get data for active members composition table - Discord, Discourse and Telegram "
  • Line 658: "Get data for active members onboarding table - Discord, Discourse and Telegram "
  • Line 803: "Get data for disengaged members composition table - Discord, Discourse and Telegram "

Remove the trailing spaces from these lines.

Also applies to: 232-232, 336-336, 385-385, 434-434, 513-513, 658-658, 803-803

🧰 Tools
🪛 yamllint (1.35.1)

[error] 167-167: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61f3e2a and 9662107.

📒 Files selected for processing (9)
  • src/constants/neo4j.constant.ts (2 hunks)
  • src/controllers/telegram.controller.ts (1 hunks)
  • src/docs/discourse.doc.yml (1 hunks)
  • src/docs/memberActivity.doc.yml (9 hunks)
  • src/docs/telegram.doc.yml (1 hunks)
  • src/services/announcement.service.ts (1 hunks)
  • src/services/module.service.ts (1 hunks)
  • src/services/telegram/memberActivity.service.ts (1 hunks)
  • src/services/telegram/members.service.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • src/services/module.service.ts
  • src/docs/discourse.doc.yml
  • src/services/announcement.service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/constants/neo4j.constant.ts
🧰 Additional context used
🪛 yamllint (1.35.1)
src/docs/memberActivity.doc.yml

[error] 167-167: trailing spaces

(trailing-spaces)


[error] 232-232: trailing spaces

(trailing-spaces)


[error] 336-336: trailing spaces

(trailing-spaces)


[error] 385-385: trailing spaces

(trailing-spaces)


[error] 434-434: trailing spaces

(trailing-spaces)


[error] 513-513: trailing spaces

(trailing-spaces)


[error] 658-658: trailing spaces

(trailing-spaces)


[error] 803-803: trailing spaces

(trailing-spaces)

🔇 Additional comments (9)
src/controllers/telegram.controller.ts (2)

12-22: LGTM: Proper error handling and timezone management

The implementation correctly handles timezone offsets and includes proper error handling via catchAsync.


24-33: Verify date range validation

While the implementation looks correct, ensure that proper validation for startDate and endDate is implemented in the validation layer.

Run the following script to check for date validation:

✅ Verification successful

Date validation is properly implemented

The verification shows that proper date validation is implemented in the validation layer. Specifically, in src/validations/telegram.validation.ts, the lineGraph schema includes:

startDate: Joi.date().required(),
endDate: Joi.date().required(),

This validation ensures that:

  • Both dates are required fields
  • Values must be valid dates
  • The validation happens before reaching the controller
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for date validation in telegram validation schema
rg -A 5 "startDate|endDate" --type ts

Length of output: 52771

src/docs/telegram.doc.yml (5)

1-46: LGTM! Well-documented heatmap chart endpoint.

The endpoint documentation is complete with proper authentication, request validation, and response schema.


56-125: LGTM! Well-structured line graph endpoint.

The endpoint documentation properly defines the response schema with percentage changes for trend analysis.


229-333: LGTM! Comprehensive table endpoint with proper filtering.

The endpoint documentation includes clear query parameters and pagination support.


334-439: LGTM! Well-defined onboarding table endpoint.

The endpoint documentation properly defines the activity composition enum for onboarding scenarios.


440-553: LGTM! Well-structured disengaged members endpoint.

The endpoint documentation properly handles array parameters with form style.

src/docs/memberActivity.doc.yml (2)

824-824: LGTM! Fixed the consistently typo.

The typo in the enum value has been fixed from "consistenly" to "consistently".


167-167: LGTM! Proper platform support documentation.

The endpoint summaries have been updated to correctly reflect support for Discord, Discourse, and Telegram platforms.

Also applies to: 232-232, 336-336, 385-385, 434-434, 513-513, 658-658, 803-803

🧰 Tools
🪛 yamllint (1.35.1)

[error] 167-167: trailing spaces

(trailing-spaces)

Comment on lines +41 to +158
async function getMembersInteractionsNetworkGraph(
platformId: string,
platformName: SupportedNeo4jPlatforms,
): Promise<memberInteractionsGraphResponseType> {
try {
const platformConnection = await DatabaseManager.getInstance().getPlatformDb(platformId);
const usersInNetworkGraph: string[] = [];
// userInteraction
const usersInteractionsQuery = `
MATCH () -[r:INTERACTED_WITH {platformId: "${platformId}"}]-()
WITH max(r.date) as latest_date
MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member})-[r:INTERACTED_WITH {platformId: "${platformId}", date: latest_date}]->(b:${NEO4J_PLATFORM_INFO[platformName].member})
RETURN a, r, b`;

const neo4jUsersInteractionsData = await Neo4j.read(usersInteractionsQuery);
const { records: neo4jUsersInteractions } = neo4jUsersInteractionsData;
const usersInteractions = neo4jUsersInteractions.map((usersInteraction) => {
// @ts-ignore
const { _fieldLookup, _fields } = usersInteraction;
const a = _fields[_fieldLookup['a']];
const r = _fields[_fieldLookup['r']];
const b = _fields[_fieldLookup['b']];

const aUserId = a?.properties?.id as string;
const rWeeklyInteraction = r?.properties?.weight as number;
const bUserId = b?.properties?.id as string;

usersInNetworkGraph.push(aUserId);
usersInNetworkGraph.push(bUserId);
const interaction = {
aUserId,
bUserId,
rWeeklyInteraction,
};

return interaction;
});

// userRadius
const userRadiusQuery = `
MATCH () -[r:INTERACTED_WITH {platformId: "${platformId}"}]-()
WITH max(r.date) as latest_date
MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member}) -[r:INTERACTED_WITH {date: latest_date, platformId :"${platformId}"}]-(:${NEO4J_PLATFORM_INFO[platformName].member})
WITH a, r
RETURN a.id as userId, SUM(r.weight) as radius`;
const neo4jUserRadiusData = await Neo4j.read(userRadiusQuery);
const { records: neo4jUserRadius } = neo4jUserRadiusData;
const userRadius = neo4jUserRadius.map((userRadius) => {
// @ts-ignore
const { _fieldLookup, _fields } = userRadius;
const userId = _fields[_fieldLookup['userId']] as string;
const radius = _fields[_fieldLookup['radius']] as number;

return { userId, radius };
});
// userStatus
const userStatusQuery = `
MATCH () -[r:INTERACTED_IN]-(g:${NEO4J_PLATFORM_INFO[platformName].platform} {id: "${platformId}"})
WITH max(r.date) as latest_date
MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member})-[r:INTERACTED_IN {date: latest_date}]->(g:${NEO4J_PLATFORM_INFO[platformName].platform} {id: "${platformId}"})
RETURN a.id as userId, r.status as status`;
const neo4jUserStatusData = await Neo4j.read(userStatusQuery);
const { records: neo4jUserStatus } = neo4jUserStatusData;
const userStatus = neo4jUserStatus.map((userStatus) => {
// @ts-ignore
const { _fieldLookup, _fields } = userStatus;
const userId = _fields[_fieldLookup['userId']] as string;
const status = _fields[_fieldLookup['status']] as number;
const stats =
status == 0 ? NodeStats.SENDER : status == 1 ? NodeStats.RECEIVER : status == 2 ? NodeStats.BALANCED : null;

return { userId, stats };
});

const usersInfo = await platformConnection.db
.collection('rawmembers')
.find({ id: { $in: usersInNetworkGraph } })
.toArray();

// prepare data
const response = usersInteractions.flatMap((interaction) => {
const { aUserId, bUserId, rWeeklyInteraction } = interaction;
// Radius
const aUserRadiusObj = userRadius.find((userRadius) => userRadius.userId == aUserId);
const aUserRadius = aUserRadiusObj?.radius as number;
const bUserRadiusObj = userRadius.find((userRadius) => userRadius.userId == bUserId);
const bUserRadius = bUserRadiusObj?.radius as number;
// Status
const aUserStatsObj = userStatus.find((userStatus) => userStatus.userId == aUserId);
const aUserStats = aUserStatsObj?.stats;
const bUserStatsObj = userStatus.find((userStatus) => userStatus.userId == bUserId);
const bUserStats = bUserStatsObj?.stats;

// userInfo
const aUser = usersInfo.find((user) => user.id === aUserId);
const bUser = usersInfo.find((user) => user.id === bUserId);
if (!aUser || !bUser) return [];

const aInfo = getUserInformationForNetworkGraph(aUser);
const bInfo = getUserInformationForNetworkGraph(bUser);

if (!aUserStats || !bUserStats) {
return [];
}

return {
from: { id: aUserId, radius: aUserRadius, stats: aUserStats, ...aInfo },
to: { id: bUserId, radius: bUserRadius, stats: bUserStats, ...bInfo },
width: rWeeklyInteraction,
};
});

return response;
} catch (error) {
logger.error(error, 'Failed to get discourse members interaction network graph');
throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, 'Failed to get discourse members interaction network graph');
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential SQL injection vulnerability and improve type safety

  1. The Neo4j queries use string interpolation which is vulnerable to injection attacks.
  2. Multiple @ts-ignore comments indicate potential type safety issues.
-    const usersInteractionsQuery = `
-    MATCH () -[r:INTERACTED_WITH {platformId: "${platformId}"}]-()
-    WITH max(r.date) as latest_date
-    MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member})-[r:INTERACTED_WITH {platformId: "${platformId}", date: latest_date}]->(b:${NEO4J_PLATFORM_INFO[platformName].member})
-    RETURN a, r, b`;
+    const usersInteractionsQuery = {
+      text: `
+        MATCH () -[r:INTERACTED_WITH {platformId: $platformId}]-()
+        WITH max(r.date) as latest_date
+        MATCH (a:$memberType)-[r:INTERACTED_WITH {platformId: $platformId, date: latest_date}]->(b:$memberType)
+        RETURN a, r, b`,
+      params: {
+        platformId,
+        memberType: NEO4J_PLATFORM_INFO[platformName].member
+      }
+    };

Also, consider creating proper types for Neo4j response records instead of using @ts-ignore:

interface Neo4jRecord {
  _fieldLookup: Record<string, number>;
  _fields: any[];
}

@Behzad-rabiei Behzad-rabiei merged commit d4c0c61 into main Dec 26, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant