Как пакетировать данные строки и отправлять единственную полезную нагрузку JSON?

#javascript #json #google-apps-script #google-sheets #aws-lambda

#javascript #json #google-apps-script #google-sheets #aws-lambda

Вопрос:

В настоящее время я использую скрипт Google Apps в Google Sheet, который отправляет данные отдельных строк в AWS API Gateway для создания скриншота. На данный момент несколько запросов полезной нагрузки JSON вызывают некоторые сбои лямбда-функции. Итак, я хочу пакетировать данные строк, а затем отправлять как единую полезную нагрузку, чтобы одна функция AWS Lambda могла выполнять несколько скриншотов.

Как я могу пакетировать полезную нагрузку JSON после повторения данных в каждой строке приведенного ниже кода?

 function S3payload () {
  var PAYLOAD_SENT = "S3 SCREENSHOT DATA SENT";
  
  var sheet = SpreadsheetApp.getActiveSheet(); // Use data from the active sheet
  
  // Add temporary column header for Payload Status new column entries
  sheet.getRange('E1').activate();
  sheet.getCurrentCell().setValue('payload status');
  
  var startRow = 2;                            // First row of data to process
  var numRows = sheet.getLastRow() - 1;        // Number of rows to process
  var lastColumn = sheet.getLastColumn();      // Last column
  var dataRange = sheet.getRange(startRow, 1, numRows, lastColumn) // Fetch the data range of the active sheet
  var data = dataRange.getValues();            // Fetch values for each row in the range
  
  // Work through each row in the spreadsheet
  for (var i = 0; i < data.length;   i) {
    var row = data[i];  
    // Assign each row a variable   
    var index = row[0];     // Col A: Index Sequence Number
    var img = row[1];   // Col B: Image Row
    var url = row[2];      // Col C: URL Row
    var payloadStatus = row[lastColumn - 1];  // Col E: Payload Status (has the payload been sent)
  
    var siteOwner = "email@example.com";
    
    // Prevent from sending payload duplicates
    if (payloadStatus !== PAYLOAD_SENT) {  
        
      /* Forward the Contact Form submission to the owner of the site
      var emailAddress = siteOwner; 
      var subject = "New contact form submission: "   name;
      var message = message;*/
      
      //Send payload body to AWS API GATEWAY
      //var sheetid = SpreadsheetApp.getActiveSpreadsheet().getId(); // get the actual id
      //var companyname = SpreadsheetApp.getActiveSpreadsheet().getName(); // get the name of the sheet (companyname)
      
      var payload = {
        "img": img,
        "url": url
      };
      
      var url = 'https://requestbin.herokuapp.com/vbxpsavc';
      var options = {
        'method': 'post',
        'payload': JSON.stringify(payload)
      };
      
      var response = UrlFetchApp.fetch(url,options);
      
      sheet.getRange(startRow   i, lastColumn).setValue(PAYLOAD_SENT); // Update the last column with "PAYLOAD_SENT"
      SpreadsheetApp.flush(); // Make sure the last cell is updated right away
      
      // Remove temporary column header for Payload Status    
      sheet.getRange('E1').activate();
      sheet.getCurrentCell().clear({contentsOnly: true, skipFilteredRows: true});
      
    }
  }
}
  

Пример отдельной полезной нагрузки JSON

 {"img":"https://s3screenshotbucket.s3.amazonaws.com/realitymine.com.png","url":"https://realitymine.com"}
  

введите описание изображения здесь

Пример желаемого выходного результата

 [
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/gavurin.com.png","url":"https://gavurin.com"},
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/google.com.png","url":"https://google.com"},
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/amazon.com","url":"https://www.amazon.com"},  
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/stackoverflow.com","url":"https://stackoverflow.com"},
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/duckduckgo.com","url":"https://duckduckgo.com"},
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/docs.aws.amazon.com","url":"https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-features.html"},  
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/github.com","url":"https://github.com"},  
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/github.com/shelfio/chrome-aws-lambda-layer","url":"https://github.com/shelfio/chrome-aws-lambda-layer"},  
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/gwww.youtube.com","url":"https://www.youtube.com"},   
    {"img":"https://s3screenshotbucket-useast1v5.s3.amazonaws.com/w3docs.com","url":"https://www.w3docs.com"}       
]
  

