Skip to content

Commit

Permalink
commit to establish the first working layer of google calendar api
Browse files Browse the repository at this point in the history
  • Loading branch information
MistahSanta committed Sep 2, 2024
1 parent 28d015c commit 18726e8
Show file tree
Hide file tree
Showing 9 changed files with 550 additions and 163 deletions.
385 changes: 339 additions & 46 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"dotenv": "^16.0.3",
"drizzle-orm": "^0.28.5",
"drizzle-zod": "^0.5.1",
"google-auth-library": "^9.14.0",
"googleapis": "^143.0.0",
"next": "^14.2.2",
"next-auth": "^4.23.0",
"postgres": "^3.3.5",
Expand Down
10 changes: 0 additions & 10 deletions src/app/api/googleCalendar.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/components/events/EventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import EventLikeButton from '../EventLikeButton';
import { getServerAuthSession } from '@src/server/auth';
import AddToCalendarButton from './calendar/AddToCalendarButton';
import AddToCalendarAuthorizedButton from './calendar/AddToCalendarAuthorizedButton';

import { api } from '@src/trpc/server';

type EventCardProps = {
event: RouterOutputs['event']['findByFilters']['events'][number];
Expand Down Expand Up @@ -67,7 +67,7 @@ const HorizontalCard = async ({

)}
{ session
? <AddToCalendarAuthorizedButton event={event} session={session} />
? <AddToCalendarAuthorizedButton event={event} tokens={ await api.account.getToken({ userId: session.user.id, provider:"google"}) } />
: <AddToCalendarButton event={event} /> }

<Link
Expand Down
67 changes: 16 additions & 51 deletions src/components/events/calendar/AddToCalendarAuthorizedButton.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,33 @@
"use client";

import { type RouterOutputs } from '@src/trpc/shared';
import { CalendarButton } from '@src/icons/Icons';
import { type Session } from 'next-auth';
import { useState } from 'react'


import { api } from '@src/trpc/react';


export const AddToCalendarAuthorizedButton = ({
event, session
event, tokens
}: {
event: RouterOutputs['event']['findByFilters']['events'][number];
session: Session;
tokens: {access_token: string, refresh_token: string | null, id_token: string | null};
}) => {
const [status, setStatus] = useState('')

// This function will check if the user is authenticated and auto add the desired event to their google calendara
const createGoogleCalenderEvent = async(session: Session, event : RouterOutputs['event']['findByFilters']['events'][number] ) => {
// Assume that session is already active since the user was able to reach this button
if (session.user && session.user.access_token ) {

const {name, startTime, endTime, description, location} = event
try {
const response = await fetch('/api/calendaar/add-event', {
method:'POST',
headers: {
'Content-Type': 'applicaation/json',
'Authorization': `Bearer ${session.user.access_token}`
},
body: JSON.stringify({
name,
startTime,
endTime,
description,
location
})
})

if (!response.ok) {
throw new Error("failed to add event to calendar!")
}

const data = await response.json();
setStatus("Event added successfully!")
console.log("Event added successfully!")
} catch( e ) {

setStatus(`Error when trying to add event: ${e}`)
console.log(`Error when trying to add event: ${e}`)
}
} else
{ // TODO redirect to the regular button sign in or ask if user want to sign in
setStatus("User is not authenticated!")
console.log("User is not authenicated!")
}
}

const {mutate} = api.calendar.addEvent.useMutation({
onSuccess: () => console.log("Successfully added event!"),
})

// TODO onClick error with async function - incompatible types.
return (
<main className="relative flex h-10 w-10 cursor-pointer items-center justify-center rounded-full shadow-md inset-0 overflow-visible">

<button onClick={createGoogleCalenderEvent(session, event)}>
<CalendarButton /> auth
<button onClick={ () => mutate({
eventName: event.name,
startTime: event.startTime,
endTime: event.endTime,
description: event.description,
location: event.location,
tokens: tokens,
})}>
<CalendarButton /> auth
</button>
</main>
);
Expand Down
5 changes: 4 additions & 1 deletion src/server/api/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { eventRouter } from './routers/event';
import { userMetadataRouter } from './routers/userMetadata';
import { formRouter } from './routers/form';
import { adminRouter } from './routers/admin';

import { accountRouter } from './routers/account';
import { calendarRouter } from './routers/calendar';
/**
* This is the primary router for your server.
*
Expand All @@ -16,6 +17,8 @@ export const appRouter = createTRPCRouter({
userMetadata: userMetadataRouter,
form: formRouter,
admin: adminRouter,
account: accountRouter,
calendar: calendarRouter,
});

// export type definition of API
Expand Down
49 changes: 49 additions & 0 deletions src/server/api/routers/account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

import { z } from 'zod';
import { createTRPCRouter, publicProcedure } from '../trpc';
import { and, eq } from 'drizzle-orm';
import { accounts } from '@src/server/db/schema/users';


const byUserID = z.object({
userId: z.string(),
provider: z.string()
})

export const accountRouter = createTRPCRouter({
getToken: publicProcedure
.input(byUserID)
.query( async({input, ctx}) => {

const { userId, provider } = input;

try {
const account = await ctx.db.query.accounts.findFirst({
where: and(
eq( accounts.provider, provider ),
eq( accounts.userId, userId ),
)
})

if ( ! account ) {
console.error("Unable to find account in the database!")
throw new Error("Unable to find Acccount in database!")
}
if ( ! account.access_token ) {
console.log("Access token for user is empty!")
account.access_token = ""
}

const token = {
access_token: account.access_token,
refresh_token: account.refresh_token,
id_token: account.id_token,
}

return token
} catch ( error ) {
console.error("Error when trying to grab account from database: ", error)
throw error
}
}),
});
65 changes: 65 additions & 0 deletions src/server/api/routers/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

import { z } from 'zod';
import { createTRPCRouter, publicProcedure } from '../trpc';
import { google } from 'googleapis';
import { type OAuth2Client } from 'google-auth-library';
import { env } from '@src/env.mjs';

const byEventDetails = z.object({
eventName: z.string(),
startTime: z.date(),
endTime: z.date(),
location: z.string().nullable(),
description: z.string(),
tokens: z.object({
access_token: z.string(),
refresh_token: z.string().nullable(),
id_token: z.string().nullable(),
})

})

export const calendarRouter = createTRPCRouter({
addEvent: publicProcedure
.input(byEventDetails)
.mutation( ( {input} ) => {
console.log(input)
const { eventName, startTime, endTime, location, tokens, description } = input;

const oauth2Client : OAuth2Client = new google.auth.OAuth2({
clientId: env.GOOGLE_CLIENT_ID ,
clientSecret: env.GOOGLE_CLIENT_SECRET,
redirectUri: env.NEXTAUTH_URL,
})


oauth2Client.setCredentials( {access_token: tokens.access_token, refresh_token: tokens.refresh_token, id_token: tokens.id_token });

const calendar = google.calendar({ version: 'v3', auth: oauth2Client });

try {
const response = calendar.events.insert({
calendarId: 'primary',
requestBody: {
summary: eventName,
description: description,
location: location,
start: {
dateTime: startTime
},
end: {
dateTime: endTime
},
}
})

return response
} catch( error) {
console.error("Recieved an error when trying to add event to calendar: ", e)
throw error
}



}),
});
Loading

0 comments on commit 18726e8

Please sign in to comment.