@@ -20,6 +20,54 @@ const GROUPING_TIMESTAMP_INDEX_NAME = 'groupingTimestamp';
2020const GROUPING_TIMESTAMP_AND_LAST_REPETITION_TIME_AND_ID_INDEX_NAME = 'groupingTimestampAndLastRepetitionTimeAndId' ;
2121const GROUPING_TIMESTAMP_AND_GROUP_HASH_INDEX_NAME = 'groupingTimestampAndGroupHash' ;
2222const MAX_SEARCH_QUERY_LENGTH = 50 ;
23+ const FALLBACK_EVENT_TITLE = 'Unknown' ;
24+
25+ /**
26+ * Ensures each daily event has non-empty payload title
27+ * and writes warning log with identifiers when fallback is used.
28+ *
29+ * @param {object } dailyEventsPortion - portion returned by events factory
30+ * @param {string|ObjectId } projectId - project id for logs
31+ * @returns {object }
32+ */
33+ function normalizeDailyEventsPayloadTitle ( dailyEventsPortion , projectId ) {
34+ if ( ! dailyEventsPortion || ! Array . isArray ( dailyEventsPortion . dailyEvents ) ) {
35+ return dailyEventsPortion ;
36+ }
37+
38+ dailyEventsPortion . dailyEvents = dailyEventsPortion . dailyEvents . map ( ( dailyEvent ) => {
39+ const event = dailyEvent && dailyEvent . event ? dailyEvent . event : null ;
40+ const payload = event && event . payload ? event . payload : null ;
41+ const hasValidTitle = payload &&
42+ typeof payload . title === 'string' &&
43+ payload . title . trim ( ) . length > 0 ;
44+
45+ if ( hasValidTitle ) {
46+ return dailyEvent ;
47+ }
48+
49+ console . warn ( '🔴🔴🔴 [ProjectResolver.dailyEventsPortion] Missing event payload title. Fallback title applied.' , {
50+ projectId : projectId ? projectId . toString ( ) : null ,
51+ dailyEventId : dailyEvent && dailyEvent . id ? dailyEvent . id . toString ( ) : null ,
52+ dailyEventGroupHash : dailyEvent && dailyEvent . groupHash ? dailyEvent . groupHash . toString ( ) : null ,
53+ eventOriginalId : event && event . originalEventId ? event . originalEventId . toString ( ) : null ,
54+ eventId : event && event . _id ? event . _id . toString ( ) : null ,
55+ } ) ;
56+
57+ return {
58+ ...dailyEvent ,
59+ event : {
60+ ...( event || { } ) ,
61+ payload : {
62+ ...( payload || { } ) ,
63+ title : FALLBACK_EVENT_TITLE ,
64+ } ,
65+ } ,
66+ } ;
67+ } ) ;
68+
69+ return dailyEventsPortion ;
70+ }
2371
2472/**
2573 * See all types and fields here {@see ../typeDefs/project.graphql}
@@ -571,19 +619,21 @@ module.exports = {
571619 } ,
572620
573621 /**
574- * Returns recent Events grouped by day
575- *
576- * @param {ProjectDBScheme } project - result of parent resolver
577- * @param {Number } limit - limit for events count
578- * @param {DailyEventsCursor } cursor - object with boundary values of the first event in the next portion
579- * @param {'BY_DATE' | 'BY_COUNT' } sort - events sort order
580- * @param {EventsFilters } filters - marks by which events should be filtered
581- * @param {String } release - release name
582- * @param {String } search - search query
622+ * Returns a paginated portion of daily-grouped events
583623 *
584- * @return {Promise<RecentEventSchema[]> }
624+ * @param {ProjectDBScheme } project - parent resolver result
625+ * @param {object } args - GraphQL arguments
626+ * @param {number } args.limit - max rows in portion
627+ * @param {object|null } args.nextCursor - pagination cursor
628+ * @param {string } args.sort - BY_DATE | BY_COUNT | BY_AFFECTED_USERS (mapped in factory)
629+ * @param {object } args.filters - mark filters only: resolved, starred, ignored (assignee uses args.assignee)
630+ * @param {string } args.search - search query
631+ * @param {string|undefined } args.release - optional release label filter
632+ * @param {string|undefined } args.assignee - user id or __filter_unassigned__ / __filter_any_assignee__
633+ * @param {object } context - GraphQL context
634+ * @returns {Promise<object> } dailyEventsPortion payload from factory
585635 */
586- async dailyEventsPortion ( project , { limit, nextCursor, sort, filters, search, release } , context ) {
636+ async dailyEventsPortion ( project , { limit, nextCursor, sort, filters, search, release, assignee } , context ) {
587637 if ( search ) {
588638 if ( search . length > MAX_SEARCH_QUERY_LENGTH ) {
589639 search = search . slice ( 0 , MAX_SEARCH_QUERY_LENGTH ) ;
@@ -592,7 +642,17 @@ module.exports = {
592642
593643 const factory = getEventsFactory ( context , project . _id ) ;
594644
595- const dailyEventsPortion = await factory . findDailyEventsPortion ( limit , nextCursor , sort , filters , search , release ) ;
645+ const dailyEventsPortion = await factory . findDailyEventsPortion (
646+ limit ,
647+ nextCursor ,
648+ sort ,
649+ filters ,
650+ search ,
651+ release ,
652+ assignee
653+ ) ;
654+
655+ normalizeDailyEventsPayloadTitle ( dailyEventsPortion , project . _id ) ;
596656
597657 return dailyEventsPortion ;
598658 } ,
0 commit comments