Skip to content

Commit

Permalink
feat: allow to query on multiple calendars of same user (#30)
Browse files Browse the repository at this point in the history
* refactor: adapt/update js client

* chore: update justfile

* ci: adapt JS client lib's CI

* test: start adding integration tests for our use cases

* ci: fix actions/setup-node usage for pnpm

* ci: update actions + install pnpm

* ci: remove cache on actions/setup-node as it's pretty fast

* test: add more tests to integration tests

* refactor: rename file + adapt requirements' tests

* feat(users): allow to provide external userId (uuid)

* chore: fix formatting

* test(requirements): provide userUuid

* feat(js lib): add format + lint biome + fixes

* refactor: convert unix timestamps to datetimes

* fix: format & lint

* fix: missing imports

* fix: format

* fix: make client lib return Dates

* chore: add js docs to client lib

* chore: add more JSDocs

* feat: allow to query multiple calendars + allow to query on name + allow query metadata

* chore: remove debug logs

* feat: add query freebusy on multiple users + fix metadata

* refactor: extract convertEventDates & convertInstanceDates

* chore: remove comment

* chore: add more comments

* refactor: rework result of "freebusy of multiple users"

* fix: client lib multiple freebusy

* chore: remove duplicate requirements

* feat: allow to query on multiple calendars of same user

* fix: properly parse IDs + add test on TS client

* chore: remove unused module
  • Loading branch information
GuillaumeDecMeetsMore authored Aug 28, 2024
1 parent 2ab3fe5 commit ed01502
Show file tree
Hide file tree
Showing 15 changed files with 568 additions and 10 deletions.
4 changes: 4 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ _setup_client_node:
dev: _setup_db
cd scheduler && cargo run

# Prepare offline SQLx
prepare_sqlx:
cd scheduler && cargo sqlx prepare --workspace

# Run the tests on a temporary DB container
test:
bash ./scripts/run_tests.sh
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions scheduler/clients/javascript/lib/domain/calendarEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,18 @@ export interface CalendarEventInstance {
*/
busy: boolean
}

/**
* Calendar event with instances
*/
export type CalendarEventWithInstances = {
/**
* Event object
*/
event: CalendarEvent
/**
* List of instances of the event
* Especially useful for recurring events
*/
instances: CalendarEventInstance[]
}
69 changes: 67 additions & 2 deletions scheduler/clients/javascript/lib/userClient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
import type { CalendarEventInstance } from './domain/calendarEvent'
import type {
CalendarEvent,
CalendarEventInstance,
CalendarEventWithInstances,
} from './domain/calendarEvent'
import { APIResponse, NettuBaseClient } from './baseClient'
import type { Metadata } from './domain/metadata'
import type { User } from './domain/user'
import type { IntegrationProvider, UUID } from '.'
import { convertInstanceDates } from './helpers/datesConverters'
import {
convertEventDates,
convertInstanceDates,
} from './helpers/datesConverters'

/**
* Request to get events of multiple calendars
*/
type GetEventsOfMultipleCalendars = {
/**
* List of calendar ids to get events from
*/
calendarIds: UUID[]
/**
* Start time of the period to get events
* @format Date in UTC
*/
startTime: Date
/**
* End time of the period to get events
* @format Date in UTC
*/
endTime: Date
}

/**
* Request to get a user's freebusy
Expand Down Expand Up @@ -96,6 +123,13 @@ type UserResponse = {
user: User
}

/**
* Response when getting events of multiple calendars
*/
type GetEventsOfMultipleCalendarsResponse = {
events: CalendarEventWithInstances[]
}

/**
* Client for the user endpoints
* This is an admin client (usually backend)
Expand Down Expand Up @@ -138,6 +172,37 @@ export class NettuUserClient extends NettuBaseClient {
return this.delete<UserResponse>(`/user/${userId}`)
}

public async getEventsOfMultipleCalendars(
userId: UUID,
req: GetEventsOfMultipleCalendars
): Promise<APIResponse<GetEventsOfMultipleCalendarsResponse>> {
const res = await this.get<GetEventsOfMultipleCalendarsResponse>(
`/user/${userId}/events`,
{
calendarIds: req.calendarIds.join(','),
startTime: req.startTime.toISOString(),
endTime: req.endTime.toISOString(),
}
)

if (!res.data) {
return res
}

return {
res: res.res,
status: res.status,
data: {
events: res.data.events.map(event => {
return {
event: convertEventDates(event.event),
instances: event.instances.map(convertInstanceDates),
}
}),
}
}
}

public async freebusy(
userId: UUID,
req: GetUserFeebusyReq
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,79 @@ describe('Requirements', () => {
})
})

describe('A user calendar can be queried for availability', () => {
describe('A user can see the events during a timespan of all his calendars', () => {
let user1: User | undefined
let user1Calendar1: Calendar | undefined
let user1Calendar2: Calendar | undefined
let user1Calendar1Event1: CalendarEvent | undefined
let user1Calendar2Event1: CalendarEvent | undefined

beforeAll(async () => {
const res = await client?.user.create()
if (!res?.data) {
throw new Error('User not created')
}
expect(res?.status).toBe(201)
user1 = res.data.user

if (!user1) {
throw new Error('No user')
}
const resCal1 = await client?.calendar.create(user1.id, {
timezone: 'Asia/Tokyo',
})
expect(resCal1?.status).toBe(201)
user1Calendar1 = resCal1?.data?.calendar

const resCal2 = await client?.calendar.create(user1.id, {
timezone: 'Asia/Tokyo',
})
expect(resCal2?.status).toBe(201)
user1Calendar2 = resCal2?.data?.calendar

if (!user1 || !user1Calendar1 || !user1Calendar2) {
throw new Error('No user or calendar')
}
const resEvent1 = await client?.events.create(user1.id, {
calendarId: user1Calendar1.id,
duration: 1000 * 60 * 60,
startTime: new Date(0),
busy: true,
})
expect(resEvent1?.status).toBe(201)
user1Calendar1Event1 = resEvent1?.data?.event

const resEvent2 = await client?.events.create(user1.id, {
calendarId: user1Calendar2.id,
duration: 1000 * 60 * 60,
startTime: new Date(1000 * 60 * 60),
busy: true,
})
expect(resEvent2?.status).toBe(201)
user1Calendar2Event1 = resEvent2?.data?.event
})

it('should list the events in the calendars', async () => {
if (!user1 || !user1Calendar1 || !user1Calendar2) {
throw new Error('One or both calendars are missing')
}
const startTime = new Date(10)
const endTime = new Date(1000 * 60 * 60 * 24 * 4)

const res = await client?.user.getEventsOfMultipleCalendars(user1.id, {
calendarIds: [user1Calendar1.id, user1Calendar2.id],
startTime,
endTime,
})
expect(res?.status).toBe(200)
expect(res?.data).toBeDefined()
expect(res?.data?.events.length).toBe(2)
expect(res?.data?.events[0].event.id).toEqual(user1Calendar1Event1?.id)
expect(res?.data?.events[1].event.id).toEqual(user1Calendar2Event1?.id)
})
})

describe('A user calendar can be queried for availability (freebusy)', () => {
let user1: User | undefined
let user1Calendar1: Calendar | undefined
let user1Calendar1Event1: CalendarEvent | undefined
Expand Down Expand Up @@ -382,11 +454,7 @@ describe('Requirements', () => {
})
})

describe('A user can be in groups', () => {
it.todo('To be implemented')
})

describe('Multiple calendars of the same user can be queried at once', () => {
describe('Multiple calendars of the same user can be queried at once (freebusy)', () => {
let user1: User | undefined
let user1Calendar1: Calendar | undefined
let user1Calendar1Event1: CalendarEvent | undefined
Expand Down
Loading

0 comments on commit ed01502

Please sign in to comment.