nodejs服务端使用服务帐户域范围委派通过Google Api操作谷歌会议日历

·  阅读 550

Google官方支持的NodeJS集成客户端,用以访问Google APIs, 支持OAuth 2.0授信及登录认证。登录以后即可在后台访问例如 Google Drive(云存储), Google Calendar, Gmail等服务。

公司业务需求,需要对公司google的会议日历做一些处理,如已离职人员预约的周期性google会议日历,需要删除,或者给周期性会议日历一个截止日期,周期性会议改成截止到当天的会议日历等。

首先创建一个NodeJS项目

创建项目目录

mkdir node-meeting

初始化

npm init

一直回车确认:

It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (emojiimages)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/diyuanwang/github/node-meeting/package.json:

{
  "name": "node-meeting",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes)
复制代码

这时候我们已经完成了nodejs项目的初始化工作。

我们得到了一个目录和一个文件node-meeting/package.json 剩下的工作就是编写js文件了。

通过以下命令安装googleapis及其依赖

$ npm install googleapis

google完整的API支持列表 https://developers.google.com/apis-explorer

index.js中引入googleapi

const {google} = require('googleapis');

在引入googleapi之前,请设置创建google服务帐户时的json文件,下载后的json中包含type,service_account,private_key_id,private_key,client_email,client_id,auth_uri,token_uri,auth_provider_x509_cert_url以及client_x509_cert_url,我们要用到的主要是private_key,用此获取到用户的访问Token并刷新此Token防止会话过期。

如果公司已有google的权限json文件,请忽略以下步骤,直接使用已有的json文件,如没有按照下面步骤获取到json文件:

1.在Google Cloud Platform中创建一个项目 2.创建一个服务帐号 3.为服务帐户启用了域范围委派 4.以JSON格式下载了服务帐户的密钥 5.API Manager>我创建了OAuth 2.0客户端ID的凭据 6.为项目启用了Gmail API 在Google Apps管理控制台中:

在“安全性”>“高级设置”>“管理API客户端访问”中,添加了上述步骤4中的客户端ID 为客户端ID添加了所有可能的范围 下载后的json内容如下图:

google.json google.json放入到node项目中 以下正式开始对某个google周期性会议日历操作,先进行get方法获取到周期性会议的周期规则(recurrence: [ 'RRULE:FREQ=WEEKLY;BYDAY=FR' ]),如每个星期三、五开一次会议,再用获取到的周期规则加上截止日期,通过google api的patch(附上patch api url developers.google.com/calendar/v3… )加上截止日期,那么已离职人员组织的周期性会议会截止到当天日期,已开过的会议不会被删除,已预约的周期性会议截止到当天:

const {google} = require('googleapis');
var dateTime = require('./util/dateTime');
var util = require('./util/index');
var config = require('./config/config');

const googleKey = require('./google.json'); 
const jwtClient = new google.auth.JWT(googleKey.client_email,null,googleKey.private_key ,['https://www.googleapis.com/auth/calendar'],config.googleAuth);


