From 2a4cea08549daafb3a4efc0c8d43b13637912f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Sat, 4 Jul 2026 23:37:15 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20publier=20un=20statut=20miroir=20bokant?= =?UTF-8?q?e=20=C3=A0=20la=20publication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parole/__tests__/lifecycles.test.js | 97 +++++++++++++++++++ .../parole/content-types/parole/lifecycles.js | 12 +++ 2 files changed, 109 insertions(+) diff --git a/src/api/parole/content-types/parole/__tests__/lifecycles.test.js b/src/api/parole/content-types/parole/__tests__/lifecycles.test.js index 4d394e3..10b2c3a 100644 --- a/src/api/parole/content-types/parole/__tests__/lifecycles.test.js +++ b/src/api/parole/content-types/parole/__tests__/lifecycles.test.js @@ -118,6 +118,103 @@ describe('beforeUpdate — notifications Telegram/Revolt', () => { }); }); +describe('beforeUpdate — publication miroir bokante', () => { + const originalEnv = {...process.env}; + const bokanteMastodon = require('../../../../../utils/bokante-mastodon'); + const originalCreateStatus = bokanteMastodon.createStatus; + + function buildStrapi(previous) { + const dbQuery = { + findOne: vi.fn(async () => previous), + updateMany: vi.fn() + }; + + return { + db: {query: vi.fn(() => dbQuery)}, + plugins: {email: {services: {email: {send: vi.fn()}}}}, + log: {error: vi.fn()} + }; + } + + function buildEvent(overrides = {}) { + return { + state: {}, + params: { + data: {documentId: 'doc-1', publishedAt: '2026-07-04T00:00:00.000Z', ...overrides} + } + }; + } + + afterEach(() => { + process.env = {...originalEnv}; + bokanteMastodon.createStatus = originalCreateStatus; + delete global.strapi; + }); + + it('publie un statut miroir et stocke bokanteStatusId sur data', async () => { + process.env.BOKANTE_ACCESS_TOKEN = 'fake-token'; + bokanteMastodon.createStatus = vi.fn(async () => ({id: '112233'})); + + const previous = {publishedAt: null, slug: 'mon-titre', titre: 'Mon titre', user: null, userAdmin: null, artistes: [], bokanteStatusId: null}; + const strapiMock = buildStrapi(previous); + + const {beforeUpdate} = await loadLifecycles(strapiMock); + const event = buildEvent(); + + await beforeUpdate(event); + + expect(bokanteMastodon.createStatus).toHaveBeenCalledTimes(1); + expect(bokanteMastodon.createStatus.mock.calls[0][0]).toContain('Mon titre'); + expect(event.params.data.bokanteStatusId).toBe('112233'); + }); + + it('ne republie pas si bokanteStatusId existe déjà', async () => { + process.env.BOKANTE_ACCESS_TOKEN = 'fake-token'; + bokanteMastodon.createStatus = vi.fn(async () => ({id: '999'})); + + const previous = {publishedAt: null, slug: 'mon-titre', titre: 'Mon titre', user: null, userAdmin: null, artistes: [], bokanteStatusId: '112233'}; + const strapiMock = buildStrapi(previous); + + const {beforeUpdate} = await loadLifecycles(strapiMock); + + await beforeUpdate(buildEvent()); + + expect(bokanteMastodon.createStatus).not.toHaveBeenCalled(); + }); + + it('ne publie rien si BOKANTE_ACCESS_TOKEN n\'est pas configuré', async () => { + delete process.env.BOKANTE_ACCESS_TOKEN; + bokanteMastodon.createStatus = vi.fn(async () => ({id: '999'})); + + const previous = {publishedAt: null, slug: 'mon-titre', titre: 'Mon titre', user: null, userAdmin: null, artistes: [], bokanteStatusId: null}; + const strapiMock = buildStrapi(previous); + + const {beforeUpdate} = await loadLifecycles(strapiMock); + + await beforeUpdate(buildEvent()); + + expect(bokanteMastodon.createStatus).not.toHaveBeenCalled(); + }); + + it('n\'interrompt pas la publication quand bokante échoue', async () => { + process.env.BOKANTE_ACCESS_TOKEN = 'fake-token'; + bokanteMastodon.createStatus = vi.fn(async () => { + throw new Error('boom'); + }); + + const previous = {publishedAt: null, slug: 'mon-titre', titre: 'Mon titre', user: null, userAdmin: null, artistes: [], bokanteStatusId: null}; + const strapiMock = buildStrapi(previous); + + const {beforeUpdate} = await loadLifecycles(strapiMock); + const event = buildEvent(); + + await expect(beforeUpdate(event)).resolves.not.toThrow(); + + expect(strapiMock.log.error).toHaveBeenCalledWith(expect.stringContaining('bokante')); + expect(event.params.data.bokanteStatusId).toBeUndefined(); + }); +}); + describe('beforeUpdate — createdBy/updatedBy', () => { afterEach(() => { delete global.strapi; diff --git a/src/api/parole/content-types/parole/lifecycles.js b/src/api/parole/content-types/parole/lifecycles.js index b0ce667..0d5abc5 100644 --- a/src/api/parole/content-types/parole/lifecycles.js +++ b/src/api/parole/content-types/parole/lifecycles.js @@ -2,6 +2,7 @@ const slugify = require('slugify'); const axios = require('axios'); +const bokanteMastodon = require('../../../../utils/bokante-mastodon'); const utils = require('@strapi/utils'); const { ApplicationError } = utils.errors; @@ -191,6 +192,17 @@ module.exports = { const previousPublishedAt = previousData.publishedAt; const currentPublished_at = data.publishedAt; if (currentPublished_at != previousPublishedAt) { + if (!previousData.bokanteStatusId && process.env.BOKANTE_ACCESS_TOKEN) { + try { + const status = await bokanteMastodon.createStatus( + `"${previousData.titre}" — nouvelle parole sur pawol.nu\n${process.env.WEBSITE_URL}/paroles/${previousData.slug}` + ); + data.bokanteStatusId = status.id; + } catch (err) { + strapi.log.error(`Publication bokante : ${err.message}`); + } + } + const message = `Nouvelle publication ❤️ \n${process.env.WEBSITE_URL}/paroles/${previousData.slug}`; if (previousData.user) {