Комментарии:

1. Я должен извиниться за мое плохое знание английского. К сожалению, из вашего вопроса я не могу понять о How can I batch the JSON payload after iterating the data on each line in the code below? . Чтобы правильно понять вашу цель, можете ли вы предоставить пример ввода и вывода, который вы ожидаете? Из этой информации я хотел бы попытаться понять вашу цель.

2. Я вижу, у вас рабочая однорядная подача, отлично! Но где ваша попытка отправки в несколько строк? Это не сервис для написания кода. Если вы предпримете попытку и поделитесь проблемами, с которыми сталкиваетесь, ваш вопрос будет воспринят гораздо более благожелательно.

Ответ №1:

Изменения

  1. Не вызывайте UrlFetchApp методы в цикле, если нет другого способа. Хотя Google предлагает щедрую квоту, она не безгранична, и вы быстро израсходуете ее при любом значительном количестве строк и частоте отправки.
  2. Используйте современные функции ES6, такие как map , для преобразования строк значений в объекты в формате желаемой полезной нагрузки. Обратите внимание, что вам нужно будет включить V8 runtime, чтобы использовать их.

Ниже приведен выполняемый фрагмент теста, демонстрирующий, как вы могли бы изменить свой скрипт. Я решил исключить из него логику обновления статуса, поскольку вам решать, как обновлять статус в случае сбоя пакетного обновления:

 //TEST MOCKS:
const SpreadsheetApp = {
  getActiveSheet() {
  
    const Sheet = {
      getLastRow() { return 3; },
      getLastColumn() { return 5; },
      getDataRange() {
      
        const Range = {
          getValues() {
            return new Array(Sheet.getLastRow())
            .fill([])
            .map(
              (r,ri) => new Array(Sheet.getLastColumn())
              .fill(`mock row ${ri}`)
              .map((c,ci) => `${c} cell ${ci}`)
            );
          }
        };
        
        return Range;
      }
    };
    
    return Sheet;
  }
};

const UrlFetchApp = {
  fetch(uri, options) {
    console.log({ uri, options });
  }
};
//END MOCKS;

const sendToS3 = () => {

  var PAYLOAD_SENT = "S3 SCREENSHOT DATA SENT";

  var sheet = SpreadsheetApp.getActiveSheet();

  var startRow = 2;
  var numRows = sheet.getLastRow() - 1;
  var lastColumn = sheet.getLastColumn();

  var dataRange = sheet.getDataRange();
  var data = dataRange.getValues();

  var siteOwner = "email@example.com";

  const appURI = 'https://hfcrequestbin.herokuapp.com/vbxpsavb';

  const payloads = data.map(([index, img, url]) => ({ img, url }));

  const options = {
    'method': 'post',
    'payload': JSON.stringify(payloads)
  };

  const response = UrlFetchApp.fetch(appURI, options);
}

sendToS3();  


Примечания

  1. При пакетной обработке полезной нагрузки POST имейте в виду, что существует квота на максимальный размер тела для каждого запроса (в настоящее время 50 МБ).
  2. Не вызывайте методы ввода-вывода, такие как getRange , getValue в цикле, они медленные по своей природе, используйте пакетные методы, такие как getDataRange , getValues , setValues и т.д., И выполняйте все модификации только для массивов в памяти.
  3. Используйте activate методы только тогда, когда вы явно хотите изменить фокус, не полагайтесь на него для определения диапазона. Просто используйте обычные ссылки на ячейки, полученные с помощью таких методов, как getRange .

Комментарии:

1. Хорошее использование mock. Скопирую в своих ответах 🙂

2. @TheMaster — спасибо 🙂 Вчера меня осенило — зачем удалять не поддающиеся редактированию фрагменты, если мы можем издеваться над сервисами?

Ответ №2:

Попробуйте отправить данные в виде списка / массива. И на стороне сервера выполните итерацию по списку / массиву.

например:

 {
    "payload": [{
        "img": "https://s3screenshotbucket.s3.amazonaws.com/website1.com.png",
        "url": "https://website1.com"
    }, {
        "img": "https://s3screenshotbucket.s3.amazonaws.com/website2.com.png",
        "url": "https://website2.com"
    }]
}