33from __future__ import annotations
44
55from dataclasses import dataclass
6- from datetime import datetime , timezone
76from enum import StrEnum
87
8+ from .timestamps import normalize_iso8601_utc_string
9+
910
1011class DeferredPayloadKind (StrEnum ):
1112 DEFERRED_COMMENT = "deferred_comment"
@@ -520,13 +521,10 @@ def _first_string(payload: dict, field_names: tuple[str, ...], diagnostic_name:
520521
521522def _recoverable_timestamp (payload : dict , field_names : tuple [str , ...]) -> str :
522523 timestamp = _first_string (payload , field_names , "source event timestamp" )
523- try :
524- parsed = datetime .fromisoformat (timestamp .replace ("Z" , "+00:00" ))
525- except ValueError as exc :
526- raise RuntimeError ("Deferred context payload source event timestamp is not parseable ISO-8601" ) from exc
527- if parsed .tzinfo is None :
528- return parsed .replace (tzinfo = timezone .utc ).isoformat ()
529- return timestamp
524+ normalized = normalize_iso8601_utc_string (timestamp )
525+ if normalized is None :
526+ raise RuntimeError ("Deferred context payload source event timestamp is not parseable ISO-8601" )
527+ return normalized
530528
531529
532530def _optional_nonempty_string (payload : dict , field_name : str ) -> str | None :
@@ -536,6 +534,19 @@ def _optional_nonempty_string(payload: dict, field_name: str) -> str | None:
536534 return None
537535
538536
537+ def _timestamp_string (value : object , diagnostic_name : str ) -> str :
538+ normalized = normalize_iso8601_utc_string (value )
539+ if normalized is None :
540+ raise RuntimeError (f"Deferred context payload { diagnostic_name } is not parseable ISO-8601" )
541+ return normalized
542+
543+
544+ def _optional_timestamp_string (value : object ) -> str | None :
545+ if value is None :
546+ return None
547+ return normalize_iso8601_utc_string (value ) or str (value )
548+
549+
539550def _diagnostic_payload (payload : dict , contract : DeferredIdentityContract , * , source_run_id : int , source_run_attempt : int , pr_number : int , source_event_key : str , source_object_id : int , actor_login : str , source_event_created_at : str ) -> dict :
540551 diagnostic = {
541552 "source_run_id" : source_run_id ,
@@ -556,6 +567,8 @@ def _diagnostic_payload(payload: dict, contract: DeferredIdentityContract, *, so
556567 "source_dismissed_at" ,
557568 ):
558569 value = _optional_nonempty_string (payload , field_name )
570+ if value is not None and field_name == "source_dismissed_at" :
571+ value = normalize_iso8601_utc_string (value ) or value
559572 if value is not None :
560573 diagnostic [field_name ] = value
561574 actor_id = payload .get ("source_actor_id" , payload .get ("comment_author_id" , payload .get ("actor_id" )))
@@ -831,7 +844,7 @@ def parse_deferred_context_payload(payload: dict) -> DeferredReviewPayload | Def
831844 identity = identity ,
832845 comment_id = comment_id ,
833846 comment_body = "" ,
834- comment_created_at = str (payload ["source_created_at" ]),
847+ comment_created_at = _timestamp_string (payload ["source_created_at" ], "source_created_at" ),
835848 comment_author = str (payload ["actor_login" ]),
836849 comment_author_id = comment_author_id ,
837850 comment_user_type = str (payload .get ("actor_user_type" ) or "User" ),
@@ -853,7 +866,7 @@ def parse_deferred_context_payload(payload: dict) -> DeferredReviewPayload | Def
853866 identity = identity ,
854867 comment_id = comment_id ,
855868 comment_body = str (payload ["comment_body" ]),
856- comment_created_at = str (payload ["comment_created_at" ]),
869+ comment_created_at = _timestamp_string (payload ["comment_created_at" ], "comment_created_at" ),
857870 comment_author = str (payload ["comment_author" ]),
858871 comment_author_id = int (payload ["comment_author_id" ]),
859872 comment_user_type = str (payload ["comment_user_type" ]),
@@ -873,7 +886,7 @@ def parse_deferred_context_payload(payload: dict) -> DeferredReviewPayload | Def
873886 common = dict (
874887 identity = identity ,
875888 review_id = review_id ,
876- source_submitted_at = ( str ( payload [ "source_submitted_at" ]) if payload .get ("source_submitted_at" ) is not None else None ),
889+ source_submitted_at = _optional_timestamp_string ( payload .get ("source_submitted_at" )),
877890 source_review_state = (str (payload ["source_review_state" ]) if payload .get ("source_review_state" ) is not None else None ),
878891 source_commit_id = (str (payload ["source_commit_id" ]) if payload .get ("source_commit_id" ) is not None else None ),
879892 actor_login = (str (payload ["actor_login" ]) if payload .get ("actor_login" ) is not None else None ),
@@ -883,7 +896,7 @@ def parse_deferred_context_payload(payload: dict) -> DeferredReviewPayload | Def
883896 return DeferredReviewSubmittedPayload (** common )
884897 return DeferredReviewDismissedPayload (
885898 ** common ,
886- source_dismissed_at = ( str ( payload [ "source_dismissed_at" ]) if payload .get ("source_dismissed_at" ) is not None else None ),
899+ source_dismissed_at = _optional_timestamp_string ( payload .get ("source_dismissed_at" )),
887900 )
888901 raise RuntimeError ("Unsupported deferred workflow_run payload" )
889902
0 commit comments