Стала задача, собрать данные с Google Merchants и внести их в 1С, для более детального анализа того, что сейчас показывается, что нет и какие проблемы при этом есть. Если кому это интересно, прошу под кат.
Опыт програмирования на платформе 1С у меня крайне маленький, порядка года, поэтому за какие-то банальные вещи, прощу прощения сразу. Статья будет основана на данной статье, но там не описан процесс парсинга + не описан процесс на HTTPСоединение, что является более универсальным и более удобным.
Все процедуры производились на клиент-серверном режиме, в конфигурации УТ 10.3 (10.3.13.2) на платформе 1С 8.3 (8.3.8.2322).
Использованные механизмы описаны в ключевых словах.
Еще перед тем, как пойдет код, я хотел бы ответить на вопрос, зачем я это вообще пишу? Во первых, для тех, кто ищет информацию по данным технологиям, во вторых, для того, чтобы получить обратную связь от вас, услышать какие-то советы и как итог — сделать себя лучше =)
Пара ссылок на основне определения:
Погнали, итак, стоит цель, получить данные с Google Merchant и занести их в 1С.
Сделаем декомпозицию задачи.
- Получить данные для авторизации в Google
- Сделать уч. запись в Google Cloud Console
- Добавить в разделе API , работу с Content API for Shopping
- Сделать учетную запись с типом "Други типы" — http://jmp.sh/9ikhvbp
- Получить ClientId и Secret
- Сделать уч. запись в Google Cloud Console
- Добавить свою учетную запись (которая для Google Cloud Console) в Merchant с обычными правами.
- Получить Токен для запросов
- Сделать регламент Запроса данных/Обновления токена
- При запросе данных парсить 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;
КонецЕсли;
Возврат Элемент;
КонецФункции
В целом все …
Если вы дочитали это, то уже спасибо =)
Если дадите какой-то хороший совет, буду прям очень рад.
Статья хороша +!
А чем не угодил «ПрочитатьJSON(ЧтениеJSON, Истина)» — на выходе будет Соответствие. и безопасность получения свойств проще, и не нужны доп. функции ))
и как себя поведет фабрика если ключом окажется не строка, а например число?
я когда на подобный джисон попал, долго мучался, было тяжело отойти от уже отлаженного механизма работы с XDTO объектами, все сломать и построить заново )).
(1) 0_о комментарий к статье… надо будет выпить сегодня.
Чем не угодил, не знаю, привык читать уже через фабрику, можно конечно и через чтение сделать, просто формат который выходил, был протестирован именно на фабрике.
+ у меня не так много опыта в этой теме и когда делаю такие парсеры, делаю не как то универсально, а именно заточенный под этот сервис.
«Добавить свою учетную запись (которая для Google Cloud Console) в Merchant с обычными правами.»
Подскажите как сделать эту операцию для Firebase?
PS content на firebase заменил, но проект запрашивает разрешение и не редиректит. А при нажатии Разрешить пытается открыть страницу с адресом res://ieframe.dll/dnserrordiagoff_webOC.htm#https://accounts.google.com/o/oauth2/approval?as=……… . после чего говорит что не может ее отобразить
(1) ПрочитатьJSON, ЗаписатьJSON очень медленныe. Здесь упоминается про этоhttps://infostart.ru/public/640996/ , опытным путем удалось подтвердить слова автора.
(4) Согласно скрина из статьи видно, что фабрика пишет медленнее, чем даже обычная ЗаписатьJSON в 2 раза.
Или я не правильно понимаю?
(5) Про фабрику не знаю, там описано только:
3. ПрочитатьJSON, ЗаписатьJSON — самые медленные
2. Запись в поток — сейчас использую вот этот метод, он почти в 2 раза быстрее, чем предыдущий
1. Запись без контроля — этот пока нет времени реализовать.
По поводу фабрики не знаю, необходимо тестировать.
(6)Привет 🙂
Дело в том, что 1С как раз по умолчанию инициализирует одну фабрику для быстрой обработки в рамках глобального контекста. Это быстро и удобно для, наверное, 99% задач, стоящих обычно перед коллегами 🙂 Это и есть ПрочитатьJSON, ЗаписатьJSON как они есть.
Мы работаем только на потоке, причем с заранее подготовленными данными, поэтому пишем без контроля, что ещё очень сильно ускоряет обработку.
Можно получать access token через 2LO авторизациюhttps://developers.google.com/identity/protocols/OAuth2ServiceAccount . Это сервер-сервер, без использования учетки пользователя. Проще или нет — субъективно, но 2LO объективно лаконичнее.