JavaScript forof, forin или другие итерационные методы не работают для нескольких вызовов API.

Моя цель — получить метаданные о видео из Brightcove CMS. API путем передачи идентификаторов видео.

Метаданные наших видео хранятся в четырех разных бизнес-подразделениях (или профилях), поэтому я должен запрашивать конечную точку каждого бизнес-подразделения отдельно. Я не знаю, какой идентификатор видео будет найден в каком бизнес-подразделении. Я могу отправить максимум 10 идентификаторов видео за вызов. Ниже приведены шаги, необходимые для получения метаданных видео по идентификатору видео.

1) Получите доступ token (на этом шаге используются учетные данные клиента, которые я предоставляю).

2) Передать доступ token к объекту запроса (sendRequest) через параметр options.

3) Разобрать ответ и поместить его в глобальный videosArray.

4) Повторите шаги с 1 по 3 для каждого набора из 10 или менее уникальных идентификаторов видео (представленных многомерным массивом).

5) Повторите шаги с 1 по 4 для каждой учетной записи видео (подразделения).

К вашему сведению: шаблонный код от Brightcove использует обратные вызовы. Я преобразовал этот код в async/await. Возможно, кто-то также может предложить улучшения моего кода async/await, если он неверен.

Я использую модули Node 8.10, ES6+ и request-promise-native (среди прочего). Любые переменные, которые кажутся неявно объявленными, объявляются в глобальной области видимости. Я просто не вставил их сюда.

bizUnit — это массив объектов как таковых (всего у меня есть четыре разных бизнес-единицы для перебора):

businessUnits = [
     bizUnitOne: {
          account_id: 'uhdafoia98243r2',
          client_id: 'oidahf982y229hr',
          client_secret: 'iuahf9o4398oyg',
          player_url: 'afdhy984wyyfsg',
     },
     bizUnitTwo: {
          account_id: 'uhdafoia98243r2',
          client_id: 'oidahf982y229hr',
          client_secret: 'iuahf9o4398oyg',
          player_url: 'afdhy984wyyfsg',
     }
]

Получить объявление функции токена доступа:

async function getAccessToken(bizUnit) {
// base64 encode the client_id:client_secret string for basic auth
let bodyObj,
    token;
authString = new Buffer(bizUnit.client_id + ':' + bizUnit.client_secret).toString('base64');
let payLoad = {
    method: 'POST',
    url: 'https://oauth.brightcove.com/v3/access_token?grant_type=client_credentials',
    headers: {
        'Authorization': 'Basic ' + authString,
        'Content-Type': 'application/json'
    },
    json: true
};
try {
    let result = await request(payLoad);
    bodyObj = await JSON.parse(result);
    token = bodyObj.access_token;
    return token;
}
catch (error) {
    console.log(oauthError, error);
}

}

Отправить заявление запроса:

async function sendRequest(options) {
let requestOptions = {
    method: 'GET',
    url: options.url,
    headers: {
        'Authorization': 'Bearer ' + options.token,
        'Content-Type': 'application/json'
    },
    json: true
};
let makeRequest = async (reqOptions) => {
    try {
        let body = await request(reqOptions);
        return JSON.parse(body);
    } catch (error) {
        console.log(apiError, error);
    }
};
// make the request
await makeRequest(requestOptions);

}

Пример массива идентификаторов видео (теоретически этот массив может содержать сотни или тысячи идентификаторов видео в блоках по 10 или меньше):

videoIdsGroup = [ [53245,2352,243252,2352352,234234,234324,2342342,24242,23542,234324], [43534, 34543, 3453, 3453345] ];

Собираем все вместе и делаем запрос:

function setUpVideoRequest(bizUnit) {
(async (bu) => {
    // note that access tokens live for 5 minutes
    // but you can always request one for each call to be safe

    for (let videoIdsArr of videoIdsGroup) {
        let endPoint,
        videoIdsString = videoIdsArr.join();
        endPoint = '/accounts/' + bu.account_id + '/videos/' + videoIdsString + '&sort=' + sort;
        options.url = baseURL + endPoint;
        options.token = await getAccessToken(bizUnit);
        const videos = await sendRequest(options);
        videosArray = videosArray.concat(videos);
    }
})(bizUnit);

}

