11/* eslint-disable functional/immutable-data */
22/* eslint-disable sonarjs/cognitive-complexity */
3-
3+
44import * as M from 'fp-ts/Monoid' ;
55import * as O from 'fp-ts/Option' ;
66import * as RA from 'fp-ts/ReadonlyArray' ;
@@ -21,32 +21,33 @@ import {
2121} from './GetNotificationDetailRecord' ;
2222import { NotificationRequest , makeNotificationRequestFromFind } from './NotificationRequest' ;
2323import { updateTimeline } from './TimelineElement' ;
24+ import { Response } from './types' ;
2425
2526export type Notification = FullSentNotificationV27 & Pick < NotificationRequest , 'notificationRequestId' > ;
2627
27- export const mkNotification = ( env : DomainEnv , notificationRequest : NotificationRequest , iun : IUN ) => ( {
28+ export const mkNotification = ( env : DomainEnv , notificationRequest : NotificationRequest , iun : IUN ) : Notification => ( {
2829 notificationRequestId : notificationRequest . notificationRequestId ,
2930 ...makeFullSentNotification ( env ) ( notificationRequest ) ( iun ) ,
3031} ) ;
3132
33+ /** ---------- IUN helpers ---------- */
3234const getIunFromFind =
33- ( notificationRequest : NotificationRequest ) => ( findNotificationRequestRecord : CheckNotificationStatusRecord ) =>
35+ ( notificationRequest : NotificationRequest ) => ( findRecord : CheckNotificationStatusRecord ) =>
3436 pipe (
35- findNotificationRequestRecord . output . statusCode === 200
36- ? O . some ( findNotificationRequestRecord . output . returned )
37- : O . none ,
37+ findRecord . output . statusCode === 200 ? O . some ( findRecord . output . returned ) : O . none ,
3838 O . filter ( ( e ) => e . notificationRequestId === notificationRequest . notificationRequestId ) ,
3939 O . filterMap ( ( e ) => O . fromNullable ( e . iun ) )
4040 ) ;
4141
4242const getIunFromConsume =
43- ( notificationRequest : NotificationRequest ) => ( consumeEventStreamRecord : ConsumeEventStreamRecord ) =>
43+ ( notificationRequest : NotificationRequest ) => ( consumeRecord : ConsumeEventStreamRecord ) =>
4444 pipe (
45- getProgressResponseList ( [ consumeEventStreamRecord ] ) ,
45+ getProgressResponseList ( [ consumeRecord ] ) ,
4646 RA . findLast ( ( e ) => e . notificationRequestId === notificationRequest . notificationRequestId ) ,
4747 O . filterMap ( ( { iun } ) => pipe ( IUN . decode ( iun ) , O . fromEither ) )
4848 ) ;
4949
50+ /** ---------- Counters ---------- */
5051const countFromFind = ( notificationRequestId : string ) =>
5152 flow (
5253 RA . filterMap ( makeNotificationRequestFromFind ) ,
@@ -57,7 +58,6 @@ const countFromFind = (notificationRequestId: string) =>
5758const countFromConsume = ( notificationRequestId : string ) =>
5859 flow (
5960 RA . filterMap ( getProgressResponse ) ,
60- // for each page remove duplicated notificationRequestId
6161 RA . map (
6262 flow (
6363 RA . filterMap ( ( e ) => O . fromNullable ( e . notificationRequestId ) ) ,
@@ -77,7 +77,8 @@ const countFromDetail = (iun: IUN) =>
7777 RA . size
7878 ) ;
7979
80- const makeStatus = ( env : DomainEnv , occurrences : number ) =>
80+ /** ---------- Status from occurrences ---------- */
81+ const makeStatus = ( env : DomainEnv , occurrences : number ) : O . Option < NotificationStatusV26Enum > =>
8182 env . occurrencesToDelivering <= occurrences && occurrences < env . occurrencesToDelivered
8283 ? O . some ( NotificationStatusV26Enum . DELIVERING )
8384 : env . occurrencesToDelivered <= occurrences && occurrences < env . occurrencesToViewed
@@ -86,14 +87,22 @@ const makeStatus = (env: DomainEnv, occurrences: number) =>
8687 ? O . some ( NotificationStatusV26Enum . VIEWED )
8788 : O . none ;
8889
89- /** Legge in modo sicuro lo status più recente (statusCode 200) dai detail */
90- const getLatestDetailStatus = (
91- records : ReadonlyArray < GetNotificationDetailRecord >
90+ /** ---------- Detail helpers & type guard ---------- */
91+ type OkDetail = { output : Response < 200 , FullSentNotificationV27 > } ;
92+
93+ const isOkDetailForIun =
94+ ( iun : IUN ) =>
95+ ( r : GetNotificationDetailRecord ) : r is GetNotificationDetailRecord & OkDetail =>
96+ r . input . iun === iun && r . output . statusCode === 200 ;
97+
98+ const latestDetailStatusForIun = (
99+ records : ReadonlyArray < GetNotificationDetailRecord > ,
100+ iun : IUN
92101) : O . Option < NotificationStatusV26Enum > =>
93102 pipe (
94103 records ,
95- RA . findLastMap ( ( r ) => ( r . output . statusCode === 200 ? O . some ( r . output . returned ) : O . none ) ) ,
96- O . map ( ( ret ) => ret . notificationStatus ) ,
104+ RA . findLast ( isOkDetailForIun ( iun ) ) ,
105+ O . map ( ( r ) => r . output . returned . notificationStatus ) ,
97106 O . filter ( ( s ) : s is NotificationStatusV26Enum => s !== undefined )
98107 ) ;
99108
@@ -102,93 +111,92 @@ const getLatestDetailStatus = (
102111 */
103112export const makeNotification =
104113 ( env : DomainEnv ) =>
105- ( findNotificationRequestRecord : ReadonlyArray < CheckNotificationStatusRecord > ) =>
106- ( consumeEventStreamRecord : ReadonlyArray < ConsumeEventStreamRecord > ) =>
107- ( getNotificationDetailRecord : ReadonlyArray < GetNotificationDetailRecord > ) =>
114+ ( findRecords : ReadonlyArray < CheckNotificationStatusRecord > ) =>
115+ ( consumeRecords : ReadonlyArray < ConsumeEventStreamRecord > ) =>
116+ ( detailRecords : ReadonlyArray < GetNotificationDetailRecord > ) =>
108117 ( notificationRequest : NotificationRequest ) : O . Option < Notification > =>
109118 pipe (
110- // get iun from find records
111- pipe ( findNotificationRequestRecord , RA . findLastMap ( getIunFromFind ( notificationRequest ) ) ) ,
112- // get iun from consume records
113- O . alt ( ( ) => pipe ( consumeEventStreamRecord , RA . findLastMap ( getIunFromConsume ( notificationRequest ) ) ) ) ,
114- // create Notification from iun if any
119+ // 1) IUN da find
120+ pipe ( findRecords , RA . findLastMap ( getIunFromFind ( notificationRequest ) ) ) ,
121+ // 2) IUN da consume
122+ O . alt ( ( ) => pipe ( consumeRecords , RA . findLastMap ( getIunFromConsume ( notificationRequest ) ) ) ) ,
123+ // 3) se ho IUN → notifica
115124 O . map ( ( iun ) => mkNotification ( env , notificationRequest , iun ) ) ,
116- // try to create notification from find records
117- // if no iun was found then create a new notification based on occurrences counter
125+ // 4) altrimenti crea notifica su base occurrences
118126 O . alt ( ( ) =>
119127 pipe (
120128 M . concatAll ( n . MonoidSum ) ( [
121- pipe ( findNotificationRequestRecord , countFromFind ( notificationRequest . notificationRequestId ) ) ,
122- pipe ( consumeEventStreamRecord , countFromConsume ( notificationRequest . notificationRequestId ) ) ,
129+ pipe ( findRecords , countFromFind ( notificationRequest . notificationRequestId ) ) ,
130+ pipe ( consumeRecords , countFromConsume ( notificationRequest . notificationRequestId ) ) ,
123131 ] ) ,
124- ( occurrences ) =>
125- occurrences >= env . occurrencesToAccepted
132+ ( occ ) =>
133+ occ >= env . occurrencesToAccepted
126134 ? O . some ( mkNotification ( env , notificationRequest , env . iunGenerator ( ) ) )
127135 : O . none
128136 )
129137 ) ,
130- O . map ( ( notification ) =>
131- pipe (
132- M . concatAll ( n . MonoidSum ) ( [
133- pipe ( findNotificationRequestRecord , countFromFind ( notificationRequest . notificationRequestId ) ) ,
134- pipe ( consumeEventStreamRecord , countFromConsume ( notificationRequest . notificationRequestId ) ) ,
135- pipe ( getNotificationDetailRecord , countFromDetail ( notification . iun ) ) ,
136- ] ) ,
137- ( occurrences ) => {
138- // Valuta lo stato (eventuale) proveniente dai "detail"
139- const maybeDetailStatus = getLatestDetailStatus ( getNotificationDetailRecord ) ;
140- const isCancelled = pipe (
141- maybeDetailStatus ,
142- O . exists ( ( st ) => st === NotificationStatusV26Enum . CANCELLED )
143- ) ;
144-
145- if ( isCancelled ) {
146- // Aggiorna subito a CANCELLED e arricchisci timeline/history
147- notification . notificationStatus = NotificationStatusV26Enum . CANCELLED ;
148- notification . cancelledIun = notification . iun ;
149- notification . timeline = [
150- ...notification . timeline ,
151- {
152- elementId : `NOTIFICATION_CANCELLATION_REQUEST.IUN_${ notification . iun } ` ,
153- timestamp : env . dateGenerator ( ) ,
154- legalFactsIds : [ ] ,
155- category : TimelineElementCategoryV27Enum . NOTIFICATION_CANCELLATION_REQUEST ,
156- details : { cancellationRequestId : '90e3f130-cb23-4b6b-a0aa-858de7ffb3a0' } ,
157- } ,
158- {
159- elementId : `NOTIFICATION_CANCELLED.IUN_${ notification . iun } ` ,
160- timestamp : env . dateGenerator ( ) ,
161- legalFactsIds : [ ] ,
162- category : TimelineElementCategoryV27Enum . NOTIFICATION_CANCELLED ,
163- details : { notificationCost : 100 , notRefinedRecipientIndexes : [ 0 ] } ,
164- } ,
165- ] ;
166- notification . notificationStatusHistory = [
167- ...notification . notificationStatusHistory ,
168- {
169- status : NotificationStatusV26Enum . CANCELLED ,
170- activeFrom : env . dateGenerator ( ) ,
171- relatedTimelineElements : [ `NOTIFICATION_CANCELLED.IUN_${ notification . iun } ` ] ,
172- } ,
173- ] ;
174- }
138+ // 5) aggiorna stato finale — evitando Option<void>
139+ O . map ( ( notification ) => {
140+ const occurrences = M . concatAll ( n . MonoidSum ) ( [
141+ pipe ( findRecords , countFromFind ( notificationRequest . notificationRequestId ) ) ,
142+ pipe ( consumeRecords , countFromConsume ( notificationRequest . notificationRequestId ) ) ,
143+ pipe ( detailRecords , countFromDetail ( notification . iun ) ) ,
144+ ] ) ;
175145
176- // update the notification according to the number of occurrences
177- return pipe (
178- makeStatus ( env , occurrences ) ,
179- O . map ( ( newStatus ) =>
180- updateTimeline ( env ) (
181- notification ,
182- isCancelled ? NotificationStatusV26Enum . CANCELLED : newStatus
183- )
184- ) ,
185- O . getOrElse ( ( ) =>
186- isCancelled
187- ? updateTimeline ( env ) ( notification , NotificationStatusV26Enum . CANCELLED )
188- : notification
189- )
190- ) ;
191- }
192- )
193- )
146+ // CANCELLED da detail (per lo stesso IUN) ha priorità
147+ const isCancelled = pipe (
148+ latestDetailStatusForIun ( detailRecords , notification . iun ) ,
149+ O . exists ( ( st ) => st === NotificationStatusV26Enum . CANCELLED )
150+ ) ;
151+
152+ if ( isCancelled ) {
153+ notification . notificationStatus = NotificationStatusV26Enum . CANCELLED ;
154+ notification . cancelledIun = notification . iun ;
155+ notification . timeline = [
156+ ...notification . timeline ,
157+ {
158+ elementId : `NOTIFICATION_CANCELLATION_REQUEST.IUN_${ notification . iun } ` ,
159+ timestamp : env . dateGenerator ( ) ,
160+ legalFactsIds : [ ] ,
161+ category : TimelineElementCategoryV27Enum . NOTIFICATION_CANCELLATION_REQUEST ,
162+ details : { cancellationRequestId : '90e3f130-cb23-4b6b-a0aa-858de7ffb3a0' } ,
163+ } ,
164+ {
165+ elementId : `NOTIFICATION_CANCELLED.IUN_${ notification . iun } ` ,
166+ timestamp : env . dateGenerator ( ) ,
167+ legalFactsIds : [ ] ,
168+ category : TimelineElementCategoryV27Enum . NOTIFICATION_CANCELLED ,
169+ details : { notificationCost : 100 , notRefinedRecipientIndexes : [ 0 ] } ,
170+ } ,
171+ ] ;
172+ notification . notificationStatusHistory = [
173+ ...notification . notificationStatusHistory ,
174+ {
175+ status : NotificationStatusV26Enum . CANCELLED ,
176+ activeFrom : env . dateGenerator ( ) ,
177+ relatedTimelineElements : [ `NOTIFICATION_CANCELLED.IUN_${ notification . iun } ` ] ,
178+ } ,
179+ ] ;
180+ }
181+
182+ const maybeStatus = makeStatus ( env , occurrences ) ;
183+
184+ // Applica side-effect e ritorna SEMPRE notification
185+ return pipe (
186+ maybeStatus ,
187+ O . match (
188+ ( ) => {
189+ if ( isCancelled ) {
190+ updateTimeline ( env ) ( notification , NotificationStatusV26Enum . CANCELLED ) ;
191+ }
192+ return notification ;
193+ } ,
194+ ( st ) => {
195+ const finalSt = isCancelled ? NotificationStatusV26Enum . CANCELLED : st ;
196+ updateTimeline ( env ) ( notification , finalSt ) ;
197+ return notification ;
198+ }
199+ )
200+ ) ;
201+ } )
194202 ) ;
0 commit comments