1С, oauth2, Google API и Google Merchant. Сбор данных и парсинг

Всем привет.
Стала задача, собрать данные с Google Merchants и внести их в 1С, для более детального анализа того, что сейчас показывается, что нет и какие проблемы при этом есть. Если кому это интересно, прошу под кат.

Опыт програмирования на платформе 1С у меня крайне маленький, порядка года, поэтому за какие-то банальные вещи, прощу прощения сразу. Статья будет основана на данной статье, но там не описан процесс парсинга + не описан процесс на HTTPСоединение, что является более универсальным и более удобным. 

Все процедуры производились на клиент-серверном режиме, в конфигурации УТ 10.3 (10.3.13.2) на платформе 1С 8.3 (8.3.8.2322).

Использованные механизмы описаны в ключевых словах.

Еще перед тем, как пойдет код, я хотел бы ответить на вопрос, зачем я это вообще пишу? Во первых, для тех, кто ищет информацию по данным технологиям, во вторых, для того, чтобы получить обратную связь от вас, услышать какие-то советы и как итог — сделать себя лучше =) 

Пара ссылок на основне определения:

  1. oauth2
  2. HTTP
  3. POST/GET запросы
  4. JSON
  5. API

 

Погнали, итак, стоит цель, получить данные с Google Merchant и занести их в 1С.

Сделаем декомпозицию задачи.

  1. Получить данные для авторизации в Google
    1. Сделать уч. запись в Google Cloud Console 
      1. Добавить в разделе API , работу с  Content API for Shopping
      2. Сделать учетную запись с типом "Други типы" — http://jmp.sh/9ikhvbp
      3. Получить ClientId и Secret
  2. Добавить свою учетную запись (которая для Google Cloud Console) в Merchant с обычными правами.
  3. Получить Токен для запросов
  4. Сделать регламент Запроса данных/Обновления токена
  5. При запросе данных парсить JSON и вносить их в свою УС

1 и 2 пункт, делаются очень быстро и заострять свое внимание здесь не будем. 

Веселуха начинается с пункта 3. 

Для получения первого токена, пришлос использовать внешнюю обработку с HTML полем  и кнопкой (Если кто знает, как сделать проще, напишите в комментариях)

При нажатии кнопки, появится окно ввода логин/Пароля для учетки Google, вводим :


client_id = ВАШ_CLIEN_ID_ПОЛУЧЕННЫЙ_В_GOOGLE_CLOUD

ЧастьЗапроса = "response_type=code"+"&";
ЧастьЗапроса = ЧастьЗапроса + "client_id="+ client_id + "&";
ЧастьЗапроса = ЧастьЗапроса + "redirect_uri=http://localhost" + "&";
ЧастьЗапроса = ЧастьЗапроса + "access_type=offline"+"&";
ЧастьЗапроса = Параметры + "scope=https://www.googleapis.com/auth/content";
АдресАвторизации = "https://accounts.google.com/o/oauth2/auth" + "?";
ПолныйАдресАвторизации = АдресАвторизации + ЧастьЗапроса;

ЭлементыФормы.ВАШ_HTTML_ПОЛЕ.Перейти(ПолныйАдресАвторизации);

Для HTML поля, в обработчике "ДокументСформирован", вставим код для отлова Code (Строка, необходимая для обмена на токен)

     Если Сред(ЭлементыФормы.Гугл.Документ.URLUnencoded,1,23) = "http://localhost/?code=" Тогда

Code = Сред(ЭлементыФормы.Гугл.Документ.URLUnencoded,24);

КонецЕсли;

Теперь у нас есть данные, для получения Token и RefreshToken

Делаем процедуру Получения Токена 

    Сервер =  "accounts.google.com";
Ресурс = "/o/oauth2/token";
КодДоступа   =  Code;
client_id  = ВАШ_CLIENT_ID
client_secret  = ВАШ_SECRET

СтрокаЗапроса = "client_id=" + client_id + "&";
СтрокаЗапроса = СтрокаЗапроса + "client_secret=" + client_secret + "&";
СтрокаЗапроса = СтрокаЗапроса + "grant_type=authorization_code" + "&";
СтрокаЗапроса = СтрокаЗапроса + "code=" + КодДоступа + "&";
СтрокаЗапроса = СтрокаЗапроса + "redirect_uri=http://localhost";

