Задача: нужно получить доступ или отобразить события частного google-календаря.Проблема: нельзя поместить частный календарь внутрь iframe или запросить события, используя ключ API.Необходимые условия: совместимость с Angular, поддержка TypeScript (сервисные врапперы, классы и типы моделей данных)Решение: google-api-nodejs-client, предоставляющий все, что нужно.
Google официально поддерживает клиентскую библиотеку для доступа к Google API; также включена поддержка авторизации и аутентификации с помощью OAuth 2.0, API-ключей и JWT .
Теперь разбираемся, как заменить часть Node.js
на Angular
.
К сожалению, эта библиотека предназначена для NodeJS, и проблематично интегрировать ее внутрь приложений, созданных в Webpack, в том числе и Angular, поскольку она зависит от некоторых вещей в NodeJS, не существующих в браузере.Но это не остановит нас, поскольку есть обходной путь (обсуждение на английском языке здесь).
Во-первых, нужно установить расширения для Webpack.
npm i -D @angular-builders/custom-webpackЗатем нужно заменить builder angular.json в architect
; в настройки также включаем путь к пользовательской конфигурации Webpack:
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
},
...
},
...
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
...
},
}
Объяснение можно найти в документации по сборке Angular.
Так в чем, собственно, хак? Мы притворимся, что все нужное этой библиотеке NodeJS есть в браузере.
global
и Buffer
.fs
, child_process
и https-proxy-agent
.global
и Buffer
Добавьте следующий полифилл под импортом приложения в файл src/polyfills.ts
.
import * as process from 'process';
(window as any).process = process;
import { Buffer } from 'buffer';
(window as any).Buffer = Buffer;
Не забудьте установить эти пакеты с npm i -D process buffer
.
Поместите это в <head>
index.html
, чтобы избавиться от ошибок, связанных с доступом к global
, поскольку его заменит window
.
<script>
if (global === undefined) {
var global = window;
}"
</script>
Имитирование библиотек
const path = require('path');
module.exports = {
resolve: {
extensions: ['.js'],
alias: {
fs: path.resolve(__dirname, 'src/mocks/fs.mock.js'),
child_process: path.resolve(
__dirname,
'src/mocks/child_process.mock.js'
),
'https-proxy-agent': path.resolve(
__dirname,
'src/mocks/https-proxy-agent.mock.js',
),
},
},
};
Что же здесь происходит? Мы говорим WebPack заменить один файл импорта другим. Все заглушки мы кладем сюда src/mock
, чтобы нашим коллегам по проекту было проще понять, что это за файлы.
Код внутри этих заглушек довольно прост. Нужно добавить несколько методов, не обязательных к использованию, чтобы они могли “ничего” не делать. И fs
и child_process
будут выглядеть так:
module.exports = {
readFileSync() {},
readFile() {},
};
С помощью https-proxy-agent
можно записать еще проще, как:module.exports = {};
.
Большинство публичных данных из API легко получить при помощи API-ключа. Но если вам нужны приватные данные (приватный календарь, например), нужно проходить аутентификацию. Это можно сделать с помощью клиента OAuth.
В перечне провайдеров app.module.ts
укажите OAuth2Client
. Это должно выглядеть так:
{
provide: OAuth2Client,
useValue: new OAuth2Client(
// Вы получите это в учетных данных проекта GCP
environment.G_API_CLIENT_ID,
environment.G_API_CLIENT_SECRET,
// URL, где будет обрабатываться успешная аутентификация
environment.G_API_REDIRECT,
),
},
Мы будем использовать перенаправленную аутентификацию, поэтому следующим шагом станет генерация аутентификационного URL.
window.location.href = this.oauth2Client.generateAuthUrl({
// 'offline' также получает refresh_token
access_type: 'offline',
// поместите сюда необходимые области
scope: [
// в первом примере мы хотим прочитать события в календаре
'https://www.googleapis.com/auth/calendar.events.readonly',
'https://www.googleapis.com/auth/calendar.readonly',
// во втором примере мы читаем данные аналитики
'https://www.googleapis.com/auth/analytics.readonly',
],
});
Благодаря refresh_token
у OAuthClient будет возможность обрабатывать обмен токена даже после истечения его срока действия, поэтому нам не нужно будет проходить окно аутентификации google каждый час после истечения срока действия токена.
Если вы любите изучать документацию, посетите страницу google-apis docs или посмотрите на Calendar, его мы используем в примере ниже.
????Использование службы календаря SDKПрава доступа
Убедитесь, что используемый вами аккаунт имеет доступ к календарю, события которого вы хотите прочесть.
Пример кода
Укажите для класса Calendar метод аутентификации по умолчанию, в нашем случае это OAuth. Добавьте к перечню провайдеров app.module.ts
следующее:
{
provide: calendar_v3.Calendar,
useFactory:
(auth: OAuth2Client) => new calendar_v3.Calendar({ auth }),
deps: [OAuth2Client],
},
Теперь у нас есть доступ к полному набору функций API календаря Google с полностью типизированным интерфейсом SDK.
Вы можете получить календарь как любой другой сервис Angular, использовать его как параметр конструктора. Его вам предоставит внедрение зависимости.
constructor(
private calendar: calendar_v3.Calendar,
) {}
Вот пример, как получить список событий определенного календаря. Мы также отфильтруем только события, помеченные значком “сегодня”, не удаленные и не отмененные.
this.calendar.events.list({
// обязательно; электронная почта или почта как id календаря
calendarId: CALENDAR_ID,
// опционально; аргументы, позволяющие фильтровать или выделять нужные события
timeMin: startOfDay(today).toISOString(),
timeMax: endOfDay(today).toISOString(),
showDeleted: false,
singleEvents: true,
}),
Вы также можете выполнять другие задачи, такие как создание событий, но не забывайте запрашивать подходящую область действия при аутентификации.
Установка прав доступа
⮕ Analytics Console ⮕ Admin ⮕ Account column ⮕ User Management ⮕⮕ Select the user ⮕ Activate the "Read & Analyze" checkboxПолучение View ID
⮕ Analytics Console ⮕ Admin ⮕ View column ⮕ View Settings ⮕⮕ Copy the "View ID" numberПример кода
Как и в предыдущем примере, нам нужен провайдер. Укажите для класса аналитики метод аутентификации по умолчанию. Добавьте к перечню провайдеров app.module.ts
следующее:
{
provide: analytics_v3.Analytics,
useFactory:
(auth: OAuth2Client) => new analytics_v3.Analytics({ auth }),
deps: [OAuth2Client],
},
И снова вы получаете его готовым для вставки при помощи DI в любой позволяющий делать вставки класс.
constructor(
private analytics: analytics_v3.Analytics,
) {}
Этот пример получит определенные метрики для желаемого временного диапазона. В данном случае мы увидим общее количество просмотров страницы за 30 дней.
this.analytics.data.ga.get({
ids: 'ga:xxxxxxxxx', // замените xxxxxxxxx вашим view ID
'start-date': '30daysAgo',
'end-date': 'today',
metrics: 'ga:pageviews',
})
Иметь готовый полностью типизированный SDK для API — это совершенно другое дело в сравнении с необходимостью искать информацию в документации, особенно, модели данных. Аутентификационная часть проблемы тоже решена довольно удобно, поэтому не остановит людей, которые не знают или не хотят разбираться, как это работает. В целом гораздо проще создавать функции, когда среда проекта и инструменты настроены правильно.
Перевод статьи Vojtech Mašek: Integrating Google APIs with Angular
Комментарии