async function main(item) {
  const calendar = google.calendar('v3');
  let res = await getRecurrence(calendar, item);
  let isNotSetRecurrence = false;
  let resRecurrence = res? res.data.recurrence: '',
      recurrenceSplit = [],
      recurrenceRRULE = [],
      recurrence = '',
      //UNTIL = '20200411T145959Z';
      UNTIL = dateTime.getYearMonthDay() + 'T155959Z';
      console.log("周期性会议修改成功",JSON.stringify(res),resRecurrence);
  //规则资料 https://blog.csdn.net/csdn15002274757/article/details/94557330
  if(!res) return;
  if(resRecurrence.length > 1){
      resRecurrence.forEach(e => {
          let temp = '';
          e.split(';').forEach((item,index) => {
              //您可以使用COUNT或UNTIL来指定事件重复的结束。请勿在同一规则中同时使用两者。
              if(item.indexOf('UNTIL') < 0 && item.indexOf('COUNT') < 0){
                // [ 'EXDATE;TZID=Asia/Hong_Kong:20200511T170000,20200518T170000,20200525T170000','RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20200530T000000Z;BYDAY=FR,MO,TH,WE' ]
                  temp += item + (item.indexOf('TZID') > -1 ? '': ';');
              }
          });
          if(temp.indexOf('EXDATE') < 0){
              temp = temp + 'UNTIL=' + UNTIL;
          }
          recurrenceSplit = recurrenceSplit.concat([temp]);
      });
  }else{
      if(resRecurrence[0].indexOf('WEEKLY') > -1 && resRecurrence[0].indexOf('INTERVAL') > -1 && resRecurrence[0].indexOf('BYDAY') > -1 && resRecurrence[0].indexOf('WKST') < 0){
        if(resRecurrence[0].indexOf('WKST') < 0){
            resRecurrence[0] += ';WKST=MO';
        }
      }
      recurrenceSplit = resRecurrence[0].split(';');
      recurrenceSplit.forEach(e => {
          //您可以使用COUNT或UNTIL来指定事件重复的结束。请勿在同一规则中同时使用两者。
          if(e.split('=')[0] != 'UNTIL' && e.split('=')[0] != 'COUNT'){
            recurrenceRRULE.push(e);
          }
          //原本就有截止日期的,不再进行UNTIL赋值删除
          if(e.split('=')[0] == 'UNTIL'){
              let UNTILInit = e.split('=')[1];
              if(UNTILInit < UNTIL){
                  UNTIL = UNTILInit;
                  isNotSetRecurrence = true;
              }
          }
      });
      recurrenceRRULE = recurrenceRRULE.join(';');
      recurrence = `${recurrenceRRULE};UNTIL=${UNTIL}`;
      console.log("周期性会议修改成功3333333",resRecurrence.length,recurrence);
  }
  if(isNotSetRecurrence) return;
  calendar.events.patch(
      {
          auth: jwtClient,
          calendarId: item.room,
          eventId: item.recurringEventId,
          resource: {
            recurrence: resRecurrence.length > 1 ? recurrenceSplit: [ recurrence ]
          }
      }, (err, res) => {
        if (err) {
            console.error("sss",err);
            config.logger.error( '修改会议截止日期失败', item );
        } else {
            let log = resRecurrence.length > 1 ? recurrenceSplit: [ recurrence ];
            config.logger.info( '修改会议截止日期成功' + log, item );
            console.log("周期性会议修改成功",res,log);
        }
      }
  );
}
async function getRecurrence(calendar, item){
  try{
        let recurrence = await calendar.events.get(
            {
                auth: jwtClient,
                calendarId: item.organizer_email,
                eventId: item.recurringEventId,
            }
        )
        return recurrence
  }catch(e){
        calendar.events.delete(
            {
                auth: jwtClient,
                calendarId: item.room,
                eventId: item.recurringEventId,
            }, (err, res) => {
                if (err) {
                    console.error("sss",err.data);
                    config.logger.error( '会议室email释放会议失败', item );
                }else{
                    console.log("shanchu",res);
                    config.logger.info( '会议室email释放会议成功', item );
                }
            }
        )
      return false
  }
}

let recurrenceGoogleData = [{organizer_email: '....@anker.com',room: '...@resource.calendar.google.com',recurringEventId:'...'}];
for(let item of recurrenceGoogleData){
    main(item).catch(e => {
      console.error(e);
      throw e;
    });
}

复制代码

recurringEventId为周期性会议id; config.googleAuth是一个email,config.googleAuth不能为null。 如config.googleAuth为null,运行时会报错:

{[Error: Bad Request]
code: 400,
errors:
  [{ 
    domain: 'global',
    reason: 'failedPrecondition',
    message: 'Bad Request'
  }]
}
复制代码

例外一个需要注意的点是,如果用会议组织者的organizer_email通过get方法查找不到此会议,那么只能通过此会议的会议室email通过delete方法释放此会议室资源。 如有其他疑问,请留言,看到后会回复,谢谢

分类:
阅读
标签:
收藏成功!
已添加到「」, 点击更改