how to read an external public shared calendar via google api

139 Views Asked by At

I have the following basic code to read some calendar events. The calendar is publicly shared from a normal private google user.

To use google api, I set up a service account with owner role, just to make things simple at first. By using https://deno.land/x/google_deno_integration I try to make sure I'm using OAuth2, and according to this answer access is possible

import { GoogleAPI } from "https://deno.land/x/google_deno_integration/mod.ts";


const serviceAccountConfig = await Deno.readTextFile(Deno.env.get('GOOGLE_APPLICATION_CREDENTIALS'));
const credentials = JSON.parse(serviceAccountConfig);

const cal_id =  'my-cal-id';

const api = new GoogleAPI({
    email: "[email protected]",
    scope: ["https://www.googleapis.com/auth/calendar.events"],
    key: credentials.private_key,
});

const calget = await api.get(`https://www.googleapis.com/calendar/v3/calendars/${cal_id}`);
console.log(calget);

Unfortunately, this is the answer I get:

$ deno run --allow-env --allow-read --allow-net index.2.ts
{
  error: {
    code: 403,
    message: "Request had insufficient authentication scopes.",
    errors: [
      {
        message: "Insufficient Permission",
        domain: "global",
        reason: "insufficientPermissions"
      }
    ],
    status: "PERMISSION_DENIED",
    details: [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        reason: "ACCESS_TOKEN_SCOPE_INSUFFICIENT",
        domain: "googleapis.com",
        metadata: {
          service: "calendar-json.googleapis.com",
          method: "calendar.v3.Calendars.Get"
        }
      }
    ]
  }
}

I don't know if I'm settings my service account badly (first hour user of google cloud here), if what I'm doing is possible, or if it's something wrong I'm not considering.

1

There are 1 best solutions below

5
Linda Lawton - DaImTo On

Yes it you can use a service account to read a public calendars. The key to this is it must be public. Which means you are only going to have read only access to it.

In the below example. I am reading from the Danish holiday calendar.

// npm install googleapis@105 @google-cloud/[email protected] --save
// npm install googleapis

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

// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/calendar'];

const CREDENTIALS_PATH = 'C:\\Development\\FreeLance\\GoogleSamples\\Credentials\\ServiceAccountCred.json';

/**
 * Load or request or authorization to call APIs.
 *
 */
async function authorize() {
    let client =  new google.auth.GoogleAuth({keyFile: CREDENTIALS_PATH, scopes: SCOPES});
    return client
}

/**
 * Lists the next 10 events on a public holiday calendar.
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
async function listEvents(auth) {
    const calendar = google.calendar({version: 'v3', auth});
    const res = await calendar.events.list({
        calendarId: 'en.danish#[email protected]',
        timeMin: new Date().toISOString(),
        maxResults: 10,
        singleEvents: true,
        orderBy: 'startTime',
    });
    const events = res.data.items;
    if (!events || events.length === 0) {
        console.log('No upcoming events found.');
        return;
    }
    console.log('Upcoming 10 events:');
    events.map((event, i) => {
        const start = event.start.dateTime || event.start.date;
        console.log(`${start} - ${event.summary}`);
    });
}

authorize().then(listEvents).catch(console.error);

I really don't know why you would want to when you can use just use an API key and get the same result

const {google} = require('googleapis');
const API_KEY = "[REDACTED]"

/**
 * Lists the next 10 events on a public holiday calendar.
 */
async function listEvents() {
    const calendar = google.calendar({version: 'v3', auth: API_KEY});
    const res = await calendar.events.list({
        calendarId: 'en.danish#[email protected]',
        timeMin: new Date().toISOString(),
        maxResults: 10,
        singleEvents: true,
        orderBy: 'startTime',
    });
    const events = res.data.items;
    if (!events || events.length === 0) {
        console.log('No upcoming events found.');
        return;
    }
    console.log('Upcoming 10 events:');
    events.map((event, i) => {
        const start = event.start.dateTime || event.start.date;
        console.log(`${start} - ${event.summary}`);
    });
}

listEvents().catch(console.error);

Deno

If you check the doc you linked google_deno_integration it clearly states Google Integration and Authentication for Deno. This is not the same as authorization which is what you need.

There for I dont think that function will work.

If you are willing to use an api key then you just need to do a http get. Which appears to be possible using fetch

The url format is

GET https://www.googleapis.com/calendar/v3/calendars/[calendarId]/events?key=[APIKEY]

So using the example above for the Danish holiday calendar you have

https://www.googleapis.com/calendar/v3/calendars/en.danish%23holiday%40group.v.calendar.google.com/events?key=[APIKEY]

or

const response = await fetch("https://www.googleapis.com/calendar/v3/calendars/en.danish%23holiday%40group.v.calendar.google.com/events?key=[APIKEY]");
console.log(response.status);  // e.g. 200
console.log(response.statusText); // e.g. "OK"
const jsonData = await response.json();