{"version":3,"file":"weekly-metrics.mjs","sources":["../../../server/src/services/weekly-metrics.ts"],"sourcesContent":["import { defaultTo } from 'lodash/fp';\nimport { add } from 'date-fns';\n\nimport type { Core } from '@strapi/types';\n\nimport { getWeeklyCronScheduleAt } from '../utils/cron';\nimport { FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport { getService } from '../utils';\n\ntype MetricStoreValue = {\n  lastWeeklyUpdate?: number;\n  weeklySchedule?: string;\n};\n\nconst ONE_WEEK = 7 * 24 * 60 * 60 * 1000;\n\nconst getMetricsStoreValue = async (): Promise<MetricStoreValue> => {\n  const value = await strapi.store.get({ type: 'plugin', name: 'upload', key: 'metrics' });\n  return defaultTo({}, value) as MetricStoreValue;\n};\nconst setMetricsStoreValue = (value: MetricStoreValue) =>\n  strapi.store.set({ type: 'plugin', name: 'upload', key: 'metrics', value });\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n  async computeMetrics() {\n    // Folder metrics\n    // @ts-expect-error - no dynamic types for the metadata\n    const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n    const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n\n    let keepOnlySlashesSQLString = '??';\n    const queryParams = [pathColName];\n    for (let i = 0; i < 10; i += 1) {\n      keepOnlySlashesSQLString = `REPLACE(${keepOnlySlashesSQLString}, ?, ?)`;\n      queryParams.push(String(i), '');\n    }\n\n    /*\n      The following query goal is to count the number of folders with depth 1, depth 2 etc.\n      The query returns :\n      [\n        { depth: 1, occurence: 4 },\n        { depth: 2, occurence: 2 },\n        { depth: 3, occurence: 5 },\n      ]\n\n      The query is built as follow:\n      1. In order to get the depth level of a folder:\n        - we take their path\n        - remove all numbers (by replacing 0123456789 by '', thus the 10 REPLACE in the query)\n        - count the remaining `/`, which correspond to their depth (by using LENGTH)\n        We now have, for each folder, its depth.\n      2. In order to get the number of folders for each depth:\n        - we group them by their depth and use COUNT(*)\n    */\n\n    const res = (await strapi.db\n      .getConnection(folderTable)\n      .select(\n        strapi.db.connection.raw(\n          `LENGTH(${keepOnlySlashesSQLString}) AS depth, COUNT(*) AS occurence`,\n          queryParams\n        )\n      )\n      .groupBy('depth')) as Array<{ depth: string; occurence: string }>;\n\n    const folderLevelsArray = res.map((map) => ({\n      depth: Number(map.depth),\n      occurence: Number(map.occurence),\n    })); // values can be strings depending on the database\n\n    let product = 0;\n    let folderNumber = 0;\n    let maxDepth = 0;\n    for (const folderLevel of folderLevelsArray) {\n      product += folderLevel.depth * folderLevel.occurence;\n      folderNumber += folderLevel.occurence;\n      if (folderLevel.depth > maxDepth) {\n        maxDepth = folderLevel.depth;\n      }\n    }\n    const averageDepth = folderNumber !== 0 ? product / folderNumber : 0;\n\n    let sumOfDeviation = 0;\n    for (const folderLevel of folderLevelsArray) {\n      sumOfDeviation += Math.abs(folderLevel.depth - averageDepth) * folderLevel.occurence;\n    }\n\n    const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;\n\n    // File metrics\n    const assetNumber = await strapi.db.query(FILE_MODEL_UID).count();\n\n    return {\n      assetNumber,\n      folderNumber,\n      averageDepth,\n      maxDepth,\n      averageDeviationDepth,\n    };\n  },\n\n  async sendMetrics() {\n    const metrics = await this.computeMetrics();\n    await getService('metrics').trackUsage('didSendUploadPropertiesOnceAWeek', {\n      groupProperties: { metrics },\n    });\n\n    const metricsInfoStored = await getMetricsStoreValue();\n    await setMetricsStoreValue({ ...metricsInfoStored, lastWeeklyUpdate: new Date().getTime() });\n  },\n\n  async ensureWeeklyStoredCronSchedule(): Promise<string> {\n    const metricsInfoStored = await getMetricsStoreValue();\n    const { weeklySchedule: currentSchedule, lastWeeklyUpdate } = metricsInfoStored;\n\n    const now = new Date();\n    let weeklySchedule = currentSchedule;\n\n    if (!weeklySchedule || !lastWeeklyUpdate || lastWeeklyUpdate + ONE_WEEK < now.getTime()) {\n      weeklySchedule = getWeeklyCronScheduleAt(add(now, { seconds: 15 }));\n      await setMetricsStoreValue({ ...metricsInfoStored, weeklySchedule });\n\n      return weeklySchedule;\n    }\n\n    return weeklySchedule;\n  },\n\n  async registerCron() {\n    const weeklySchedule = await this.ensureWeeklyStoredCronSchedule();\n\n    strapi.cron.add({\n      uploadWeekly: {\n        task: this.sendMetrics.bind(this),\n        options: weeklySchedule,\n      },\n    });\n  },\n});\n"],"names":["ONE_WEEK","getMetricsStoreValue","value","strapi","store","get","type","name","key","defaultTo","setMetricsStoreValue","set","computeMetrics","pathColName","db","metadata","FOLDER_MODEL_UID","attributes","path","columnName","folderTable","getModel","collectionName","keepOnlySlashesSQLString","queryParams","i","push","String","res","getConnection","select","connection","raw","groupBy","folderLevelsArray","map","depth","Number","occurence","product","folderNumber","maxDepth","folderLevel","averageDepth","sumOfDeviation","Math","abs","averageDeviationDepth","assetNumber","query","FILE_MODEL_UID","count","sendMetrics","metrics","getService","trackUsage","groupProperties","metricsInfoStored","lastWeeklyUpdate","Date","getTime","ensureWeeklyStoredCronSchedule","weeklySchedule","currentSchedule","now","getWeeklyCronScheduleAt","add","seconds","registerCron","cron","uploadWeekly","task","bind","options"],"mappings":"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"}