WildH0g / Medium-GAS-bypassing-max-exec-time

10 stars 12 forks source link

Trigger not activating #4

Open jesse-schewe opened 10 months ago

jesse-schewe commented 10 months ago

Hello,

I'm new to using Google App Script and this is my first project. I tried two different versions of the script -- following along with the full article writing it myself and copy/pasting from github, but I'm still having trouble. Would you be able to help? I'm using an education Google account, so the execution limit is 30 minutes. I'm using this folders within a shared drive since exceptions are thrown when starting with the root of the shared drive. Below is the code I've been running as pasted from github with a few minor adjustments as denoted with comments with my initials and some extra console.logs to troubleshoot. It seems like the code is failing somewhere in the Trigger function based on the console.log messages. I'd be super appreciative of any help or advice.

Thanks, js

// jshint esversion: 9
// jshint laxbreak: true

const scan = folderId => {
  const fs = new FileStructure(folderId);
  if (fs.threshold <= fs.timer.getDuration()) {
    fs.scanUnfinished = true;
    return;
  }

  if (undefined === fs.tree[folderId]) {
    try {
      const newFolder = new Folder({
        id: folderId,
        isRoot: 'root' === folderId,
      });
      fs.addFolder(newFolder);
      newFolder.subfolders.forEach(subFolder => scan(subFolder));
    } catch (err) {
      console.log('ERROR', err);
    }
  } else {
    fs.tree[folderId].subfolders.forEach(subfolder => scan(subfolder));
  }
};

const saveFile = () => {
  const fileName = 'folder-scanner.json';
  const files = DriveApp.getFilesByName(fileName);
  let file;
  if (files.hasNext()) file = files.next();
  else file = DriveApp.createFile(fileName, '');
  file.setContent(JSON.stringify(new FileStructure()));
  return file; //js added file
};

const readFile = () => {
  const fileName = 'folder-scanner.json';
  const files = DriveApp.getFilesByName(fileName);
  if (files.hasNext())
    return JSON.parse(files.next().getBlob().getDataAsString());
  return null;
};

const deleteFile = () => {
  const fileName = 'folder-scanner.json';
  const files = DriveApp.getFilesByName(fileName);
  if (files.hasNext()) files.next().setTrashed(true);
};

const cleanUp = e => {
  deleteFile();
  Trigger.deleteTrigger(e);
  const url = Spreadsheet.create(new FileStructure());
  console.log('spreadsheet created:', url);
};

const pickUpScan = e => {
  console.log('pickUpScan is running');
  if ('running' === ScanStatus.get()) {
    console.log('scan already running, exiting');
    return;
  }
  ScanStatus.set('running');
  const fileTree = readFile();
  if (!fileTree) return;
  const fs = new FileStructure();
  fs.import(fileTree);
  scan('root');

  ScanStatus.set('not running');
  if (true === fs.scanUnfinished) return saveFile();
  cleanUp(e);
};

const main = () => {
  ScanStatus.set('running');
  scan('string of folder ID');
  const fs = new FileStructure();

  ScanStatus.set('not running');
  if (true === fs.scanUnfinished) {
    console.log('did not finish');
    console.log('moving to saveFile()');
    saveFile();
    console.log('moving to return new trigger');
    return new Trigger('pickUpScan', 1);
  } 
  console.log('does it pass the return new trigger? no it does not');
  cleanUp();
};

/*const test = () => {
  ScriptApp.getProjectTriggers().forEach(trigger =>
    ScriptApp.deleteTrigger(trigger)
  );
};*/

// jshint esversion: 9
// jshint laxbreak: true
class Folder {
  constructor(options) {
    let { id, isRoot = false } = options;

    const folder = DriveApp.getFolderById(id);
    const subfolders = [];
    const files = [];
    const parent = isRoot ? null : folder.getParents().next().getId();
    const name = folder.getName();
    const _subfolders = folder.getFolders();
    const _files = folder.getFiles();

    while (_subfolders.hasNext()) {
      subfolders.push(_subfolders.next().getId());
    }

    while (_files.hasNext()) {
      const _id = _files.next().getId();
      const _file = new File(_id);
      files.push(_file.options);
    }
    return { id, name, parent, subfolders, files };
  }
}