Соединение = Новый HTTPСоединение(Сервер,443,,,,,Новый ЗащищенноеСоединениеOpenSSL);

Заголовки  = Новый Соответствие;
Заголовки.Вставить("Content-Type","application/x-www-form-urlencoded");

ЗапросХТТП = Новый HTTPЗапрос(Ресурс,Заголовки);
ЗапросХТТП.УстановитьТелоИзСтроки(СтрокаЗапроса);

Ответ = Соединение.ВызватьHTTPМетод("POST",ЗапросХТТП);

Если Ответ.КодСостояния <> 200 Тогда Возврат КонецЕсли;

Строка = Ответ.ПолучитьТелоКакСтроку();

Чтение = Новый ЧтениеJSON();
Чтение.УстановитьСтроку(Строка);

Фабрика = ФабрикаXDTO.ПрочитатьJSON(Чтение);

Чтение.Закрыть();

Токен           = Фабрика.access_token;
ТокенОбновления = Фабрика.refresh_token;

Тут конечно стоило бы написать кучу инфы про ФабрикуXDTO , но ее на просторах Инфостарта столько, что уже просто стыдно, что-то писать далее. Но если не читали, то очень советую прочитать цикл статей  , очень подробно и интересно расписали.

Но если вкратце, то фабрика преобразует наш XML/JSON в объект, с которым можно работать, через точку, что крайне удобно и не надо парсить документ старыми методами, перебором узлов, с проверкой на закрытие-открытие.

После получения токена, сделаем сразу еще одну процедуру, обновления такового, так как он имеет срок жизни.

 

    Сервер =  "accounts.google.com";
Ресурс = "/o/oauth2/token";
КодДоступа   =  Code;
client_id  = ВАШ_CLIEN_ID
client_secret  = ВАШ_SECRET

СтрокаЗапроса = "client_id="  + client_id + "&";
СтрокаЗапроса = СтрокаЗапроса + "client_secret=" + client_secret + "&";
СтрокаЗапроса = СтрокаЗапроса + "grant_type=refresh_token" + "&";
СтрокаЗапроса = СтрокаЗапроса + "refresh_token=" + ТокенОбновления;

Соединение = Новый HTTPСоединение(Сервер,443,,,,,Новый ЗащищенноеСоединениеOpenSSL);

Заголовки  = Новый Соответствие;
Заголовки.Вставить("Content-Type","application/x-www-form-urlencoded");

ЗапросХТТП = Новый HTTPЗапрос(Ресурс,Заголовки);
ЗапросХТТП.УстановитьТелоИзСтроки(СтрокаЗапроса);

Ответ = Соединение.ВызватьHTTPМетод("POST",ЗапросХТТП);
Если Ответ.КодСостояния <> 200 Тогда Возврат КонецЕсли;

Строка = Ответ.ПолучитьТелоКакСтроку();
Чтение = Новый ЧтениеJSON();
Чтение.УстановитьСтроку(Строка);

Фабрика = ФабрикаXDTO.ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Токен = Фабрика.access_token;

 

Теперь, когда мы имеем токен и Refresh токен, можем смело делать запросы.. и тут я столкнулся с бедой… в документации тупо не указано, как его делать, кроме строки

Google сделал, конечно, хороший  инструмент, но в нем просто не написана правильная локига создания запроса, поэтому в дело идут инструменты разработчика. 

 

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

GET https://www.googleapis.com/content/v2/ИДЕНТИФИКАТОР_МАГАЗИНА_В_MERCHANT/products

Но если вы его сделаете, получите просто ошибку … 

Поэтому лезем в инструменты разработчика и ловим запрос, процесс описывать не буду, но запрос принимает уже такой вид

https://www.googleapis.com/content/v2/ID_МАГАЗИНА_В_МЕРЧАНТ/productstatuses?maxResults=250&key=ВАШ_CLIENT_ID

 + Заголовок с токен

Теперь код процедуры, с рекурсией =) 

Почему так, потому что гугл, отдаем всего 250 позиций, за один запрос. Так как у нас 10 000 тороговых предложений, то мне надо примерно 41 раз сделать вызов метода …

Поэтому, рекурсия =) 

Сам вызов процедуры

ПолучениеФайлаСМерчанта(АдресРесурса,ТокенСтраницы)

