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.
- af_gds_client-0.0.1/PKG-INFO +23 -0
- af_gds_client-0.0.1/README.md +5 -0
- af_gds_client-0.0.1/gds_client/README.md +4 -0
- af_gds_client-0.0.1/gds_client/__init__.py +4 -0
- af_gds_client-0.0.1/gds_client/client.py +114 -0
- af_gds_client-0.0.1/gds_client/config.py +15 -0
- af_gds_client-0.0.1/gds_client/pyproject.toml +19 -0
- af_gds_client-0.0.1/gds_schemas/common/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/common/enums.py +73 -0
- af_gds_client-0.0.1/gds_schemas/common/models/__init__.py +19 -0
- af_gds_client-0.0.1/gds_schemas/common/models/base.py +20 -0
- af_gds_client-0.0.1/gds_schemas/common/models/book.py +75 -0
- af_gds_client-0.0.1/gds_schemas/common/utils.py +11 -0
- af_gds_client-0.0.1/gds_schemas/flights/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/flights/data/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/flights/data/aircraft.json +266 -0
- af_gds_client-0.0.1/gds_schemas/flights/data/airlines.json +1003 -0
- af_gds_client-0.0.1/gds_schemas/flights/data/airports.json +95602 -0
- af_gds_client-0.0.1/gds_schemas/flights/data/countries.json +247 -0
- af_gds_client-0.0.1/gds_schemas/flights/data/imported_data.py +108 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/constants.py +42 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/enums.py +243 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/__init__.py +181 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/booking.py +177 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/booking_intent.py +39 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/cache_data.py +71 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/cancel_booking.py +19 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/cancel_ticket.py +31 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/common.py +80 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/get_booking.py +790 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/revalidate.py +31 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/search.py +552 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/seat_selection.py +83 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/ticket_issue.py +74 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1/models/ticket_status.py +18 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1_5/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1_5/models/__init__.py +19 -0
- af_gds_client-0.0.1/gds_schemas/flights/v1_5/models/search.py +50 -0
- af_gds_client-0.0.1/gds_schemas/hotels/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/__init__.py +0 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/enums.py +35 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/__init__.py +75 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/base.py +70 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/booking.py +58 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/booking_intent.py +28 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/constants.py +3 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/content.py +119 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/details.py +72 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/revalidate.py +46 -0
- af_gds_client-0.0.1/gds_schemas/hotels/v1/models/search.py +78 -0
- af_gds_client-0.0.1/pyproject.toml +25 -0
- af_gds_client-0.0.1/src/allfly/__init__.py +0 -0
- af_gds_client-0.0.1/src/allfly/gds/__init__.py +0 -0
- af_gds_client-0.0.1/src/allfly/gds/client/__init__.py +3 -0
- af_gds_client-0.0.1/src/allfly/gds/schemas/__init__.py +0 -0
- af_gds_client-0.0.1/src/allfly/gds/schemas/common/__init__.py +1 -0
- af_gds_client-0.0.1/src/allfly/gds/schemas/flights/__init__.py +1 -0
- 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,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
|
|
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
|
+
}
|