flightbyte 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
flightbyte/_models.py ADDED
@@ -0,0 +1,644 @@
1
+ """The shapes the FlightByte API answers with.
2
+
3
+ Responses are returned exactly as the API sent them, as plain ``dict`` objects:
4
+ keys keep the API's own camelCase names, timestamps stay ISO 8601 strings, and
5
+ nothing is renamed or dropped. These ``TypedDict`` classes describe them, so a
6
+ type checker and your editor know every key. Type names follow the API's
7
+ OpenAPI document.
8
+
9
+ Keys the API may leave out are not required, and values it may send as
10
+ ``null`` include ``None``, so ``delayMinutes == 0`` and "no delay reading" stay
11
+ apart. Read a key that may be missing with ``.get()``.
12
+
13
+ String enums (phases, statuses, change and event types) are typed as ``str``:
14
+ a value the API adds after this release is still a valid string, so nothing
15
+ breaks. The values this version knows are listed in ``FLIGHT_PHASES``,
16
+ ``SUBSCRIPTION_STATUSES``, ``FLIGHT_CHANGE_TYPES``, ``WEBHOOK_EVENT_TYPES`` and
17
+ ``DELIVERY_STATUSES``.
18
+ """
19
+
20
+ # Each model with keys the API may leave out is split in two: a base with the
21
+ # keys that are always there, and the public class with ``total=False`` for
22
+ # the rest. ``typing.NotRequired`` would read better but needs Python 3.11,
23
+ # and this package supports 3.10 without depending on typing_extensions.
24
+
25
+ from typing import Final, TypeAlias, TypedDict
26
+
27
+ __all__ = [
28
+ "DELIVERY_STATUSES",
29
+ "FLIGHT_CHANGE_TYPES",
30
+ "FLIGHT_PHASES",
31
+ "SUBSCRIPTION_STATUSES",
32
+ "WEBHOOK_EVENT_TYPES",
33
+ "AirportRef",
34
+ "AmbiguousLeg",
35
+ "CarrierRef",
36
+ "CodeshareRef",
37
+ "Country",
38
+ "CountryRef",
39
+ "CreatedSubscription",
40
+ "DeliveryRecord",
41
+ "DeliveryStatus",
42
+ "DurationInfo",
43
+ "EndpointTimes",
44
+ "EquipmentInfo",
45
+ "FlightChange",
46
+ "FlightChangeType",
47
+ "FlightEndpoint",
48
+ "FlightLegRef",
49
+ "FlightPhase",
50
+ "FlightSnapshot",
51
+ "FlightStatusInfo",
52
+ "FlightTime",
53
+ "FlightTimeline",
54
+ "Subscription",
55
+ "SubscriptionPage",
56
+ "SubscriptionStatus",
57
+ "TimelineEntry",
58
+ "WebhookEvent",
59
+ "WebhookEventType",
60
+ "best_gate_time",
61
+ "is_terminal_phase",
62
+ ]
63
+
64
+ FlightPhase: TypeAlias = str
65
+ """The coarse lifecycle bucket of a flight; one of ``FLIGHT_PHASES`` or a newer value."""
66
+
67
+ SubscriptionStatus: TypeAlias = str
68
+ """The lifecycle of a subscription; one of ``SUBSCRIPTION_STATUSES`` or a newer value.
69
+
70
+ ``active`` polls and delivers, ``pending`` waits for the flight to be published
71
+ upstream, ``paused`` was paused by you, ``completed`` finished after the flight
72
+ did, ``suspended`` was disabled by the API, for example after repeated delivery
73
+ failures.
74
+ """
75
+
76
+ FlightChangeType: TypeAlias = str
77
+ """The kind of change a ``flight.updated`` webhook reports, and what a
78
+ subscription's ``events`` filter is made of; one of ``FLIGHT_CHANGE_TYPES`` or
79
+ a newer value."""
80
+
81
+ WebhookEventType: TypeAlias = str
82
+ """The type of a webhook event; one of ``WEBHOOK_EVENT_TYPES`` or a newer value."""
83
+
84
+ DeliveryStatus: TypeAlias = str
85
+ """The outcome of one webhook delivery attempt; one of ``DELIVERY_STATUSES`` or a newer value."""
86
+
87
+ FLIGHT_PHASES: Final[tuple[str, ...]] = (
88
+ "scheduled",
89
+ "boarding",
90
+ "departed",
91
+ "en_route",
92
+ "landed",
93
+ "arrived",
94
+ "cancelled",
95
+ "diverted",
96
+ "redirected",
97
+ "unknown",
98
+ )
99
+ """Every flight phase this version knows, in the API's order."""
100
+
101
+ SUBSCRIPTION_STATUSES: Final[tuple[str, ...]] = (
102
+ "active",
103
+ "pending",
104
+ "paused",
105
+ "completed",
106
+ "suspended",
107
+ )
108
+ """Every subscription status this version knows."""
109
+
110
+ FLIGHT_CHANGE_TYPES: Final[tuple[str, ...]] = (
111
+ "status.changed",
112
+ "flight.departed",
113
+ "flight.arrived",
114
+ "flight.cancelled",
115
+ "flight.diverted",
116
+ "flight.reinstated",
117
+ "departure.gate.changed",
118
+ "departure.terminal.changed",
119
+ "arrival.gate.changed",
120
+ "arrival.terminal.changed",
121
+ "arrival.baggage.changed",
122
+ "departure.scheduled.changed",
123
+ "departure.estimated.changed",
124
+ "departure.actual.changed",
125
+ "arrival.scheduled.changed",
126
+ "arrival.estimated.changed",
127
+ "arrival.actual.changed",
128
+ "delay.increased",
129
+ "delay.decreased",
130
+ "aircraft.changed",
131
+ "aircraft.tail.changed",
132
+ "route.changed",
133
+ "codeshares.changed",
134
+ )
135
+ """Every flight change type this version knows, in the API's order."""
136
+
137
+ WEBHOOK_EVENT_TYPES: Final[tuple[str, ...]] = (
138
+ "subscription.created",
139
+ "subscription.activated",
140
+ "subscription.test",
141
+ "subscription.completed",
142
+ "subscription.suspended",
143
+ "flight.updated",
144
+ )
145
+ """Every webhook event type this version knows."""
146
+
147
+ DELIVERY_STATUSES: Final[tuple[str, ...]] = ("delivered", "failed")
148
+ """Every delivery status this version knows."""
149
+
150
+ _TERMINAL_PHASES: Final = frozenset({"arrived", "cancelled", "diverted", "redirected"})
151
+
152
+
153
+ def is_terminal_phase(phase: FlightPhase) -> bool:
154
+ """Whether the flight is finished: arrived, cancelled, diverted or redirected."""
155
+ return phase in _TERMINAL_PHASES
156
+
157
+
158
+ class CountryRef(TypedDict):
159
+ """A country as it appears inside an airport."""
160
+
161
+ code: str
162
+ """ISO 3166-1 alpha-2 code, for example ``TR``."""
163
+ name: str
164
+ """Country name."""
165
+ emoji: str
166
+ """Flag emoji."""
167
+ flagSvg: str
168
+ """Absolute URL of the 4:3 SVG flag."""
169
+ flagSvgSquare: str
170
+ """Absolute URL of the 1:1 SVG flag."""
171
+
172
+
173
+ class Country(CountryRef):
174
+ """A country from the ``/countries`` endpoints."""
175
+
176
+ alpha2: str
177
+ """ISO 3166-1 alpha-2 code."""
178
+ hasFlag: bool
179
+ """Whether a flag image exists for the country."""
180
+
181
+
182
+ class _AirportRefRequired(TypedDict):
183
+ iata: str
184
+ """IATA code, for example ``IST``."""
185
+ name: str
186
+ """Airport name."""
187
+ country: CountryRef
188
+ """Country."""
189
+
190
+
191
+ class AirportRef(_AirportRefRequired, total=False):
192
+ """An airport."""
193
+
194
+ icao: str | None
195
+ """ICAO code, for example ``LTFM``."""
196
+ code: str | None
197
+ """The upstream's own airport code; it may differ from ``iata``."""
198
+ city: str | None
199
+ """City."""
200
+ state: str | None
201
+ """State or region."""
202
+ timezone: str | None
203
+ """IANA time zone, for example ``Europe/Istanbul``."""
204
+
205
+
206
+ class _FlightTimeRequired(TypedDict):
207
+ utc: str
208
+ """The instant, ISO 8601 in UTC."""
209
+
210
+
211
+ class FlightTime(_FlightTimeRequired, total=False):
212
+ """A point in time, in UTC and in the airport's local time."""
213
+
214
+ local: str | None
215
+ """Airport-local wall time without an offset, for example ``2026-09-08T12:10:00.000``.
216
+
217
+ Pair it with the airport's time zone.
218
+ """
219
+ timezone: str | None
220
+ """Time zone abbreviation, for example ``EEST``."""
221
+
222
+
223
+ class EndpointTimes(TypedDict, total=False):
224
+ """The gate and runway times of a departure or an arrival."""
225
+
226
+ scheduledGate: FlightTime | None
227
+ """Scheduled gate time."""
228
+ estimatedGate: FlightTime | None
229
+ """Estimated gate time."""
230
+ actualGate: FlightTime | None
231
+ """Actual gate time."""
232
+ scheduledRunway: FlightTime | None
233
+ """Scheduled runway time."""
234
+ estimatedRunway: FlightTime | None
235
+ """Estimated runway time."""
236
+ actualRunway: FlightTime | None
237
+ """Actual runway time."""
238
+
239
+
240
+ def best_gate_time(times: EndpointTimes) -> FlightTime | None:
241
+ """The best known gate time: actual, else estimated, else scheduled."""
242
+ for value in (times.get("actualGate"), times.get("estimatedGate"), times.get("scheduledGate")):
243
+ if value is not None:
244
+ return value
245
+ return None
246
+
247
+
248
+ class _FlightEndpointRequired(TypedDict):
249
+ airport: AirportRef
250
+ """The airport."""
251
+ times: EndpointTimes
252
+ """Gate and runway times."""
253
+
254
+
255
+ class FlightEndpoint(_FlightEndpointRequired, total=False):
256
+ """The departure or arrival side of a flight."""
257
+
258
+ terminal: str | None
259
+ """Terminal."""
260
+ gate: str | None
261
+ """Gate."""
262
+ baggage: str | None
263
+ """Baggage carousel, on arrival."""
264
+ bestTimeUtc: str | None
265
+ """Best known time in UTC: actual, else estimated, else scheduled."""
266
+ delayMinutes: int | None
267
+ """Gate delay in minutes; negative when early."""
268
+
269
+
270
+ class _CarrierRefRequired(TypedDict):
271
+ iata: str
272
+ """IATA code, for example ``TK``."""
273
+ name: str
274
+ """Airline name."""
275
+
276
+
277
+ class CarrierRef(_CarrierRefRequired, total=False):
278
+ """The marketing carrier."""
279
+
280
+ icao: str | None
281
+ """ICAO code, for example ``THY``."""
282
+
283
+
284
+ class CodeshareRef(TypedDict):
285
+ """A codeshare flight number sold on the same flight."""
286
+
287
+ carrier: str
288
+ """Carrier code."""
289
+ name: str
290
+ """Carrier name."""
291
+ flightNumber: str
292
+ """Flight number."""
293
+
294
+
295
+ class _FlightStatusInfoRequired(TypedDict):
296
+ code: str
297
+ """Upstream status code: ``S``, ``A``, ``L``, ``C``, ``D``, ``R``, ``U`` or ``NO``."""
298
+ label: str
299
+ """Human-readable status, for example ``Arrived``."""
300
+ phase: FlightPhase
301
+ """The lifecycle phase."""
302
+ delayed: bool
303
+ """Whether the flight is delayed."""
304
+ cancelled: bool
305
+ """Whether the flight is cancelled."""
306
+ diverted: bool
307
+ """Whether the flight is diverted."""
308
+
309
+
310
+ class FlightStatusInfo(_FlightStatusInfoRequired, total=False):
311
+ """The status of a flight."""
312
+
313
+ description: str | None
314
+ """Longer description, for example ``On time``."""
315
+ color: str | None
316
+ """Suggested color, for example ``green``."""
317
+ departureGateDelayMinutes: int | None
318
+ """Departure gate delay in minutes."""
319
+ arrivalGateDelayMinutes: int | None
320
+ """Arrival gate delay in minutes."""
321
+
322
+
323
+ class EquipmentInfo(TypedDict, total=False):
324
+ """The aircraft."""
325
+
326
+ tailNumber: str | None
327
+ """Registration, for example ``TC-LTP``."""
328
+ iata: str | None
329
+ """IATA aircraft type, for example ``32Q``."""
330
+ name: str | None
331
+ """Aircraft name, for example ``Airbus A321neo``."""
332
+ source: str | None
333
+ """Where the data came from, for example ``Actual``."""
334
+
335
+
336
+ class DurationInfo(TypedDict, total=False):
337
+ """Block times in minutes."""
338
+
339
+ scheduledMinutes: int | None
340
+ """Scheduled duration."""
341
+ estimatedMinutes: int | None
342
+ """Estimated duration."""
343
+ actualMinutes: int | None
344
+ """Actual duration."""
345
+
346
+
347
+ class _TimelineEntryRequired(TypedDict):
348
+ occurredAt: str
349
+ """When it happened, ISO 8601."""
350
+ title: str
351
+ """Title, for example ``Time Adjustment``."""
352
+ events: list[str]
353
+ """What changed, as sentences."""
354
+
355
+
356
+ class TimelineEntry(_TimelineEntryRequired, total=False):
357
+ """One entry of a flight's upstream history."""
358
+
359
+ source: str | None
360
+ """Source, for example ``Airline``."""
361
+
362
+
363
+ class _FlightLegRefRequired(TypedDict):
364
+ departureIata: str
365
+ """Origin IATA code."""
366
+ arrivalIata: str
367
+ """Destination IATA code."""
368
+ selected: bool
369
+ """Whether this is the leg the snapshot describes."""
370
+
371
+
372
+ class FlightLegRef(_FlightLegRefRequired, total=False):
373
+ """A leg flown under the same flight number on the same date.
374
+
375
+ Flight numbers are not unique per day, so the API lists every leg and
376
+ marks the one the snapshot describes with ``selected``.
377
+ """
378
+
379
+ providerFlightId: str | None
380
+ """Upstream flight id; pass it as ``flight_id`` to select this leg."""
381
+ carrier: str | None
382
+ """Operating carrier code."""
383
+ scheduledDepartureLocal: str | None
384
+ """Scheduled local departure, ``HH:mm``."""
385
+ scheduledArrivalLocal: str | None
386
+ """Scheduled local arrival, ``HH:mm``."""
387
+
388
+
389
+ class _FlightSnapshotRequired(TypedDict):
390
+ flightKey: str
391
+ """Stable key, for example ``TK1848:2026-09-08`` or ``TK1:2026-09-08@IST-JFK``."""
392
+ carrierCode: str
393
+ """Carrier code from the request, for example ``TK``."""
394
+ flightNumber: str
395
+ """Flight number without leading zeros, for example ``1848``."""
396
+ date: str
397
+ """Local departure date at the origin, ``YYYY-MM-DD``."""
398
+ fetchedAt: str
399
+ """When the API fetched this data from upstream, ISO 8601."""
400
+ carrier: CarrierRef
401
+ """Marketing carrier."""
402
+ codeshares: list[CodeshareRef]
403
+ """Codeshare flight numbers."""
404
+ departure: FlightEndpoint
405
+ """Departure side."""
406
+ arrival: FlightEndpoint
407
+ """Arrival side."""
408
+ status: FlightStatusInfo
409
+ """Status."""
410
+ equipment: EquipmentInfo
411
+ """Aircraft."""
412
+ duration: DurationInfo
413
+ """Block times."""
414
+ timeline: list[TimelineEntry]
415
+ """Upstream history, newest first."""
416
+ alternates: list[FlightLegRef]
417
+ """Every leg flown under this flight number on this date."""
418
+ fingerprint: str
419
+ """Hash over the change-relevant fields."""
420
+
421
+
422
+ class FlightSnapshot(_FlightSnapshotRequired, total=False):
423
+ """Everything known about one flight leg at one moment."""
424
+
425
+ providerFlightId: str | None
426
+ """Upstream flight id."""
427
+ operatedBy: str | None
428
+ """Operating carrier name, when different."""
429
+ diverted: FlightEndpoint | None
430
+ """Where the flight actually went, when diverted."""
431
+ cached: bool
432
+ """Whether a lookup was answered from the API's cache.
433
+
434
+ Sent with lookups, not inside webhook events.
435
+ """
436
+
437
+
438
+ class FlightTimeline(TypedDict):
439
+ """The response of ``flights.timeline``."""
440
+
441
+ flightKey: str
442
+ """The flight's key."""
443
+ count: int
444
+ """The number of entries."""
445
+ data: list[TimelineEntry]
446
+ """History entries, newest first."""
447
+
448
+
449
+ class _SubscriptionRequired(TypedDict):
450
+ id: str
451
+ """Id, for example ``sub_7Kd91mQx0Zb2c3d4e5f6``."""
452
+ status: SubscriptionStatus
453
+ """Lifecycle status."""
454
+ flightKey: str
455
+ """The watched flight's key, for example ``TK1848:2026-09-08``."""
456
+ carrier: str
457
+ """Carrier code."""
458
+ flightNumber: str
459
+ """Flight number."""
460
+ date: str
461
+ """Local departure date at the origin, ``YYYY-MM-DD``."""
462
+ webhookUrl: str
463
+ """Where webhooks are posted."""
464
+ events: list[FlightChangeType]
465
+ """The change types that trigger a webhook."""
466
+ delayThresholdMinutes: int
467
+ """Delay swings smaller than this many minutes are ignored."""
468
+ createdAt: str
469
+ """When it was created, ISO 8601."""
470
+ updatedAt: str
471
+ """When it last changed, ISO 8601."""
472
+ deliveredCount: int
473
+ """Webhooks delivered."""
474
+ failedCount: int
475
+ """Webhook attempts that failed."""
476
+ flightResolved: bool
477
+ """``False`` while the flight is not found upstream yet."""
478
+
479
+
480
+ class _SubscriptionOptional(TypedDict, total=False):
481
+ headers: dict[str, str] | None
482
+ """Extra headers sent with every delivery."""
483
+ metadata: dict[str, str] | None
484
+ """Your own data, echoed back in every webhook."""
485
+ lastPolledAt: str | None
486
+ """When the flight was last polled, ISO 8601."""
487
+ lastChangeAt: str | None
488
+ """When a change was last detected, ISO 8601."""
489
+ lastDeliveryAt: str | None
490
+ """When a webhook was last delivered, ISO 8601."""
491
+ statusReason: str | None
492
+ """Why the subscription was suspended or completed."""
493
+
494
+
495
+ class Subscription(_SubscriptionRequired, _SubscriptionOptional, total=False):
496
+ """A watched flight.
497
+
498
+ The API polls it and posts a signed webhook to ``webhookUrl`` whenever
499
+ something about it changes.
500
+ """
501
+
502
+ secretHint: str
503
+ """The masked signing secret, for example ``whsec_AbCdE...wxyz``."""
504
+ secret: str
505
+ """The full signing secret.
506
+
507
+ Only the creation response carries it; read it again with
508
+ ``subscriptions.secret``.
509
+ """
510
+
511
+
512
+ class _CreatedSubscriptionRequired(_SubscriptionRequired, _SubscriptionOptional):
513
+ secret: str
514
+ """The full webhook signing secret.
515
+
516
+ Returned in full only here and from ``subscriptions.secret``; store it
517
+ where your webhook receiver can read it.
518
+ """
519
+ secretHint: str
520
+ """The masked signing secret."""
521
+ reused: bool
522
+ """``True`` when an earlier request with the same idempotency key was replayed.
523
+
524
+ Within 24 hours, the original subscription is returned and nothing new is
525
+ created or billed.
526
+ """
527
+
528
+
529
+ class CreatedSubscription(_CreatedSubscriptionRequired, total=False):
530
+ """The response of ``subscriptions.create``.
531
+
532
+ It has every key of ``Subscription``, with ``secret`` and ``secretHint``
533
+ always present.
534
+ """
535
+
536
+ flight: FlightSnapshot | None
537
+ """The flight as first seen, when it was found. Absent when ``reused``."""
538
+
539
+
540
+ class SubscriptionPage(TypedDict):
541
+ """One page of ``subscriptions.list``."""
542
+
543
+ total: int
544
+ """The number of subscriptions matching the filters."""
545
+ limit: int
546
+ """The page size used."""
547
+ offset: int
548
+ """The number of subscriptions skipped."""
549
+ data: list[Subscription]
550
+ """The subscriptions on this page, newest first."""
551
+
552
+
553
+ class _DeliveryRecordRequired(TypedDict):
554
+ eventId: str
555
+ """The delivered event's id."""
556
+ attempt: int
557
+ """Attempt number, starting at 1."""
558
+ status: DeliveryStatus
559
+ """The outcome."""
560
+ durationMs: int
561
+ """How long the attempt took, in milliseconds."""
562
+ at: str
563
+ """When the attempt was made, ISO 8601."""
564
+ changeTypes: list[FlightChangeType]
565
+ """The change types the event carried."""
566
+
567
+
568
+ class DeliveryRecord(_DeliveryRecordRequired, total=False):
569
+ """One webhook delivery attempt."""
570
+
571
+ httpStatus: int | None
572
+ """The receiver's HTTP status, when it answered."""
573
+ error: str | None
574
+ """What went wrong, when the attempt failed."""
575
+
576
+
577
+ # ``from`` is a Python keyword, so this one is spelled the functional way.
578
+ _FlightChangeRequired = TypedDict(
579
+ "_FlightChangeRequired",
580
+ {
581
+ "type": FlightChangeType,
582
+ "path": str,
583
+ "from": str | int | float | bool | None,
584
+ "to": str | int | float | bool | None,
585
+ "message": str,
586
+ },
587
+ )
588
+
589
+
590
+ class FlightChange(_FlightChangeRequired, total=False):
591
+ """One change inside a ``flight.updated`` event.
592
+
593
+ Keys: ``type`` (what changed), ``path`` (dotted path into the snapshot,
594
+ for example ``departure.gate``), ``from`` and ``to`` (the old and the new
595
+ value; times are ISO 8601 UTC strings), ``message`` (a sentence describing
596
+ the change) and ``deltaMinutes``.
597
+ """
598
+
599
+ deltaMinutes: int | None
600
+ """Signed minute delta, for time changes."""
601
+
602
+
603
+ class _WebhookEventRequired(TypedDict):
604
+ id: str
605
+ """Event id, ``evt_<uuid>``. The same across delivery retries: deduplicate on it."""
606
+ type: WebhookEventType
607
+ """The event type."""
608
+ createdAt: str
609
+ """When the event was created, ISO 8601."""
610
+ subscriptionId: str
611
+ """The subscription it belongs to."""
612
+ flightKey: str
613
+ """The flight's key."""
614
+ changes: list[FlightChange]
615
+ """What changed. Empty except on ``flight.updated``."""
616
+
617
+
618
+ class WebhookEvent(_WebhookEventRequired, total=False):
619
+ """A webhook event, as posted to your receiver and as listed by ``subscriptions.events``."""
620
+
621
+ flight: FlightSnapshot | None
622
+ """The flight after the change. Missing on some lifecycle events."""
623
+ previousFetchedAt: str | None
624
+ """When the previous snapshot was fetched, on ``flight.updated``."""
625
+ metadata: dict[str, str] | None
626
+ """The subscription's metadata."""
627
+
628
+
629
+ class _AmbiguousLegRequired(TypedDict):
630
+ origin: str
631
+ """Origin IATA code."""
632
+ destination: str
633
+ """Destination IATA code."""
634
+
635
+
636
+ class AmbiguousLeg(_AmbiguousLegRequired, total=False):
637
+ """One of the legs listed by an ambiguous-flight ``BadRequestError``."""
638
+
639
+ carrier: str
640
+ """Operating carrier."""
641
+ scheduledDepartureLocal: str
642
+ """Scheduled local departure, ``HH:mm``."""
643
+ flightId: str
644
+ """Upstream flight id."""