АдресРесурса = Это тот же адрес, только с доп параметром pageToken=, с этим параметром, гугл отдаст следующую страницу, с новыми позициями.

    Путь   = Я_ФАЙЛЫ_СОХРАНИЛ_НА_ДИСК
Сервер = "www.googleapis.com";
Запрос =  АдресРесурса;
Соединение = Новый HTTPСоединение(Сервер,443,,,,,Новый ЗащищенноеСоединениеOpenSSL);
Заголовки  = Новый Соответствие;
Заголовки.Вставить("Authorization","Bearer " + Токен);
ЗапросХТТП = Новый HTTPЗапрос;
ЗапросХТТП.АдресРесурса = Запрос;
ЗапросХТТП.Заголовки = Заголовки;

Соединение.ВызватьHTTPМетод("GET",ЗапросХТТП);
Ответ = Соединение.ВызватьHTTPМетод("GET",ЗапросХТТП);
Файл = Ответ.ПолучитьТелоКакДвоичныеДанные();
Файл.Записать(Путь + "json_" + ТокенСтраницы + ".json");

Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(Путь + "json_" + ТокенСтраницы + ".json");

Фабрика = ФабрикаXDTO.ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Если ОбъектXDTOСодержитСвойство(Фабрика,"nextPageToken") Тогда
Если Фабрика.nextPageToken <> "" Тогда
ПолучениеФайлаСМерчанта("/content/v2/ID_МАГАЗИНА/productstatuses?maxResults=250&pageToken="+Фабрика.nextPageToken+"&key=897236016313-ceggltgsseaoke0bfb48sivrr3dj766k.apps.googleusercontent.com",Фабрика.nextPageToken);
КонецЕсли;
КонецЕсли;

 

Вот теперь у нас уже есть все для внесения данных в 1С, можно файлы было не сохранять, а сразу парсить и вносить в тот же регистр сведений, к примеру. Но я пока тестил, сделал внесение в табличную часть.

МассивФайлов = НайтиФайлы(Путь,"*.json");

Если МассивФайлов.Количество() = 0 Тогда
Сообщить("Файлов не найдено");
Возврат;
КонецЕсли;

ТабличноеПоле8.Очистить();
Для Каждого Файл из МассивФайлов Цикл
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(Файл.ПолноеИмя);
Фабрика = ФабрикаXDTO.ПрочитатьJSON(Чтение);
Чтение.Закрыть();

Для каждого Товар из КоллекцияXDTO(Фабрика.resources.resources) Цикл

стр = ТабличноеПоле8.Добавить();
Стр.Номенклатура  =  Справочники.Номенклатура.НайтиПоРеквизиту("Артикул",Сред(Товар.ProductID,14));
Стр.Артикул   = Стр.Номенклатура.Артикул;
Стр.ЦеноваяГруппа = Стр.Номенклатура.ЦеноваяГруппа;
Стр.Категория  = Стр.Номенклатура.МЮС_КаталогНоменклатуры;
Стр.Ответственный = Стр.Номенклатура.МЮС_КаталогНоменклатуры.Ответственный;
Стр.Ссылка    = Товар.Link;
Стр.ProductID  = Товар.ProductID;
Стр.Displayed       =   Товар.destinationStatuses.destinationStatuses[0].approvalStatus;
Стр.Shoping      =   Товар.destinationStatuses.destinationStatuses[1].approvalStatus;


Если ОбъектXDTOСодержитСвойство(Товар,"dataQualityIssues") тогда
Для каждого Строка из КоллекцияXDTO(Товар.dataQualityIssues.dataQualityIssues) Цикл

Стр.IDerror  =  ?(ОбъектXDTOСодержитСвойство(Строка,"id")   ,Строка.id  ,"");
Стр.severity  =  ?(ОбъектXDTOСодержитСвойство(Строка,"severity")  ,Строка.severity ,"");
Стр.timestamp  =  ?(ОбъектXDTOСодержитСвойство(Строка,"timestamp") ,Строка.timestamp ,"");
Стр.Location = ?(ОбъектXDTOСодержитСвойство(Строка,"Location")  ,Строка.Location ,"");

КонецЦикла;
КонецЕсли;


КонецЦикла;
УдалитьФайлы(Файл.ПолноеИмя);

КонецЦикла;

