Skip to content

Commit 5690aaa

Browse files
committed
Merge branch 'better-apis' into develop
2 parents 2460751 + 4eeecd2 commit 5690aaa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1295
-613
lines changed

cloud/firebase.json

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,30 @@
3333
},
3434
"singleProjectMode": true
3535
},
36-
"hosting": {
36+
"hosting": [{
3737
"site": "voxxrin-v3",
3838
"public": "hosting/public",
3939
"ignore": [
4040
"firebase.json",
4141
"**/.*",
4242
"**/node_modules/**"
4343
],
44+
"rewrites": [{
45+
"source": "!(/assets/**|/favicon.*|/sw.js|/manifest.webmanifest)",
46+
"destination": "/index.html"
47+
}]
48+
}, {
49+
"site": "voxxrin-v3-api",
4450
"rewrites": [
45-
{
46-
"source": "!(/assets/**|/favicon.*|/sw.js|/manifest.webmanifest)",
47-
"destination": "/index.html"
48-
}
51+
{ "source": "/api/**", "function": "api" },
52+
{ "source": "hello", "function": "hello" },
53+
{ "source": "crawl", "function": "crawl" },
54+
{ "source": "talkFeedbacksViewers", "function": "talkFeedbacksViewers" },
55+
{ "source": "attendeesFeedbacks", "function": "attendeesFeedbacks" },
56+
{ "source": "publicEventStats", "function": "publicEventStats" },
57+
{ "source": "eventStats", "function": "eventStats" },
58+
{ "source": "migrateFirestoreSchema", "function": "migrateFirestoreSchema" },
59+
{ "source": "globalStats", "function": "globalStats" }
4960
]
50-
}
61+
}]
5162
}

cloud/functions/package-lock.json

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/functions/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"firebase-admin": "^11.5.0",
2525
"firebase-functions": "4.8.2",
2626
"lodash": "^4.17.21",
27-
"ts-pattern": "4.3.0",
27+
"ts-pattern": "5.1.1",
2828
"uuid": "9.0.0",
2929
"yaml": "2.3.2",
3030
"zod": "3.22.4"
@@ -39,7 +39,7 @@
3939
"firebase-functions-test": "^3.0.0",
4040
"firebase-tools": "13.6.0",
4141
"ts-essentials": "9.4.1",
42-
"typescript": "^4.9.0",
42+
"typescript": "5.4.5",
4343
"vitest": "0.31.1"
4444
},
4545
"private": true

cloud/functions/src/crawlers/bdxio/crawler.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,8 @@ export const BDXIO_CRAWLER: CrawlerKind<typeof BDXIO_PARSER> = {
261261
return detailedTalks.concat(additionalDetailedTalks);
262262
}))).flatMap(perSlotDetailedTalks => perSlotDetailedTalks);
263263

