af-gds-client 0.0.1__tar.gz

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.
Files changed (59) hide show
  1. af_gds_client-0.0.1/PKG-INFO +23 -0
  2. af_gds_client-0.0.1/README.md +5 -0
  3. af_gds_client-0.0.1/gds_client/README.md +4 -0
  4. af_gds_client-0.0.1/gds_client/__init__.py +4 -0
  5. af_gds_client-0.0.1/gds_client/client.py +114 -0
  6. af_gds_client-0.0.1/gds_client/config.py +15 -0
  7. af_gds_client-0.0.1/gds_client/pyproject.toml +19 -0
  8. af_gds_client-0.0.1/gds_schemas/common/__init__.py +0 -0
  9. af_gds_client-0.0.1/gds_schemas/common/enums.py +73 -0
  10. af_gds_client-0.0.1/gds_schemas/common/models/__init__.py +19 -0
  11. af_gds_client-0.0.1/gds_schemas/common/models/base.py +20 -0
  12. af_gds_client-0.0.1/gds_schemas/common/models/book.py +75 -0
  13. af_gds_client-0.0.1/gds_schemas/common/utils.py +11 -0
  14. af_gds_client-0.0.1/gds_schemas/flights/__init__.py +0 -0
  15. af_gds_client-0.0.1/gds_schemas/flights/data/__init__.py +0 -0
  16. af_gds_client-0.0.1/gds_schemas/flights/data/aircraft.json +266 -0
  17. af_gds_client-0.0.1/gds_schemas/flights/data/airlines.json +1003 -0
  18. af_gds_client-0.0.1/gds_schemas/flights/data/airports.json +95602 -0
  19. af_gds_client-0.0.1/gds_schemas/flights/data/countries.json +247 -0
  20. af_gds_client-0.0.1/gds_schemas/flights/data/imported_data.py +108 -0
  21. af_gds_client-0.0.1/gds_schemas/flights/v1/__init__.py +0 -0
  22. af_gds_client-0.0.1/gds_schemas/flights/v1/constants.py +42 -0
  23. af_gds_client-0.0.1/gds_schemas/flights/v1/enums.py +243 -0
  24. af_gds_client-0.0.1/gds_schemas/flights/v1/models/__init__.py +181 -0
  25. af_gds_client-0.0.1/gds_schemas/flights/v1/models/booking.py +177 -0
  26. af_gds_client-0.0.1/gds_schemas/flights/v1/models/booking_intent.py +39 -0
  27. af_gds_client-0.0.1/gds_schemas/flights/v1/models/cache_data.py +71 -0
  28. af_gds_client-0.0.1/gds_schemas/flights/v1/models/cancel_booking.py +19 -0
  29. af_gds_client-0.0.1/gds_schemas/flights/v1/models/cancel_ticket.py +31 -0
  30. af_gds_client-0.0.1/gds_schemas/flights/v1/models/common.py +80 -0
  31. af_gds_client-0.0.1/gds_schemas/flights/v1/models/get_booking.py +790 -0
  32. af_gds_client-0.0.1/gds_schemas/flights/v1/models/revalidate.py +31 -0
  33. af_gds_client-0.0.1/gds_schemas/flights/v1/models/search.py +552 -0
  34. af_gds_client-0.0.1/gds_schemas/flights/v1/models/seat_selection.py +83 -0
  35. af_gds_client-0.0.1/gds_schemas/flights/v1/models/ticket_issue.py +74 -0
  36. af_gds_client-0.0.1/gds_schemas/flights/v1/models/ticket_status.py +18 -0
  37. af_gds_client-0.0.1/gds_schemas/flights/v1_5/__init__.py +0 -0
  38. af_gds_client-0.0.1/gds_schemas/flights/v1_5/models/__init__.py +19 -0
  39. af_gds_client-0.0.1/gds_schemas/flights/v1_5/models/search.py +50 -0
  40. af_gds_client-0.0.1/gds_schemas/hotels/__init__.py +0 -0
  41. af_gds_client-0.0.1/gds_schemas/hotels/v1/__init__.py +0 -0
  42. af_gds_client-0.0.1/gds_schemas/hotels/v1/enums.py +35 -0
  43. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/__init__.py +75 -0
  44. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/base.py +70 -0
  45. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/booking.py +58 -0
  46. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/booking_intent.py +28 -0
  47. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/constants.py +3 -0
  48. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/content.py +119 -0
  49. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/details.py +72 -0
  50. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/revalidate.py +46 -0
  51. af_gds_client-0.0.1/gds_schemas/hotels/v1/models/search.py +78 -0
  52. af_gds_client-0.0.1/pyproject.toml +25 -0
  53. af_gds_client-0.0.1/src/allfly/__init__.py +0 -0
  54. af_gds_client-0.0.1/src/allfly/gds/__init__.py +0 -0
  55. af_gds_client-0.0.1/src/allfly/gds/client/__init__.py +3 -0
  56. af_gds_client-0.0.1/src/allfly/gds/schemas/__init__.py +0 -0
  57. af_gds_client-0.0.1/src/allfly/gds/schemas/common/__init__.py +1 -0
  58. af_gds_client-0.0.1/src/allfly/gds/schemas/flights/__init__.py +1 -0
  59. af_gds_client-0.0.1/src/allfly/gds/schemas/hotels/__init__.py +1 -0
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: af-gds-client
3
+ Version: 0.0.1
4
+ Summary: Allfly GDS client and schemas
5
+ Author: Allfly
6
+ Author-email: engineering@allfly.io
7
+ Requires-Python: >=3.13,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Requires-Dist: af-sig-tool (==0.0.3)
12
+ Requires-Dist: httpx (>=0.28.0,<0.29.0)
13
+ Requires-Dist: loguru (>=0.7.2,<0.8.0)
14
+ Requires-Dist: pydantic (>=2.10.6,<3.0.0)
15
+ Requires-Dist: pydantic-settings (>=2.8.0,<3.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ # af-gds-client
19
+
20
+ Allfly GDS client and schemas library.
21
+
22
+ Exposes `allfly.gds.client` and `allfly.gds.schemas` namespaces.
23
+
@@ -0,0 +1,5 @@
1
+ # af-gds-client
2
+
3
+ Allfly GDS client and schemas library.
4
+
5
+ Exposes `allfly.gds.client` and `allfly.gds.schemas` namespaces.
@@ -0,0 +1,4 @@
1
+ # GDS Client
2
+
3
+ An exposed client for the gds-api service, it's an HTTP client that can be used to make requests to the gds-api service.
4
+ It will be shared across our internal services like core-api etc.
@@ -0,0 +1,4 @@
1
+ from gds_client.client import GdsClient
2
+ from gds_client.config import GdsClientConfig
3
+
4
+ __all__ = ["GdsClient", "GdsClientConfig"]
@@ -0,0 +1,114 @@
1
+ import json
2
+ import os
3
+ from datetime import datetime
4
+ from enum import StrEnum
5
+ from typing import Dict, Any
6
+
7
+ import httpx
8
+ from allfly.sig.tool.utils import generate_signature
9
+ from loguru import logger
10
+
11
+ from gds_client.config import GdsClientConfig
12
+
13
+
14
+ class GdsClient:
15
+ """
16
+ A Client ( HTTP Client/Manager ) for interacting with GDS-API service.
17
+ It handles:
18
+ 1. Pre-request processing ( request signing + headers )
19
+ 2. Authentication
20
+ """
21
+
22
+ def __init__(self, settings: GdsClientConfig):
23
+ self.settings = settings
24
+ self.client = httpx.Client(timeout=settings.timeout_seconds)
25
+
26
+ @staticmethod
27
+ def _get_default_headers() -> Dict[str, str]:
28
+ return {
29
+ "Accept": "application/json",
30
+ "Content-Type": "application/json",
31
+ "X-Signature": "",
32
+ }
33
+
34
+ def _build_url(self, endpoint: StrEnum) -> str:
35
+ return f"{self.settings.base_url}{endpoint}"
36
+
37
+ @staticmethod
38
+ def _write(name: str, text: dict, extension: str = "json") -> None:
39
+ os.makedirs(os.path.dirname(name), exist_ok=True)
40
+ text = json.dumps(text, indent=4)
41
+ with open(f"{name}.{extension}", "w") as file:
42
+ file.write(text)
43
+
44
+ def _make_request(
45
+ self,
46
+ method: str,
47
+ endpoint: StrEnum,
48
+ data: Dict[str, Any] | None = None,
49
+ params: Dict[str, Any] | None = None,
50
+ headers: Dict[str, Any] | None = None,
51
+ ) -> Dict[str, Any]:
52
+ """
53
+ Performs pre-processing and signing for the request and makes the actual request.
54
+ """
55
+ url = self._build_url(endpoint)
56
+ try:
57
+ headers = headers or self._get_default_headers()
58
+ headers["X-Signature"] = generate_signature(self.settings.api_key, data)
59
+
60
+ for header_provider in self.settings.header_providers:
61
+ header_name, header_value = header_provider()
62
+ if header_name and header_value:
63
+ headers[header_name] = header_value
64
+
65
+ logger.info(f"[GDS Request Manager] Making request to endpoint: {url}")
66
+ if self.settings.debug:
67
+ self._write(
68
+ f"logs/REQUEST_{endpoint.split('/')[-1]}_{str(datetime.now())}",
69
+ data,
70
+ )
71
+ response = self.client.request(
72
+ method=method, url=url, json=data, params=params, headers=headers
73
+ )
74
+ logger.info(
75
+ f"[GDS Request Manager] Received response from endpoint: {url} : {response.status_code}"
76
+ )
77
+ if self.settings.debug:
78
+ self._write(
79
+ f"logs/RESPONSE_{endpoint.split('/')[-1]}_{str(datetime.now())}",
80
+ response.json(),
81
+ )
82
+ response.raise_for_status()
83
+ return response.json()
84
+ except (httpx.HTTPStatusError, Exception) as exc:
85
+ logger.exception(
86
+ f"[GDS Request Manager] {method} request failed for endpoint: {url} with error: {exc}"
87
+ )
88
+ raise
89
+
90
+ def get(
91
+ self, endpoint: StrEnum, params: Dict[str, Any] | None = None
92
+ ) -> Dict[str, Any]:
93
+ return self._make_request(method="GET", endpoint=endpoint, params=params)
94
+
95
+ def post(
96
+ self, endpoint: StrEnum, data: Dict[str, Any] | None = None
97
+ ) -> Dict[str, Any]:
98
+ return self._make_request(method="POST", endpoint=endpoint, data=data)
99
+
100
+ def put(
101
+ self, endpoint: StrEnum, data: Dict[str, Any] | None = None
102
+ ) -> Dict[str, Any]:
103
+ return self._make_request(method="PUT", endpoint=endpoint, data=data)
104
+
105
+ def patch(
106
+ self, endpoint: StrEnum, data: Dict[str, Any] | None = None
107
+ ) -> Dict[str, Any]:
108
+ return self._make_request(method="PATCH", endpoint=endpoint, data=data)
109
+
110
+ def delete(self, endpoint: StrEnum) -> Dict[str, Any]:
111
+ return self._make_request(
112
+ method="DELETE",
113
+ endpoint=endpoint,
114
+ )
@@ -0,0 +1,15 @@
1
+ from typing import Callable, Tuple, List
2
+ from pydantic import Field
3
+ from pydantic_settings import BaseSettings
4
+
5
+
6
+ class GdsClientConfig(BaseSettings):
7
+ """
8
+ Extend this to over-ride the model_config: SettingsConfigDict as per the consumer's env vars implementation.
9
+ """
10
+
11
+ api_key: str
12
+ base_url: str
13
+ timeout_seconds: int = Field(default=30)
14
+ debug: bool = Field(default=False)
15
+ header_providers: List[Callable[[], Tuple[str, str]]] = Field(default_factory=list)
@@ -0,0 +1,19 @@
1
+ [tool.poetry]
2
+ name = "gds-client"
3
+ version = "0.1.0"
4
+ description = "HTTP client for interacting with the gds-api service"
5
+ authors = ["Irshad Nawaz <irshad@allfly.io>"]
6
+ readme = "README.md"
7
+ packages = [{ include = "gds_client", from = ".." }]
8
+
9
+ [tool.poetry.dependencies]
10
+ python = "^3.13"
11
+ httpx = "^0.28.0"
12
+ loguru = "^0.7.2"
13
+ af-sig-tool = "0.0.3"
14
+ pydantic = "^2.10.6"
15
+ pydantic-settings = "^2.8.0"
16
+
17
+ [build-system]
18
+ requires = ["poetry-core"]
19
+ build-backend = "poetry.core.masonry.api"
File without changes
@@ -0,0 +1,73 @@
1
+ from enum import Enum, StrEnum
2
+
3
+
4
+ class PassengerTitle(str, Enum):
5
+ MISTER = "MR"
6
+ MISS = "MS"
7
+ MRS = "MRS"
8
+ MASTER = "MASTER"
9
+ MISS_CHILD = "MISS"
10
+ INFANT = "INFANT"
11
+
12
+
13
+ class PassengerGender(str, Enum):
14
+ MALE = "M"
15
+ FEMALE = "F"
16
+
17
+
18
+ class PassengerType(str, Enum):
19
+ ADULT = "adult"
20
+ CHILD = "child"
21
+ LAP_INFANT = "lap_infant"
22
+ SEAT_INFANT = "seat_infant"
23
+
24
+
25
+ class BookingType(str, Enum):
26
+ FLIGHT = "FLIGHT"
27
+ CAR = "CAR"
28
+ HOTEL = "HOTEL"
29
+
30
+
31
+ class BookingStatus(str, Enum):
32
+ CONFIRMED = "CONFIRMED"
33
+ CANCELLED = "CANCELLED"
34
+ ERROR = "ERROR"
35
+
36
+
37
+ class CardTypeCode(StrEnum):
38
+ VISA = "VI"
39
+ MASTERCARD = "CA"
40
+ AMERICAN_EXPRESS = "AX"
41
+ MAESTRO = "SW"
42
+ DISCOVER = "DS"
43
+
44
+
45
+ class CardType(StrEnum):
46
+ VISA = "VISA"
47
+ MASTERCARD = "MASTERCARD"
48
+ AMERICAN_EXPRESS = "AMERICAN_EXPRESS"
49
+ MAESTRO = "MAESTRO"
50
+ DISCOVER = "DISCOVER"
51
+
52
+ @classmethod
53
+ def get_code(cls, card_type: "CardType") -> CardTypeCode:
54
+ mapper = {
55
+ CardType.VISA: CardTypeCode.VISA,
56
+ CardType.MASTERCARD: CardTypeCode.MASTERCARD,
57
+ CardType.AMERICAN_EXPRESS: CardTypeCode.AMERICAN_EXPRESS,
58
+ CardType.MAESTRO: CardTypeCode.MAESTRO,
59
+ CardType.DISCOVER: CardTypeCode.DISCOVER,
60
+ }
61
+ return mapper[card_type]
62
+
63
+
64
+ class PaymentType(StrEnum):
65
+ PAYMENT_CARD = "PAYMENT_CARD"
66
+ # Use this when you don't need to pass payment details into payload.
67
+ AGENCY_PAYMENT = "AGENCY_PAYMENT"
68
+
69
+
70
+ class LoyaltyProgramType(StrEnum):
71
+ AIRLINE = "AIRLINE"
72
+ HOTEL = "HOTEL"
73
+ CAR = "CAR"
@@ -0,0 +1,19 @@
1
+ from .book import (
2
+ BasePassengerDetail as GDSBasePassengerDetail,
3
+ BaseContactDetails as GDSBaseContactDetails,
4
+ BookingPassengerDetails as GDSBookingPassengerDetails,
5
+ BookingContactDetails as GDSBookingContactDetails,
6
+ BaseBooking as GDSBaseBooking,
7
+ PaymentCardDetails as GDSPaymentCardDetails,
8
+ PaymentDetails as GDSPaymentDetails,
9
+ )
10
+
11
+ __all__ = [
12
+ "GDSBasePassengerDetail",
13
+ "GDSBaseContactDetails",
14
+ "GDSBookingPassengerDetails",
15
+ "GDSBookingContactDetails",
16
+ "GDSBaseBooking",
17
+ "GDSPaymentCardDetails",
18
+ "GDSPaymentDetails",
19
+ ]
@@ -0,0 +1,20 @@
1
+ import re
2
+
3
+ from pydantic import GetCoreSchemaHandler
4
+ from pydantic_core import core_schema
5
+
6
+
7
+ class NameStr(str):
8
+ NAME_REGEX = r"[^A-Za-z0-9]+"
9
+
10
+ @classmethod
11
+ def __get_pydantic_core_schema__(cls, _, handler: GetCoreSchemaHandler):
12
+ str_schema = handler(str)
13
+ return core_schema.no_info_after_validator_function(cls._validate, str_schema)
14
+
15
+ @classmethod
16
+ def _validate(cls, value):
17
+ if not isinstance(value, str):
18
+ raise TypeError(f"string required for name while {value} was provided")
19
+ sanitized_name = re.sub(cls.NAME_REGEX, " ", value).strip()
20
+ return cls(sanitized_name)
@@ -0,0 +1,75 @@
1
+ from datetime import date, datetime, timezone
2
+ from typing import List
3
+
4
+ from pydantic import BaseModel, StrictStr, StrictInt, Field, EmailStr, model_validator
5
+
6
+ from gds_schemas.common.enums import (
7
+ PassengerTitle,
8
+ BookingType,
9
+ BookingStatus,
10
+ PaymentType,
11
+ CardType,
12
+ )
13
+ from gds_schemas.common.models.base import NameStr
14
+
15
+
16
+ class BasePassengerDetail(BaseModel):
17
+ id: StrictInt
18
+ first_name: NameStr = Field(examples=["John"])
19
+ middle_name: NameStr = Field(default="")
20
+ last_name: NameStr = Field(examples=["Doe"])
21
+ title: PassengerTitle
22
+ date_of_birth: date
23
+ email: EmailStr = Field(examples=["john_doe@example.com"], default=None)
24
+ phone_number: StrictStr = Field(examples=["+15551234567"], default=None)
25
+
26
+
27
+ class BaseContactDetails(BaseModel):
28
+ first_name: NameStr = Field(examples=["John"])
29
+ last_name: NameStr = Field(examples=["Doe"])
30
+ email: EmailStr = Field(examples=["john_doe@example.com"])
31
+ phone_number: StrictStr = Field(examples=["+15551234567"])
32
+ title: PassengerTitle | None = Field(default=None)
33
+
34
+
35
+ class BookingPassengerDetails(BaseModel):
36
+ id: StrictInt
37
+ first_name: NameStr
38
+ last_name: NameStr
39
+
40
+
41
+ class BookingContactDetails(BaseModel):
42
+ email: EmailStr
43
+ phone: StrictStr
44
+
45
+
46
+ class BaseBooking(BaseModel):
47
+ provider: StrictStr
48
+ booking_type: BookingType
49
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
50
+ passengers: List[BookingPassengerDetails] = Field(default_factory=list)
51
+ confirmation_code: StrictStr = Field(default="")
52
+ status: BookingStatus = Field(default=BookingStatus.ERROR)
53
+ contact_details: BookingContactDetails | dict = Field(default_factory=dict)
54
+ errors: List[StrictStr] = Field(default_factory=list)
55
+
56
+
57
+ class PaymentCardDetails(BaseModel):
58
+ card_type: CardType
59
+ card_number: StrictInt
60
+ expiry_date: StrictStr = Field(description="MM/YY")
61
+ card_holder_name: NameStr = Field(default="")
62
+ security_code: StrictInt = Field(title="Security Code/CVV of Card")
63
+ cvv: StrictStr = Field(default="", title="Security Code/CVV/PIN of Card")
64
+
65
+
66
+ class PaymentDetails(BaseModel):
67
+ type: PaymentType = Field(default=PaymentType.PAYMENT_CARD)
68
+ card_details: PaymentCardDetails | None = None
69
+
70
+ @model_validator(mode="after")
71
+ @classmethod
72
+ def check_card_details(cls, values):
73
+ if values.type == PaymentType.PAYMENT_CARD.value and not values.card_details:
74
+ raise ValueError("Card Details are required for Payment Card Type")
75
+ return values
@@ -0,0 +1,11 @@
1
+ import random
2
+ import string
3
+
4
+
5
+ def generate_random_id() -> str:
6
+ """
7
+ Generate a random search ID of specified length (default 8).
8
+ Uses uppercase letters and digits for better readability.
9
+ """
10
+ characters = string.ascii_uppercase + string.digits
11
+ return "".join(random.choices(characters, k=8))
File without changes
@@ -0,0 +1,266 @@
1
+ {
2
+ "A4F": "Antonov An-124 Ruslan",
3
+ "A40": "Antonov An-140",
4
+ "A81": "Antonov An-148",
5
+ "A58": "Antonov An-158",
6
+ "31N": "Airbus A319neo",
7
+ "32N": "Airbus A320neo",
8
+ "32Q": "Airbus A321neo",
9
+ "A5F": "Antonov An-225 Mriya",
10
+ "AB6": "Airbus A300-600",
11
+ "ABY": "Airbus A300-600 Freighter",
12
+ "AB4": "Airbus A300B2, A300B4, and A300C4",
13
+ "312": "Airbus A310-200",
14
+ "313": "Airbus A310-300",
15
+ "318": "Airbus A318",
16
+ "32C": "Airbus A318 (sharklets)",
17
+ "319": "Airbus A319",
18
+ "32D": "Airbus A319 (sharklets)",
19
+ "320": "Airbus A320",
20
+ "32A": "Airbus A320 (sharklets)",
21
+ "321": "Airbus A321",
22
+ "32B": "Airbus A321 (sharklets)",
23
+ "332": "Airbus A330-200",
24
+ "333": "Airbus A330-300",
25
+ "33X": "Airbus A330-200 Freighter",
26
+ "33Y": "Airbus A330-300 Freighter",
27
+ "338": "Airbus A330-800",
28
+ "339": "Airbus A330-900",
29
+ "342": "Airbus A340-200",
30
+ "343": "Airbus A340-300",
31
+ "345": "Airbus A340-500",
32
+ "346": "Airbus A340-600",
33
+ "359": "Airbus A350-900",
34
+ "351": "Airbus A350-1000",
35
+ "388": "Airbus A380-800",
36
+ "ABB": "Airbus A300-600ST Super Transporter/ Beluga",
37
+ "HS7": "Hawker Siddeley HS 748",
38
+ "ACP": "Gulfstream/Rockwell (Aero) Commander 680",
39
+ "ACT": "Gulfstream/Rockwell (Aero) Turbo Commander 690",
40
+ "C27": "COMAC ARJ21",
41
+ "ANF": "Antonov An-12",
42
+ "AN4": "Antonov An-24",
43
+ "A26": "Antonov An-26",
44
+ "A28": "Antonov An-28",
45
+ "A30": "Antonov An-30",
46
+ "A32": "Antonov An-32",
47
+ "AN7": "Antonov An-72 / An-74",
48
+ "APH": "Eurocopter AS332 Super Puma",
49
+ "NDE": "Eurocopter AS350 Écureuil / AS550 Fennec",
50
+ "AT4": "Aerospatiale/Alenia ATR 42-300 / 320",
51
+ "AT5": "Aerospatiale/Alenia ATR 42-500",
52
+ "AT7": "Aerospatiale/Alenia ATR 72-201/-202",
53
+ "ATR": "Aerospatiale/Alenia ATR 72-211/-212/-212A",
54
+ "ATP": "British Aerospace ATP",
55
+ "MBH": "Eurocopter (MBB) Bo.105",
56
+ "BEH": "Beechcraft 1900",
57
+ "BH2": "Bell 212/412/429",
58
+ "7M7": "Boeing 737 MAX 7",
59
+ "7M8": "Boeing 737 MAX 8",
60
+ "7M9": "Boeing 737 MAX 9",
61
+ "7MJ": "Boeing 737 MAX 10",
62
+ "141": "BAe 146-100 / Avro RJ70",
63
+ "142": "BAe 146-200 / Avro RJ85",
64
+ "143": "BAe 146-300 / Avro RJ100",
65
+ "703": "Boeing 707",
66
+ "717": "Boeing 717",
67
+ "B72": "Boeing 720B",
68
+ "721": "Boeing 727-100",
69
+ "722": "Boeing 727-200",
70
+ "732": "Boeing 737-200",
71
+ "73F": "Boeing 737-200 Freighter",
72
+ "733": "Boeing 737-300",
73
+ "73C": "Boeing 737-300 Winglets",
74
+ "73Y": "Boeing 737-300 Freighter",
75
+ "734": "Boeing 737-400",
76
+ "73P": "Boeing 737-400 Freighter",
77
+ "735": "Boeing 737-500",
78
+ "73E": "Boeing 737-500 Winglets",
79
+ "736": "Boeing 737-600",
80
+ "738": "Boeing 737-800",
81
+ "739": "Boeing 737-900 / Boeing 737-900ER",
82
+ "73G": "Boeing 737-700 / Boeing 737-700ER",
83
+ "73W": "Boeing 737-700 Winglets",
84
+ "73H": "Boeing 737-800 Winglets",
85
+ "73K": "Boeing 737-800 Freighter Winglets",
86
+ "73U": "Boeing 737-800 Freighter",
87
+ "73J": "Boeing 737-900 Winglets",
88
+ "741": "Boeing 747-100",
89
+ "74T": "Boeing 747-100 Freighter",
90
+ "742": "Boeing 747-200",
91
+ "74C": "Boeing 747-200M",
92
+ "74X": "Boeing 747-200F",
93
+ "743": "Boeing 747-300",
94
+ "74D": "Boeing 747-300M",
95
+ "744": "Boeing 747-400 / Boeing 747-400ER",
96
+ "74E": "Boeing 747-400M",
97
+ "74Y": "Boeing 747-400F / Boeing 747-400ERF",
98
+ "74H": "Boeing 747-8I",
99
+ "74N": "Boeing 747-8F",
100
+ "74R": "Boeing 747SR",
101
+ "74V": "Boeing 747SR Freighter",
102
+ "74L": "Boeing 747SP",
103
+ "752": "Boeing 757-200",
104
+ "75F": "Boeing 757F",
105
+ "753": "Boeing 757-300",
106
+ "762": "Boeing 767-200 / Boeing 767-200ER",
107
+ "76X": "Boeing 767-200 Freighter / Boeing 767-200ER",
108
+ "763": "Boeing 767-300",
109
+ "76W": "Boeing 767-300 Winglets / Boeing 767-300ER",
110
+ "76Y": "Boeing 767-300 Freighter",
111
+ "764": "Boeing 767-400ER",
112
+ "772": "Boeing 777-200 / Boeing 777-200ER",
113
+ "773": "Boeing 777-300",
114
+ "778": "Boeing 777-8",
115
+ "779": "Boeing 777-9",
116
+ "77X": "Boeing 777-200 Freighter",
117
+ "77L": "Boeing 777-200LR",
118
+ "77W": "Boeing 777-300ER",
119
+ "788": "Boeing 787-8",
120
+ "789": "Boeing 787-9",
121
+ "781": "Boeing 787-10",
122
+ "B11": "British Aerospace (BAC) One Eleven",
123
+ "221": "Airbus A220-100",
124
+ "223": "Airbus A220-300",
125
+ "SHB": "Shorts SC-5 Belfast",
126
+ "74B": "Boeing 747 LCF Dreamlifter",
127
+ "BNI": "Pilatus Britten-Norman BN-2A/B Islander",
128
+ "LOH": "Lockheed C-130/ L-182 / 282 / 382 (L-100) Hercules",
129
+ "CN1": "Cessna 208 Caravan",
130
+ "CS2": "CASA / IPTN 212 Aviocar",
131
+ "CWC": "Curtiss C-46 Commando",
132
+ "CNJ": "Cessna Citation CJ2/CJ3/CJ4",
133
+ "CCJ": "Canadair Challenger 600",
134
+ "CS5": "CASA/IPTN CN-235",
135
+ "L49": "Lockheed L-1049 Super Constellation",
136
+ "CR1": "Canadair Regional Jet 100",
137
+ "CR2": "Canadair Regional Jet 200",
138
+ "CR7": "Canadair Regional Jet 700 | Regional Jet 550",
139
+ "CR9": "Canadair Regional Jet 900",
140
+ "CRK": "Canadair Regional Jet 1000",
141
+ "CV4": "Convair CV-240 & -440",
142
+ "CV5": "Convair CV-580, Convair CV-600, Convair CV-640",
143
+ "D28": "Dornier 228",
144
+ "D38": "Fairchild Dornier Do.328",
145
+ "D11": "Douglas DC-10-10 / -15 Passenger",
146
+ "D1C": "Douglas DC-10-30 / -40 Passenger",
147
+ "D1M": "Douglas DC-10-30 Combi",
148
+ "D1X": "Douglas DC-10-10 Freighter",
149
+ "D1Y": "Douglas DC-10-30 / -40 Freighter",
150
+ "D3F": "Douglas DC-3",
151
+ "D6F": "Douglas DC-6",
152
+ "D8T": "Douglas DC-8-50",
153
+ "D8L": "Douglas DC-8-62",
154
+ "D8Q": "Douglas DC-8-72",
155
+ "D91": "Douglas DC-9-10",
156
+ "D92": "Douglas DC-9-20",
157
+ "D93": "Douglas DC-9-30",
158
+ "D94": "Douglas DC-9-40",
159
+ "D95": "Douglas DC-9-50",
160
+ "DHR": "De Havilland Canada DHC-2 Turbo-Beaver",
161
+ "DH1": "De Havilland Canada DHC-8-100 Dash 8 / 8Q",
162
+ "DH2": "De Havilland Canada DHC-8-200 Dash 8 / 8Q",
163
+ "DH3": "De Havilland Canada DHC-8-300 Dash 8 / 8Q",
164
+ "DH4": "De Havilland Canada DHC-8-400 Dash 8Q",
165
+ "DHC": "De Havilland Canada DHC-4/-5",
166
+ "DHT": "De Havilland Canada DHC-6 Twin Otter",
167
+ "DH7": "De Havilland Canada DHC-7 Dash 7",
168
+ "DHD": "De Havilland DH.104 Dove",
169
+ "EMB": "Embraer EMB 110 Bandeirante",
170
+ "EM2": "Embraer EMB 120 Brasilia",
171
+ "ER3": "Embraer RJ135/Legacy 600/Legacy 650",
172
+ "ERD": "Embraer RJ140",
173
+ "ER4": "Embraer RJ145",
174
+ "E70": "Embraer 170",
175
+ "E90": "Embraer 190 / Lineage 1000",
176
+ "E95": "Embraer 195",
177
+ "290": "Embraer E190-E2",
178
+ "295": "Embraer E195-E2",
179
+ "EP1": "Embraer Phenom 100",
180
+ "EP3": "Embraer Phenom 300",
181
+ "E7W": "Embraer 175 (long wing)",
182
+ "E75": "Embraer 175 (short wing)",
183
+ "MD9": "MD Helicopters MD900 Explorer",
184
+ "100": "Fokker 100",
185
+ "F27": "Fokker F27 Friendship",
186
+ "F21": "Fokker F28 Fellowship",
187
+ "D20": "Dassault Falcon 2000",
188
+ "CNT": "Reims-Cessna F406 Caravan II",
189
+ "F50": "Fokker 50",
190
+ "F70": "Fokker 70",
191
+ "DF9": "Dassault Falcon 900",
192
+ "DF3": "Dassault Falcon 50",
193
+ "DF7": "Dassault Falcon 7X",
194
+ "GRS": "Gulfstream Aerospace G-159 Gulfstream I",
195
+ "GRG": "Grumman G-21 Goose",
196
+ "GR3": "Gulfstream G280",
197
+ "GRM": "Grumman G-73 Turbo Mallard",
198
+ "CCX": "Bombardier BD-700 Global 5000/Express",
199
+ "GJ4": "Gulfstream IV",
200
+ "GJ5": "Gulfstream V",
201
+ "GJ6": "Gulfstream G650",
202
+ "H25": "British Aerospace 125 series / Hawker/Raytheon 700/800/800XP/850/900",
203
+ "HHJ": "Honda HA-420",
204
+ "DHH": "De Havilland DH.114 Heron",
205
+ "I14": "Ilyushin Il-114",
206
+ "IL8": "Ilyushin Il-18",
207
+ "IL6": "Ilyushin Il-62",
208
+ "IL7": "Ilyushin Il-76",
209
+ "ILW": "Ilyushin Il-86",
210
+ "I93": "Ilyushin Il-96",
211
+ "FRJ": "Fairchild Dornier 328JET",
212
+ "J31": "British Aerospace Jetstream 31",
213
+ "J32": "British Aerospace Jetstream 32",
214
+ "J41": "British Aerospace Jetstream 41",
215
+ "JU5": "Junkers Ju 52/3M",
216
+ "K35": "Boeing KC-135 Stratotanker",
217
+ "L10": "Lockheed L-1011 Tristar",
218
+ "LOE": "Lockheed L-188 Electra",
219
+ "L4T": "LET 410",
220
+ "LRJ": "Learjet 35 / 36 / C-21A / 60",
221
+ "M11": "McDonnell Douglas MD-11",
222
+ "M1F": "McDonnell Douglas MD-11F",
223
+ "M1M": "McDonnell Douglas MD-11C",
224
+ "M81": "McDonnell Douglas MD-81",
225
+ "M82": "McDonnell Douglas MD-82",
226
+ "M83": "McDonnell Douglas MD-83",
227
+ "M87": "McDonnell Douglas MD-87",
228
+ "M88": "McDonnell Douglas MD-88",
229
+ "M90": "McDonnell Douglas MD-90",
230
+ "MIH": "MIL Mi-8 / Mi-17 / Mi-171 / Mil-172",
231
+ "MU2": "Mitsubishi Mu-2",
232
+ "ND2": "Aerospatiale (Nord) 262",
233
+ "CD2": "Government Aircraft Factories N22B / N24A Nomad",
234
+ "P18": "Piaggio P.180 Avanti",
235
+ "T12": "Tecnam P2012 Traveller",
236
+ "PN6": "Partenavia P.68",
237
+ "PA2": "Piper PA-31 Navajo",
238
+ "PL2": "Pilatus PC-12",
239
+ "PL6": "Pilatus PC-6 Turbo Porter",
240
+ "PL4": "Pilatus PC-24",
241
+ "AR1": "Avro RJ100",
242
+ "AR7": "Avro RJ70",
243
+ "AR8": "Avro RJ85",
244
+ "S58": "Sikorsky S-58T",
245
+ "NDC": "Aerospatiale SN.601 Corvette",
246
+ "S61": "Sikorsky S-61",
247
+ "NDH": "Eurocopter (Aerospatiale) SA365C Dauphin 2",
248
+ "S76": "Sikorsky S-76",
249
+ "S92": "Sikorsky S-92",
250
+ "S20": "Saab 2000",
251
+ "SHS": "Shorts SC-7 Skyvan",
252
+ "SF3": "Saab SF340A/B",
253
+ "SH3": "Shorts SD.330",
254
+ "SH6": "Shorts SD.360",
255
+ "SU9": "Sukhoi Superjet 100-95",
256
+ "SW4": "Fairchild Swearingen Metroliner",
257
+ "TU3": "Tupolev Tu-134",
258
+ "TU5": "Tupolev Tu-154",
259
+ "T20": "Tupolev Tu-204 / Tu-214",
260
+ "BNT": "Pilatus Britten-Norman BN-2A Mk III Trislander",
261
+ "WWP": "Israel Aircraft Industries 1124 Westwind",
262
+ "YN2": "Harbin Y-12",
263
+ "YK4": "Yakovlev Yak-40",
264
+ "YK2": "Yakovlev Yak-42",
265
+ "YS1": "NAMC YS-11"
266
+ }