class File {
  constructor(id) {
    const file = DriveApp.getFileById(id);
    const name = file.getName();
    const mimeType = file.getMimeType();
    const size = file.getSize();
    const dateCreated = file.getDateCreated(); //js added line
    const lastUpdated = file.getLastUpdated(); //js added line
    const owner = file.getOwner(); //.getName(); //js added line
    this.options = { name, mimeType, id, size, dateCreated, lastUpdated, owner }; //js adjusted to accomodate extra data
    return this;
  }
}

class FileStructure {
  constructor(rootId) {
    if (FileStructure.instance) return FileStructure.instance;
    const root = !!rootId ? new Folder({ id: rootId, isRoot: true }) : null;

    this.tree = {
      root,
    };

    this.timer = new Timer();
    this.timer.start();
    // this.threshold = 5 * 60 * 1000;
    this.threshold = 29 * 60 * 1000; //29 minutes in milliseconds

    FileStructure.instance = this;
    return FileStructure.instance;
  }

  addFolder(folderObj) {
    if (this.tree && this.tree.root && this.tree.root.id === folderObj.id)
      return;
    this.tree[folderObj.id] = folderObj;
    return this;
  }

  import(fs) {
    this.tree = fs.tree;
    return this;
  }
}

// jshint esversion: 9
// jshint laxbreak: true
class ScanStatus {
  static set(status) {
    PropertiesService.getScriptProperties().setProperty('status', status);
  }

  static get() {
    return PropertiesService.getScriptProperties().getProperty('status');  //js changed key to 'status' per github comments
  }
}

// jshint esversion: 9
// jshint laxbreak: true
class Spreadsheet {
  static create(fs) {
    const { tree } = fs ;
    const data = Object.values(tree).reduce((ar, node) => {
      const { files, id, name } = node;
      const fileData = files.map(file => [file.name, file.id, file.size, file.mimeType, name, id, file.dateCreated, file.lastUpdated, file.owner]); //js adjusted to accomodate extra data
      ar.push(...fileData);
      return ar;
    }, [['File name', 'File ID', 'Size', 'Mime Type', 'Folder Name', 'Folder ID', 'Date Created', 'Last Updated', 'Owner']]); //js adjusted to accomodate extra data
    const ss = SpreadsheetApp.create('Folder Scanner');
    ss.getSheets()[0].getRange(1, 1, data.length, data[0].length).setValues(data);
    SpreadsheetApp.flush();
    return ss.getUrl();
  }
}

/*const testSS = () => {
  Spreadsheet.create(readFile());
};*/

// jshint esversion: 9
// jshint laxbreak: true

class Timer {
  start() {
    this.start = Date.now();
  }

  getDuration() {
    return Date.now() - this.start;
  }
}

// jshint esversion: 9
// jshint laxbreak: true
const Trigger = (function () {
  class Trigger {
    constructor(functionName, everyMinutes) {
      return ScriptApp.newTrigger(functionName)
        .timeBased()
        .everyMinutes(everyMinutes)
        .create();
    }

    static deleteTrigger(e) {
      console.log(typeof e);
      if (typeof e !== 'object')
        return console.log(`${e} is not an event object`);
      if (!e.triggerUid)
        return console.log(`${JSON.stringify(e)} doesn't have a triggerUid`);
      ScriptApp.getProjectTriggers().forEach(trigger => {
        if (trigger.getUniqueId() === e.triggerUid) {
          console.log('deleting trigger', e.triggerUid);
          return ScriptApp.deleteTrigger(trigger);
        }
      });
    }
  }
  return Trigger;
})();
WildH0g commented 10 months ago

What's the error message?

jesse-schewe commented 10 months ago

Hi @WildH0g, thanks for replying! No errors necessarily, but it completes execution and stops rather than triggering another run. Screenshot of execution log attached image

WildH0g commented 10 months ago

Is the trigger created? Can you see one in the IDE?