264-
const confDescriptor: ConferenceDescriptor = {
264+
const confDescriptor: FullEvent['conferenceDescriptor'] = {
265265
id: eventId,
266-
eventFamily: 'bdxio',
267266
title: descriptor.title,
268267
days: descriptor.days as Day[],
269268
headingTitle: descriptor.headingTitle,
@@ -342,7 +341,6 @@ export const BDXIO_CRAWLER: CrawlerKind<typeof BDXIO_PARSER> = {
342341
id: eventId,
343342
info: {
344343
id: eventId,
345-
eventFamily: 'web2day',
346344
title: descriptor.title,
347345
days: descriptor.days as any,
348346
theming: descriptor.theming as any,

cloud/functions/src/crawlers/camping-des-speakers/crawler.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,8 @@ export const CAMPING_DES_SPEAKERS_CRAWLER: CrawlerKind<typeof CAMPING_DES_SPEAKE
140140
return { url: spUrl, speaker };
141141
}))
142142

143-
const confDescriptor: ConferenceDescriptor = {
143+
const confDescriptor: FullEvent['conferenceDescriptor'] = {
144144
id: eventId,
145-
eventFamily: 'camping-des-speakers',
146145
title: descriptor.title,
147146
days: descriptor.days,
148147
headingTitle: descriptor.headingTitle,
@@ -251,7 +250,6 @@ export const CAMPING_DES_SPEAKERS_CRAWLER: CrawlerKind<typeof CAMPING_DES_SPEAKE
251250
id: eventId,
252251
info: {
253252
id: eventId,
254-
eventFamily: confDescriptor.eventFamily,
255253
title: descriptor.title,
256254
days: descriptor.days as any,
257255
theming: descriptor.theming as any,

cloud/functions/src/crawlers/codeurs-en-seine/crawler.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,8 @@ export const CODEURS_EN_SEINE_CRAWLER: CrawlerKind<typeof CODEURS_EN_SEINE_PARSE
241241
timeSlots: timeslots
242242
}]
243243

244-
const confDescriptor: ConferenceDescriptor = {
244+
const confDescriptor: FullEvent['conferenceDescriptor'] = {
245245
id: eventId,
246-
eventFamily: 'codeurs-en-seine',
247246
title: descriptor.title,
248247
days: descriptor.days as Day[],
249248
headingTitle: descriptor.headingTitle,
@@ -268,7 +267,6 @@ export const CODEURS_EN_SEINE_CRAWLER: CrawlerKind<typeof CODEURS_EN_SEINE_PARSE
268267
id: eventId,
269268
info: {
270269
id: eventId,
271-
eventFamily: confDescriptor.eventFamily,
272270
title: confDescriptor.title,
273271
days: confDescriptor.days,
274272
theming: confDescriptor.theming,

cloud/functions/src/crawlers/crawl.ts

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import {
1515
Track
1616
} from "../../../../shared/daily-schedule.firestore";
1717
import {ensureRoomsStatsFilledFor} from "../functions/firestore/services/stats-utils";
18+
import {getEventOrganizerToken, getFamilyOrganizerToken} from "../functions/firestore/services/publicTokens-utils";
19+
import {getCrawlersMatching} from "../functions/firestore/services/crawlers-utils";
20+
import {ListableEvent} from "../../../../shared/event-list.firestore";
21+
import {ConferenceDescriptor} from "../../../../shared/conference-descriptor.firestore";
1822

1923
export type CrawlerKind<ZOD_TYPE extends z.ZodType> = {
2024
crawlerImpl: (eventId: string, crawlerDescriptor: z.infer<ZOD_TYPE>, criteria: { dayIds?: string[]|undefined }) => Promise<FullEvent>,
@@ -60,6 +64,33 @@ export type CrawlCriteria = {
6064
dayIds?: string[]|undefined;
6165
}
6266

67+
async function resolveCrawlerDescriptorsMatchingWithToken(crawlingToken: string) {
68+
const fbCrawlerDescriptors = await match(crawlingToken)
69+
.with(P.string.startsWith("familyOrganizer:"), async (familyOrganizerToken) => {
70+
const familyToken = await getFamilyOrganizerToken(familyOrganizerToken);
71+
return getCrawlersMatching(crawlersColl =>
72+
crawlersColl.where("eventFamily", "in", familyToken.eventFamilies)
73+
)
74+
}).with(P.string.startsWith("eventOrganizer:"), async (eventOrganizerToken) => {
75+
const eventToken = await getEventOrganizerToken(eventOrganizerToken);
76+
return getCrawlersMatching(crawlersColl =>
77+
crawlersColl.where("eventName", "in", eventToken.eventNames)
78+
)
79+
}).otherwise((legacyCrawlingToken) => {
80+
// TODO: Remove me once every event will have migrated to the new public-tokens
81+
// resolution policy
82+
return getCrawlersMatching(crawlersColl =>
83+
crawlersColl.where("legacyCrawlingKeys", "array-contains", legacyCrawlingToken)
84+
)
85+
})
86+
87+
if(!fbCrawlerDescriptors.length) {
88+
throw new Error(`No crawler found matching [${crawlingToken}] token !`)
89+
}
90+
91+
return fbCrawlerDescriptors;
92+
}
93+
6394
const crawlAll = async function(criteria: CrawlCriteria) {
6495
if(!criteria.crawlingToken) {
6596
throw new Error(`Missing crawlingToken mandatory query parameter !`)
@@ -68,31 +99,20 @@ const crawlAll = async function(criteria: CrawlCriteria) {
6899
info("Starting crawling");
69100
const start = Date.now();
70101

71-
const fbCrawlerDescriptorSnapshot = await db.collection("crawlers")
72-
.where("crawlingKeys", "array-contains", criteria.crawlingToken)
73-
.get();
74-
75-
76-
if (fbCrawlerDescriptorSnapshot.empty) {
77-
throw new Error(`No crawler found matching [${criteria.crawlingToken}] token !`)
78-
return;
79-
}
102+
const crawlerDescriptors = await resolveCrawlerDescriptorsMatchingWithToken(criteria.crawlingToken);
80103

81-
const isAutoCrawling = criteria.crawlingToken.startsWith("auto:");
82-
const matchingCrawlerDescriptors = fbCrawlerDescriptorSnapshot.docs.map((snap, _) => {
83-
return {...FIREBASE_CRAWLER_DESCRIPTOR_PARSER.parse(snap.data()), id: snap.id }
84-
}).filter(firestoreCrawler => {
85-
const dateConstraintMatches = isAutoCrawling
86-
|| Temporal.Now.instant().epochMilliseconds < Date.parse(firestoreCrawler.stopAutoCrawlingAfter)
104+
const matchingCrawlerDescriptors = crawlerDescriptors.filter(firestoreCrawler => {
105+
const dateConstraintMatches = Temporal.Now.instant().epochMilliseconds < Date.parse(firestoreCrawler.stopAutoCrawlingAfter)
87106

88-
const eventIdConstraintMatches = !criteria.eventIds || !criteria.eventIds.length || criteria.eventIds.includes(firestoreCrawler.id);
107+
const eventIdConstraintMatches = !criteria.eventIds
108+
|| !criteria.eventIds.length
109+
|| criteria.eventIds.includes(firestoreCrawler.id);
89110

90-
return dateConstraintMatches && eventIdConstraintMatches;
111+
return dateConstraintMatches && eventIdConstraintMatches;
91112
});
92113

93114
if(!matchingCrawlerDescriptors.length) {
94-
throw new Error(`No crawler found matching either eventIds=${JSON.stringify(criteria.eventIds)} or crawlers' 'stopAutoCrawlingAfter' deadline`);
95-
return;
115+
throw new Error(`No crawler found matching either eventIds=${JSON.stringify(criteria.eventIds)} or crawlers' 'stopAutoCrawlingAfter' deadline`);
96116
}
97117

98118
return await Promise.all(matchingCrawlerDescriptors.map(async crawlerDescriptor => {
@@ -102,7 +122,6 @@ const crawlAll = async function(criteria: CrawlCriteria) {
102122
const crawler = await resolveCrawler(crawlerDescriptor.kind);
103123
if(!crawler) {
104124
throw new Error(`Error: no crawler found for kind: ${crawlerDescriptor.kind} (with id=${crawlerDescriptor.id})`)
105-
return;
106125
}
107126

108127
info(`crawling event ${crawlerDescriptor.id} of type [${crawlerDescriptor.kind}]...`)
@@ -111,7 +130,7 @@ const crawlAll = async function(criteria: CrawlCriteria) {
111130

112131
const event = await crawler.crawlerImpl(crawlerDescriptor.id, crawlerKindDescriptor, { dayIds: criteria.dayIds });
113132
const messages = sanityCheckEvent(event);
114-
await saveEvent(event)
133+
await saveEvent(event, crawlerDescriptor)
115134

116135
const end = Temporal.Now.instant()
117136
return {
@@ -194,10 +213,16 @@ function sanityCheckEvent(event: FullEvent): string[] {
194213
return crawlingMessages;
195214
}
196215

197-
const saveEvent = async function(event: FullEvent) {
216+
const saveEvent = async function (event: FullEvent, crawlerDescriptor: z.infer<typeof FIREBASE_CRAWLER_DESCRIPTOR_PARSER>) {
198217
info("saving event " + event.id)
199218

200-
await db.collection("events").doc(event.id).set(event.info)
219+
const listableEvent: ListableEvent = {
220+
...event.info,
221+
eventFamily: crawlerDescriptor.eventFamily,
222+
eventName: crawlerDescriptor.eventName,
223+
}
224+
225+
await db.collection("events").doc(event.id).set(listableEvent)
201226

202227
const talksStatsAllInOneDoc = await db.doc(`events/${event.id}/talksStats-allInOne/self`).get()
203228
if(!talksStatsAllInOneDoc.exists) {
@@ -284,6 +309,7 @@ const saveEvent = async function(event: FullEvent) {
284309
organizerSpaceContent.talkFeedbackViewerTokens.push({
285310
eventId: event.id,
286311
talkId: talk.id,
312+
speakersFullNames: talk.speakers.map(sp => sp.fullName),
287313
secretToken: talkFeedbackViewerSecretToken
288314
});
289315
}
@@ -303,9 +329,15 @@ const saveEvent = async function(event: FullEvent) {
303329
// TODO: Remove me once watch later will be properly implemented !
304330
event.conferenceDescriptor.features.remindMeOnceVideosAreAvailableEnabled = false;
305331

332+
const confDescriptor: ConferenceDescriptor = {
333+
...listableEvent,
334+
...event.conferenceDescriptor,
335+
}
336+
306337
await firestoreEvent.collection('event-descriptor')
307338
.doc('self')
308-
.set(event.conferenceDescriptor);
339+
.set(confDescriptor);
340+
309341
}catch(e) {
310342
error(`Error while storing conference descriptor ${event.conferenceDescriptor.id}: ${e?.toString()}`)
311343
}

cloud/functions/src/crawlers/crawler-parsers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,9 @@ export const FULL_EVENT_PARSER = z.object({
243243
})
244244

245245
export const FIREBASE_CRAWLER_DESCRIPTOR_PARSER = z.object({
246-
crawlingKeys: z.array(z.string()),
246+
legacyCrawlingKeys: z.array(z.string()),
247+
eventFamily: z.string(),
248+
eventName: z.string(),
247249
descriptorUrl: z.string(),
248250
kind: z.string(),
249251
stopAutoCrawlingAfter: ISO_DATETIME_PARSER,

cloud/functions/src/crawlers/devoxx-scala/crawler.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,25 +267,22 @@ export const DEVOXX_SCALA_CRAWLER: CrawlerKind<typeof DEVOXX_SCALA_DESCRIPTOR_PA
267267
return voxxrinSchedule;
268268
}));
269269

270-
const eventInfo = {
270+
const eventInfo: FullEvent['info'] = {
271271
id: eventId,
272-
eventFamily: descriptor.eventFamily || 'devoxx',
273272
title: descriptor.title,
274273
description: conferenceResourceUrl.resource.label,
275274
peopleDescription: descriptor.peopleDescription,
276275
timezone: descriptor.timezone,
277-
start: Temporal.PlainDate.from(descriptor.days[0].localDate).toZonedDateTime(descriptor.timezone).startOfDay().toInstant().toString() as ISODatetime,
278-
end: Temporal.PlainDate.from(descriptor.days[0].localDate).toZonedDateTime(descriptor.timezone).startOfDay().add({days:1}).subtract({seconds:1}).toInstant().toString() as ISODatetime,
279276
days: descriptor.days,
280277
logoUrl: descriptor.logoUrl,
281278
backgroundUrl: descriptor.backgroundUrl,
282279
websiteUrl: descriptor.websiteUrl,
283280
location: descriptor.location,
284281
theming: descriptor.theming,
285282
keywords: descriptor.keywords
286-
} as ListableEvent
283+
}
287284

288-
const eventDescriptor: ConferenceDescriptor = {
285+
const eventDescriptor: FullEvent['conferenceDescriptor'] = {
289286
...eventInfo,
290287
headingTitle: descriptor.headingTitle,
291288
features: descriptor.features,

0 commit comments

Comments
 (0)