Начало выполнения:

   for (let bu of businessUnits) {

        /*the counter below is to reveal how many times and in what sequence this for...of loop is executing. my console.log indicates that this loop iterates through all businessUnits immediately, but then it runs again and eventually succeeds in some of the calls and repeats those calls even though it already got data.*/ 
        let parentCounter = 0;
        console.log("BizUnit COUNTER", parentCounter++);

        try {
    promises.push(setUpVideoRequest(bu));
} catch (error) {
    throw error;
}

}

Сообщение об ошибке, которое я получаю (это повторяется, а затем я время от времени получаю некоторые данные, а затем ошибка повторяется снова):

    statusCode: 400,
  message: '400 - {"error":"invalid_client","error_description":"The "client_id" parameter is missing, does not name a client registration that is applicable for the requested call, or is not properly authenticated."}',

Я знаю, что циклы for...of работают неправильно, но я пробовал обычный цикл for с итератором [i], я также пробовал for...in и forEach. Ни один из них не работает корректно.

Я просто хочу иметь возможность сделать запрос для каждого идентификатора учетной записи (бизнес-подразделения) и для каждого массива видео, получить все метаданные видео и поместить их в глобальный videosArray примерно так:

Promise.all(promises).then((results) => {
    promiseVidArray.push(results);
});

Заранее спасибо за вашу помощь и понимание.


person Aluma    schedule 17.09.2018    source источник
comment
Если вы хотите использовать await, вы должны иметь обещание и не использовать API обратного вызова request. Если вы уже используете request-promise-*, просто не передавайте обратный вызов! Посмотрите его документацию.   -  person Bergi    schedule 17.09.2018
comment
Кроме того, никогда не позволяйте async function принимать callback. Вы должны просто await выполнять асинхронные действия, а затем return.   -  person Bergi    schedule 17.09.2018
comment
Если вы не знакомы с вызовами async/await´ yet, try using promises without any syntactic sugar, i.e. use .then()`   -  person Bergi    schedule 17.09.2018
comment
@ Берги Спасибо. Я очистил структуру, связанную с обещаниями. Отредактирует исходный пост и добавит новую версию. Все еще сталкиваюсь с подобными проблемами.   -  person Aluma    schedule 20.09.2018


Ответы (2)


Вам не хватает закрывающей скобки в объекте businessUnits

businessUnits = [{
     bizUnitOne: {
          account_id: 'uhdafoia98243r2',
          client_id: 'oidahf982y229hr',
          client_secret: 'iuahf9o4398oyg',
          player_url: 'afdhy984wyyfsg',
     },
     bizUnitTwo: {
          account_id: 'uhdafoia98243r2',
          client_id: 'oidahf982y229hr',
          client_secret: 'iuahf9o4398oyg',
          player_url: 'afdhy984wyyfsg',
     }
}]; // <-- Here it is the one you're missing
person Pablo Abad    schedule 21.09.2018
comment
Спасибо. К счастью, в моем реальном коде такой проблемы не было. Я набрал этот объект исключительно для целей этого поста, и именно здесь я напортачил. Мой производственный код хорош. - person Aluma; 21.09.2018

После внесения некоторых исправлений в мой синтаксис async/await (спасибо, @Bergi) я обнаружил корень проблемы: я отправлял неверные учетные данные клиента для 3 из 4 запросов. Причина довольно интересная. Я подробно описал это ниже.

Когда вы создаете ключ аутентификации API в Brightcove Video Cloud, вы указываете имя для ключа, выбираете бизнес-сегменты для авторизации с использованием этого ключа и выбираете конечные точки, которые вы хотите предоставить этим бизнес-подразделениям.

Я создал один ключ для каждого бизнес-подразделения, и Brightcove предоставил мне уникальные «account_id», «client_id», «client_secret» для каждого ключа. Я немедленно скопировал / вставил их в файл блокнота, потому что «client_secret» исчезает через 5 минут и не может быть восстановлен (вам придется удалить ключ и создать новый).

Сегодня я снова вошел в панель управления Brightcove, чтобы убедиться, что мои ключи верны. К моему удивлению, все четыре «client_id» были точно такими же (идентификаторы клиентов, которые я скопировал/вставил, были уникальными). В своем запросе API я отправлял уникальный идентификатор client_id для каждого бизнес-подразделения, поскольку именно его предоставил мне Brightcove, когда я создавал ключ аутентификации API. Каким-то образом все они изменились и стали одним и тем же ключом для всех четырех бизнес-подразделений. Я до сих пор не понимаю, почему и как это произошло.

Таким образом, мои запросы не выполнялись для 3 из 4 учетных записей.

person Aluma    schedule 21.09.2018