ЭлементыФормы.ТабличноеПоле8.СоздатьКолонки();

 

Тут используется 2 функции, одна проверяет на наличие свойства, вторая проверяет тип объекта XDTO, список он, или единичный

 

Код обеих, авторство не укажу, найдены на просторах Инфостарта.

Функция   ОбъектXDTOСодержитСвойство(ОбъектXDTO, Свойство)

ЕстьСвойство = ОбъектXDTO.Свойства().Получить(Свойство) <> Неопределено
И ОбъектXDTO[Свойство] <> Неопределено;

Возврат ЕстьСвойство;

КонецФункции
Функция   КоллекцияXDTO(Элемент)
Если ТипЗнч(Элемент)=Тип("ОбъектXDTO") Тогда
МассивXDTO=Новый Массив;
МассивXDTO.Добавить(Элемент);
Возврат МассивXDTO;
КонецЕсли;
Возврат Элемент;
КонецФункции

 

В целом все …

Если вы дочитали это, то уже спасибо =)

Если дадите какой-то хороший совет, буду прям очень рад.

8 Comments

  1. shmellevich

    Статья хороша +!

    А чем не угодил «ПрочитатьJSON(ЧтениеJSON, Истина)» — на выходе будет Соответствие. и безопасность получения свойств проще, и не нужны доп. функции ))

    и как себя поведет фабрика если ключом окажется не строка, а например число?

    {
    «key1″:»value1»,
    100:{«part1»:50,
    «part2»:30,
    «part3»:20}
    }

    я когда на подобный джисон попал, долго мучался, было тяжело отойти от уже отлаженного механизма работы с XDTO объектами, все сломать и построить заново )).

    Reply
  2. Mi11er

    (1) 0_о комментарий к статье… надо будет выпить сегодня.

    Чем не угодил, не знаю, привык читать уже через фабрику, можно конечно и через чтение сделать, просто формат который выходил, был протестирован именно на фабрике.

    + у меня не так много опыта в этой теме и когда делаю такие парсеры, делаю не как то универсально, а именно заточенный под этот сервис.

    Reply
  3. huse

    «Добавить свою учетную запись (которая для Google Cloud Console) в Merchant с обычными правами.»

    Подскажите как сделать эту операцию для Firebase?

    PS content на firebase заменил, но проект запрашивает разрешение и не редиректит. А при нажатии Разрешить пытается открыть страницу с адресом res://ieframe.dll/dnserrordiagoff_webOC.htm#https://accounts.google.com/o/oauth2/approval?as=………. после чего говорит что не может ее отобразить

    Reply
  4. pbazeliuk

    (1) ПрочитатьJSON, ЗаписатьJSON очень медленныe. Здесь упоминается про это https://infostart.ru/public/640996/, опытным путем удалось подтвердить слова автора.

    Reply
  5. shmellevich

    (4) Согласно скрина из статьи видно, что фабрика пишет медленнее, чем даже обычная ЗаписатьJSON в 2 раза.

    Или я не правильно понимаю?

    Reply
  6. pbazeliuk

    (5) Про фабрику не знаю, там описано только:

    3. ПрочитатьJSON, ЗаписатьJSON — самые медленные

    2. Запись в поток — сейчас использую вот этот метод, он почти в 2 раза быстрее, чем предыдущий

    1. Запись без контроля — этот пока нет времени реализовать.

    По поводу фабрики не знаю, необходимо тестировать.

    Reply
  7. Inkasor

    (6)Привет 🙂

    ПрочитатьJSON, ЗаписатьJSON — самые медленные

    Дело в том, что 1С как раз по умолчанию инициализирует одну фабрику для быстрой обработки в рамках глобального контекста. Это быстро и удобно для, наверное, 99% задач, стоящих обычно перед коллегами 🙂 Это и есть ПрочитатьJSON, ЗаписатьJSON как они есть.

    Мы работаем только на потоке, причем с заранее подготовленными данными, поэтому пишем без контроля, что ещё очень сильно ускоряет обработку.

    Reply
  8. uno-c
    Если кто знает, как сделать проще, напишите в комментариях

    Можно получать access token через 2LO авторизацию https://developers.google.com/identity/protocols/OAuth2ServiceAccount. Это сервер-сервер, без использования учетки пользователя. Проще или нет — субъективно, но 2LO объективно лаконичнее.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *