diff --git a/src/back/jobs/UpdatesMissingForumPost.ts b/src/back/jobs/UpdatesMissingForumPost.ts new file mode 100644 index 000000000..a5c6baf5b --- /dev/null +++ b/src/back/jobs/UpdatesMissingForumPost.ts @@ -0,0 +1,17 @@ +import UpdateModel from '../../entities/Updates/model' +import { DiscourseService } from '../../services/DiscourseService' +import { ProposalService } from '../../services/ProposalService' +import logger from '../../utils/logger' + +export async function restoreMissingUpdatesForumPost() { + const affectedUpdates = await UpdateModel.getUpdatesWithoutForumPost() + logger.log( + `Found ${affectedUpdates.length} updates without forum post. IDs: ${affectedUpdates + .map((update) => update.id) + .join(', ')}` + ) + for (const update of affectedUpdates) { + const { title } = await ProposalService.getProposal(update.proposal_id) + await DiscourseService.createUpdate(update, title) + } +} diff --git a/src/back/routes/debug.ts b/src/back/routes/debug.ts index a4f3e3ea1..2fa72c507 100644 --- a/src/back/routes/debug.ts +++ b/src/back/routes/debug.ts @@ -6,12 +6,14 @@ import { DEBUG_ADDRESSES } from '../../constants' import CacheService from '../../services/CacheService' import { ErrorService } from '../../services/ErrorService' import { giveAndRevokeLandOwnerBadges, giveTopVoterBadges, runQueuedAirdropJobs } from '../jobs/BadgeAirdrop' +import { restoreMissingUpdatesForumPost } from '../jobs/UpdatesMissingForumPost' import { validateDebugAddress } from '../utils/validations' const FUNCTIONS_MAP: { [key: string]: () => Promise } = { runQueuedAirdropJobs, giveAndRevokeLandOwnerBadges, giveTopVoterBadges, + restoreMissingUpdatesForumPost, } export default routes((router) => { diff --git a/src/entities/Updates/model.ts b/src/entities/Updates/model.ts index 8dda1ba9a..cf9ab6a1d 100644 --- a/src/entities/Updates/model.ts +++ b/src/entities/Updates/model.ts @@ -1,5 +1,6 @@ import crypto from 'crypto' import { Model } from 'decentraland-gatsby/dist/entities/Database/model' +import { SQL, table } from 'decentraland-gatsby/dist/entities/Database/utils' import { UpdateAttributes, UpdateStatus } from './types' @@ -23,4 +24,15 @@ export default class UpdateModel extends Model { ...update, }) } + + static async getUpdatesWithoutForumPost() { + const query = SQL` + SELECT * + FROM ${table(this)} + WHERE health IS NOT NULL + AND discourse_topic_id IS NULL + AND completion_date > '2023-08-10' + ` + return this.namedQuery('get_updates_without_forum_post', query) + } } diff --git a/src/services/DiscourseService.ts b/src/services/DiscourseService.ts index f91004055..f5ce652a9 100644 --- a/src/services/DiscourseService.ts +++ b/src/services/DiscourseService.ts @@ -60,20 +60,21 @@ export class DiscourseService { } } - static async createUpdate(data: UpdateAttributes, proposalTitle: string) { + static async createUpdate(newUpdate: UpdateAttributes, proposalTitle: string) { try { - const updates = await UpdateService.getAllByProposalId(data.proposal_id) + const updates = await UpdateService.getAllByProposalId(newUpdate.proposal_id) const publicUpdates = getPublicUpdates(updates) - const updateIndex = publicUpdates.length - const discoursePost = this.getUpdatePost(data, data.id, updateIndex, proposalTitle) + const updateIndex = publicUpdates.findIndex((update) => update.id === newUpdate.id) + const latestUpdateIndex = updateIndex < 0 ? publicUpdates.length : publicUpdates.length - updateIndex + const discoursePost = this.getUpdatePost(newUpdate, latestUpdateIndex, proposalTitle) const discourseUpdate = await this.createPostWithRetry(discoursePost) - await UpdateService.updateWithDiscoursePost(data.id, discourseUpdate) + await UpdateService.updateWithDiscoursePost(newUpdate.id, discourseUpdate) this.logPostCreation(discourseUpdate) return discourseUpdate } catch (error: any) { ErrorService.report('Error creating discourse post for update', { - update_id: data.id, - proposal_id: data.proposal_id, + update_id: newUpdate.id, + proposal_id: newUpdate.proposal_id, error: `${error}`, category: ErrorCategory.Discourse, }) @@ -105,13 +106,13 @@ export class DiscourseService { } } - private static getUpdatePost(data: UpdateAttributes, update_id: string, updateIndex: number, proposalTitle: string) { - const { author, proposal_id } = data + private static getUpdatePost(update: UpdateAttributes, updateIndex: number, proposalTitle: string) { + const { id, author, proposal_id } = update const template: updateTemplates.ForumTemplate = { author: author || '', - title: `Update #${updateIndex} for proposal "${proposalTitle}"`, - update_url: getUpdateUrl(update_id, proposal_id), - ...data, + title: `Update #${updateIndex} for project "${proposalTitle}"`, + update_url: getUpdateUrl(id, proposal_id), + ...update, } return {