All files / platform/modules/salon/src/Jobs CleanUpSalonPhotos.js

96.55% Statements 56/58
87.5% Branches 14/16
100% Functions 10/10
96.43% Lines 54/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131    1x   1x 1x 1x   1x 1x       5x 5x 5x       8x       1x       1x                 4x   4x 4x   3x 3x   3x   3x 3x       3x 3x     3x         3x   5x 5x 5x 5x 5x       5x           3x     3x 3x 2x 2x 2x   2x       3x   2x 2x 3x 3x 3x     2x 2x   1x 2x 2x         3x 1x       2x     2x                 2x       1x  
'use strict';
 
const _ = require('lodash');
 
const Config = use('Config');
const Logger = use('Logger');
const AwsService = use('C2C/Services/AwsService');
 
const cleanUpSalonPhotosCron = Config.get('modules.salon.general.cleanUpSalonPhotosCron');
const defaultTimezone = Config.get('app.defaultTimezone');
 
class CleanUpSalonPhotos {
  constructor() {
    this.aws = new AwsService();
    const salonConfigs = Config.get('modules.salon.general');
    this.salonService = make(`${salonConfigs.namespace}/Services/SalonService`);
  }
 
  static get key() {
    return 'CleanUpSalonPhotos-job';
  }
 
  static get concurrency() {
    return 1;
  }
 
  static get options() {
    return {
      repeat: {
        cron: cleanUpSalonPhotosCron,
        tz: defaultTimezone,
      },
    };
  }
 
  async handle() {
    let totalRowProcess = 0;
    // count all photos with deletedAt < today 00:00:00
    const countPhotosForCleanUp = await this.salonService.countPhotosForCleanUp();
    if (countPhotosForCleanUp === 0) return totalRowProcess;
 
    const limit = 1000; // api deleteObjects support delete up to 1000 Key
    const totalPage = Math.ceil(countPhotosForCleanUp / limit);
 
    for (let i = 1; i <= totalPage; i++) {
      // get all photos with deletedAt < today 00:00:00 with limit = 1000
      const photos = await this.salonService.getPhotosForCleanUp(limit);
      Iif (photos.rows.length === 0) {
        break;
      }
 
      const rowProcess = await this._deleteListS3Url(photos);
      totalRowProcess += rowProcess;
    }
 
    return totalRowProcess;
  }
 
  async _deleteListS3Url(photos) {
    // 1. set up data
    const { mapS3WillDelete, uniqUrls } = photos.rows.reduce(
      (m, p) => {
        const key = 'images' + p.url.split('/images')[1];
        const photoIdsWillDelete = m.mapS3WillDelete[key];
        Eif (!photoIdsWillDelete) {
          m.mapS3WillDelete[key] = [p.id];
          m.uniqUrls.push(p.url);
        } else {
          photoIdsWillDelete.push(p.id);
        }
        return m;
      },
      { mapS3WillDelete: {}, uniqUrls: [] },
    );
 
    // 2. get list photos still active by list urls
    const listPhotoIgnore = await this.salonService.getPhotosByListUrls(uniqUrls);
 
    // 3. Remove photo key still active and save list photo ids will hard delete in DB.
    const photoIdsWillHardDelete = [];
    listPhotoIgnore.rows.forEach((p) => {
      const key = 'images' + p.url.split('/images')[1];
      const photoIds = mapS3WillDelete[key];
      photoIds && photoIdsWillHardDelete.push(...photoIds);
 
      delete mapS3WillDelete[key];
    });
 
    // 4. Delete unused photos on S3
    if (!_.isEmpty(mapS3WillDelete)) {
      // Must decodeURIComponent image url to s3 key format since the image url is encoded string
      const mapS3Key = {};
      const s3Objects = Object.keys(mapS3WillDelete).map((key) => {
        const decodeKey = decodeURIComponent(key);
        mapS3Key[decodeKey] = mapS3WillDelete[key];
        return { Key: decodeKey };
      });
 
      const s3DeleteResult = await this.aws.deleteObjects(s3Objects);
      if (s3DeleteResult) {
        // Add photoIds which have been deleted success on S3 to hard delete in DB.
        s3DeleteResult.Deleted.forEach((item) => {
          const photoIds = mapS3Key[item.Key];
          photoIds && photoIdsWillHardDelete.push(...photoIds);
        });
      }
    }
 
    if (photoIdsWillHardDelete.length === 0) {
      return 0;
    }
 
    // 5. force hard delete
    const resultHardDelete = await this.salonService.forceDeletePhotos(photoIdsWillHardDelete);
 
    // 6. logger
    Logger.info(
      `[CleanUpSalonPhotos._deleteListS3Url] - Deleted photos: %s`,
      JSON.stringify({
        photoIds: photoIdsWillHardDelete,
        countPhotoIds: photoIdsWillHardDelete.length,
        resultHardDelete,
      }),
    );
 
    return photoIdsWillHardDelete.length;
  }
}
 
module.exports = CleanUpSalonPhotos;