feat: create mediawiki schedule on hivemind module activation#472
feat: create mediawiki schedule on hivemind module activation#472Behzad-rabiei merged 1 commit intomainfrom
Conversation
WalkthroughThis update introduces several new service functions and a new service class to manage MediaWiki schedules and workflows. The changes include methods for creating, deleting, and terminating schedules or workflows in the MediaWiki context. An entry point is provided for the MediaWiki service and a new branch is added in a module update function to handle MediaWiki-specific logic. Furthermore, a Temporal service class is added to manage scheduling and workflow operations, complete with error handling and logging. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant CMS as MediaWiki Core Service
participant TS as Temporal MediaWiki Service
C->>CMS: createMediaWikiSchedule(platformId)
CMS->>TS: createSchedule(platformId)
TS-->>CMS: scheduleId (or error)
CMS->>C: return scheduleId (or throw ApiError)
sequenceDiagram
participant M as Module Service (updateModule)
participant H as handleHivemindMediaWikiCase
participant CMS as MediaWiki Core Service
participant TS as Temporal MediaWiki Service
M->>H: Process Hivemind for MediaWiki platform
alt Platform Activated
H->>CMS: createMediaWikiSchedule(platformId)
CMS->>TS: createSchedule(platformId)
TS-->>CMS: scheduleId
CMS-->>H: return scheduleId
else Platform Deactivated
H->>CMS: deleteMediaWikiSchedule(scheduleId)
CMS->>TS: deleteSchedule(scheduleId)
TS-->>CMS: schedule deleted
H->>CMS: terminateMediaWikiWorkflow(communityId)
CMS->>TS: terminateWorkflow(workflowId)
TS-->>CMS: workflow terminated
CMS-->>H: deletion complete
end
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
src/services/module.service.ts (1)
95-101: Consider refactoring the platform handling logic for better scalability.The if-else chain for platform handling could become unwieldy as more platforms are added. Consider implementing a more scalable approach, such as a strategy pattern or a lookup table.
- if (module.name === ModuleNames.Hivemind) { - if (newPlatform.name === PlatformNames.Website) { - await handleHivemindWebsiteCase(newPlatform); - } else if (newPlatform.name === PlatformNames.MediaWiki) { - await handleHivemindMediaWikiCase(newPlatform); - } - } + if (module.name === ModuleNames.Hivemind) { + const platformHandlers = { + [PlatformNames.Website]: handleHivemindWebsiteCase, + [PlatformNames.MediaWiki]: handleHivemindMediaWikiCase, + }; + + const handler = platformHandlers[newPlatform.name]; + if (handler) { + await handler(newPlatform); + } + }src/services/temporal/mediawiki.service.ts (2)
11-11: Consider renaming the class for better clarity.The class name
TemporalWMediaWikiServicehas an unusual naming convention with the "W" in the middle. Consider renaming to a more descriptive name likeTemporalMediaWikiService.
12-48: Consider making the scheduling more configurable.The
createSchedulemethod always creates a weekly schedule based on the current time. This might not be flexible enough for all use cases. Consider allowing the schedule to be configured via parameters.Also, apply consistent error handling approach - use try-catch blocks with specific error logging similar to the core service methods.
public async createSchedule(platformId: Types.ObjectId): Promise<ScheduleHandle> { + try { const initiationTime = new Date(); // ... existing code ... const client: Client = await this.getClient(); return client.schedule.create({ // ... existing code ... }); + } catch (error) { + logger.error(error, `Failed to create mediawiki schedule: ${(error as Error).message}`); + throw new Error(`Failed to create or update mediawiki ingestion schedule: ${(error as Error).message}`); + } }Also, consider adding a parameter to configure the schedule frequency:
- public async createSchedule(platformId: Types.ObjectId): Promise<ScheduleHandle> { + public async createSchedule( + platformId: Types.ObjectId, + scheduleConfig?: { dayOfWeek?: string; hour?: number; minute?: number } + ): Promise<ScheduleHandle> { const initiationTime = new Date(); const dayNumber = initiationTime.getUTCDay(); const hour = initiationTime.getUTCHours(); const minute = initiationTime.getUTCMinutes(); const DAY_NAMES = ['SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'] as const; - const dayOfWeek = DAY_NAMES[dayNumber]; + const dayOfWeek = scheduleConfig?.dayOfWeek ?? DAY_NAMES[dayNumber]; const calendarSpec: CalendarSpec = { dayOfWeek, - hour, - minute, + hour: scheduleConfig?.hour ?? hour, + minute: scheduleConfig?.minute ?? minute, comment: `Weekly schedule for ${dayOfWeek} at ${hour}:${minute} UTC`, };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/services/mediawiki/core.service.ts(1 hunks)src/services/mediawiki/index.ts(1 hunks)src/services/module.service.ts(3 hunks)src/services/temporal/mediawiki.service.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/services/mediawiki/core.service.ts (2)
src/middlewares/index.ts (1)
error(8-8)src/utils/index.ts (1)
ApiError(9-9)
src/services/temporal/mediawiki.service.ts (2)
src/services/temporal/core.service.ts (1)
TemporalCoreService(4-33)src/services/temporal/configs/temporal.config.ts (1)
queues(1-7)
🔇 Additional comments (8)
src/services/mediawiki/index.ts (1)
1-5: LGTM! Clean entry point for the MediaWiki service.The file follows a good pattern of creating a clear entry point for the MediaWiki service, making the code more maintainable and easier to use.
src/services/module.service.ts (1)
7-7: LGTM! Proper import of the new MediaWiki service.Good addition of the MediaWiki service import to support the new functionality.
src/services/mediawiki/core.service.ts (3)
1-8: LGTM! Good setup with proper imports and logger configuration.The imports and logger setup follow good practices. Using a child logger with a specific module name aids in debugging.
39-43: LGTM! Clean export of service functions.The export is well-structured and makes the service functions easily accessible.
9-19:Details
✅ Verification successful
Use standard HTTP error codes instead of custom ones.
The error code 590 is not a standard HTTP status code. Standard HTTP error codes are typically between 400-599. Consider using a standard code like 500 (Internal Server Error) or document this custom error code.
Also, consider including the original error message in the log to aid debugging.
} catch (error) { - logger.error(error, 'Failed to trigger mediawiki schedule.'); - throw new ApiError(590, 'Failed to create mediawiki schedule.'); + logger.error(error, `Failed to trigger mediawiki schedule: ${(error as Error).message}`); + throw new ApiError(500, 'Failed to create mediawiki schedule.'); }
🌐 Web query:
What are the standard HTTP status codes for server errors?💡 Result:
HTTP server error status codes (5xx) indicate issues on the server side when processing valid requests. These codes help diagnose problems affecting website functionality and user experience. Below are the standard server error codes, their meanings, and typical use cases:
Core 5xx Status Codes
500 Internal Server Error
A generic error when the server encounters an unexpected condition (e.g., coding errors, misconfigurations)[3][7][8].501 Not Implemented
The server lacks the capability to fulfill the request (e.g., unsupported HTTP methods like PUT or DELETE)[3][7][8].502 Bad Gateway
A gateway or proxy server received an invalid response from an upstream server (common during server outages or overloads)[2][3][7][8].503 Service Unavailable
The server is temporarily unable to handle requests due to maintenance, overload, or downtime[2][3][7][8].504 Gateway Timeout
A gateway/proxy server did not receive a timely response from an upstream server[3][7][8][9].505 HTTP Version Not Supported
The server does not support the HTTP protocol version used in the request[7][8][10].Extended 5xx Codes (Specialized Use Cases)
506 Variant Also Negotiates
The server has a configuration error causing circular content negotiation[7][8].507 Insufficient Storage (WebDAV)
The server cannot store the representation needed to complete the request[7][8].508 Loop Detected (WebDAV)
The server detected an infinite loop while processing the request[7][8].510 Not Extended
The server requires additional extensions to fulfill the request[8][10].511 Network Authentication Required
The client must authenticate to gain network access (e.g., captive portals)[7][8].
Server errors often require immediate attention, as they disrupt user access and can impact SEO[5]. Tools like monitoring services and server logs help diagnose issues such as overloaded servers, misconfigured proxies, or resource limitations[2][5][9]. For transient errors (e.g., 503), implementing a
Retry-Afterheader improves user experience by indicating when to retry[3][7].Citations:
- 1: https://moz.com/learn/seo/http-status-codes
- 2: https://www.dotcom-monitor.com/blog/the-10-most-common-http-status-codes/
- 3: https://www.sitelock.com/blog/http-status-codes-explained/
- 4: https://blog.postman.com/what-are-http-status-codes/
- 5: https://developers.google.com/search/docs/crawling-indexing/http-network-errors
- 6: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
- 7: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- 8: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
- 9: https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/iis/health-diagnostic-performance/http-status-code
- 10: https://www.w3schools.com/tags/ref_httpmessages.asp
Action: Use standard HTTP error code 500 and include original error details for debugging
The custom error code (590) should be replaced with a standard 5xx status code. Based on standard HTTP server error codes, use 500 (Internal Server Error) to indicate a generic server failure. Also, include the original error message in the log to enhance debugging clarity.
- Update the log message to append the original error’s message.
- Change the custom status code from 590 to 500.
} catch (error) { - logger.error(error, 'Failed to trigger mediawiki schedule.'); - throw new ApiError(590, 'Failed to create mediawiki schedule.'); + logger.error(error, `Failed to trigger mediawiki schedule: ${(error as Error).message}`); + throw new ApiError(500, 'Failed to create mediawiki schedule.'); }src/services/temporal/mediawiki.service.ts (3)
1-10: LGTM! Good import structure and logger setup.The imports and logger setup follow good practices, providing a solid foundation for the service.
63-70: LGTM! Good error handling in the terminateWorkflow method.The implementation checks the workflow status before attempting to terminate it, which is a good practice to prevent errors.
73-73: LGTM! Appropriate singleton export pattern.Exporting a singleton instance is a good pattern for service classes.
| const handleHivemindMediaWikiCase = async (platform: any) => { | ||
| const platformDoc = await platformService.getPlatformById(platform.platform); | ||
|
|
||
| if (!platformDoc) return; | ||
|
|
||
| const isActivated = platform.metadata?.activated; | ||
| const existingScheduleId = platformDoc.get('metadata.scheduleId'); | ||
|
|
||
| if (isActivated === true) { | ||
| if (!existingScheduleId) { | ||
| const scheduleId = await mediawikiService.coreService.createMediaWikiSchedule(platform.platform); | ||
| platformDoc.set('metadata.scheduleId', scheduleId); | ||
|
|
||
| await platformDoc.save(); | ||
| } | ||
| } else if (isActivated === false) { | ||
| if (existingScheduleId) { | ||
| await mediawikiService.coreService.deleteMediaWikiSchedule(existingScheduleId); | ||
| await mediawikiService.coreService.terminateMediaWikiWorkflow(platformDoc.community.toString()); | ||
| platformDoc.set('metadata.scheduleId', null); | ||
|
|
||
| await platformDoc.save(); | ||
| } | ||
| } | ||
| }; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Consider defining a proper type for the platform parameter.
The platform: any type is too generic. Define a specific interface for better type safety and code clarity.
Also, note the code duplication between this function and handleHivemindWebsiteCase. Consider refactoring to reduce duplication in the future.
-const handleHivemindMediaWikiCase = async (platform: any) => {
+interface PlatformWithMetadata {
+ platform: Types.ObjectId;
+ metadata?: {
+ activated?: boolean;
+ scheduleId?: string;
+ };
+}
+
+const handleHivemindMediaWikiCase = async (platform: PlatformWithMetadata) => {Let's check how similar these two handler functions are:
🏁 Script executed:
#!/bin/bash
# Compare the two handler functions to assess duplication level
echo "handleHivemindWebsiteCase:"
sed -n '/const handleHivemindWebsiteCase/,/^};/p' src/services/module.service.ts
echo -e "\nhandleHivemindMediaWikiCase:"
sed -n '/const handleHivemindMediaWikiCase/,/^};/p' src/services/module.service.tsLength of output: 2111
Action Required: Refactor platform parameter typing and consolidate duplicated logic.
-
Type Safety: Replace the generic
anytype with a specific interface (e.g., aPlatformWithMetadatainterface) to ensure clear type safety. For example:-const handleHivemindMediaWikiCase = async (platform: any) => { +interface PlatformWithMetadata { + platform: Types.ObjectId; + metadata?: { + activated?: boolean; + scheduleId?: string; + }; +} + +const handleHivemindMediaWikiCase = async (platform: PlatformWithMetadata) => {
-
Deduplication: The two functions—
handleHivemindWebsiteCaseandhandleHivemindMediaWikiCase—share an almost identical control flow, differing only in the called service methods (e.g.,createWebsiteSchedulevs.createMediaWikiSchedule). Consider abstracting the common logic into a shared helper function or employing a strategy pattern to reduce maintenance overhead.
📝 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.
| const handleHivemindMediaWikiCase = async (platform: any) => { | |
| const platformDoc = await platformService.getPlatformById(platform.platform); | |
| if (!platformDoc) return; | |
| const isActivated = platform.metadata?.activated; | |
| const existingScheduleId = platformDoc.get('metadata.scheduleId'); | |
| if (isActivated === true) { | |
| if (!existingScheduleId) { | |
| const scheduleId = await mediawikiService.coreService.createMediaWikiSchedule(platform.platform); | |
| platformDoc.set('metadata.scheduleId', scheduleId); | |
| await platformDoc.save(); | |
| } | |
| } else if (isActivated === false) { | |
| if (existingScheduleId) { | |
| await mediawikiService.coreService.deleteMediaWikiSchedule(existingScheduleId); | |
| await mediawikiService.coreService.terminateMediaWikiWorkflow(platformDoc.community.toString()); | |
| platformDoc.set('metadata.scheduleId', null); | |
| await platformDoc.save(); | |
| } | |
| } | |
| }; | |
| interface PlatformWithMetadata { | |
| platform: Types.ObjectId; | |
| metadata?: { | |
| activated?: boolean; | |
| scheduleId?: string; | |
| }; | |
| } | |
| const handleHivemindMediaWikiCase = async (platform: PlatformWithMetadata) => { | |
| const platformDoc = await platformService.getPlatformById(platform.platform); | |
| if (!platformDoc) return; | |
| const isActivated = platform.metadata?.activated; | |
| const existingScheduleId = platformDoc.get('metadata.scheduleId'); | |
| if (isActivated === true) { | |
| if (!existingScheduleId) { | |
| const scheduleId = await mediawikiService.coreService.createMediaWikiSchedule(platform.platform); | |
| platformDoc.set('metadata.scheduleId', scheduleId); | |
| await platformDoc.save(); | |
| } | |
| } else if (isActivated === false) { | |
| if (existingScheduleId) { | |
| await mediawikiService.coreService.deleteMediaWikiSchedule(existingScheduleId); | |
| await mediawikiService.coreService.terminateMediaWikiWorkflow(platformDoc.community.toString()); | |
| platformDoc.set('metadata.scheduleId', null); | |
| await platformDoc.save(); | |
| } | |
| } | |
| }; |
| async function terminateMediaWikiWorkflow(communityId: string): Promise<void> { | ||
| try { | ||
| await temporalMediaWiki.terminateWorkflow(`api:mediawiki:${communityId}`); | ||
| } catch (error) { | ||
| logger.error(error, 'Failed to terminate mediawiki workflow.'); | ||
| throw new ApiError(590, 'Failed to terminate mediawiki workflow.'); | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Apply consistent error handling across all methods.
Similar to the previous comments, use standard HTTP error codes and include the original error message in the log.
} catch (error) {
- logger.error(error, 'Failed to terminate mediawiki workflow.');
- throw new ApiError(590, 'Failed to terminate mediawiki workflow.');
+ logger.error(error, `Failed to terminate mediawiki workflow: ${(error as Error).message}`);
+ throw new ApiError(500, 'Failed to terminate mediawiki workflow.');
}📝 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.
| async function terminateMediaWikiWorkflow(communityId: string): Promise<void> { | |
| try { | |
| await temporalMediaWiki.terminateWorkflow(`api:mediawiki:${communityId}`); | |
| } catch (error) { | |
| logger.error(error, 'Failed to terminate mediawiki workflow.'); | |
| throw new ApiError(590, 'Failed to terminate mediawiki workflow.'); | |
| } | |
| } | |
| async function terminateMediaWikiWorkflow(communityId: string): Promise<void> { | |
| try { | |
| await temporalMediaWiki.terminateWorkflow(`api:mediawiki:${communityId}`); | |
| } catch (error) { | |
| logger.error(error, `Failed to terminate mediawiki workflow: ${(error as Error).message}`); | |
| throw new ApiError(500, 'Failed to terminate mediawiki workflow.'); | |
| } | |
| } |
| async function deleteMediaWikiSchedule(scheduleId: string): Promise<void> { | ||
| try { | ||
| await temporalMediaWiki.deleteSchedule(scheduleId); | ||
| } catch (error) { | ||
| logger.error(error, 'Failed to delete mediawiki schedule.'); | ||
| throw new ApiError(590, 'Failed to delete mediawiki schedule.'); | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Apply consistent error handling across all methods.
Similar to the previous comment, use standard HTTP error codes and include the original error message in the log.
} catch (error) {
- logger.error(error, 'Failed to delete mediawiki schedule.');
- throw new ApiError(590, 'Failed to delete mediawiki schedule.');
+ logger.error(error, `Failed to delete mediawiki schedule: ${(error as Error).message}`);
+ throw new ApiError(500, 'Failed to delete mediawiki schedule.');
}📝 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.
| async function deleteMediaWikiSchedule(scheduleId: string): Promise<void> { | |
| try { | |
| await temporalMediaWiki.deleteSchedule(scheduleId); | |
| } catch (error) { | |
| logger.error(error, 'Failed to delete mediawiki schedule.'); | |
| throw new ApiError(590, 'Failed to delete mediawiki schedule.'); | |
| } | |
| } | |
| async function deleteMediaWikiSchedule(scheduleId: string): Promise<void> { | |
| try { | |
| await temporalMediaWiki.deleteSchedule(scheduleId); | |
| } catch (error) { | |
| logger.error(error, `Failed to delete mediawiki schedule: ${(error as Error).message}`); | |
| throw new ApiError(500, 'Failed to delete mediawiki schedule.'); | |
| } | |
| } |
| public async pauseSchedule(scheduleId: string): Promise<void> { | ||
| const client: Client = await this.getClient(); | ||
| const handle = client.schedule.getHandle(scheduleId); | ||
| await handle.pause(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for the pauseSchedule method.
Consider adding proper error handling and checking if the schedule exists before attempting to pause it.
public async pauseSchedule(scheduleId: string): Promise<void> {
- const client: Client = await this.getClient();
- const handle = client.schedule.getHandle(scheduleId);
- await handle.pause();
+ try {
+ const client: Client = await this.getClient();
+ const handle = client.schedule.getHandle(scheduleId);
+ await handle.pause();
+ } catch (error) {
+ logger.error(error, `Failed to pause mediawiki schedule: ${(error as Error).message}`);
+ throw new Error(`Failed to pause mediawiki schedule: ${(error as Error).message}`);
+ }
}📝 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.
| public async pauseSchedule(scheduleId: string): Promise<void> { | |
| const client: Client = await this.getClient(); | |
| const handle = client.schedule.getHandle(scheduleId); | |
| await handle.pause(); | |
| } | |
| public async pauseSchedule(scheduleId: string): Promise<void> { | |
| try { | |
| const client: Client = await this.getClient(); | |
| const handle = client.schedule.getHandle(scheduleId); | |
| await handle.pause(); | |
| } catch (error) { | |
| logger.error(error, `Failed to pause mediawiki schedule: ${(error as Error).message}`); | |
| throw new Error(`Failed to pause mediawiki schedule: ${(error as Error).message}`); | |
| } | |
| } |
| public async deleteSchedule(scheduleId: string): Promise<void> { | ||
| const client: Client = await this.getClient(); | ||
| const handle = client.schedule.getHandle(scheduleId); | ||
|
|
||
| await handle.delete(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for the deleteSchedule method.
Consider adding proper error handling and checking if the schedule exists before attempting to delete it, similar to how it's done in the terminateWorkflow method.
public async deleteSchedule(scheduleId: string): Promise<void> {
- const client: Client = await this.getClient();
- const handle = client.schedule.getHandle(scheduleId);
-
- await handle.delete();
+ try {
+ const client: Client = await this.getClient();
+ const handle = client.schedule.getHandle(scheduleId);
+ await handle.delete();
+ } catch (error) {
+ logger.error(error, `Failed to delete mediawiki schedule: ${(error as Error).message}`);
+ throw new Error(`Failed to delete mediawiki schedule: ${(error as Error).message}`);
+ }
}📝 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.
| public async deleteSchedule(scheduleId: string): Promise<void> { | |
| const client: Client = await this.getClient(); | |
| const handle = client.schedule.getHandle(scheduleId); | |
| await handle.delete(); | |
| } | |
| public async deleteSchedule(scheduleId: string): Promise<void> { | |
| try { | |
| const client: Client = await this.getClient(); | |
| const handle = client.schedule.getHandle(scheduleId); | |
| await handle.delete(); | |
| } catch (error) { | |
| logger.error(error, `Failed to delete mediawiki schedule: ${(error as Error).message}`); | |
| throw new Error(`Failed to delete mediawiki schedule: ${(error as Error).message}`); | |
| } | |
| } |
Summary by CodeRabbit