diff --git a/package.json b/package.json index 2e57474..579e3ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cobot-zapier", - "version": "2.0.1", + "version": "2.1.0", "description": "", "main": "index.js", "scripts": { diff --git a/src/authentication.ts b/src/authentication.ts index 819ec78..0b64577 100644 --- a/src/authentication.ts +++ b/src/authentication.ts @@ -7,7 +7,7 @@ const AUTHORIZE_URL = `${BASE_URL}/oauth/authorize`; const ACCESS_TOKEN_URL = `${BASE_URL}/oauth/access_token`; const TEST_AUTH_URL = `${BASE_URL}/api/user`; const scopes = - "read read_admins read_bookings read_external_bookings read_memberships read_resources read_spaces read_user write_activities write_subscriptions"; + "read read_admins read_bookings read_events read_external_bookings read_memberships read_resources read_spaces read_user write_activities write_subscriptions"; const getAccessToken = async ( z: ZObject, diff --git a/src/index.ts b/src/index.ts index 2dfe233..72e98ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import triggerMembershipConfirmed from "./triggers/triggerMembershipConfirmed"; import triggerMembershipPlanChanged from "./triggers/triggerMembershipPlanChanged"; import triggerExternalBooking from "./triggers/triggerExternalBookingCreated"; import getSubdomains from "./triggers/dropdowns/getSubdomains"; +import triggerEventPublished from "./triggers/triggerEventPublished"; const { version } = require("../package.json"); @@ -30,6 +31,7 @@ export default { [triggerBookingWillBegin.key]: triggerBookingWillBegin, [triggerMembershipConfirmed.key]: triggerMembershipConfirmed, [triggerMembershipPlanChanged.key]: triggerMembershipPlanChanged, + [triggerEventPublished.key]: triggerEventPublished, [triggerExternalBooking.key]: triggerExternalBooking, // Lists for dropdowns [getSubdomains.key]: getSubdomains, diff --git a/src/test/triggers/triggerBookingCreated.test.ts b/src/test/triggers/triggerBookingCreated.test.ts index b4b7bc5..a7cf79a 100644 --- a/src/test/triggers/triggerBookingCreated.test.ts +++ b/src/test/triggers/triggerBookingCreated.test.ts @@ -3,6 +3,7 @@ import * as nock from "nock"; import App from "../../index"; import { prepareMocksForWebhookSubscribeTest } from "../utils/prepareMocksForWebhookSubscribeTest"; import triggerBookingCreated from "../../triggers/triggerBookingCreated"; +import { HookTrigger } from "../../types/trigger"; const appTester = createAppTester(App); nock.disableNetConnect(); @@ -10,12 +11,12 @@ nock.disableNetConnect(); afterEach(() => nock.cleanAll()); describe("triggerBookingCreated", () => { - it("creates new webhook upon through CM API upon subscribe", async () => { + it("creates new webhook through CM API upon subscribe", async () => { const bundle = prepareMocksForWebhookSubscribeTest( triggerBookingCreated.key, ); - const subscribe = - App.triggers[triggerBookingCreated.key].operation.performSubscribe; + const subscribe = (App.triggers[triggerBookingCreated.key] as HookTrigger) + .operation.performSubscribe; const result = await appTester(subscribe as any, bundle as any); diff --git a/src/test/triggers/triggerBookingWillBegin.test.ts b/src/test/triggers/triggerBookingWillBegin.test.ts index 89374e7..75eafeb 100644 --- a/src/test/triggers/triggerBookingWillBegin.test.ts +++ b/src/test/triggers/triggerBookingWillBegin.test.ts @@ -3,6 +3,7 @@ import * as nock from "nock"; import App from "../../index"; import { prepareMocksForWebhookSubscribeTest } from "../utils/prepareMocksForWebhookSubscribeTest"; import triggerBookingWillBegin from "../../triggers/triggerBookingWillBegin"; +import { HookTrigger } from "../../types/trigger"; const appTester = createAppTester(App); nock.disableNetConnect(); @@ -10,12 +11,12 @@ nock.disableNetConnect(); afterEach(() => nock.cleanAll()); describe("triggerBookingWillBegin", () => { - it("creates new webhook upon through CM API upon subscribe", async () => { + it("creates new webhook through CM API upon subscribe", async () => { const bundle = prepareMocksForWebhookSubscribeTest( triggerBookingWillBegin.key, ); - const subscribe = - App.triggers[triggerBookingWillBegin.key].operation.performSubscribe; + const subscribe = (App.triggers[triggerBookingWillBegin.key] as HookTrigger) + .operation.performSubscribe; const result = await appTester(subscribe as any, bundle as any); diff --git a/src/test/triggers/triggerEventPublished.test.ts b/src/test/triggers/triggerEventPublished.test.ts new file mode 100644 index 0000000..7346fb8 --- /dev/null +++ b/src/test/triggers/triggerEventPublished.test.ts @@ -0,0 +1,94 @@ +import { createAppTester } from "zapier-platform-core"; +import * as nock from "nock"; +import App from "../../index"; +import { + prepareBundle, + prepareMocksForWebhookSubscribeTest, +} from "../utils/prepareMocksForWebhookSubscribeTest"; +import triggerEventPublished from "../../triggers/triggerEventPublished"; +import { HookTrigger } from "../../types/trigger"; +import { UserApiResponse, EventApiResponse } from "../../types/api-responses"; + +const appTester = createAppTester(App); +nock.disableNetConnect(); +const trigger = App.triggers[triggerEventPublished.key] as HookTrigger; + +afterEach(() => nock.cleanAll()); + +describe("triggerEventPublished", () => { + it("creates new webhook through CM API upon subscribe", async () => { + const bundle = prepareMocksForWebhookSubscribeTest( + triggerEventPublished.key, + ); + const subscribe = trigger.operation.performSubscribe; + + const result = await appTester(subscribe as any, bundle as any); + + expect(result).toMatchInlineSnapshot(` +{ + "url": "https://trial.cobot.me/api/event/callback", +} +`); + }); + + it("lists recent events", async () => { + const bundle = prepareBundle(); + const userResponse: UserApiResponse = { + included: [{ id: "space-1", attributes: { subdomain: "trial" } }], + }; + const imageItem = { + url: "https://www.example.com/image.png", + width: 100, + height: 100, + }; + const eventResponse: EventApiResponse = { + id: "1", + attributes: { + title: "event 1", + from: "2024-12-20T06:22:29+01:00", + to: "2024-12-20T08:22:29+01:00", + description: "event 1 description", + tags: ["free"], + videoUrl: "https://www.youtube.com/watch?v=123", + capacity: 12, + publicUrl: "https://trial.cobot.me/events/1", + audience: "membersOnly", + color: "#ff0000", + image: { + default: imageItem, + small: imageItem, + icon: imageItem, + medium: imageItem, + large: imageItem, + }, + }, + }; + + const scope = nock("https://api.cobot.me"); + scope.get("/user?include=adminOf").reply(200, userResponse); + scope + .get(/\/spaces\/space-1\/events/) + .reply(200, { data: [eventResponse] }); + + const listRecentEvents = trigger.operation.performList; + + const results = await appTester(listRecentEvents as any, bundle as any); + + expect(results).toStrictEqual([ + { + audience: "membersOnly", + capacity: 12, + color: "#ff0000", + description: "event 1 description", + from: "2024-12-20T05:22:29.000Z", + id: "1", + image_url: "https://www.example.com/image.png", + public_url: "https://trial.cobot.me/events/1", + tags: ["free"], + title: "event 1", + to: "2024-12-20T07:22:29.000Z", + video_url: "https://www.youtube.com/watch?v=123", + }, + ]); + }); +}); diff --git a/src/test/triggers/triggerExternalBooking.test.ts b/src/test/triggers/triggerExternalBooking.test.ts index 57a37ce..7657229 100644 --- a/src/test/triggers/triggerExternalBooking.test.ts +++ b/src/test/triggers/triggerExternalBooking.test.ts @@ -11,17 +11,18 @@ import { ResourceApiResponse, UserApiResponse, } from "../../types/api-responses"; +import { HookTrigger } from "../../types/trigger"; const appTester = createAppTester(App); nock.disableNetConnect(); +const trigger = App.triggers[triggerExternalBooking.key] as HookTrigger; afterEach(() => nock.cleanAll()); describe("triggerExternalBooking", () => { - it("creates new webhook upon through CM API upon subscribe", async () => { + it("creates new webhook through CM API upon subscribe", async () => { const bundle = prepareMocksForWebhookSubscribeTest("created_booking"); - const subscribe = - App.triggers[triggerExternalBooking.key].operation.performSubscribe; + const subscribe = trigger.operation.performSubscribe; const result = await appTester(subscribe as any, bundle as any); @@ -89,8 +90,7 @@ describe("triggerExternalBooking", () => { .get(/\/spaces\/space-1\/resources/) .reply(200, { data: [resourceResponse] }); - const listRecentExternalBookings = - App.triggers[triggerExternalBooking.key].operation.performList; + const listRecentExternalBookings = trigger.operation.performList; const results = await appTester( listRecentExternalBookings as any, @@ -120,7 +120,7 @@ describe("triggerExternalBooking", () => { ]); }); - it("returns no booking if no matcyhing resource is found", async () => { + it("returns no booking if no matching resource is found", async () => { const bundle = prepareBundle(); const userResponse: UserApiResponse = { included: [{ id: "space-1", attributes: { subdomain: "trial" } }], @@ -177,8 +177,7 @@ describe("triggerExternalBooking", () => { .get(/\/spaces\/space-1\/resources/) .reply(200, { data: [resourceResponse] }); - const listRecentExternalBookings = - App.triggers[triggerExternalBooking.key].operation.performList; + const listRecentExternalBookings = trigger.operation.performList; const results = await appTester( listRecentExternalBookings as any, diff --git a/src/test/triggers/triggerMembershipConfirmed.test.ts b/src/test/triggers/triggerMembershipConfirmed.test.ts index e1f714f..be00711 100644 --- a/src/test/triggers/triggerMembershipConfirmed.test.ts +++ b/src/test/triggers/triggerMembershipConfirmed.test.ts @@ -3,6 +3,7 @@ import * as nock from "nock"; import App from "../../index"; import { prepareMocksForWebhookSubscribeTest } from "../utils/prepareMocksForWebhookSubscribeTest"; import triggerMembershipConfirmed from "../../triggers/triggerMembershipConfirmed"; +import { HookTrigger } from "../../types/trigger"; const appTester = createAppTester(App); nock.disableNetConnect(); @@ -10,12 +11,13 @@ nock.disableNetConnect(); afterEach(() => nock.cleanAll()); describe("triggerMembershipConfirmed", () => { - it("creates new webhook upon through CM API upon subscribe", async () => { + it("creates new webhook through CM API upon subscribe", async () => { const bundle = prepareMocksForWebhookSubscribeTest( triggerMembershipConfirmed.key, ); - const subscribe = - App.triggers[triggerMembershipConfirmed.key].operation.performSubscribe; + const subscribe = ( + App.triggers[triggerMembershipConfirmed.key] as HookTrigger + ).operation.performSubscribe; const result = await appTester(subscribe as any, bundle as any); diff --git a/src/test/triggers/triggerMembershipPlanChanged.test.ts b/src/test/triggers/triggerMembershipPlanChanged.test.ts index e4c542f..487cdce 100644 --- a/src/test/triggers/triggerMembershipPlanChanged.test.ts +++ b/src/test/triggers/triggerMembershipPlanChanged.test.ts @@ -3,6 +3,7 @@ import * as nock from "nock"; import App from "../../index"; import { prepareMocksForWebhookSubscribeTest } from "../utils/prepareMocksForWebhookSubscribeTest"; import triggerMembershipPlanChanged from "../../triggers/triggerMembershipPlanChanged"; +import { HookTrigger } from "../../types/trigger"; const appTester = createAppTester(App); nock.disableNetConnect(); @@ -10,12 +11,13 @@ nock.disableNetConnect(); afterEach(() => nock.cleanAll()); describe("triggerMembershipPlanChanged", () => { - it("creates new webhook upon through CM API upon subscribe", async () => { + it("creates new webhook through CM API upon subscribe", async () => { const bundle = prepareMocksForWebhookSubscribeTest( triggerMembershipPlanChanged.key, ); - const subscribe = - App.triggers[triggerMembershipPlanChanged.key].operation.performSubscribe; + const subscribe = ( + App.triggers[triggerMembershipPlanChanged.key] as HookTrigger + ).operation.performSubscribe; const result = await appTester(subscribe as any, bundle as any); diff --git a/src/triggers/dropdowns/getSubdomains.ts b/src/triggers/dropdowns/getSubdomains.ts index 56cba34..163edad 100644 --- a/src/triggers/dropdowns/getSubdomains.ts +++ b/src/triggers/dropdowns/getSubdomains.ts @@ -2,6 +2,7 @@ import { ZObject } from "zapier-platform-core"; import { KontentBundle } from "../../types/kontentBundle"; import { OutputField } from "../../fields/output/outputField"; import { OutputFromOutputFields } from "../../fields/output/outputFromOutputFields"; +import { PollingTrigger } from "../../types/trigger"; const execute = (z: ZObject, bundle: KontentBundle<{}>): Output => { return bundle.authData.adminOf @@ -26,7 +27,7 @@ const outputFields = [ type Output = ReadonlyArray>; -export default { +const trigger: PollingTrigger = { key: "get_subdomains", noun: "Subdomain choice", display: { @@ -43,4 +44,5 @@ export default { }, outputFields, }, -} as const; +}; +export default trigger; diff --git a/src/triggers/triggerBookingCreated.ts b/src/triggers/triggerBookingCreated.ts index c073e26..63591b6 100644 --- a/src/triggers/triggerBookingCreated.ts +++ b/src/triggers/triggerBookingCreated.ts @@ -12,6 +12,7 @@ import { bookingSample } from "../utils/samples"; import { BookingOutput } from "../types/outputs"; import { apiResponseToBookingOutput } from "../utils/api-to-output"; import { BookingApiResponse } from "../types/api-responses"; +import { HookTrigger } from "../types/trigger"; const hookLabel = "Booking Created"; const event = "created_booking"; @@ -49,7 +50,7 @@ async function parsePayload( } } -export default { +const trigger: HookTrigger = { key: event, noun: hookLabel, display: { @@ -74,4 +75,5 @@ export default { }, sample: bookingSample, }, -} as const; +}; +export default trigger; diff --git a/src/triggers/triggerBookingWillBegin.ts b/src/triggers/triggerBookingWillBegin.ts index cd0b01c..ea1cb3e 100644 --- a/src/triggers/triggerBookingWillBegin.ts +++ b/src/triggers/triggerBookingWillBegin.ts @@ -12,6 +12,7 @@ import { apiResponseToBookingOutput } from "../utils/api-to-output"; import { BookingOutput } from "../types/outputs"; import { bookingSample } from "../utils/samples"; import { BookingApiResponse } from "../types/api-responses"; +import { HookTrigger, Trigger } from "../types/trigger"; const hookLabel = "Booking Will Begin"; const event = "booking_will_begin"; @@ -49,7 +50,7 @@ async function parsePayload( } } -export default { +const trigger: HookTrigger = { key: event, noun: hookLabel, display: { @@ -74,4 +75,5 @@ export default { }, sample: bookingSample, }, -} as const; +}; +export default trigger; diff --git a/src/triggers/triggerEventPublished.ts b/src/triggers/triggerEventPublished.ts new file mode 100644 index 0000000..b827561 --- /dev/null +++ b/src/triggers/triggerEventPublished.ts @@ -0,0 +1,81 @@ +import { ZObject } from "zapier-platform-core"; +import { KontentBundle } from "../types/kontentBundle"; +import { + apiCallUrl, + listRecentEvents, + subscribeHook, + unsubscribeHook, +} from "../utils/api"; +import { SubscribeBundleInputType } from "../types/subscribeType"; +import { getSubdomainField } from "../fields/getSudomainsField"; +import { EventOutput } from "../types/outputs"; +import { EventApiResponse } from "../types/api-responses"; +import { apiResponseToEventOutput } from "../utils/api-to-output"; +import { eventSample } from "../utils/samples"; +import { HookTrigger, Trigger } from "../types/trigger"; + +const hookLabel = "Event Published"; +const event = "published_event"; + +async function subscribeHookExecute( + z: ZObject, + bundle: KontentBundle, +) { + return subscribeHook(z, bundle, { + event, + callback_url: bundle.targetUrl ?? "", + }); +} + +async function unsubscribeHookExecute( + z: ZObject, + bundle: KontentBundle, +) { + const webhook = bundle.subscribeData; + return unsubscribeHook(z, bundle, webhook?.id ?? ""); +} + +async function parsePayload( + z: ZObject, + bundle: KontentBundle<{}>, +): Promise { + if (bundle.cleanedRequest) { + const event = (await apiCallUrl( + z, + bundle.cleanedRequest.url, + )) as EventApiResponse; + return [apiResponseToEventOutput(event)]; + } else { + return []; + } +} + +const trigger: HookTrigger = { + key: event, + noun: hookLabel, + display: { + label: hookLabel, + description: "Triggers when an admin publishes an event.", + }, + operation: { + type: "hook", + + inputFields: [getSubdomainField()], + + performSubscribe: subscribeHookExecute, + performUnsubscribe: unsubscribeHookExecute, + + perform: parsePayload, + performList: async ( + z: ZObject, + bundle: KontentBundle, + ): Promise => { + const apiEvents = await listRecentEvents(z, bundle); + return apiEvents.map((e: EventApiResponse) => + apiResponseToEventOutput(e), + ); + }, + sample: eventSample, + }, +}; +export default trigger; diff --git a/src/triggers/triggerExternalBookingCreated.ts b/src/triggers/triggerExternalBookingCreated.ts index 55ca184..6635f3a 100644 --- a/src/triggers/triggerExternalBookingCreated.ts +++ b/src/triggers/triggerExternalBookingCreated.ts @@ -12,6 +12,7 @@ import { getSubdomainField } from "../fields/getSudomainsField"; import { externalBookingSample } from "../utils/samples"; import { ExternalBookingOutput } from "../types/outputs"; import { apiResponseToExternalBookingOutput } from "../utils/api-to-output"; +import { HookTrigger } from "../types/trigger"; const hookLabel = "External Booking Created"; const event = "created_booking"; @@ -47,7 +48,7 @@ async function parsePayload( } } -export default { +const trigger: HookTrigger = { key: `${event}_external`, noun: hookLabel, display: { @@ -73,4 +74,5 @@ export default { sample: externalBookingSample, }, -} as const; +}; +export default trigger; diff --git a/src/triggers/triggerMembershipConfirmed.ts b/src/triggers/triggerMembershipConfirmed.ts index d00be30..5f65a78 100644 --- a/src/triggers/triggerMembershipConfirmed.ts +++ b/src/triggers/triggerMembershipConfirmed.ts @@ -11,6 +11,7 @@ import { getSubdomainField } from "../fields/getSudomainsField"; import { MembershipOutput } from "../types/outputs"; import { apiResponseToMembershipOutput } from "../utils/api-to-output"; import { membershipSample } from "../utils/samples"; +import { HookTrigger } from "../types/trigger"; const hookLabel = "Membership Confirmed"; const event = "confirmed_membership"; @@ -45,7 +46,7 @@ async function parsePayload( } } -export default { +const trigger: HookTrigger = { key: event, noun: hookLabel, display: { @@ -71,4 +72,5 @@ export default { sample: membershipSample, }, -} as const; +}; +export default trigger; diff --git a/src/triggers/triggerMembershipPlanChanged.ts b/src/triggers/triggerMembershipPlanChanged.ts index 7659a66..7ae0f12 100644 --- a/src/triggers/triggerMembershipPlanChanged.ts +++ b/src/triggers/triggerMembershipPlanChanged.ts @@ -11,6 +11,7 @@ import { getSubdomainField } from "../fields/getSudomainsField"; import { MembershipOutput } from "../types/outputs"; import { apiResponseToMembershipOutput } from "../utils/api-to-output"; import { membershipSample } from "../utils/samples"; +import { HookTrigger } from "../types/trigger"; const hookLabel = "Membership Plan Change Date Reached"; const event = "membership_plan_change_date_reached"; @@ -45,7 +46,7 @@ async function parsePayload( } } -export default { +const trigger: HookTrigger = { key: event, noun: hookLabel, display: { @@ -71,4 +72,5 @@ export default { sample: membershipSample, }, -} as const; +}; +export default trigger; diff --git a/src/types/api-responses.d.ts b/src/types/api-responses.d.ts index 4cc70a2..c2de0e3 100644 --- a/src/types/api-responses.d.ts +++ b/src/types/api-responses.d.ts @@ -28,6 +28,37 @@ type Amount = { }[]; }; +type PhotoItem = { + url: string; + width: number; + height: number; +}; + +type Photo = { + icon: PhotoItem; + default: PhotoItem; + small: PhotoItem; + medium: PhotoItem; + large: PhotoItem; +}; + +export type EventApiResponse = { + id: string; + attributes: { + title: string; + from: string; + to: string; + description: string | null; + tags: string[]; + videoUrl: string | null; + capacity: number | null; + publicUrl: string | null; + audience: "membersOnly" | "public"; + color: string; + image: Photo | null; + }; +}; + export type ExternalBookingStatus = "approved" | "pending" | "canceled"; export type ExternalBookingApiResponse = { diff --git a/src/types/outputs.d.ts b/src/types/outputs.d.ts index e82cfb8..9712533 100644 --- a/src/types/outputs.d.ts +++ b/src/types/outputs.d.ts @@ -13,6 +13,21 @@ export type BookingOutput = { member_name: string | null; }; +export type EventOutput = { + id: string; + title: string; + from: string; + to: string; + description: string | null; + tags: string[]; + video_url: string | null; + capacity: number | null; + public_url: string | null; + audience: "membersOnly" | "public"; + color: string; + image_url: string | null; +}; + export type ExternalBookingOutput = { id: string; from: string; diff --git a/src/types/trigger.d.ts b/src/types/trigger.d.ts new file mode 100644 index 0000000..6d5514b --- /dev/null +++ b/src/types/trigger.d.ts @@ -0,0 +1,43 @@ +import { ZObject } from "zapier-platform-core"; +import { Field } from "../fields/field"; +import { KontentBundle } from "./kontentBundle"; +import { SubscribeBundleInputType } from "./subscribeType"; + +export type HookOperation = { + type: "hook"; + inputFields: Field[]; + performSubscribe: ( + z: ZObject, + bundle: KontentBundle, + ) => Promise; + performUnsubscribe: ( + z: ZObject, + bundle: KontentBundle, + ) => Promise; + perform: (z: ZObject, bundle: KontentBundle<{}>) => Promise; + performList: ( + z: ZObject, + bundle: KontentBundle, + ) => Promise; + sample: unknown; +}; + +export type PollingOperation = { + type: "polling"; + perform: (z: ZObject, bundle: KontentBundle<{}>) => unknown; + sample: unknown; + outputFields: ReadonlyArray; +}; + +type Trigger = Readonly<{ + key: string; + noun: string; + display: { + label: string; + description: string; + hidden?: boolean; + }; +}>; + +export type HookTrigger = Trigger & { operation: HookOperation }; +export type PollingTrigger = Trigger & { operation: PollingOperation }; diff --git a/src/utils/api-to-output.ts b/src/utils/api-to-output.ts index 2357ed9..9aca2aa 100644 --- a/src/utils/api-to-output.ts +++ b/src/utils/api-to-output.ts @@ -1,9 +1,11 @@ import { BookingApiResponse, + EventApiResponse, MembershipApiResponse, } from "../types/api-responses"; import { BookingOutput, + EventOutput, ExternalBookingOutput, MembershipOutput, } from "../types/outputs"; @@ -23,6 +25,24 @@ export function apiResponseToMembershipOutput( }; } +export function apiResponseToEventOutput(event: EventApiResponse): EventOutput { + const attributes = event.attributes; + return { + id: event.id, + title: attributes.title, + from: timeToIso8601(attributes.from), + to: timeToIso8601(attributes.to), + description: attributes.description, + tags: attributes.tags, + video_url: attributes.videoUrl, + capacity: attributes.capacity, + public_url: attributes.publicUrl, + audience: attributes.audience, + color: attributes.color, + image_url: attributes.image?.default.url || null, + }; +} + export function apiResponseToBookingOutput( booking: BookingApiResponse, ): BookingOutput { diff --git a/src/utils/api.ts b/src/utils/api.ts index b45e4c4..6f0f7a0 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -9,17 +9,36 @@ import { get } from "lodash"; import { DateTime } from "luxon"; import { BookingApiResponse, + EventApiResponse, ExternalBookingApiResponse, MembershipApiResponse, ResourceApiResponse, UserApiResponse, } from "../types/api-responses"; +type Space = { + id: string; + attributes: { + subdomain: string; + }; +}; + +const spaceForSubdomain = async ( + z: ZObject, + subdomain: string, +): Promise => { + const userV2 = await getUserDetailV2(z); + var space = userV2.included.find( + (x) => get(x, "attributes.subdomain", "") === subdomain, + ); + return space; +}; + export const subscribeHook = async ( z: ZObject, bundle: KontentBundle, data: SubscribePayloadType, -) => { +): Promise => { const url = `https://${bundle.inputData.subdomain}.cobot.me/api/subscriptions`; const response = await z.request({ url, @@ -34,7 +53,7 @@ export const unsubscribeHook = async ( z: ZObject, bundle: KontentBundle, subscribeId: string, -) => { +): Promise => { const url = `https://${bundle.inputData.subdomain}.cobot.me/api/subscriptions/${subscribeId}`; return await z.request({ url, @@ -72,6 +91,34 @@ export const listRecentBookings = async ( return response.data; }; +export const listRecentEvents = async ( + z: ZObject, + bundle: KontentBundle, +): Promise => { + const subdomain = bundle.inputData.subdomain; + const space = await spaceForSubdomain(z, subdomain); + if (space) { + const spaceId = space.id; + + const [from, to] = getDateRange(); + const response = await z.request({ + url: `https://api.cobot.me/spaces/${spaceId}/events`, + method: "GET", + headers: { + Accept: "application/vnd.api+json", + }, + params: { + "filter[from]": from, + "filter[to]": to, + "filter[sortOrder]": "desc", + "filter[publishedState]": "published", + }, + }); + return response.data.data as EventApiResponse[]; + } + return []; +}; + export const listMemberships = async ( z: ZObject, bundle: KontentBundle, @@ -100,10 +147,7 @@ export const listRecentExternalBookings = async ( bundle: KontentBundle, ): Promise => { const subdomain = bundle.inputData.subdomain; - const userV2 = await getUserDetailV2(z); - var space = userV2.included.find( - (x) => get(x, "attributes.subdomain", "") === subdomain, - ); + const space = await spaceForSubdomain(z, subdomain); if (space) { const spaceId = space.id; @@ -130,13 +174,14 @@ export const listRecentExternalBookings = async ( }, }) ).data.data as ResourceApiResponse[]; - const resourcesById = resourcesResponse.reduce( - (acc, resource) => ({ - ...acc, - [resource.id]: resource, - }), - {}, - ); + const resourcesById: { [propName: string]: ResourceApiResponse | null } = + resourcesResponse.reduce( + (acc, resource) => ({ + ...acc, + [resource.id]: resource, + }), + {}, + ); return bookings .map((booking) => { @@ -149,11 +194,15 @@ export const listRecentExternalBookings = async ( resource, }; }) - .filter((x) => x); + .filter(notEmpty); } return []; }; +function notEmpty(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + export type ExternalBookingWithResourceApiResponse = ExternalBookingApiResponse & { resource: ResourceApiResponse }; diff --git a/src/utils/samples.ts b/src/utils/samples.ts index ed56349..e658b46 100644 --- a/src/utils/samples.ts +++ b/src/utils/samples.ts @@ -1,5 +1,6 @@ import { BookingOutput, + EventOutput, ExternalBookingOutput, MembershipOutput, } from "../types/outputs"; @@ -17,6 +18,21 @@ export const bookingSample: BookingOutput = { units: 1, }; +export const eventSample: EventOutput = { + id: "d58b612aaa62619aae546dd336587eb2", + title: "test event", + description: "test event description", + from: "2012-04-12T12:00:00.000Z", + to: "2012-04-12T18:00:00.000Z", + tags: ["tag1", "tag2"], + video_url: "https://us06web.zoom.us/j/8582118861", + image_url: "https://www.example.com/image.jpg", + capacity: 10, + public_url: "https://example.cobot.me/event/example-event", + audience: "membersOnly", + color: "#ff0000", +}; + export const externalBookingSample: ExternalBookingOutput = { id: "d58b612aaa62619aae546dd336587eb2", from: "2012-04-12T12:00:00.000Z", diff --git a/tsconfig.json b/tsconfig.json index 8efe952..16bdafa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "lib": ["esnext", "DOM"], "outDir": "./lib", "rootDir": "./src", - "strict": false, - "noUncheckedIndexedAccess": true + "strict": true } }