简体   繁体   中英

Getting user information for a large number of G Suite users quickly with AppScript

I'm using a slightly modified version of this code snippet to get the last login time from a large number of users on a G Suite domain. Due to the large number of users, this code can't finish running in under 30 minutes and hits the execution time limit of 30 minutes and fails. I'm looking for a way to speed up the execution.

The application this runs on combines this data with a similar code snipped that reads values from the enterprise license manager API, which I expect will hit the same issue.

If it's not possible to make the code faster, I at least need to ensure it doesn't hit the execution time limit. I need to call this API for all users at least once for the application to work properly - it sorts a list of users by their last login time. The app is in App Maker using App Script on a G Suite Enterprise domain.

function getParameterValues(parameters) {
  return parameters.reduce(function(result, parameter) {
    var name = parameter.name;
    var value;
    if (parameter.intValue !== undefined) {
      value = parameter.intValue;
    } else if (parameter.stringValue !== undefined) {
      value = parameter.stringValue;
    } else if (parameter.datetimeValue !== undefined) {
      value = new Date(parameter.datetimeValue);
    } else if (parameter.boolValue !== undefined) {
      value = parameter.boolValue;
    }
    result[name] = value;
    return result;
  }, {});
}

function generateLoginActivityReport() {
  var today = new Date();
  var oneWeekAgo = new Date(today.getTime() - (7 * 24 * 60 * 60 * 1000));
  var timezone = Session.getScriptTimeZone();
  var date = Utilities.formatDate(oneWeekAgo, timezone, 'yyyy-MM-dd');

  var parameters = [
    'accounts:last_login_time',
    'drive:num_items_created'
  ];
  var rows = [];
  var pageToken;
  var page;
    do {
    page = AdminReports.UserUsageReport.get('all', date, {
      parameters: parameters.join(','),
      maxResults: 500,
      pageToken: pageToken,
    });

    var reports = page.usageReports;

    if (reports) {
      for (var i = 0; i < reports.length; i++) { 
        var report = reports[i];
        try {
          var parameterValues = getParameterValues(report.parameters);
          var row = [
            report.date,
            report.entity.userEmail,
            parameterValues['accounts:last_login_time'],
            //parameterValues['drive:num_items_created']
          ];
          rows.push(row);
          //var ar = app.models.ActivityReport.newRecord();
          //ar.LastLogin = parameterValues['accounts:last_login_time'];
          console.log(report.entity.userEmail);
          //ar.DocsAdded = 0; //getting this value is another issue but unrelated so it's set to 0 for now.
          //ar.Email = report.entity.userEmail.toString();
          //app.saveRecords([ar]);
        }
        catch(error) {
          console.error("Error: \n"+error);
        }
      }
    }
  } while (pageToken);
  }

And here's a sample execution:

[19-07-15 15:58:30:784 CDT] Starting execution
[19-07-15 15:58:30:796 CDT] Session.getScriptTimeZone() [0 seconds]
[19-07-15 15:58:30:797 CDT] Utilities.formatDate([Mon Jul 08 13:58:30 PDT 2019, America/Mexico_City, yyyy-MM-dd]) [0 seconds]
[19-07-15 15:58:32:202 CDT] console.log([user1@test.mavenwave.com, []]) [0.003 seconds]
[19-07-15 15:58:32:203 CDT] console.log([ad.test.admin@test.mavenwave.com, []]) [0 seconds]
[19-07-15 15:58:32:204 CDT] console.log([user3@test.mavenwave.com, []]) [0 seconds]
///more entries, roughly 195 total
[19-07-15 15:58:32:441 CDT] console.log([user4@test.mavenwave.com, []]) [0 seconds]
[19-07-15 15:58:32:441 CDT] console.log([user5@test.mavenwave.com, []]) [0 seconds]
[19-07-15 15:58:32:443 CDT] Execution succeeded [1.645 seconds total runtime]


Instead of getting the last login using the Reports API, would it be faster to just get it directly from a list of all user records? Use AdminDirectory.Users.list and retrieve the user.lastLoginTime property?

If you still need the number of Drive items (that you have commented out above), you might have to run that through a separate function, but you could do it based on some other criteria or starting with the most recently logged in users first.

I got around this by using App Maker's client scripting. The client script calls the server script, which gets 100 users' reports and then passes the page token back to the client script, which calls it again using the page token- this gets around the 30 minute time limit and continues the call in order.

function crunLoginActivityReport(page) {
  // this loops over every page token and gets the data and writes it to the DB.
  console.log("Running activity report for page: " + page);
  google.script.run.withSuccessHandler(function(result) {
    console.log("Got the following from the server, handing back to client runner" + result);
    if (result === null) {
      console.log("Result was null, stopping get");
      return; 
    } else {
    crunLoginActivityReport(result); }
  }).runLoginActivityReport(page);
  return;
}

function cinitLoginActivityReport() {
  google.script.run.withSuccessHandler(function(result) {
    crunLoginActivityReport(result);
    return;
  }).initLoginActivityReport();
  return;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM