diff --git a/OPI/src/CommonModules/OPI_GoogleCalendar/Module.bsl b/OPI/src/CommonModules/OPI_GoogleCalendar/Module.bsl
new file mode 100644
index 000000000..42fc32a7e
--- /dev/null
+++ b/OPI/src/CommonModules/OPI_GoogleCalendar/Module.bsl
@@ -0,0 +1,202 @@
+// MIT License
+
+// Copyright (c) 2023 Anton Tsitavets
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// https://github.com/Bayselonarrend/OpenIntegrations
+
+#Область ПрограммныйИнтерфейс
+
+#Область РаботаСоСпискомКалендарей
+
+// Получить список календарей.
+//
+// Параметры:
+// Токен - Строка - Токен
+//
+// Возвращаемое значение:
+// Соответствие Из КлючИЗначение - Массив соответствий данных календарей
+Функция ПолучитьСписокКалендарей(Знач Токен) Экспорт
+
+ Заголовки = ПолучитьЗаголовокАвторизации(Токен);
+ МассивКалендарей = Новый Массив;
+
+ ПолучитьСписокКалендарейРекурсивно(Заголовки, МассивКалендарей);
+
+ Возврат МассивКалендарей;
+
+КонецФункции
+
+// Добавить календарь в список.
+//
+// Параметры:
+// Токен - Строка - Токен
+// Календарь - Строка - ID календаря
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - ответ сервера Google
+Функция ДобавитьКалендарьВСписок(Знач Токен, Знач Календарь) Экспорт
+
+ Заголовки = ПолучитьЗаголовокАвторизации(Токен);
+ URL = "https://www.googleapis.com/calendar/v3/users/me/calendarList";
+
+ Параметры = Новый Структура;
+ Параметры.Вставить("id", Календарь);
+
+ Ответ = OPI_Инструменты.Post(URL, Параметры, Заголовки);
+
+ Возврат Ответ;
+
+КонецФункции
+
+#КонецОбласти
+
+#Область РаботаСКалендарями
+
+// Создать календарь.
+//
+// Параметры:
+// Токен - Строка - Токен
+// Наименование - Строка - Наименование создаваемого календаря
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - ответ сервера Google
+Функция СоздатьКалендарь(Знач Токен, Знач Наименование) Экспорт
+
+ Заголовки = ПолучитьЗаголовокАвторизации(Токен);
+ URL = "https://www.googleapis.com/calendar/v3/calendars";
+
+ Параметры = Новый Структура;
+ Параметры.Вставить("summary", Наименование);
+
+ Ответ = OPI_Инструменты.Post(URL, Параметры, Заголовки);
+
+ Возврат Ответ;
+
+КонецФункции
+
+// Получить календарь.
+//
+// Параметры:
+// Токен - Строка - Токен
+// Календарь - Строка - ID календаря
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - ответ сервера Google
+Функция ПолучитьКалендарь(Знач Токен, Знач Календарь) Экспорт
+
+ Заголовки = ПолучитьЗаголовокАвторизации(Токен);
+ URL = "https://www.googleapis.com/calendar/v3/calendars/" + Календарь;
+ Ответ = OPI_Инструменты.Get(URL, , Заголовки);
+
+ Возврат Ответ;
+
+КонецФункции
+
+// Изменить календарь.
+//
+// Параметры:
+// Токен - Строка - Токен
+// Календарь - Строка - ID календаря
+// Наименование - Строка - Новое наименование
+// Описание - Строка - Новое описание календаря
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - ответ сервера Google
+Функция ИзменитьКалендарь(Знач Токен, Знач Календарь, Знач Наименование = "", Знач Описание = "") Экспорт
+
+ Заголовки = ПолучитьЗаголовокАвторизации(Токен);
+ URL = "https://www.googleapis.com/calendar/v3/calendars/" + Календарь;
+
+ Параметры = Новый Структура;
+
+ Если ЗначениеЗаполнено(Наименование) Тогда
+ Параметры.Вставить("summary", Наименование);
+ КонецЕсли;
+
+ Если ЗначениеЗаполнено(Описание) Тогда
+ Параметры.Вставить("description", Описание);
+ КонецЕсли;
+
+ Ответ = OPI_Инструменты.Put(URL, Параметры, Заголовки);
+
+ Возврат Ответ;
+
+КонецФункции
+
+// Удалить календарь.
+//
+// Параметры:
+// Токен - Строка - Токен
+// Календарь - Строка - ID календаря
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - ответ сервера Google
+Функция УдалитьКалендарь(Знач Токен, Знач Календарь) Экспорт
+
+ Заголовки = ПолучитьЗаголовокАвторизации(Токен);
+ URL = "https://www.googleapis.com/calendar/v3/calendars/" + Календарь;
+ Ответ = OPI_Инструменты.Delete(URL, , Заголовки);
+
+ Возврат Ответ;
+
+КонецФункции
+
+#КонецОбласти
+
+#КонецОбласти
+
+#Область СлужебныеПроцедурыИФункции
+
+Функция ПолучитьЗаголовокАвторизации(Знач Токен)
+
+ Заголовки = Новый Соответствие;
+ Заголовки.Вставить("Authorization", "Bearer " + Токен);
+
+ Возврат Заголовки;
+
+КонецФункции
+
+Процедура ПолучитьСписокКалендарейРекурсивно(Знач Заголовки, МассивКалендарей, Страница = "")
+
+ Параметры = Новый Структура;
+
+ Если ЗначениеЗаполнено(Страница) Тогда
+ Параметры.Вставить("pageToken", Страница);
+ КонецЕсли;
+
+ Результат = OPI_Инструменты.Get("https://www.googleapis.com/calendar/v3/users/me/calendarList"
+ ,
+ , Заголовки);
+
+ Календари = Результат["items"];
+ Страница = Результат["nextPageToken"];
+
+ Для Каждого Календарь Из Календари Цикл
+ МассивКалендарей.Добавить(Календарь);
+ КонецЦикла;
+
+ Если Календари.Количество() > 0 И ЗначениеЗаполнено(Страница) Тогда
+ ПолучитьСписокКалендарейРекурсивно(Заголовки, МассивКалендарей, Страница);
+ КонецЕсли;
+
+КонецПроцедуры
+
+#КонецОбласти
\ No newline at end of file
diff --git a/OPI/src/CommonModules/OPI_GoogleCalendar/OPI_GoogleCalendar.mdo b/OPI/src/CommonModules/OPI_GoogleCalendar/OPI_GoogleCalendar.mdo
new file mode 100644
index 000000000..3d38018f5
--- /dev/null
+++ b/OPI/src/CommonModules/OPI_GoogleCalendar/OPI_GoogleCalendar.mdo
@@ -0,0 +1,11 @@
+
+
+ OPI_GoogleCalendar
+
+
+ OPI google calendar
+
+ true
+ true
+ true
+
diff --git a/OPI/src/CommonModules/OPI_GoogleWorkspace/Module.bsl b/OPI/src/CommonModules/OPI_GoogleWorkspace/Module.bsl
new file mode 100644
index 000000000..2c7b09568
--- /dev/null
+++ b/OPI/src/CommonModules/OPI_GoogleWorkspace/Module.bsl
@@ -0,0 +1,114 @@
+// MIT License
+
+// Copyright (c) 2023 Anton Tsitavets
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// https://github.com/Bayselonarrend/OpenIntegrations
+
+#Область ПрограммныйИнтерфейс
+
+// Сформировать ссылку получения кода.
+//
+// Параметры:
+// ClientID - Строка - Client ID
+//
+// Возвращаемое значение:
+// Строка - Сформировать ссылку получения кода
+Функция СформироватьСсылкуПолученияКода(Знач ClientID) Экспорт
+
+ URL = "https://accounts.google.com/o/oauth2/auth";
+
+ ПараметрыURL = Новый Структура;
+ ПараметрыURL.Вставить("response_type", "code");
+ ПараметрыURL.Вставить("client_id" , ClientID);
+ ПараметрыURL.Вставить("redirect_uri" , "http://localhost");
+ ПараметрыURL.Вставить("access_type" , "offline");
+ ПараметрыURL.Вставить("scope" , ПолучитьСписокРазрешений());
+
+ URL = URL + OPI_Инструменты.ПараметрыЗапросаВСтроку(ПараметрыURL);
+
+ Возврат URL;
+
+КонецФункции
+
+// Получить токен по коду.
+//
+// Параметры:
+// ClientID - Строка - Client ID
+// ClientSecret - Строка - Client secret
+// Code - Строка - Code из браузера
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - Получить токен по коду
+Функция ПолучитьТокенПоКоду(Знач ClientID, Знач ClientSecret, Знач Code) Экспорт
+
+ URL = "https://accounts.google.com/o/oauth2/token";
+
+ ПараметрыURL = Новый Структура;
+ ПараметрыURL.Вставить("grant_type" , "authorization_code");
+ ПараметрыURL.Вставить("client_id" , ClientID);
+ ПараметрыURL.Вставить("client_secret", ClientSecret);
+ ПараметрыURL.Вставить("redirect_uri" , "http://localhost");
+ ПараметрыURL.Вставить("code" , Code);
+
+ Ответ = OPI_Инструменты.Post(URL, ПараметрыURL, , Ложь);
+
+ Возврат Ответ;
+
+КонецФункции
+
+// Обновить токен.
+//
+// Параметры:
+// ClientID - Строка - Client ID
+// ClientSecret - Строка - Client secret
+// RefreshToken - Строка - Refresh token
+//
+// Возвращаемое значение:
+// Строка, Произвольный, ДвоичныеДанные, Неопределено, HTTPОтвет - Обновить токен
+Функция ОбновитьТокен(Знач ClientID, Знач ClientSecret, Знач RefreshToken) Экспорт
+
+ URL = "https://accounts.google.com/o/oauth2/token";
+
+ ПараметрыURL = Новый Структура;
+ ПараметрыURL.Вставить("grant_type" , "refresh_token");
+ ПараметрыURL.Вставить("client_id" , ClientID);
+ ПараметрыURL.Вставить("client_secret", ClientSecret);
+ ПараметрыURL.Вставить("refresh_token", RefreshToken);
+
+ Ответ = OPI_Инструменты.Post(URL, ПараметрыURL, , Ложь);
+
+ Возврат Ответ;
+
+КонецФункции
+
+#КонецОбласти
+
+#Область СлужебныеПроцедурыИфункции
+
+Функция ПолучитьСписокРазрешений()
+
+ МассивРазрешений = Новый Массив;
+ МассивРазрешений.Добавить("https://www.googleapis.com/auth/calendar");
+
+ Возврат СтрСоединить(МассивРазрешений, " ");
+
+КонецФункции
+#КонецОбласти
\ No newline at end of file
diff --git a/OPI/src/CommonModules/OPI_GoogleWorkspace/OPI_GoogleWorkspace.mdo b/OPI/src/CommonModules/OPI_GoogleWorkspace/OPI_GoogleWorkspace.mdo
new file mode 100644
index 000000000..45fb1957c
--- /dev/null
+++ b/OPI/src/CommonModules/OPI_GoogleWorkspace/OPI_GoogleWorkspace.mdo
@@ -0,0 +1,11 @@
+
+
+ OPI_GoogleWorkspace
+
+
+ OPI google workspace
+
+ true
+ true
+ true
+
diff --git a/OPI/src/CommonModules/OPI_Инструменты/Module.bsl b/OPI/src/CommonModules/OPI_Инструменты/Module.bsl
index 8b0648914..7204b85a3 100644
--- a/OPI/src/CommonModules/OPI_Инструменты/Module.bsl
+++ b/OPI/src/CommonModules/OPI_Инструменты/Module.bsl
@@ -137,7 +137,7 @@
URL = СтрЗаменить(URL, "https://", "");
URL = СтрЗаменить(URL, "http://", "");
- URL = СтрЗаменить(URL, "www.", "");
+ //URL = СтрЗаменить(URL, "www.", "");
URL = СтрЗаменить(URL, ":443", "");
СтруктураВозврата = Новый Структура;
diff --git a/OPI/src/CommonModules/YAX_Тесты/Module.bsl b/OPI/src/CommonModules/YAX_Тесты/Module.bsl
index 60e7b3e84..8264b5ce4 100644
--- a/OPI/src/CommonModules/YAX_Тесты/Module.bsl
+++ b/OPI/src/CommonModules/YAX_Тесты/Module.bsl
@@ -58,7 +58,14 @@
.ДобавитьСерверныйТест("ЯДиск_ПолучитьСписокФайлов" , "Получить список файлов")
.ДобавитьСерверныйТест("ЯДиск_ПереместитьОбъект" , "Переместить объект")
.ДобавитьСерверныйТест("ЯДиск_ДействияПубличныхОбъектов" , "Действия с публичными объектами")
- .ДобавитьСерверныйТест("ЯДиск_ПолучитьСписокОпубликованных" , "Получить список опубликованных");
+ .ДобавитьСерверныйТест("ЯДиск_ПолучитьСписокОпубликованных" , "Получить список опубликованных")
+
+ .ДобавитьТестовыйНабор("Google Calendar")
+ .ДобавитьСерверныйТест("ГК_ПолучитьСсылкуАвторизации" , "Получить ссылку авторизации")
+ .ДобавитьСерверныйТест("ГК_ПолучитьТокен" , "Получить токен")
+ .ДобавитьСерверныйТест("ГК_ОбновитьТокен" , "Обновить токен")
+ .ДобавитьСерверныйТест("ГК_ПолучитьСписокКалендарей" , "Получить список календарей")
+ .ДобавитьСерверныйТест("ГК_СоздатьУдалитьКалендарь" , "Создать/удалить календарь");
КонецПроцедуры
@@ -1484,6 +1491,108 @@
#КонецОбласти
+#Область GoogleCalendar
+
+Процедура ГК_ПолучитьСсылкуАвторизации() Экспорт
+
+ ClientID = ПолучитьПараметр("Google_ClientID");
+ Результат = OPI_GoogleWorkspace.СформироватьСсылкуПолученияКода(ClientID);
+
+ ЮТест.ОжидаетЧто(Результат)
+ .ИмеетТип("Строка")
+ .Заполнено();
+
+ ЗаписатьПараметр("Google_Link", Результат);
+
+КонецПроцедуры
+
+Процедура ГК_ПолучитьТокен() Экспорт
+
+ ClientID = ПолучитьПараметр("Google_ClientID");
+ ClientSecret = ПолучитьПараметр("Google_ClientSecret");
+ Code = ПолучитьПараметр("Google_Code");
+
+ Результат = OPI_GoogleWorkspace.ПолучитьТокенПоКоду(ClientID, ClientSecret, Code);
+
+ Если ЗначениеЗаполнено(Результат["access_token"])
+ И ЗначениеЗаполнено(Результат["refresh_token"]) Тогда
+
+ ЗаписатьПараметр("Google_Token" , Результат["access_token"]);
+ ЗаписатьПараметр("Google_Refresh", Результат["refresh_token"]);
+
+ КонецЕсли;
+
+КонецПроцедуры
+
+Процедура ГК_ОбновитьТокен() Экспорт
+
+ ClientID = ПолучитьПараметр("Google_ClientID");
+ ClientSecret = ПолучитьПараметр("Google_ClientSecret");
+ RefreshToken = ПолучитьПараметр("Google_Refresh");
+
+ Результат = OPI_GoogleWorkspace.ОбновитьТокен(ClientID, ClientSecret, RefreshToken);
+
+ ЮТест.ОжидаетЧто(Результат)
+ .ИмеетТип("Соответствие")
+ .Свойство("access_token").Заполнено();
+
+ ЗаписатьПараметр("Google_Token" , Результат["access_token"]);
+
+КонецПроцедуры
+
+Процедура ГК_ПолучитьСписокКалендарей() Экспорт
+
+ Токен = ПолучитьПараметр("Google_Token");
+ Результат = OPI_GoogleCalendar.ПолучитьСписокКалендарей(Токен);
+
+ ЮТест.ОжидаетЧто(Результат)
+ .ИмеетТип("Массив");
+
+КонецПроцедуры
+
+Процедура ГК_СоздатьУдалитьКалендарь() Экспорт
+
+ Токен = ПолучитьПараметр("Google_Token");
+ Наименование = "Тестовый календарь";
+ Описание = "Тестовое описание";
+ НаименованиеИзмененное = Наименование + " (изм.)";
+
+ Результат = OPI_GoogleCalendar.СоздатьКалендарь(Токен, Наименование);
+
+ ЮТест.ОжидаетЧто(Результат)
+ .ИмеетТип("Соответствие")
+ .Свойство("summary").Равно(Наименование)
+ .Свойство("id").ИмеетТип("Строка").Заполнено();
+
+ Календарь = Результат["id"];
+
+ Результат = OPI_GoogleCalendar.ИзменитьКалендарь(Токен
+ , Календарь
+ , НаименованиеИзмененное
+ , Описание);
+
+ ЮТест.ОжидаетЧто(Результат)
+ .ИмеетТип("Соответствие")
+ .Свойство("summary").Равно(НаименованиеИзмененное)
+ .Свойство("description").Равно(Описание)
+ .Свойство("id").ИмеетТип("Строка").Заполнено();
+
+ Результат = OPI_GoogleCalendar.ПолучитьКалендарь(Токен, Календарь);
+
+ ЮТест.ОжидаетЧто(Результат)
+ .ИмеетТип("Соответствие")
+ .Свойство("summary").Равно(НаименованиеИзмененное)
+ .Свойство("description").Равно(Описание)
+ .Свойство("id").ИмеетТип("Строка").Заполнено();
+
+ Результат = OPI_GoogleCalendar.УдалитьКалендарь(Токен, Календарь);
+
+ ЮТест.ОжидаетЧто(Результат).НеЗаполнено();
+
+КонецПроцедуры
+
+#КонецОбласти
+
#КонецОбласти
#КонецОбласти
diff --git a/OPI/src/Configuration/Configuration.mdo b/OPI/src/Configuration/Configuration.mdo
index de2572f32..f4f294643 100644
--- a/OPI/src/Configuration/Configuration.mdo
+++ b/OPI/src/Configuration/Configuration.mdo
@@ -48,13 +48,15 @@
ru
CommonModule.OPI_Инструменты
+ CommonModule.OPI_Криптография
CommonModule.OPI_Telegram
CommonModule.OPI_VK
CommonModule.OPI_Viber
CommonModule.OPI_Twitter
CommonModule.OPI_Notion
- CommonModule.OPI_Криптография
CommonModule.OPI_YandexID
CommonModule.OPI_YandexDisk
+ CommonModule.OPI_GoogleWorkspace
+ CommonModule.OPI_GoogleCalendar
CommonModule.YAX_Тесты