Несколько разбитых на страницы вызовов GET API параллельно / асинхронно в узле - PullRequest
0 голосов
/ 16 октября 2018

Я звоню в API-интерфейс bitbucket, чтобы получить все файлы, находящиеся в репо.Я дошел до того, что смог получить список всех папок в репо и сделать первый API-вызов для всех корневых папок в репо параллельно и получить список первых 1000 файлов для всех папок.

Но проблема в том, что API-интерфейс bitbucket может выдавать мне только 1000 файлов в папке за раз.

Мне нужно добавить параметр запроса & start = nextPageStart и повторить вызов, пока он не станет нулевым и значение isLastPage не станет истинным для API.Как я могу добиться этого с помощью приведенного ниже кода ??

Я получаю nextPageStart от первого вызова API.См. Ответ API ниже.

Ниже приведен код, который у меня имеется.

Любая помощь или руководство приветствуются.

Ответ от отдельных API, который называетсядля каждой папки.

{
    "values": [
        "/src/js/abc.js",
        "/src/js/efg.js",
        "/src/js/ffg.js",
        ...
    ],
    "size": 1000,
    "isLastPage": false,
    "start": 0,
    "limit": 1000,
    "nextPageStart": 1000
}

функция, в которой я выполнял асинхронные вызовы для получения списка файлов

export function getFilesList() {
  const foldersURL: any[] = [];
  getFoldersFromRepo().then((response) => {
    const values = response.values;
    values.forEach((value: any) => {
    //creating API URL for each folder in the repo
      const URL = 'https://bitbucket.abc.com/stash/rest/api/latest/projects/'
                   + value.project.key + '/repos/' + value.slug + '/files?limit=1000';
      foldersURL.push(URL);
        });
    return foldersURL;
      }).then((res) => {
    // console.log('Calling all the URLS in parallel');
    async.map(res, (link, callback) => {
       const options = {
         url: link,
         auth: {
           password: 'password',
           username: 'username',
         },
       };
       request(options, (error, response, body) => {

      // TODO: How do I make the get call again so that i can paginate and append the response to the body till the last page.

         callback(error, body);
       });
     }, (err, results) => {
       console.log('In err, results function');
       if (err) {
         return console.log(err);
       }
       //Consolidated results after all API calls.
       console.log('results', results);
     });
  })
   .catch((error) => error);
}

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Я смог заставить его работать, создав функцию с обратным вызовом.

export function getFilesList() {
  const foldersURL: any[] = [];
  getFoldersFromRepo().then((response) => {
    const values = response.values;
    values.forEach((value: any) => {
    //creating API URL for each folder in the repo
      const URL = 'https://bitbucket.abc.com/stash/rest/api/latest/projects/'
                   + value.project.key + '/repos/' + value.slug + '/files?limit=1000';
      foldersURL.push(URL);
        });
    return foldersURL;
      }).then((res) => {
    // console.log('Calling all the URLS in parallel');
    async.map(res, (link, callback) => {
       const options = {
         url: link,
         auth: {
           password: 'password',
           username: 'username',
         },
       };
      const myarray = [];
// This function will consolidate response till the last Page per API.
      consolidatePaginatedResponse(options, link, myarray, callback);
     }, (err, results) => {
       console.log('In err, results function');
       if (err) {
         return console.log(err);
       }
       //Consolidated results after all API calls.
       console.log('results', results);
     });
  })
   .catch((error) => error);
}

function consolidatePaginatedResponse(options, link, myarray, callback) {
  request(options, (error, response, body) => {
    const content = JSON.parse(body);
    content.link = options.url;
    myarray.push(content);
    if (content.isLastPage === false) {
      options.url = link + '&start=' + content.nextPageStart;
      consolidatePaginatedResponse(options, link, myarray, callback);
    } else {
// Final response after consolidation per API
      callback(error, JSON.stringify(myarray));
    }
  });
}
0 голосов
/ 24 октября 2018

Я думаю, что лучший способ - это обернуть его в старом цикле for for (forEach не работает с асинхронной синхронизацией, поскольку он синхронный и вызывает одновременное создание всех запросов).

Что я понял, так это то, что вы выполняете какой-то загрузочный запрос, где вы получаете массив values, а затем вы должны выполнять итерацию по страницам.Вот некоторый код, я не полностью понял API, поэтому я дам упрощенный (и, надеюсь, читабельный) ответ, вы сможете его адаптировать:

export async function getFilesList() {

    logger.info(`Fetching all the available values ...`);

    await getFoldersFromRepo().then( async values => {

        logger.info("... Folders values fetched.");

        for (let i = 0; ; i++ ) {

            logger.info( `Working on page ${i}`);

            try {
                // if you are using TypeScript, the result is not the promise but the succeeded value already
                const pageResult: PageResult = await yourPagePromise(i);
                if (pageResult.isLastPage) {
                    break;
                }
            } catch(err) {
                console.err(`Error on page ${i}`, err);
                break;
            }

        }

        logger.info("Done.");

    });

    logger.info(`All finished!`);

}

Логика заключается в том, что сначала getFoldersFromRepo() возвращает обещание, которое возвращает значения, и затем я последовательно перебираю все доступные страницы с помощью функции yourPagePromise (которая возвращает обещание).Конструкция async / await позволяет писать более читаемый код, а не иметь водопад then ().

Я не уверен, что он уважает ваши спецификации API, но эту логику вы можете использовать в качестве основы!^^

...