diff --git a/backend/config/custom-environment-variables.json b/backend/config/custom-environment-variables.json index a73b1730c5..c81d436228 100644 --- a/backend/config/custom-environment-variables.json +++ b/backend/config/custom-environment-variables.json @@ -145,6 +145,9 @@ "slackAlerting": { "url": "CROWD_SLACK_ALERTING_URL" }, + "sampleData": { + "tenantId": "CROWD_SAMPLE_DATA_TENANT_ID" + }, "unleash": { "url": "CROWD_UNLEASH_URL", "adminApiKey": "CROWD_UNLEASH_ADMIN_API_KEY", diff --git a/backend/config/default.json b/backend/config/default.json index 71039aaade..3309cb8657 100644 --- a/backend/config/default.json +++ b/backend/config/default.json @@ -40,5 +40,8 @@ }, "slackAlerting": { "url": "" + }, + "sampleData": { + "tenantId": "" } } diff --git a/backend/src/bin/jobs/index.ts b/backend/src/bin/jobs/index.ts index 7ab1cc8021..52ea31e0dc 100644 --- a/backend/src/bin/jobs/index.ts +++ b/backend/src/bin/jobs/index.ts @@ -7,6 +7,7 @@ import refreshMaterializedViews from './refreshMaterializedViews' import downgradeExpiredPlans from './downgradeExpiredPlans' import eagleEyeEmailDigestTicks from './eagleEyeEmailDigestTicks' import integrationDataChecker from './integrationDataChecker' +import refreshSampleData from './refreshSampleData' const jobs: CrowdJob[] = [ weeklyAnalyticsEmailsCoordinator, @@ -17,6 +18,7 @@ const jobs: CrowdJob[] = [ downgradeExpiredPlans, eagleEyeEmailDigestTicks, integrationDataChecker, + refreshSampleData, ] export default jobs diff --git a/backend/src/bin/jobs/refreshSampleData.ts b/backend/src/bin/jobs/refreshSampleData.ts new file mode 100644 index 0000000000..21383c6ab4 --- /dev/null +++ b/backend/src/bin/jobs/refreshSampleData.ts @@ -0,0 +1,19 @@ +import { CrowdJob } from '../../types/jobTypes' +import { sendNodeWorkerMessage } from '../../serverless/utils/nodeWorkerSQS' +import { NodeWorkerMessageType } from '../../serverless/types/workerTypes' +import { NodeWorkerMessageBase } from '../../types/mq/nodeWorkerMessageBase' + +const job: CrowdJob = { + name: 'Refresh sample data', + // every hour + cronTime: '0 * * * *', + // cronTime: '0 0 * * *', + onTrigger: async () => { + await sendNodeWorkerMessage('refresh-sample-data', { + type: NodeWorkerMessageType.NODE_MICROSERVICE, + service: 'refresh-sample-data', + } as NodeWorkerMessageBase) + }, +} + +export default job diff --git a/backend/src/config/configTypes.ts b/backend/src/config/configTypes.ts index 835967698e..09bf39a076 100644 --- a/backend/src/config/configTypes.ts +++ b/backend/src/config/configTypes.ts @@ -204,3 +204,7 @@ export interface UnleashConfiguration { export interface SlackAlertingConfiguration { url: string } + +export interface SampleDataConfiguration { + tenantId: string +} diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 8346ad25be..fe910a065f 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -26,6 +26,7 @@ import { EagleEyeConfiguration, UnleashConfiguration, SlackAlertingConfiguration, + SampleDataConfiguration, } from './configTypes' // TODO-kube @@ -240,3 +241,9 @@ export const SLACK_ALERTING_CONFIG: SlackAlertingConfiguration = KUBE_MODE : { url: process.env.SLACK_ALERTING_URL, } + +export const SAMPLE_DATA_CONFIG: SampleDataConfiguration = KUBE_MODE + ? config.get('sampleData') + : { + tenantId: process.env.SAMPLE_DATA_TENANT_ID, + } diff --git a/backend/src/serverless/microservices/nodejs/integration-data-checker/refreshSampleDataWorker.ts b/backend/src/serverless/microservices/nodejs/integration-data-checker/refreshSampleDataWorker.ts new file mode 100644 index 0000000000..e370c9adb0 --- /dev/null +++ b/backend/src/serverless/microservices/nodejs/integration-data-checker/refreshSampleDataWorker.ts @@ -0,0 +1,37 @@ +import { QueryTypes } from 'sequelize' +import { API_CONFIG, SAMPLE_DATA_CONFIG } from '../../../../config' +import getUserContext from '../../../../database/utils/getUserContext' + +async function refreshSampleDataWorker(): Promise { + // This is only needed for hosted edition + if (API_CONFIG.edition === 'crowd-hosted') { + const tenantId = SAMPLE_DATA_CONFIG.tenantId + const userContext = await getUserContext(SAMPLE_DATA_CONFIG.tenantId) + const updateDays = 9 // Every day we need to refresh + + // These are all the tables that have columns that need to be updated + const tables = [ + { name: 'activities', columns: ['createdAt', 'timestamp'] }, + { name: 'members', columns: ['joinedAt', 'createdAt'] }, + { name: 'notes', columns: ['createdAt'] }, + { name: 'conversations', columns: ['createdAt'] }, + { name: 'organizations', columns: ['createdAt'] }, + { name: 'tags', columns: ['createdAt'] }, + ] + + // We are using a direct query because it is very specific functionality. + // There is no point creating repository methods. + for (const table of tables) { + for (const column of table.columns) { + const query = ` + UPDATE ${table.name} + SET "${column}" = "${column}" + INTERVAL '${updateDays} days' + WHERE "tenantId" = '${tenantId}'; + ` + await userContext.database.sequelize.query(query, { type: QueryTypes.UPDATE }) + } + } + } +} + +export { refreshSampleDataWorker } diff --git a/backend/src/serverless/microservices/nodejs/workerFactory.ts b/backend/src/serverless/microservices/nodejs/workerFactory.ts index fac185fe3e..b2bb6103b3 100644 --- a/backend/src/serverless/microservices/nodejs/workerFactory.ts +++ b/backend/src/serverless/microservices/nodejs/workerFactory.ts @@ -22,6 +22,7 @@ import { processSendgridWebhook } from '../../integrations/workers/sendgridWebho import { bulkEnrichmentWorker } from './bulk-enrichment/bulkEnrichmentWorker' import { eagleEyeEmailDigestWorker } from './eagle-eye-email-digest/eagleEyeEmailDigestWorker' import { integrationDataCheckerWorker } from './integration-data-checker/integrationDataCheckerWorker' +import { refreshSampleDataWorker } from './integration-data-checker/refreshSampleDataWorker' /** * Worker factory for spawning different microservices @@ -49,6 +50,9 @@ async function workerFactory(event: NodeMicroserviceMessage): Promise { integrationDataCheckerMessage.integrationId, integrationDataCheckerMessage.tenantId, ) + case 'refresh-sample-data': + return refreshSampleDataWorker() + case 'csv-export': const csvExportMessage = event as CsvExportMessage return csvExportWorker(