WebSDK is a library for quickly and easily creating SDKs for integration with third-party APIs.
Project description
WebSDK is a library for quickly and easily creating SDKs for integration with third-party APIs.
Installation
REST installation
Install using pip install web-sdk[rest] or uv add web-sdk[rest]
If you want to use web_sdk.sdks.rest.XmlResponse
Install using pip install web-sdk[rest,xml] or uv add web-sdk[rest,xml]
SOAP installation
Install using pip install web-sdk[soap] or uv add web-sdk[soap]
Minimal example
Let's imagine that we have the following data schemas
# docs/examples/home/minimal/dtos.py
from pydantic import BaseModel
# Response short data structure
class ShortData(BaseModel):
pk: int = 1
q: bool | None = None
# Response data structure
class Data(ShortData):
nested: ShortData
To make the example simpler, we'll write the server part using fastapi.
# docs/examples/home/minimal/server.py
from fastapi import FastAPI
# Create FastAPI app
app = FastAPI(root_path="/api/v1")
[@app](https://github.com/app).get("/data/{pk}/info")
async def get_data_info(pk: int, q: bool | None = None) -> Data:
"""Return full data info."""
return Data(pk=pk, q=q, nested=ShortData(pk=pk, q=q))
# route with short data return
[@app](https://github.com/app).get("/data/{pk}/info/short")
async def get_short_data_info(pk: int, q: bool | None = None) -> ShortData:
"""Return short data info."""
return ShortData(pk=pk, q=q)
To link with the routes declare in server code, you only need the following client code
# docs/examples/home/minimal/client.py
from web_sdk.core.fields import APath
from web_sdk.enums import HTTPMethod
from web_sdk.sdks.rest import Client, ClientService, JsonResponse, Method, Service, Settings, get_res
# declare service for group of methods
class FooService(Service, path="data/{pk}/info"):
get_data = Method[JsonResponse[Data]](method=HTTPMethod.GET)
# declare method with return type and path (default method is GET)
get_short_data = Method[JsonResponse[ShortData]](path="short")
# declare client service for group of real methods in client class
class FooClientService(ClientService):
# declare method with certain signature pk is path part,
# q is param (param type is default for GET method)
[@FooService](https://github.com/FooService).get_data
def get_data(self, pk: APath[int], q: bool | None = None) -> JsonResponse[Data]: ...
get_short_data = FooService.get_short_data.from_method(get_data)
# declare client class
class FooClient(Client):
# set client services as annotation
service: FooClientService
All you have to do next is init the client and call methods you need.
# docs/examples/home/minimal/usage.py
# init client settings
settings = Settings(protocol="http", host="127.0.0.1", api_path="api/v1", port=8000)
# init client instance
client = FooClient(settings=settings)
# make get_data request
data_response = client.service.get_data(pk=1, q=True)
# extract data from response
data = get_res(data_response)
# Data(pk=1, q=True, nested=ShortData(pk=1, q=True))
# make get_short_data request
short_data_response = client.service.get_short_data(pk=1, q=True)
# extract data from response
short_data = get_res(short_data_response)
# ShortData(pk=1, q=True)
Features
- Annotation like request parts mapper
- All requests methods support
- Pydantic validation output and input data
- File sending
- Custom extra and context data during request
- Errors logging
- Custom client settings
- Custom and token auth
- Test mode settings support
- Requests REST support
- Service declaration base request configuring
- Method declaration base request configuring
- Method call base request configuring
- Request call base request configuring
- Path part mapping without field annotation
- Requests SOAP support
- Custom transport for file sending
- Service declaration base request configuring
- Method declaration base request configuring
- Method call base request configuring
- Request call base request configuring
- HTTPX REST support
- HTTPX SOAP support
- MkDocs documentation
Supported backends
Sync backends
Requests REST
Client and utils for declare the sync SDK based on requests.
Declare custom or use default settings
# docs/examples/home/sync/rest/settings.py
from pydantic_settings import SettingsConfigDict
from web_sdk.sdks.rest import Settings
class FooSettings(Settings):
protocol: str = "https"
"""API protocol"""
host: str = "example.com"
"""API host"""
port: int | None = 8000
"""API port"""
api_path: str = "/api/v1"
"""API path"""
model_config = SettingsConfigDict(
env_prefix="FOO_CLIENT_",
)
Create responses schemas
# docs/examples/home/sync/rest/schemas.py
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel
from web_sdk.sdks.rest import JsonResponse
class PaymentShortInfoDTO(BaseModel):
id: str
is_success: bool
class PaymentInfoDTO(PaymentShortInfoDTO):
order_id: str
payment_date: datetime
payment_amount: Decimal
class OrderShortInfoDTO(BaseModel):
id: str
reference: str
class OrderInfoDTO(OrderShortInfoDTO):
class _TargetDTO(BaseModel):
id: str
type: str
price: Decimal
# it is working with nested model
target: _TargetDTO
GetPaymentResponse = JsonResponse[PaymentInfoDTO]
# it is working with list data
GetPaymentsResponse = JsonResponse[list[PaymentInfoDTO]]
MakePaymentResponse = JsonResponse[PaymentShortInfoDTO]
GetOrderResponse = JsonResponse[OrderInfoDTO]
Declare services with methods for using in client
# docs/examples/home/sync/rest/methods.py
from web_sdk.enums import HTTPMethod
from web_sdk.sdks.rest import Method, Service
from . import schemas
class PaymentsService(
Service,
path="payments",
description="Payments service",
):
get = Method[schemas.GetPaymentResponse](
path="{payment_id}",
description="Get payment by id",
) # full path is "{settings.url}/payments/{payment_id}"
make = Method[schemas.MakePaymentResponse](
method=HTTPMethod.POST,
path="make/{order_id}",
description="Make payment for order",
) # full path is "{settings.url}/payments/make/{order_id}"
class OrdersService(
Service,
path="orders/{order_id}",
description="Get order information by id",
):
get = Method[schemas.GetOrderResponse](
description="Get order information",
) # full path is "{settings.url}/orders/{order_id}"
payments = Method[schemas.GetPaymentsResponse](
path="payments",
description="Get order payments",
) # full path is "{settings.url}/orders/{order_id}/payments"
Declare client and client services
# docs/examples/home/sync/rest/client.py
from decimal import Decimal
from typing import Annotated
from typing_extensions import Unpack
from web_sdk.core.backends.requests.rest.kwargs import RestRequestsKwargsWithSettings
from web_sdk.core.fields import APath, ASetting, Body, Param, Path
from web_sdk.sdks.rest import Client, ClientService
from . import schemas
from .methods import OrdersService, PaymentsService
from .settings import FooSettings
class BaseFooClient(Client, base=True):
"""Here you can customize the client logic to suit your needs."""
__default_settings_class__ = FooSettings
class FooClientService(ClientService[BaseFooClient], client=BaseFooClient):
"""This class need for isolate subclasses registering in user class."""
class PaymentsClientService(FooClientService):
[@PaymentsService](https://github.com/PaymentsService).get
def get(
self,
# ALike aliases (shortcut for Annotated[T, Field])
payment_id: APath[int],
# Unpack with TypedDict
**kwargs: Unpack[RestRequestsKwargsWithSettings],
) -> schemas.GetPaymentResponse: ...
[@PaymentsService](https://github.com/PaymentsService).make(timeout=5)
def make(
self,
# Annotated[T, Field] like annotations
order_id: Annotated[str, Path],
# Other variant with Field call
amount: Annotated[Decimal, Body(ge=Decimal("0"))],
# Field as default value. You can also use a field without
# specifying a default value, then the field will be
# required (arg: bool = Param) or (arg: bool = Param()).
immediately: bool | None = Param(None), # type: ignore
# Using settings to change Client.make_request behavior
raise_exception: ASetting[bool | None] = None,
) -> schemas.MakePaymentResponse: ...
class OrdersClientService(FooClientService):
[@OrdersService](https://github.com/OrdersService).get
def get(self, order_id: APath[int]) -> schemas.GetOrderResponse: ...
[@OrdersService](https://github.com/OrdersService).get
def payments(
self,
order_id: APath[int],
# For GET, DELETE, OPTION, HEAD methods default field is Param,
# for POST, PATCH, PUT methods default field id Body
success_only: bool = False,
) -> schemas.GetPaymentsResponse: ...
class FooClient(BaseFooClient):
payments: PaymentsClientService
orders: PaymentsClientService
Usage example
# docs/examples/home/sync/rest/usage.py
from web_sdk.core.utils import make_client_factory
from web_sdk.sdks.rest import get_res
from .client import FooClient
from .settings import FooSettings
# You can just create client instance
# client = FooClient()
# But I recommend creating a client factory to cache instances
# based on the settings and logger hashes to avoid creating duplicate instances
client_factory = make_client_factory(FooClient, FooSettings)
# Create client
client = client_factory()
# Method response or error response if you use raise_exceptions=False
# or None if you use skip_for_tests
response = client.payments.get(
payment_id=1,
timeout=1,
raise_exceptions=False,
)
result_or_none = get_res(response, required=False)
result = get_res(response)
Requests SOAP
Client and utils for declare the sync SDK based on requests, and zeep.
Declare custom or use default settings
# docs/examples/home/sync/soap/settings.py
from pydantic_settings import SettingsConfigDict
from web_sdk.sdks.soap import Settings
class FooSettings(Settings):
protocol: str = "https"
"""API protocol"""
host: str = "example.com"
"""API host"""
port: int | None = 8000
"""API port"""
api_path: str = "/api/v1"
"""API path"""
service_name: str | None = "service"
"""The name of wsdl service."""
port_name: str | None = "port"
"""The name of wsdl port."""
model_config = SettingsConfigDict(
env_prefix="FOO_CLIENT_",
)
Create responses schemas
# docs/examples/home/sync/soap/schemas.py
from datetime import datetime
from decimal import Decimal
from typing import Generic
from pydantic import BaseModel
from web_sdk.contrib.pydantic.models import PydanticModel
from web_sdk.core.bases.soap import SoapFile
from web_sdk.sdks.soap import SoapResponse
from web_sdk.types import TData
class PaymentShortInfoDTO(PydanticModel):
id: str
is_success: bool
class PaymentInfoDTO(PydanticModel):
order_id: str
payment_date: datetime
payment_amount: Decimal
document: SoapFile
class PaymentsInfosDTO(PydanticModel):
payments: list[PaymentInfoDTO]
class OrderShortInfoDTO(PydanticModel):
id: str
reference: str
class OrderInfoDTO(PydanticModel):
class _TargetDTO(BaseModel):
id: str
type: str
price: Decimal
# it is working with nested model
target: _TargetDTO
class FooResponse(SoapResponse, Generic[TData]):
success: bool
data: TData
GetPaymentResponse = FooResponse[PaymentInfoDTO]
GetPaymentsResponse = FooResponse[list[PaymentInfoDTO]]
MakePaymentResponse = FooResponse[PaymentShortInfoDTO]
GetOrderResponse = FooResponse[OrderInfoDTO]
Declare services with methods for using in client
# docs/examples/home/sync/soap/methods.py
from web_sdk.sdks.soap import Method, Service
from . import schemas
class PaymentsService(
Service,
path="Payments",
description="Payments service",
):
get = Method[schemas.GetPaymentResponse](
path="getPayment",
description="Get payment by id",
) # method path is "Payments.getPayment"
make = Method[schemas.MakePaymentResponse](
path="makePayment",
description="Make payment for order",
) # method path is "Payments.makePayment"
class OrdersService(
Service,
description="Get order information by id",
):
get = Method[schemas.GetOrderResponse](
description="Get order information",
) # method path is "get"
payments = Method[schemas.GetPaymentsResponse](
path="paymentsWithPath",
description="Get order payments",
) # method path is "paymentsWithPath"
Declare client and client services
# docs/examples/home/sync/soap/client.py
from decimal import Decimal
from typing import Annotated
from typing_extensions import Unpack
from web_sdk.core.backends.requests.soap.kwargs import SoapRequestsKwargsWithSettings
from web_sdk.core.fields import AFile, ASetting, Body
from web_sdk.sdks.soap import Client, ClientService, SoapFile
from . import schemas
from .methods import OrdersService, PaymentsService
from .settings import FooSettings
class BaseFooClient(Client, base=True):
"""Here you can customize the client logic to suit your needs."""
__default_settings_class__ = FooSettings
class FooClientService(ClientService[BaseFooClient], client=BaseFooClient):
"""This class need for isolate subclasses registering in user class."""
# For soap client Body is base field type
class PaymentsClientService(FooClientService):
[@PaymentsService](https://github.com/PaymentsService).get
def get(
self,
# ALike aliases (shortcut for Annotated[T, Field])
payment_id: int,
# Unpack with TypedDict
**kwargs: Unpack[SoapRequestsKwargsWithSettings],
) -> schemas.GetPaymentResponse: ...
[@PaymentsService](https://github.com/PaymentsService).make
def make(
self,
# Annotated[T, Field] like annotations
order_id: Annotated[str, Body],
# Other variant with Field call
amount: Annotated[Decimal, Body(ge=Decimal("0"))],
# Field as default value. You can also use a field without
# specifying a default value, then the field will be
# required (arg: bool = Body) or (arg: bool = Body()).
immediately: bool | None = Body(None), # type: ignore
# Send single file with request
payment_file: AFile[SoapFile | None] = None,
# Send multiple files with request
other_files: AFile[list[SoapFile] | None] = None,
# Using settings to change Client.make_request behavior
raise_exception: ASetting[bool | None] = None,
) -> schemas.MakePaymentResponse: ...
class OrdersClientService(FooClientService):
[@OrdersService](https://github.com/OrdersService).get
def get(self, order_id: int) -> schemas.GetOrderResponse: ...
[@OrdersService](https://github.com/OrdersService).get
def payments(
self,
order_id: int,
success_only: bool = False,
) -> schemas.GetPaymentsResponse: ...
class FooClient(BaseFooClient):
payments: PaymentsClientService
orders: PaymentsClientService
Usage example
# docs/examples/home/sync/soap/usage.py
from decimal import Decimal
from web_sdk.core.bases.soap import SoapFile
from web_sdk.core.utils import make_client_factory
from .client import FooClient
from .settings import FooSettings
# You can just create client instance
# client = FooClient()
# But I recommend creating a client factory to cache instances
# based on the settings and logger hashes to avoid creating duplicate instances
client_factory = make_client_factory(FooClient, FooSettings)
# Create client
client = client_factory()
# Method response or error response if you use raise_exceptions=False
# or None if you use skip_for_tests
response = client.payments.make(
order_id="123",
amount=Decimal("100"),
payment_file=SoapFile(
filename="payment.txt",
content_type="text/plain",
content=b"content",
),
raise_exception=False,
)
Async backends
Planned...
Httpx REST
Planned...
Httpx SOAP
Planned...
Changelog
v0.1.3
- Add docs
v0.1.2
- Add minimal docs
v0.1.1
- Add project structure
- Add core logic for requests soap and rest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file web_sdk-0.1.3.tar.gz.
File metadata
- Download URL: web_sdk-0.1.3.tar.gz
- Upload date:
- Size: 66.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17c8c3e23f7a276a2e13399fb83c6e28932ccb5d2f652773b2a44da3b87ac77f
|
|
| MD5 |
0f2c37ebbf23503e4abcdff0d168265f
|
|
| BLAKE2b-256 |
dcc2b4dbf461188feb89210367dd75e7ca76c0e20a89c7ab02e38b0dc729ed95
|
Provenance
The following attestation bundles were made for web_sdk-0.1.3.tar.gz:
Publisher:
ci.yml on extralait-web/web-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
web_sdk-0.1.3.tar.gz -
Subject digest:
17c8c3e23f7a276a2e13399fb83c6e28932ccb5d2f652773b2a44da3b87ac77f - Sigstore transparency entry: 847793245
- Sigstore integration time:
-
Permalink:
extralait-web/web-sdk@76365f88ab4886fd23d43bbae5e7259df8729552 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/extralait-web
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@76365f88ab4886fd23d43bbae5e7259df8729552 -
Trigger Event:
push
-
Statement type:
File details
Details for the file web_sdk-0.1.3-py3-none-any.whl.
File metadata
- Download URL: web_sdk-0.1.3-py3-none-any.whl
- Upload date:
- Size: 66.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc1a3334cd432c1e962c38aa0f068def4cc3e1cfe200e69c07e556c1e748bd62
|
|
| MD5 |
db12912bed98f4fe13630703aa51d193
|
|
| BLAKE2b-256 |
383e5eca806bdba1cbe321858be244baa43839f259ec2684d4a40d4369124691
|
Provenance
The following attestation bundles were made for web_sdk-0.1.3-py3-none-any.whl:
Publisher:
ci.yml on extralait-web/web-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
web_sdk-0.1.3-py3-none-any.whl -
Subject digest:
bc1a3334cd432c1e962c38aa0f068def4cc3e1cfe200e69c07e556c1e748bd62 - Sigstore transparency entry: 847793323
- Sigstore integration time:
-
Permalink:
extralait-web/web-sdk@76365f88ab4886fd23d43bbae5e7259df8729552 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/extralait-web
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@76365f88ab4886fd23d43bbae5e7259df8729552 -
Trigger Event:
push
-
Statement type: