Browse Source

feat(news): skip cron when news:isDeliveryEnabled is false

Read the delivery flag at the start of every `executeJob()` tick and
short-circuit before any HTTP fetch or DB write when it resolves to
`false`. The flag is evaluated through configManager so a value
written by an admin via the UI takes effect on the next scheduled run
without restarting the pod (Requirements 9.5, 9.6).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ryotaro Nagahara 1 week ago
parent
commit
0a3a682b76

+ 38 - 0
apps/app/src/features/news/server/services/news-cron-service.spec.ts

@@ -4,6 +4,12 @@ const mocks = vi.hoisted(() => {
   const deleteItemsNotInFeed = vi.fn();
   const mockFetch = vi.fn();
   const getGrowiVersion = vi.fn(() => '7.5.0');
+  // Default delivery to enabled so existing tests behave as before.
+  // Tests that need OFF state can override via mocks.getConfig.mockImplementationOnce.
+  const getConfig = vi.fn<(key: string) => unknown>((key: string) => {
+    if (key === 'news:isDeliveryEnabled') return true;
+    return undefined;
+  });
 
   return {
     NewsService: vi.fn(() => ({
@@ -14,6 +20,7 @@ const mocks = vi.hoisted(() => {
     deleteItemsNotInFeed,
     mockFetch,
     getGrowiVersion,
+    getConfig,
   };
 });
 
@@ -25,6 +32,12 @@ vi.mock('~/utils/growi-version', () => ({
   getGrowiVersion: mocks.getGrowiVersion,
 }));
 
+vi.mock('~/server/service/config-manager', () => ({
+  configManager: {
+    getConfig: mocks.getConfig,
+  },
+}));
+
 // Mock global fetch
 vi.stubGlobal('fetch', mocks.mockFetch);
 
@@ -82,6 +95,31 @@ describe('NewsCronService', () => {
   });
 
   describe('executeJob', () => {
+    test('should skip when news:isDeliveryEnabled is false', async () => {
+      process.env.NEWS_FEED_URL = 'https://example.com/feed.json';
+      mocks.getConfig.mockImplementationOnce((key: string) =>
+        key === 'news:isDeliveryEnabled' ? false : undefined,
+      );
+
+      await service.executeJob();
+
+      // Delivery flag short-circuits before any network call or DB write
+      expect(mocks.mockFetch).not.toHaveBeenCalled();
+      expect(mocks.upsertNewsItems).not.toHaveBeenCalled();
+    });
+
+    test('should run when news:isDeliveryEnabled is true (default)', async () => {
+      process.env.NEWS_FEED_URL = 'https://example.com/feed.json';
+      mocks.mockFetch.mockResolvedValue(
+        mockResponse({ version: '1.0', items: [] }),
+      );
+
+      await service.executeJob();
+
+      expect(mocks.getConfig).toHaveBeenCalledWith('news:isDeliveryEnabled');
+      expect(mocks.mockFetch).toHaveBeenCalled();
+    });
+
     test('should skip when NEWS_FEED_URL is not set', async () => {
       delete process.env.NEWS_FEED_URL;
 

+ 9 - 0
apps/app/src/features/news/server/services/news-cron-service.ts

@@ -1,3 +1,4 @@
+import { configManager } from '~/server/service/config-manager';
 import CronService from '~/server/service/cron';
 import { getGrowiVersion } from '~/utils/growi-version';
 import loggerFactory from '~/utils/logger';
@@ -67,6 +68,14 @@ export class NewsCronService extends CronService {
   }
 
   override async executeJob(): Promise<void> {
+    // Read the delivery toggle (DB > env > defaultValue: true) on every tick so
+    // an admin's UI change takes effect from the next scheduled run, with no
+    // pod restart required (Requirements 9.5, 9.6).
+    if (!configManager.getConfig('news:isDeliveryEnabled')) {
+      logger.debug('News delivery is disabled, skipping news feed sync');
+      return;
+    }
+
     const feedUrl = process.env.NEWS_FEED_URL;
 
     if (!feedUrl || feedUrl.trim() === '') {