ユーザーは Google カレンダーの予定を更新または削除できます。ユーザーが会議を作成した後にイベントを更新した場合、Google Workspace アドオンは会議データを更新することで、その変更に対応する必要がある場合があります。サードパーティ製の会議システムがイベントデータのトラッキングに依存している場合、イベントの変更時に会議を更新しないと、会議が使用できなくなる可能性があります。
カレンダーの予定の変更に合わせて会議データを更新するプロセスは、同期と呼ばれます。特定のカレンダーで予定が変更されるたびに起動する Google Apps Script のインストール可能なトリガーを作成して、予定の変更を同期します。トリガーはどのイベントが変更されたかをレポートせず、作成した会議を含むイベントのみに制限することもできません。前回の同期以降にカレンダーに加えられたすべての変更のリストをリクエストし、イベント リストをフィルタして、それに応じて更新します。
一般的な同期の手順は次のとおりです。
- ユーザーが初めて会議を作成すると、同期プロセスが初期化されます。
- ユーザーがカレンダーの予定を作成、更新、削除するたびに、トリガーはアドオン プロジェクトのトリガー関数を実行します。
- トリガー関数は、前回の同期以降のイベント変更のセットを調べ、関連付けられたサードパーティの会議を更新する必要があるかどうかを判断します。
- 必要な更新は、サードパーティの API リクエストを行うことで会議に適用されます。
- 新しい同期トークンが保存され、次のトリガー実行ではカレンダーの最新の変更のみが調べられます。
同期を初期化する
初期化プロセスにより、アドオンがカレンダーの変更に対応できるようになります。アドオンがサードパーティ システムで会議を正常に作成したら、トリガーがまだ存在しない場合は、このカレンダーのイベントの変更に応答するインストール可能なトリガーを作成する必要があります。
トリガーを作成すると、初期同期トークンが作成され、初期化が完了します。これは、トリガー関数を直接実行することで行われます。
カレンダー トリガーを作成する
同期するには、会議が添付されたカレンダーの予定が変更されたときにアドオンが検出する必要があります。これは、EventUpdated
インストール可能なトリガーを作成することで実現されます。アドオンに必要なトリガーはカレンダーごとに 1 つのみで、プログラムで作成できます。
トリガーを作成するおすすめのタイミングは、ユーザーが初めて会議を作成するときです。この時点で、ユーザーはアドオンの使用を開始します。会議を作成してエラーがないことを確認したら、アドオンは、このユーザーのトリガーが存在するかどうかを確認し、存在しない場合はトリガーを作成する必要があります。
トリガーを作成するには、アドオンに https://www.googleapis.com/auth/calendar.readonly と https://www.googleapis.com/auth/script.scriptapp のスコープが必要です。
同期トリガー関数を実装する
トリガー関数は、Apps Script がトリガーを起動する条件を検出すると実行されます。EventUpdated カレンダー トリガーは、ユーザーが指定したカレンダーで予定を作成、変更、削除したときに起動します。
アドオンで使用するトリガー関数を実装します。このトリガー関数は、次の処理を行う必要があります。
syncTokenを使用してカレンダーの高度なサービスCalendar.Events.list呼び出しを行い、前回の同期以降に変更されたイベントのリストを取得します。同期トークンを使用すると、アドオンが検証する必要があるイベントの数を減らすことができます。有効な同期トークンなしでトリガー関数が実行されると、完全同期にバックオフします。完全同期では、新しい有効な同期トークンを生成するために、指定された期間内のすべてのイベントを取得しようとします。
変更された各イベントが、関連付けられたサードパーティの会議があるかどうかを判断するために調べられます。
イベントに会議が含まれている場合は、変更内容が確認されます。変更によっては、関連する会議の変更が必要になる場合があります。たとえば、イベントが削除された場合、アドオンは会議を削除する必要があります。
会議に必要な変更は、サードパーティ システムに API 呼び出しを行うことで行われます。
必要な変更をすべて行った後、
Calendar.Events.listメソッドから返されたnextSyncTokenを保存します。この同期トークンは、Calendar.Events.list呼び出しによって返される結果の最後のページにあります。
カレンダーの予定を更新する
同期の実行時にカレンダーの予定を更新したい場合があります。この場合、適切な Calendar の高度なサービス リクエストを使用して、イベントを更新します。If-Match ヘッダーで条件付き更新を使用してください。これにより、ユーザーが別のクライアントで行った同時変更が上書きされるのを防ぐことができます。
例
次の例は、カレンダーの予定と関連する会議の同期を設定する方法を示しています。
/** * Initializes syncing of conference data by creating a sync trigger and * sync token if either does not exist yet. * * @param {String} calendarId The ID of the Google Calendar. */ function initializeSyncing(calendarId) { // Create a syncing trigger if it doesn't exist yet. createSyncTrigger(calendarId); // Perform an event sync to create the initial sync token. syncEvents({'calendarId': calendarId}); } /** * Creates a sync trigger if it does not exist yet. * * @param {String} calendarId The ID of the Google Calendar. */ function createSyncTrigger(calendarId) { // Check to see if the trigger already exists; if does, return. var allTriggers = ScriptApp.getProjectTriggers(); for (var i = 0; i < allTriggers.length; i++) { var trigger = allTriggers[i]; if (trigger.getTriggerSourceId() == calendarId) { return; } } // Trigger does not exist, so create it. The trigger calls the // 'syncEvents()' trigger function when it fires. var trigger = ScriptApp.newTrigger('syncEvents') .forUserCalendar(calendarId) .onEventUpdated() .create(); } /** * Sync events for the given calendar; this is the syncing trigger * function. If a sync token already exists, this retrieves all events * that have been modified since the last sync, then checks each to see * if an associated conference needs to be updated and makes any required * changes. If the sync token does not exist or is invalid, this * retrieves future events modified in the last 24 hours instead. In * either case, a new sync token is created and stored. * * @param {Object} e If called by a event updated trigger, this object * contains the Google Calendar ID, authorization mode, and * calling trigger ID. Only the calendar ID is actually used here, * however. */ function syncEvents(e) { var calendarId = e.calendarId; var properties = PropertiesService.getUserProperties(); var syncToken = properties.getProperty('syncToken'); var options; if (syncToken) { // There's an existing sync token, so configure the following event // retrieval request to only get events that have been modified // since the last sync. options = { syncToken: syncToken }; } else { // No sync token, so configure to do a 'full' sync instead. In this // example only recently updated events are retrieved in a full sync. // A larger time window can be examined during a full sync, but this // slows down the script execution. Consider the trade-offs while // designing your add-on. var now = new Date(); var yesterday = new Date(); yesterday.setDate(now.getDate() - 1); options = { timeMin: now.toISOString(), // Events that start after now... updatedMin: yesterday.toISOString(), // ...and were modified recently maxResults: 50, // Max. number of results per page of responses orderBy: 'updated' } } // Examine the list of updated events since last sync (or all events // modified after yesterday if the sync token is missing or invalid), and // update any associated conferences as required. var events; var pageToken; do { try { options.pageToken = pageToken; events = Calendar.Events.list(calendarId, options); } catch (err) { // Check to see if the sync token was invalidated by the server; // if so, perform a full sync instead. if (err.message === "Sync token is no longer valid, a full sync is required.") { properties.deleteProperty('syncToken'); syncEvents(e); return; } else { throw new Error(err.message); } } // Read through the list of returned events looking for conferences // to update. if (events.items && events.items.length > 0) { for (var i = 0; i < events.items.length; i++) { var calEvent = events.items[i]; // Check to see if there is a record of this event has a // conference that needs updating. if (eventHasConference(calEvent)) { updateConference(calEvent, calEvent.conferenceData.conferenceId); } } } pageToken = events.nextPageToken; } while (pageToken); // Record the new sync token. if (events.nextSyncToken) { properties.setProperty('syncToken', events.nextSyncToken); } } /** * Returns true if the specified event has an associated conference * of the type managed by this add-on; retuns false otherwise. * * @param {Object} calEvent The Google Calendar event object, as defined by * the Calendar API. * @return {boolean} */ function eventHasConference(calEvent) { var name = calEvent.conferenceData.conferenceSolution.name || null; // This version checks if the conference data solution name matches the // one of the solution names used by the add-on. Alternatively you could // check the solution's entry point URIs or other solution-specific // information. if (name) { if (name === "My Web Conference" || name === "My Recorded Web Conference") { return true; } } return false; } /** * Update a conference based on new Google Calendar event information. * The exact implementation of this function is highly dependant on the * details of the third-party conferencing system, so only a rough outline * is shown here. * * @param {Object} calEvent The Google Calendar event object, as defined by * the Calendar API. * @param {String} conferenceId The ID used to identify the conference on * the third-party conferencing system. */ function updateConference(calEvent, conferenceId) { // Check edge case: the event was cancelled if (calEvent.status === 'cancelled' || eventHasConference(calEvent)) { // Use the third-party API to delete the conference too. } else { // Extract any necessary information from the event object, then // make the appropriate third-party API requests to update the // conference with that information. } }