davido
davido

Reputation: 121

Is it possible to get fresh Google Slides presentation data in AppsScript?

It seems that if one uses SlidesApp.getActivePresentation() in AppsScript, the result of the function is not fresh but rather something that was already prepared beforehand.

Scenario

Imagine you have two users performing the following function in AppsScript simultaneously:

function updateSlideText(slideId) {
  // Request exclusive write access to the document
  var lock = LockService.getDocumentLock();
  lock.waitLock(15000);

  // Perform changes
  var presentation = SlidesApp.getActivePresentation();
  var textBox = presentation.getSlideById(MY_SLIDE_ID).getPageElementById(MY_TEXTBOX_ID);
  textBox.asShape().getText().setText('My text');

  // Save and release lock
  presentation.saveAndClose();
  lock.releaseLock();
}

If this function is called twice at the same time, the resulting slide contains text "My textMy text".Example outpu

When I add Utilities.sleep(10000) just before the lock release, it delays the 2nd execution by 10s but after those 10s I still end up with the same result. On the other hand, if I actually delay calling the function 10s, the output is fine.

From this I conclude that it does not matter if I call saveAndClose and use locks. Once the function is called, it will always have stale data. Is there a way around this? Is it not possible to request that fresh data will be loaded after the lock is aquired?

More details

Some more pseudo-code to better illustrate the problem use-case:

// The addon frontend
websocket.onMessage((message) => {
  if (message.type === 'pollUpdate') {
    const slideWithPoll = store.getState().slides.find(
      slide => slide.pollId === message.pollId
    );

    if (slideWithPoll.title !== message.poll.title) {
      google.script.run.updateSlideText(slideWithPoll.id, message.poll.title);
    }
  }
});

Upvotes: 3

Views: 239

Answers (1)

Tanaike
Tanaike

Reputation: 201603

I believe your goal as follows.

  • When 2 users are run your script for Google Slides, simultaneously, you want to run the script individually.

For this, how about this answer?

Issue and workaround:

When I tested your situation, I could confirm the same issue like My textMy text. When I tested several times, in this case, I thought that the LockService might not affect to Google Slides. So as a workaround, I would like to propose to use Web Apps as the wrapper. Because it has already been known that Web Apps can run exclusively by the LockService. The flow of this workaround is as follows.

  1. When the script is run, the script requests to Web Apps.
  2. At Web Apps, your script is run.

By this, even when the script is run, simultaneously, the script can be exclusively run with the LockService.

Usage:

The usage of this sample script is as follows. Please do the following flow.

1. Prepare script.

When your script is used, it becomes as follows. Please copy and paste the following script to the script editor. Please set MY_SLIDE_ID and MY_TEXTBOX_ID.

function doGet() {

  // This is your script.
  var presentation = SlidesApp.getActivePresentation();
  var textBox = presentation.getSlideById(MY_SLIDE_ID).getPageElementById(MY_TEXTBOX_ID);
  var text = textBox.asShape().getText();
  text.setText('My text');

  return ContentService.createTextOutput("ok");
}

// Please run this function.
function main() {
  var lock = LockService.getDocumentLock();
  if (lock.tryLock(10000)) {
    try {
      const url = "https://script.google.com/macros/s/###/exec";  // Please set the URL of Web Apps after you set the Web Apps.
      const res = UrlFetchApp.fetch(url);
      console.log(res.getContentText())
    } catch(e) {
      throw new Error(e);
    } finally {
      lock.releaseLock();
    }
  }
}

2. Deploy Web Apps.

  1. On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
  2. Select "Me" for "Execute the app as:".
    • By this, the script is run as the owner.
  3. Select "Anyone, even anonymous" for "Who has access to the app:".
    • In this case, no access token is required to be request. I think that I recommend this setting for testing this workaround.
    • Of course, you can also use the access token. At that time, please set this to "Anyone". And please include the scope of https://www.googleapis.com/auth/drive.readonly and https://www.googleapis.com/auth/drive to the access token. These scopes are required to access to Web Apps.
  4. Click "Deploy" button as new "Project version".
  5. Automatically open a dialog box of "Authorization required".
    1. Click "Review Permissions".
    2. Select own account.
    3. Click "Advanced" at "This app isn't verified".
    4. Click "Go to ### project name ###(unsafe)"
    5. Click "Allow" button.
  6. Click "OK".
  7. Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.

    • When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
  8. Please set the URL of https://script.google.com/macros/s/###/exec to url of above script. And please redeploy Web Apps. By this, the latest script is reflected to the Web Apps. So please be careful this.

4. Test this workaround.

Please run the function of main() by 2 users, simultaneously as you have tested. By this, it is found that the script is run exclusively. In my environment, in this case, I confirmed that even when the LockService is not used, the script is exclusively run. But I would like to recommend to use the LockService just in case.

Note:

  • This is a simple sample script for explaining this workaround. So when you use this, please modify it for your actual situation.
  • About the situation that the LockService might not affect to Google Slides, in the current stage, although I'm not sure whether this is the bug, how about reporting this to the Google issue tracker? Unfortunately, I couldn't find this issue at the current Google issue tracker.

References:

Upvotes: 2

Related Questions