diff --git a/.fern/metadata.json b/.fern/metadata.json new file mode 100644 index 0000000..fc2c8d6 --- /dev/null +++ b/.fern/metadata.json @@ -0,0 +1,13 @@ +{ + "cliVersion": "3.5.0", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "4.44.0", + "generatorConfig": { + "client": { + "class_name": "Client", + "filename": "client.py", + "exported_class_name": "Pipedream", + "exported_filename": "pipedream.py" + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 4db038a..0d0af28 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,14 @@ client = Pipedream( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) -response = client.apps.list() +response = client.apps.list( + after="after", + before="before", + limit=1, + q="q", + sort_key="name", + sort_direction="asc", +) for item in response: yield item # alternatively, you can paginate page-by-page @@ -112,6 +119,15 @@ for page in response.iter_pages(): yield page ``` +```python +# You can also iterate through pages and access the typed response per page +pager = client.apps.list(...) +for page in pager.iter_pages(): + print(page.response) # access the typed response for each page + for item in page: + print(item) +``` + ## Advanced ### Access Raw Response Data @@ -129,11 +145,11 @@ response = client.actions.with_raw_response.run(...) print(response.headers) # access the response headers print(response.data) # access the underlying object pager = client.apps.list(...) -print(pager.response.headers) # access the response headers for the first page +print(pager.response) # access the typed response for the first page for item in pager: print(item) # access the underlying object(s) for page in pager.iter_pages(): - print(page.response.headers) # access the response headers for each page + print(page.response) # access the typed response for each page for item in page: print(item) # access the underlying object(s) ``` diff --git a/poetry.lock b/poetry.lock index a19d7e2..9decbf8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -75,6 +75,20 @@ typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + [[package]] name = "h11" version = "0.16.0" @@ -418,6 +432,26 @@ pytest = ">=7.0.0,<9" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -557,4 +591,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8551b871abee465e23fb0966d51f2c155fd257b55bdcb0c02d095de19f92f358" +content-hash = "bcf31a142c86d9e556553c8c260a93b563ac64a043076dbd48b26111d422c26e" diff --git a/pyproject.toml b/pyproject.toml index 2af2770..376f1d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,10 @@ [project] name = "pipedream" +dynamic = ["version"] [tool.poetry] name = "pipedream" -version = "1.0.12" +version = "1.0.13" description = "" readme = "README.md" authors = [] @@ -30,7 +31,7 @@ packages = [ { include = "pipedream", from = "src"} ] -[project.urls] +[tool.poetry.urls] Repository = 'https://github.com/PipedreamHQ/pipedream-sdk-python' [tool.poetry.dependencies] @@ -44,6 +45,7 @@ typing_extensions = ">= 4.0.0" mypy = "==1.13.0" pytest = "^7.4.0" pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" python-dateutil = "^2.9.0" types-python-dateutil = "^2.9.0.20240316" ruff = "==0.11.5" diff --git a/src/pipedream/accounts/client.py b/src/pipedream/accounts/client.py index 4c97f9d..79d552f 100644 --- a/src/pipedream/accounts/client.py +++ b/src/pipedream/accounts/client.py @@ -6,6 +6,7 @@ from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions from ..types.account import Account +from ..types.list_accounts_response import ListAccountsResponse from .raw_client import AsyncRawAccountsClient, RawAccountsClient # this is used as the default value for optional parameters @@ -38,7 +39,7 @@ def list( app: typing.Optional[str] = None, include_credentials: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Account]: + ) -> SyncPager[Account, ListAccountsResponse]: """ Retrieve all connected accounts for the project with optional filtering @@ -69,7 +70,7 @@ def list( Returns ------- - SyncPager[Account] + SyncPager[Account, ListAccountsResponse] accounts listed Examples @@ -82,7 +83,15 @@ def list( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) - response = client.accounts.list() + response = client.accounts.list( + external_user_id="external_user_id", + oauth_app_id="oauth_app_id", + after="after", + before="before", + limit=1, + app="app", + include_credentials=True, + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -152,6 +161,8 @@ def create( client_secret="YOUR_CLIENT_SECRET", ) client.accounts.create( + external_user_id="external_user_id", + oauth_app_id="oauth_app_id", app_slug="app_slug", cfmap_json="cfmap_json", connect_token="connect_token", @@ -205,6 +216,7 @@ def retrieve( ) client.accounts.retrieve( account_id="account_id", + include_credentials=True, ) """ _response = self._raw_client.retrieve( @@ -303,7 +315,7 @@ async def list( app: typing.Optional[str] = None, include_credentials: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Account]: + ) -> AsyncPager[Account, ListAccountsResponse]: """ Retrieve all connected accounts for the project with optional filtering @@ -334,7 +346,7 @@ async def list( Returns ------- - AsyncPager[Account] + AsyncPager[Account, ListAccountsResponse] accounts listed Examples @@ -352,7 +364,15 @@ async def list( async def main() -> None: - response = await client.accounts.list() + response = await client.accounts.list( + external_user_id="external_user_id", + oauth_app_id="oauth_app_id", + after="after", + before="before", + limit=1, + app="app", + include_credentials=True, + ) async for item in response: yield item @@ -431,6 +451,8 @@ async def create( async def main() -> None: await client.accounts.create( + external_user_id="external_user_id", + oauth_app_id="oauth_app_id", app_slug="app_slug", cfmap_json="cfmap_json", connect_token="connect_token", @@ -492,6 +514,7 @@ async def retrieve( async def main() -> None: await client.accounts.retrieve( account_id="account_id", + include_credentials=True, ) diff --git a/src/pipedream/accounts/raw_client.py b/src/pipedream/accounts/raw_client.py index ac44791..9539d78 100644 --- a/src/pipedream/accounts/raw_client.py +++ b/src/pipedream/accounts/raw_client.py @@ -7,7 +7,7 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder -from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..errors.too_many_requests_error import TooManyRequestsError @@ -33,7 +33,7 @@ def list( app: typing.Optional[str] = None, include_credentials: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Account]: + ) -> SyncPager[Account, ListAccountsResponse]: """ Retrieve all connected accounts for the project with optional filtering @@ -64,7 +64,7 @@ def list( Returns ------- - SyncPager[Account] + SyncPager[Account, ListAccountsResponse] accounts listed """ _response = self._client_wrapper.httpx_client.request( @@ -106,16 +106,14 @@ def list( include_credentials=include_credentials, request_options=request_options, ) - return SyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) - ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -199,9 +197,9 @@ def create( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -258,9 +256,9 @@ def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -297,9 +295,9 @@ def delete(self, account_id: str, *, request_options: typing.Optional[RequestOpt raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -338,9 +336,9 @@ def delete_by_app( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -366,7 +364,7 @@ async def list( app: typing.Optional[str] = None, include_credentials: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Account]: + ) -> AsyncPager[Account, ListAccountsResponse]: """ Retrieve all connected accounts for the project with optional filtering @@ -397,7 +395,7 @@ async def list( Returns ------- - AsyncPager[Account] + AsyncPager[Account, ListAccountsResponse] accounts listed """ _response = await self._client_wrapper.httpx_client.request( @@ -442,16 +440,14 @@ async def _get_next(): request_options=request_options, ) - return AsyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) - ) + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -535,9 +531,9 @@ async def create( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -594,9 +590,9 @@ async def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -635,9 +631,9 @@ async def delete( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -676,9 +672,9 @@ async def delete_by_app( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/actions/__init__.py b/src/pipedream/actions/__init__.py index 5cde020..d93c65a 100644 --- a/src/pipedream/actions/__init__.py +++ b/src/pipedream/actions/__init__.py @@ -2,3 +2,33 @@ # isort: skip_file +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ActionsListRequestRegistry +_dynamic_imports: typing.Dict[str, str] = {"ActionsListRequestRegistry": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ActionsListRequestRegistry"] diff --git a/src/pipedream/actions/client.py b/src/pipedream/actions/client.py index 5f86f30..14e4eaa 100644 --- a/src/pipedream/actions/client.py +++ b/src/pipedream/actions/client.py @@ -8,10 +8,12 @@ from ..types.component import Component from ..types.configure_prop_response import ConfigurePropResponse from ..types.configured_props import ConfiguredProps +from ..types.get_components_response import GetComponentsResponse from ..types.reload_props_response import ReloadPropsResponse from ..types.run_action_opts_stash_id import RunActionOptsStashId from ..types.run_action_response import RunActionResponse from .raw_client import AsyncRawActionsClient, RawActionsClient +from .types.actions_list_request_registry import ActionsListRequestRegistry # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -40,8 +42,9 @@ def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ActionsListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Component]: + ) -> SyncPager[Component, GetComponentsResponse]: """ Retrieve available actions with optional search and app filtering @@ -62,13 +65,16 @@ def list( app : typing.Optional[str] The ID or name slug of the app to filter the actions + registry : typing.Optional[ActionsListRequestRegistry] + The registry to retrieve actions from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncPager[Component] - actions listed + SyncPager[Component, GetComponentsResponse] + behaves like registry=all Examples -------- @@ -80,7 +86,14 @@ def list( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) - response = client.actions.list() + response = client.actions.list( + after="after", + before="before", + limit=1, + q="q", + app="app", + registry="public", + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -88,7 +101,7 @@ def list( yield page """ return self._raw_client.list( - after=after, before=before, limit=limit, q=q, app=app, request_options=request_options + after=after, before=before, limit=limit, q=q, app=app, registry=registry, request_options=request_options ) def retrieve( @@ -146,7 +159,7 @@ def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ConfigurePropResponse: @@ -178,7 +191,7 @@ def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -380,8 +393,9 @@ async def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ActionsListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Component]: + ) -> AsyncPager[Component, GetComponentsResponse]: """ Retrieve available actions with optional search and app filtering @@ -402,13 +416,16 @@ async def list( app : typing.Optional[str] The ID or name slug of the app to filter the actions + registry : typing.Optional[ActionsListRequestRegistry] + The registry to retrieve actions from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncPager[Component] - actions listed + AsyncPager[Component, GetComponentsResponse] + behaves like registry=all Examples -------- @@ -425,7 +442,14 @@ async def list( async def main() -> None: - response = await client.actions.list() + response = await client.actions.list( + after="after", + before="before", + limit=1, + q="q", + app="app", + registry="public", + ) async for item in response: yield item @@ -437,7 +461,7 @@ async def main() -> None: asyncio.run(main()) """ return await self._raw_client.list( - after=after, before=before, limit=limit, q=q, app=app, request_options=request_options + after=after, before=before, limit=limit, q=q, app=app, registry=registry, request_options=request_options ) async def retrieve( @@ -503,7 +527,7 @@ async def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ConfigurePropResponse: @@ -535,7 +559,7 @@ async def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] diff --git a/src/pipedream/actions/raw_client.py b/src/pipedream/actions/raw_client.py index 47bb1ed..c8a464d 100644 --- a/src/pipedream/actions/raw_client.py +++ b/src/pipedream/actions/raw_client.py @@ -7,10 +7,11 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder -from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata +from ..errors.bad_request_error import BadRequestError from ..errors.too_many_requests_error import TooManyRequestsError from ..types.component import Component from ..types.configure_prop_response import ConfigurePropResponse @@ -20,6 +21,7 @@ from ..types.reload_props_response import ReloadPropsResponse from ..types.run_action_opts_stash_id import RunActionOptsStashId from ..types.run_action_response import RunActionResponse +from .types.actions_list_request_registry import ActionsListRequestRegistry # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -37,8 +39,9 @@ def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ActionsListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Component]: + ) -> SyncPager[Component, GetComponentsResponse]: """ Retrieve available actions with optional search and app filtering @@ -59,13 +62,16 @@ def list( app : typing.Optional[str] The ID or name slug of the app to filter the actions + registry : typing.Optional[ActionsListRequestRegistry] + The registry to retrieve actions from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncPager[Component] - actions listed + SyncPager[Component, GetComponentsResponse] + behaves like registry=all """ _response = self._client_wrapper.httpx_client.request( f"v1/connect/{jsonable_encoder(self._client_wrapper._project_id)}/actions", @@ -76,6 +82,7 @@ def list( "limit": limit, "q": q, "app": app, + "registry": registry, }, request_options=request_options, ) @@ -100,18 +107,28 @@ def list( limit=limit, q=q, app=app, + registry=registry, request_options=request_options, ) - return SyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), ) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -170,9 +187,9 @@ def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -193,7 +210,7 @@ def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[ConfigurePropResponse]: @@ -225,7 +242,7 @@ def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -276,9 +293,9 @@ def configure_prop( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -362,9 +379,9 @@ def reload_props( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -426,7 +443,7 @@ def run( ), "dynamic_props_id": dynamic_props_id, "stash_id": convert_and_respect_annotation_metadata( - object_=stash_id, annotation=typing.Optional[RunActionOptsStashId], direction="write" + object_=stash_id, annotation=RunActionOptsStashId, direction="write" ), }, headers={ @@ -449,9 +466,9 @@ def run( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -474,8 +491,9 @@ async def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ActionsListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Component]: + ) -> AsyncPager[Component, GetComponentsResponse]: """ Retrieve available actions with optional search and app filtering @@ -496,13 +514,16 @@ async def list( app : typing.Optional[str] The ID or name slug of the app to filter the actions + registry : typing.Optional[ActionsListRequestRegistry] + The registry to retrieve actions from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncPager[Component] - actions listed + AsyncPager[Component, GetComponentsResponse] + behaves like registry=all """ _response = await self._client_wrapper.httpx_client.request( f"v1/connect/{jsonable_encoder(self._client_wrapper._project_id)}/actions", @@ -513,6 +534,7 @@ async def list( "limit": limit, "q": q, "app": app, + "registry": registry, }, request_options=request_options, ) @@ -539,19 +561,29 @@ async def _get_next(): limit=limit, q=q, app=app, + registry=registry, request_options=request_options, ) - return AsyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), ) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -610,9 +642,9 @@ async def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -633,7 +665,7 @@ async def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[ConfigurePropResponse]: @@ -665,7 +697,7 @@ async def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -716,9 +748,9 @@ async def configure_prop( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -802,9 +834,9 @@ async def reload_props( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -866,7 +898,7 @@ async def run( ), "dynamic_props_id": dynamic_props_id, "stash_id": convert_and_respect_annotation_metadata( - object_=stash_id, annotation=typing.Optional[RunActionOptsStashId], direction="write" + object_=stash_id, annotation=RunActionOptsStashId, direction="write" ), }, headers={ @@ -889,9 +921,9 @@ async def run( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/actions/types/__init__.py b/src/pipedream/actions/types/__init__.py new file mode 100644 index 0000000..b12743c --- /dev/null +++ b/src/pipedream/actions/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .actions_list_request_registry import ActionsListRequestRegistry +_dynamic_imports: typing.Dict[str, str] = {"ActionsListRequestRegistry": ".actions_list_request_registry"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ActionsListRequestRegistry"] diff --git a/src/pipedream/actions/types/actions_list_request_registry.py b/src/pipedream/actions/types/actions_list_request_registry.py new file mode 100644 index 0000000..6fa9f7b --- /dev/null +++ b/src/pipedream/actions/types/actions_list_request_registry.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ActionsListRequestRegistry = typing.Union[typing.Literal["public", "private", "all"], typing.Any] diff --git a/src/pipedream/apps/client.py b/src/pipedream/apps/client.py index 5d44a36..4ab1cb0 100644 --- a/src/pipedream/apps/client.py +++ b/src/pipedream/apps/client.py @@ -7,6 +7,7 @@ from ..core.request_options import RequestOptions from ..types.app import App from ..types.get_app_response import GetAppResponse +from ..types.list_apps_response import ListAppsResponse from .raw_client import AsyncRawAppsClient, RawAppsClient from .types.apps_list_request_sort_direction import AppsListRequestSortDirection from .types.apps_list_request_sort_key import AppsListRequestSortKey @@ -38,7 +39,7 @@ def list( sort_direction: typing.Optional[AppsListRequestSortDirection] = None, category_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[App]: + ) -> SyncPager[App, ListAppsResponse]: """ Retrieve all available apps with optional filtering and sorting @@ -70,7 +71,7 @@ def list( Returns ------- - SyncPager[App] + SyncPager[App, ListAppsResponse] apps listed Examples @@ -83,7 +84,14 @@ def list( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) - response = client.apps.list() + response = client.apps.list( + after="after", + before="before", + limit=1, + q="q", + sort_key="name", + sort_direction="asc", + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -162,7 +170,7 @@ async def list( sort_direction: typing.Optional[AppsListRequestSortDirection] = None, category_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[App]: + ) -> AsyncPager[App, ListAppsResponse]: """ Retrieve all available apps with optional filtering and sorting @@ -194,7 +202,7 @@ async def list( Returns ------- - AsyncPager[App] + AsyncPager[App, ListAppsResponse] apps listed Examples @@ -212,7 +220,14 @@ async def list( async def main() -> None: - response = await client.apps.list() + response = await client.apps.list( + after="after", + before="before", + limit=1, + q="q", + sort_key="name", + sort_direction="asc", + ) async for item in response: yield item diff --git a/src/pipedream/apps/raw_client.py b/src/pipedream/apps/raw_client.py index 3bf84a7..b71b5a3 100644 --- a/src/pipedream/apps/raw_client.py +++ b/src/pipedream/apps/raw_client.py @@ -7,7 +7,7 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder -from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..types.app import App @@ -32,7 +32,7 @@ def list( sort_direction: typing.Optional[AppsListRequestSortDirection] = None, category_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[App]: + ) -> SyncPager[App, ListAppsResponse]: """ Retrieve all available apps with optional filtering and sorting @@ -64,7 +64,7 @@ def list( Returns ------- - SyncPager[App] + SyncPager[App, ListAppsResponse] apps listed """ _response = self._client_wrapper.httpx_client.request( @@ -106,9 +106,7 @@ def list( category_ids=category_ids, request_options=request_options, ) - return SyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) - ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) @@ -169,7 +167,7 @@ async def list( sort_direction: typing.Optional[AppsListRequestSortDirection] = None, category_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[App]: + ) -> AsyncPager[App, ListAppsResponse]: """ Retrieve all available apps with optional filtering and sorting @@ -201,7 +199,7 @@ async def list( Returns ------- - AsyncPager[App] + AsyncPager[App, ListAppsResponse] apps listed """ _response = await self._client_wrapper.httpx_client.request( @@ -246,9 +244,7 @@ async def _get_next(): request_options=request_options, ) - return AsyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) - ) + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) diff --git a/src/pipedream/client.py b/src/pipedream/client.py index 514ef44..8ad0305 100644 --- a/src/pipedream/client.py +++ b/src/pipedream/client.py @@ -6,11 +6,11 @@ import typing import httpx +from .types.project_environment import ProjectEnvironment from .core.api_error import ApiError from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from .core.oauth_token_provider import OAuthTokenProvider +from .core.oauth_token_provider import AsyncOAuthTokenProvider, OAuthTokenProvider from .environment import PipedreamEnvironment -from .types.project_environment import ProjectEnvironment if typing.TYPE_CHECKING: from .accounts.client import AccountsClient, AsyncAccountsClient @@ -310,16 +310,16 @@ def __init__( raise ApiError( body="The client must be instantiated be either passing in client_secret or setting PIPEDREAM_CLIENT_SECRET" ) - oauth_token_provider = OAuthTokenProvider( + oauth_token_provider = AsyncOAuthTokenProvider( client_id=client_id, client_secret=client_secret, - client_wrapper=SyncClientWrapper( + client_wrapper=AsyncClientWrapper( base_url=_get_base_url(base_url=base_url, environment=environment), project_id=project_id, project_environment=project_environment, - httpx_client=httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + httpx_client=httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects) if follow_redirects is not None - else httpx.Client(timeout=_defaulted_timeout), + else httpx.AsyncClient(timeout=_defaulted_timeout), timeout=_defaulted_timeout, ), ) @@ -327,7 +327,8 @@ def __init__( base_url=_get_base_url(base_url=base_url, environment=environment), project_id=project_id, project_environment=project_environment, - token=_token_getter_override if _token_getter_override is not None else oauth_token_provider.get_token, + token=_token_getter_override, + async_token=oauth_token_provider.get_token, httpx_client=httpx_client if httpx_client is not None else httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects) diff --git a/src/pipedream/components/__init__.py b/src/pipedream/components/__init__.py index 5cde020..47a8ac3 100644 --- a/src/pipedream/components/__init__.py +++ b/src/pipedream/components/__init__.py @@ -2,3 +2,33 @@ # isort: skip_file +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ComponentsListRequestRegistry +_dynamic_imports: typing.Dict[str, str] = {"ComponentsListRequestRegistry": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ComponentsListRequestRegistry"] diff --git a/src/pipedream/components/client.py b/src/pipedream/components/client.py index 2004396..1e8afde 100644 --- a/src/pipedream/components/client.py +++ b/src/pipedream/components/client.py @@ -9,8 +9,10 @@ from ..types.component_type import ComponentType from ..types.configure_prop_response import ConfigurePropResponse from ..types.configured_props import ConfiguredProps +from ..types.get_components_response import GetComponentsResponse from ..types.reload_props_response import ReloadPropsResponse from .raw_client import AsyncRawComponentsClient, RawComponentsClient +from .types.components_list_request_registry import ComponentsListRequestRegistry # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -39,9 +41,10 @@ def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ComponentsListRequestRegistry] = None, component_type: typing.Optional[ComponentType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Component]: + ) -> SyncPager[Component, GetComponentsResponse]: """ Retrieve available components with optional search and app filtering @@ -62,6 +65,9 @@ def list( app : typing.Optional[str] The ID or name slug of the app to filter the components + registry : typing.Optional[ComponentsListRequestRegistry] + The registry to retrieve components from. Defaults to 'all' ('public', 'private', or 'all') + component_type : typing.Optional[ComponentType] The type of the component to filter the components @@ -70,8 +76,8 @@ def list( Returns ------- - SyncPager[Component] - components listed + SyncPager[Component, GetComponentsResponse] + behaves like registry=all Examples -------- @@ -83,7 +89,15 @@ def list( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) - response = client.components.list() + response = client.components.list( + after="after", + before="before", + limit=1, + q="q", + app="app", + registry="public", + component_type="trigger", + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -96,6 +110,7 @@ def list( limit=limit, q=q, app=app, + registry=registry, component_type=component_type, request_options=request_options, ) @@ -155,7 +170,7 @@ def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ConfigurePropResponse: @@ -187,7 +202,7 @@ def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -323,9 +338,10 @@ async def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ComponentsListRequestRegistry] = None, component_type: typing.Optional[ComponentType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Component]: + ) -> AsyncPager[Component, GetComponentsResponse]: """ Retrieve available components with optional search and app filtering @@ -346,6 +362,9 @@ async def list( app : typing.Optional[str] The ID or name slug of the app to filter the components + registry : typing.Optional[ComponentsListRequestRegistry] + The registry to retrieve components from. Defaults to 'all' ('public', 'private', or 'all') + component_type : typing.Optional[ComponentType] The type of the component to filter the components @@ -354,8 +373,8 @@ async def list( Returns ------- - AsyncPager[Component] - components listed + AsyncPager[Component, GetComponentsResponse] + behaves like registry=all Examples -------- @@ -372,7 +391,15 @@ async def list( async def main() -> None: - response = await client.components.list() + response = await client.components.list( + after="after", + before="before", + limit=1, + q="q", + app="app", + registry="public", + component_type="trigger", + ) async for item in response: yield item @@ -389,6 +416,7 @@ async def main() -> None: limit=limit, q=q, app=app, + registry=registry, component_type=component_type, request_options=request_options, ) @@ -456,7 +484,7 @@ async def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ConfigurePropResponse: @@ -488,7 +516,7 @@ async def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] diff --git a/src/pipedream/components/raw_client.py b/src/pipedream/components/raw_client.py index 3453921..8209c3d 100644 --- a/src/pipedream/components/raw_client.py +++ b/src/pipedream/components/raw_client.py @@ -7,10 +7,11 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder -from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata +from ..errors.bad_request_error import BadRequestError from ..errors.too_many_requests_error import TooManyRequestsError from ..types.component import Component from ..types.component_type import ComponentType @@ -19,6 +20,7 @@ from ..types.get_component_response import GetComponentResponse from ..types.get_components_response import GetComponentsResponse from ..types.reload_props_response import ReloadPropsResponse +from .types.components_list_request_registry import ComponentsListRequestRegistry # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -36,9 +38,10 @@ def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ComponentsListRequestRegistry] = None, component_type: typing.Optional[ComponentType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Component]: + ) -> SyncPager[Component, GetComponentsResponse]: """ Retrieve available components with optional search and app filtering @@ -59,6 +62,9 @@ def list( app : typing.Optional[str] The ID or name slug of the app to filter the components + registry : typing.Optional[ComponentsListRequestRegistry] + The registry to retrieve components from. Defaults to 'all' ('public', 'private', or 'all') + component_type : typing.Optional[ComponentType] The type of the component to filter the components @@ -67,8 +73,8 @@ def list( Returns ------- - SyncPager[Component] - components listed + SyncPager[Component, GetComponentsResponse] + behaves like registry=all """ _response = self._client_wrapper.httpx_client.request( f"v1/connect/{jsonable_encoder(self._client_wrapper._project_id)}/components", @@ -79,6 +85,7 @@ def list( "limit": limit, "q": q, "app": app, + "registry": registry, "component_type": component_type, }, request_options=request_options, @@ -104,19 +111,29 @@ def list( limit=limit, q=q, app=app, + registry=registry, component_type=component_type, request_options=request_options, ) - return SyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), ) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -175,9 +192,9 @@ def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -198,7 +215,7 @@ def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[ConfigurePropResponse]: @@ -230,7 +247,7 @@ def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -281,9 +298,9 @@ def configure_prop( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -367,9 +384,9 @@ def reload_props( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -392,9 +409,10 @@ async def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[ComponentsListRequestRegistry] = None, component_type: typing.Optional[ComponentType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Component]: + ) -> AsyncPager[Component, GetComponentsResponse]: """ Retrieve available components with optional search and app filtering @@ -415,6 +433,9 @@ async def list( app : typing.Optional[str] The ID or name slug of the app to filter the components + registry : typing.Optional[ComponentsListRequestRegistry] + The registry to retrieve components from. Defaults to 'all' ('public', 'private', or 'all') + component_type : typing.Optional[ComponentType] The type of the component to filter the components @@ -423,8 +444,8 @@ async def list( Returns ------- - AsyncPager[Component] - components listed + AsyncPager[Component, GetComponentsResponse] + behaves like registry=all """ _response = await self._client_wrapper.httpx_client.request( f"v1/connect/{jsonable_encoder(self._client_wrapper._project_id)}/components", @@ -435,6 +456,7 @@ async def list( "limit": limit, "q": q, "app": app, + "registry": registry, "component_type": component_type, }, request_options=request_options, @@ -462,20 +484,30 @@ async def _get_next(): limit=limit, q=q, app=app, + registry=registry, component_type=component_type, request_options=request_options, ) - return AsyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), ) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -534,9 +566,9 @@ async def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -557,7 +589,7 @@ async def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[ConfigurePropResponse]: @@ -589,7 +621,7 @@ async def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -640,9 +672,9 @@ async def configure_prop( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -726,9 +758,9 @@ async def reload_props( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/components/types/__init__.py b/src/pipedream/components/types/__init__.py new file mode 100644 index 0000000..96e7fcb --- /dev/null +++ b/src/pipedream/components/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .components_list_request_registry import ComponentsListRequestRegistry +_dynamic_imports: typing.Dict[str, str] = {"ComponentsListRequestRegistry": ".components_list_request_registry"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ComponentsListRequestRegistry"] diff --git a/src/pipedream/components/types/components_list_request_registry.py b/src/pipedream/components/types/components_list_request_registry.py new file mode 100644 index 0000000..d44e5a0 --- /dev/null +++ b/src/pipedream/components/types/components_list_request_registry.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ComponentsListRequestRegistry = typing.Union[typing.Literal["public", "private", "all"], typing.Any] diff --git a/src/pipedream/core/__init__.py b/src/pipedream/core/__init__.py index bfce76d..3b5240a 100644 --- a/src/pipedream/core/__init__.py +++ b/src/pipedream/core/__init__.py @@ -8,6 +8,7 @@ if typing.TYPE_CHECKING: from .api_error import ApiError from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .custom_pagination import AsyncCustomPager, SyncCustomPager from .datetime_utils import serialize_datetime from .file import File, convert_file_dict_to_httpx_tuples, with_content_type from .http_client import AsyncHttpClient, HttpClient @@ -30,6 +31,7 @@ _dynamic_imports: typing.Dict[str, str] = { "ApiError": ".api_error", "AsyncClientWrapper": ".client_wrapper", + "AsyncCustomPager": ".custom_pagination", "AsyncHttpClient": ".http_client", "AsyncHttpResponse": ".http_response", "AsyncPager": ".pagination", @@ -41,6 +43,7 @@ "IS_PYDANTIC_V2": ".pydantic_utilities", "RequestOptions": ".request_options", "SyncClientWrapper": ".client_wrapper", + "SyncCustomPager": ".custom_pagination", "SyncPager": ".pagination", "UniversalBaseModel": ".pydantic_utilities", "UniversalRootModel": ".pydantic_utilities", @@ -82,6 +85,7 @@ def __dir__(): __all__ = [ "ApiError", "AsyncClientWrapper", + "AsyncCustomPager", "AsyncHttpClient", "AsyncHttpResponse", "AsyncPager", @@ -93,6 +97,7 @@ def __dir__(): "IS_PYDANTIC_V2", "RequestOptions", "SyncClientWrapper", + "SyncCustomPager", "SyncPager", "UniversalBaseModel", "UniversalRootModel", diff --git a/src/pipedream/core/client_wrapper.py b/src/pipedream/core/client_wrapper.py index b75073a..4e00d0b 100644 --- a/src/pipedream/core/client_wrapper.py +++ b/src/pipedream/core/client_wrapper.py @@ -27,10 +27,10 @@ def __init__( def get_headers(self) -> typing.Dict[str, str]: headers: typing.Dict[str, str] = { - "User-Agent": "pipedream/1.0.12", + "User-Agent": "pipedream/1.0.13", "X-Fern-Language": "Python", "X-Fern-SDK-Name": "pipedream", - "X-Fern-SDK-Version": "1.0.12", + "X-Fern-SDK-Version": "1.0.13", **(self.get_custom_headers() or {}), } if self._project_environment is not None: @@ -94,6 +94,7 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, httpx_client: httpx.AsyncClient, ): super().__init__( @@ -104,9 +105,18 @@ def __init__( base_url=base_url, timeout=timeout, ) + self._async_token = async_token self.httpx_client = AsyncHttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + async_base_headers=self.async_get_headers, ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/src/pipedream/core/custom_pagination.py b/src/pipedream/core/custom_pagination.py new file mode 100644 index 0000000..5de2c7a --- /dev/null +++ b/src/pipedream/core/custom_pagination.py @@ -0,0 +1,152 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +Custom Pagination Support + +This file is designed to be modified by SDK users to implement their own +pagination logic. The generator will import SyncCustomPager and AsyncCustomPager +from this module when custom pagination is used. + +Users should: +1. Implement their custom pager (e.g., PayrocPager, MyCustomPager, etc.) +2. Create adapter classes (SyncCustomPager/AsyncCustomPager) that bridge + between the generated SDK code and their custom pager implementation +""" + +from __future__ import annotations + +from typing import Any, AsyncIterator, Generic, Iterator, TypeVar + +# Import the base utilities you'll need +# Adjust these imports based on your actual structure +try: + from .client_wrapper import AsyncClientWrapper, SyncClientWrapper +except ImportError: + # Fallback for type hints + AsyncClientWrapper = Any # type: ignore + SyncClientWrapper = Any # type: ignore + +TItem = TypeVar("TItem") +TResponse = TypeVar("TResponse") + + +class SyncCustomPager(Generic[TItem, TResponse]): + """ + Adapter for custom synchronous pagination. + + The generator will call this with: + SyncCustomPager(initial_response=response, client_wrapper=client_wrapper) + + Implement this class to extract pagination metadata from your response + and delegate to your custom pager implementation. + + Example implementation: + + class SyncCustomPager(Generic[TItem, TResponse]): + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: SyncClientWrapper, + ): + # Extract data and pagination metadata from response + data = initial_response.data # Adjust based on your response structure + links = initial_response.links + + # Initialize your custom pager + self._pager = MyCustomPager( + current_page=Page(data), + httpx_client=client_wrapper.httpx_client, + get_headers=client_wrapper.get_headers, + # ... other parameters + ) + + def __iter__(self): + return iter(self._pager) + + # Delegate other methods to your pager... + """ + + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: SyncClientWrapper, + ): + """ + Initialize the custom pager. + + Args: + initial_response: The parsed API response from the first request + client_wrapper: The client wrapper providing HTTP client and utilities + """ + raise NotImplementedError( + "SyncCustomPager must be implemented. " + "Please implement this class in core/custom_pagination.py to define your pagination logic. " + "See the class docstring for examples." + ) + + def __iter__(self) -> Iterator[TItem]: + """Iterate through all items across all pages.""" + raise NotImplementedError("Must implement __iter__ method") + + +class AsyncCustomPager(Generic[TItem, TResponse]): + """ + Adapter for custom asynchronous pagination. + + The generator will call this with: + AsyncCustomPager(initial_response=response, client_wrapper=client_wrapper) + + Implement this class to extract pagination metadata from your response + and delegate to your custom async pager implementation. + + Example implementation: + + class AsyncCustomPager(Generic[TItem, TResponse]): + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: AsyncClientWrapper, + ): + # Extract data and pagination metadata from response + data = initial_response.data # Adjust based on your response structure + links = initial_response.links + + # Initialize your custom async pager + self._pager = MyAsyncCustomPager( + current_page=Page(data), + httpx_client=client_wrapper.httpx_client, + get_headers=client_wrapper.get_headers, + # ... other parameters + ) + + async def __aiter__(self): + return self._pager.__aiter__() + + # Delegate other methods to your pager... + """ + + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: AsyncClientWrapper, + ): + """ + Initialize the custom async pager. + + Args: + initial_response: The parsed API response from the first request + client_wrapper: The client wrapper providing HTTP client and utilities + """ + raise NotImplementedError( + "AsyncCustomPager must be implemented. " + "Please implement this class in core/custom_pagination.py to define your pagination logic. " + "See the class docstring for examples." + ) + + async def __aiter__(self) -> AsyncIterator[TItem]: + """Asynchronously iterate through all items across all pages.""" + raise NotImplementedError("Must implement __aiter__ method") diff --git a/src/pipedream/core/http_client.py b/src/pipedream/core/http_client.py index e4173f9..f4a7c07 100644 --- a/src/pipedream/core/http_client.py +++ b/src/pipedream/core/http_client.py @@ -14,13 +14,13 @@ from .force_multipart import FORCE_MULTIPART from .jsonable_encoder import jsonable_encoder from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict from .request_options import RequestOptions from httpx._types import RequestFiles -INITIAL_RETRY_DELAY_SECONDS = 0.5 -MAX_RETRY_DELAY_SECONDS = 10 -MAX_RETRY_DELAY_SECONDS_FROM_HEADER = 30 +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: @@ -64,6 +64,38 @@ def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float return seconds +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + def _retry_timeout(response: httpx.Response, retries: int) -> float: """ Determine the amount of time to wait before retrying a request. @@ -71,17 +103,19 @@ def _retry_timeout(response: httpx.Response, retries: int) -> float: with a jitter to determine the number of seconds to wait. """ - # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + # 1. Check Retry-After header first retry_after = _parse_retry_after(response.headers) - if retry_after is not None and retry_after <= MAX_RETRY_DELAY_SECONDS_FROM_HEADER: - return retry_after + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) - # Apply exponential backoff, capped at MAX_RETRY_DELAY_SECONDS. - retry_delay = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) - # Add a randomness / jitter to the retry delay to avoid overwhelming the server with retries. - timeout = retry_delay * (1 - 0.25 * random()) - return timeout if timeout >= 0 else 0 + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) def _should_retry(response: httpx.Response) -> bool: @@ -89,6 +123,21 @@ def _should_retry(response: httpx.Response) -> bool: return response.status_code >= 500 or response.status_code in retryable_400s +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + def remove_omit_from_dict( original: typing.Dict[str, typing.Optional[typing.Any]], omit: typing.Optional[typing.Any], @@ -210,6 +259,8 @@ def request( if (request_files is None or len(request_files) == 0) and force_multipart: request_files = FORCE_MULTIPART + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -307,6 +358,8 @@ def stream( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -353,12 +406,19 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers + self.async_base_headers = async_base_headers self.httpx_client = httpx_client + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url if self.base_url is not None and base_url is None: @@ -408,6 +468,11 @@ async def request( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -415,7 +480,7 @@ async def request( headers=jsonable_encoder( remove_none_from_dict( { - **self.base_headers(), + **_headers, **(headers if headers is not None else {}), **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), } @@ -505,13 +570,18 @@ async def stream( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), headers=jsonable_encoder( remove_none_from_dict( { - **self.base_headers(), + **_headers, **(headers if headers is not None else {}), **(request_options.get("additional_headers", {}) if request_options is not None else {}), } diff --git a/src/pipedream/core/oauth_token_provider.py b/src/pipedream/core/oauth_token_provider.py index c158e70..e6bc534 100644 --- a/src/pipedream/core/oauth_token_provider.py +++ b/src/pipedream/core/oauth_token_provider.py @@ -1,10 +1,14 @@ # This file was auto-generated by Fern from our API Definition. +import asyncio import datetime as dt +import threading import typing +from asyncio import Lock as asyncio_Lock +from threading import Lock as threading_Lock -from ..oauth_tokens.client import OauthTokensClient -from .client_wrapper import SyncClientWrapper +from ..oauth_tokens.client import AsyncOauthTokensClient, OauthTokensClient +from .client_wrapper import AsyncClientWrapper, SyncClientWrapper class OAuthTokenProvider: @@ -16,11 +20,15 @@ def __init__(self, *, client_id: str, client_secret: str, client_wrapper: SyncCl self._access_token: typing.Optional[str] = None self._expires_at: dt.datetime = dt.datetime.now() self._auth_client = OauthTokensClient(client_wrapper=client_wrapper) + self._lock: threading_Lock = threading.Lock() def get_token(self) -> str: if self._access_token and self._expires_at > dt.datetime.now(): return self._access_token - return self._refresh() + with self._lock: + if self._access_token and self._expires_at > dt.datetime.now(): + return self._access_token + return self._refresh() def _refresh(self) -> str: token_response = self._auth_client.create(client_id=self._client_id, client_secret=self._client_secret) @@ -32,3 +40,34 @@ def _refresh(self) -> str: def _get_expires_at(self, *, expires_in_seconds: int, buffer_in_minutes: int): return dt.datetime.now() + dt.timedelta(seconds=expires_in_seconds) - dt.timedelta(minutes=buffer_in_minutes) + + +class AsyncOAuthTokenProvider: + BUFFER_IN_MINUTES = 2 + + def __init__(self, *, client_id: str, client_secret: str, client_wrapper: AsyncClientWrapper): + self._client_id = client_id + self._client_secret = client_secret + self._access_token: typing.Optional[str] = None + self._expires_at: dt.datetime = dt.datetime.now() + self._auth_client = AsyncOauthTokensClient(client_wrapper=client_wrapper) + self._lock: asyncio_Lock = asyncio.Lock() + + async def get_token(self) -> str: + if self._access_token and self._expires_at > dt.datetime.now(): + return self._access_token + async with self._lock: + if self._access_token and self._expires_at > dt.datetime.now(): + return self._access_token + return await self._refresh() + + async def _refresh(self) -> str: + token_response = await self._auth_client.create(client_id=self._client_id, client_secret=self._client_secret) + self._access_token = token_response.access_token + self._expires_at = self._get_expires_at( + expires_in_seconds=token_response.expires_in, buffer_in_minutes=self.BUFFER_IN_MINUTES + ) + return self._access_token + + def _get_expires_at(self, *, expires_in_seconds: int, buffer_in_minutes: int): + return dt.datetime.now() + dt.timedelta(seconds=expires_in_seconds) - dt.timedelta(minutes=buffer_in_minutes) diff --git a/src/pipedream/core/pagination.py b/src/pipedream/core/pagination.py index 97bcb64..760b089 100644 --- a/src/pipedream/core/pagination.py +++ b/src/pipedream/core/pagination.py @@ -5,10 +5,10 @@ from dataclasses import dataclass from typing import AsyncIterator, Awaitable, Callable, Generic, Iterator, List, Optional, TypeVar -from .http_response import BaseHttpResponse - # Generic to represent the underlying type of the results within a page T = TypeVar("T") +# Generic to represent the type of the API response +R = TypeVar("R") # SDKs implement a Page ABC per-pagination request, the endpoint then returns a pager that wraps this type @@ -23,11 +23,11 @@ @dataclass(frozen=True) -class SyncPager(Generic[T]): - get_next: Optional[Callable[[], Optional[SyncPager[T]]]] +class SyncPager(Generic[T, R]): + get_next: Optional[Callable[[], Optional[SyncPager[T, R]]]] has_next: bool items: Optional[List[T]] - response: Optional[BaseHttpResponse] + response: R # Here we type ignore the iterator to avoid a mypy error # caused by the type conflict with Pydanitc's __iter__ method @@ -37,8 +37,8 @@ def __iter__(self) -> Iterator[T]: # type: ignore[override] if page.items is not None: yield from page.items - def iter_pages(self) -> Iterator[SyncPager[T]]: - page: Optional[SyncPager[T]] = self + def iter_pages(self) -> Iterator[SyncPager[T, R]]: + page: Optional[SyncPager[T, R]] = self while page is not None: yield page @@ -49,16 +49,16 @@ def iter_pages(self) -> Iterator[SyncPager[T]]: if page is None or page.items is None or len(page.items) == 0: return - def next_page(self) -> Optional[SyncPager[T]]: + def next_page(self) -> Optional[SyncPager[T, R]]: return self.get_next() if self.get_next is not None else None @dataclass(frozen=True) -class AsyncPager(Generic[T]): - get_next: Optional[Callable[[], Awaitable[Optional[AsyncPager[T]]]]] +class AsyncPager(Generic[T, R]): + get_next: Optional[Callable[[], Awaitable[Optional[AsyncPager[T, R]]]]] has_next: bool items: Optional[List[T]] - response: Optional[BaseHttpResponse] + response: R async def __aiter__(self) -> AsyncIterator[T]: async for page in self.iter_pages(): @@ -66,8 +66,8 @@ async def __aiter__(self) -> AsyncIterator[T]: for item in page.items: yield item - async def iter_pages(self) -> AsyncIterator[AsyncPager[T]]: - page: Optional[AsyncPager[T]] = self + async def iter_pages(self) -> AsyncIterator[AsyncPager[T, R]]: + page: Optional[AsyncPager[T, R]] = self while page is not None: yield page @@ -78,5 +78,5 @@ async def iter_pages(self) -> AsyncIterator[AsyncPager[T]]: if page is None or page.items is None or len(page.items) == 0: return - async def next_page(self) -> Optional[AsyncPager[T]]: + async def next_page(self) -> Optional[AsyncPager[T, R]]: return await self.get_next() if self.get_next is not None else None diff --git a/src/pipedream/core/pydantic_utilities.py b/src/pipedream/core/pydantic_utilities.py index 8906cdf..185e5c4 100644 --- a/src/pipedream/core/pydantic_utilities.py +++ b/src/pipedream/core/pydantic_utilities.py @@ -220,7 +220,9 @@ def universal_root_validator( ) -> Callable[[AnyCallable], AnyCallable]: def decorator(func: AnyCallable) -> AnyCallable: if IS_PYDANTIC_V2: - return cast(AnyCallable, pydantic.model_validator(mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] return decorator diff --git a/src/pipedream/deployed_triggers/client.py b/src/pipedream/deployed_triggers/client.py index d7ae086..08dbc65 100644 --- a/src/pipedream/deployed_triggers/client.py +++ b/src/pipedream/deployed_triggers/client.py @@ -11,6 +11,7 @@ from ..types.emitter_type import EmitterType from ..types.get_trigger_webhooks_response import GetTriggerWebhooksResponse from ..types.get_trigger_workflows_response import GetTriggerWorkflowsResponse +from ..types.get_triggers_response import GetTriggersResponse from .raw_client import AsyncRawDeployedTriggersClient, RawDeployedTriggersClient # this is used as the default value for optional parameters @@ -41,7 +42,7 @@ def list( limit: typing.Optional[int] = None, emitter_type: typing.Optional[EmitterType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Emitter]: + ) -> SyncPager[Emitter, GetTriggersResponse]: """ Retrieve all deployed triggers for a specific external user @@ -67,7 +68,7 @@ def list( Returns ------- - SyncPager[Emitter] + SyncPager[Emitter, GetTriggersResponse] deployed triggers listed Examples @@ -81,7 +82,11 @@ def list( client_secret="YOUR_CLIENT_SECRET", ) response = client.deployed_triggers.list( + after="after", + before="before", + limit=1, external_user_id="external_user_id", + emitter_type="email", ) for item in response: yield item @@ -246,6 +251,7 @@ def delete( client.deployed_triggers.delete( trigger_id="trigger_id", external_user_id="external_user_id", + ignore_hook_errors=True, ) """ _response = self._raw_client.delete( @@ -298,6 +304,7 @@ def list_events( client.deployed_triggers.list_events( trigger_id="trigger_id", external_user_id="external_user_id", + n=1, ) """ _response = self._raw_client.list_events( @@ -512,7 +519,7 @@ async def list( limit: typing.Optional[int] = None, emitter_type: typing.Optional[EmitterType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Emitter]: + ) -> AsyncPager[Emitter, GetTriggersResponse]: """ Retrieve all deployed triggers for a specific external user @@ -538,7 +545,7 @@ async def list( Returns ------- - AsyncPager[Emitter] + AsyncPager[Emitter, GetTriggersResponse] deployed triggers listed Examples @@ -557,7 +564,11 @@ async def list( async def main() -> None: response = await client.deployed_triggers.list( + after="after", + before="before", + limit=1, external_user_id="external_user_id", + emitter_type="email", ) async for item in response: yield item @@ -747,6 +758,7 @@ async def main() -> None: await client.deployed_triggers.delete( trigger_id="trigger_id", external_user_id="external_user_id", + ignore_hook_errors=True, ) @@ -807,6 +819,7 @@ async def main() -> None: await client.deployed_triggers.list_events( trigger_id="trigger_id", external_user_id="external_user_id", + n=1, ) diff --git a/src/pipedream/deployed_triggers/raw_client.py b/src/pipedream/deployed_triggers/raw_client.py index dab281a..63b6847 100644 --- a/src/pipedream/deployed_triggers/raw_client.py +++ b/src/pipedream/deployed_triggers/raw_client.py @@ -7,7 +7,7 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder -from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -39,7 +39,7 @@ def list( limit: typing.Optional[int] = None, emitter_type: typing.Optional[EmitterType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Emitter]: + ) -> SyncPager[Emitter, GetTriggersResponse]: """ Retrieve all deployed triggers for a specific external user @@ -65,7 +65,7 @@ def list( Returns ------- - SyncPager[Emitter] + SyncPager[Emitter, GetTriggersResponse] deployed triggers listed """ _response = self._client_wrapper.httpx_client.request( @@ -103,16 +103,14 @@ def list( emitter_type=emitter_type, request_options=request_options, ) - return SyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) - ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -166,9 +164,9 @@ def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -253,9 +251,9 @@ def update( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -309,9 +307,9 @@ def delete( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -374,9 +372,9 @@ def list_events( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -429,9 +427,9 @@ def list_workflows( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -499,9 +497,9 @@ def update_workflows( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -554,9 +552,9 @@ def list_webhooks( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -624,9 +622,9 @@ def update_webhooks( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -650,7 +648,7 @@ async def list( limit: typing.Optional[int] = None, emitter_type: typing.Optional[EmitterType] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Emitter]: + ) -> AsyncPager[Emitter, GetTriggersResponse]: """ Retrieve all deployed triggers for a specific external user @@ -676,7 +674,7 @@ async def list( Returns ------- - AsyncPager[Emitter] + AsyncPager[Emitter, GetTriggersResponse] deployed triggers listed """ _response = await self._client_wrapper.httpx_client.request( @@ -717,16 +715,14 @@ async def _get_next(): request_options=request_options, ) - return AsyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) - ) + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -780,9 +776,9 @@ async def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -867,9 +863,9 @@ async def update( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -923,9 +919,9 @@ async def delete( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -988,9 +984,9 @@ async def list_events( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -1043,9 +1039,9 @@ async def list_workflows( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -1113,9 +1109,9 @@ async def update_workflows( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -1168,9 +1164,9 @@ async def list_webhooks( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -1238,9 +1234,9 @@ async def update_webhooks( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/errors/__init__.py b/src/pipedream/errors/__init__.py index a6d06ac..2717162 100644 --- a/src/pipedream/errors/__init__.py +++ b/src/pipedream/errors/__init__.py @@ -6,8 +6,14 @@ from importlib import import_module if typing.TYPE_CHECKING: + from .bad_request_error import BadRequestError + from .not_found_error import NotFoundError from .too_many_requests_error import TooManyRequestsError -_dynamic_imports: typing.Dict[str, str] = {"TooManyRequestsError": ".too_many_requests_error"} +_dynamic_imports: typing.Dict[str, str] = { + "BadRequestError": ".bad_request_error", + "NotFoundError": ".not_found_error", + "TooManyRequestsError": ".too_many_requests_error", +} def __getattr__(attr_name: str) -> typing.Any: @@ -31,4 +37,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["TooManyRequestsError"] +__all__ = ["BadRequestError", "NotFoundError", "TooManyRequestsError"] diff --git a/src/pipedream/errors/bad_request_error.py b/src/pipedream/errors/bad_request_error.py new file mode 100644 index 0000000..ec78e26 --- /dev/null +++ b/src/pipedream/errors/bad_request_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError + + +class BadRequestError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=400, headers=headers, body=body) diff --git a/src/pipedream/errors/not_found_error.py b/src/pipedream/errors/not_found_error.py new file mode 100644 index 0000000..75f557d --- /dev/null +++ b/src/pipedream/errors/not_found_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError + + +class NotFoundError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=404, headers=headers, body=body) diff --git a/src/pipedream/errors/too_many_requests_error.py b/src/pipedream/errors/too_many_requests_error.py index 2705399..705d6f1 100644 --- a/src/pipedream/errors/too_many_requests_error.py +++ b/src/pipedream/errors/too_many_requests_error.py @@ -6,5 +6,5 @@ class TooManyRequestsError(ApiError): - def __init__(self, body: typing.Optional[typing.Any], headers: typing.Optional[typing.Dict[str, str]] = None): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): super().__init__(status_code=429, headers=headers, body=body) diff --git a/src/pipedream/file_stash/raw_client.py b/src/pipedream/file_stash/raw_client.py index 0a4d576..08c8725 100644 --- a/src/pipedream/file_stash/raw_client.py +++ b/src/pipedream/file_stash/raw_client.py @@ -57,9 +57,9 @@ def _stream() -> HttpResponse[typing.Iterator[bytes]]: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -119,9 +119,9 @@ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/projects/client.py b/src/pipedream/projects/client.py index e8ec738..aeb73a9 100644 --- a/src/pipedream/projects/client.py +++ b/src/pipedream/projects/client.py @@ -3,10 +3,16 @@ import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions +from ..types.list_projects_response import ListProjectsResponse +from ..types.project import Project from ..types.project_info_response import ProjectInfoResponse from .raw_client import AsyncRawProjectsClient, RawProjectsClient +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + class ProjectsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): @@ -23,6 +29,211 @@ def with_raw_response(self) -> RawProjectsClient: """ return self._raw_client + def list( + self, + *, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, + limit: typing.Optional[int] = None, + q: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Project, ListProjectsResponse]: + """ + List the projects that are available to the authenticated Connect client + + Parameters + ---------- + after : typing.Optional[str] + The cursor to start from for pagination + + before : typing.Optional[str] + The cursor to end before for pagination + + limit : typing.Optional[int] + The maximum number of results to return + + q : typing.Optional[str] + A search query to filter the projects + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Project, ListProjectsResponse] + projects listed + + Examples + -------- + from pipedream import Pipedream + + client = Pipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + response = client.projects.list( + after="after", + before="before", + limit=1, + q="q", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(after=after, before=before, limit=limit, q=q, request_options=request_options) + + def create( + self, + *, + name: str, + app_name: typing.Optional[str] = OMIT, + support_email: typing.Optional[str] = OMIT, + external_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: + """ + Create a new project for the authenticated workspace + + Parameters + ---------- + name : str + Name of the project + + app_name : typing.Optional[str] + Display name for the Connect application + + support_email : typing.Optional[str] + Support email displayed to end users + + external_url : typing.Optional[str] + External URL for the project, if configured + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + project created + + Examples + -------- + from pipedream import Pipedream + + client = Pipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + client.projects.create( + name="name", + ) + """ + _response = self._raw_client.create( + name=name, + app_name=app_name, + support_email=support_email, + external_url=external_url, + request_options=request_options, + ) + return _response.data + + def retrieve(self, *, request_options: typing.Optional[RequestOptions] = None) -> Project: + """ + Get the project details for a specific project + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + project retrieved + + Examples + -------- + from pipedream import Pipedream + + client = Pipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + client.projects.retrieve() + """ + _response = self._raw_client.retrieve(request_options=request_options) + return _response.data + + def delete(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Delete a project owned by the authenticated workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from pipedream import Pipedream + + client = Pipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + client.projects.delete() + """ + _response = self._raw_client.delete(request_options=request_options) + return _response.data + + def update(self, *, logo: str, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Upload or replace the project logo + + Parameters + ---------- + logo : str + Data URI containing the new Base64 encoded image + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from pipedream import Pipedream + + client = Pipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + client.projects.update( + logo="data:image/png;base64,AAAAAA...", + ) + """ + _response = self._raw_client.update(logo=logo, request_options=request_options) + return _response.data + def retrieve_info(self, *, request_options: typing.Optional[RequestOptions] = None) -> ProjectInfoResponse: """ Retrieve project configuration and environment details @@ -68,6 +279,254 @@ def with_raw_response(self) -> AsyncRawProjectsClient: """ return self._raw_client + async def list( + self, + *, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, + limit: typing.Optional[int] = None, + q: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Project, ListProjectsResponse]: + """ + List the projects that are available to the authenticated Connect client + + Parameters + ---------- + after : typing.Optional[str] + The cursor to start from for pagination + + before : typing.Optional[str] + The cursor to end before for pagination + + limit : typing.Optional[int] + The maximum number of results to return + + q : typing.Optional[str] + A search query to filter the projects + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Project, ListProjectsResponse] + projects listed + + Examples + -------- + import asyncio + + from pipedream import AsyncPipedream + + client = AsyncPipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + response = await client.projects.list( + after="after", + before="before", + limit=1, + q="q", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list( + after=after, before=before, limit=limit, q=q, request_options=request_options + ) + + async def create( + self, + *, + name: str, + app_name: typing.Optional[str] = OMIT, + support_email: typing.Optional[str] = OMIT, + external_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: + """ + Create a new project for the authenticated workspace + + Parameters + ---------- + name : str + Name of the project + + app_name : typing.Optional[str] + Display name for the Connect application + + support_email : typing.Optional[str] + Support email displayed to end users + + external_url : typing.Optional[str] + External URL for the project, if configured + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + project created + + Examples + -------- + import asyncio + + from pipedream import AsyncPipedream + + client = AsyncPipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + await client.projects.create( + name="name", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + app_name=app_name, + support_email=support_email, + external_url=external_url, + request_options=request_options, + ) + return _response.data + + async def retrieve(self, *, request_options: typing.Optional[RequestOptions] = None) -> Project: + """ + Get the project details for a specific project + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Project + project retrieved + + Examples + -------- + import asyncio + + from pipedream import AsyncPipedream + + client = AsyncPipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + await client.projects.retrieve() + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve(request_options=request_options) + return _response.data + + async def delete(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Delete a project owned by the authenticated workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from pipedream import AsyncPipedream + + client = AsyncPipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + await client.projects.delete() + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(request_options=request_options) + return _response.data + + async def update(self, *, logo: str, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Upload or replace the project logo + + Parameters + ---------- + logo : str + Data URI containing the new Base64 encoded image + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from pipedream import AsyncPipedream + + client = AsyncPipedream( + project_id="YOUR_PROJECT_ID", + project_environment="YOUR_PROJECT_ENVIRONMENT", + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + await client.projects.update( + logo="data:image/png;base64,AAAAAA...", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update(logo=logo, request_options=request_options) + return _response.data + async def retrieve_info(self, *, request_options: typing.Optional[RequestOptions] = None) -> ProjectInfoResponse: """ Retrieve project configuration and environment details diff --git a/src/pipedream/projects/raw_client.py b/src/pipedream/projects/raw_client.py index 54d4b1c..557276d 100644 --- a/src/pipedream/projects/raw_client.py +++ b/src/pipedream/projects/raw_client.py @@ -7,16 +7,334 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError from ..errors.too_many_requests_error import TooManyRequestsError +from ..types.list_projects_response import ListProjectsResponse +from ..types.project import Project from ..types.project_info_response import ProjectInfoResponse +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + class RawProjectsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper + def list( + self, + *, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, + limit: typing.Optional[int] = None, + q: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Project, ListProjectsResponse]: + """ + List the projects that are available to the authenticated Connect client + + Parameters + ---------- + after : typing.Optional[str] + The cursor to start from for pagination + + before : typing.Optional[str] + The cursor to end before for pagination + + limit : typing.Optional[int] + The maximum number of results to return + + q : typing.Optional[str] + A search query to filter the projects + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Project, ListProjectsResponse] + projects listed + """ + _response = self._client_wrapper.httpx_client.request( + "v1/connect/projects", + method="GET", + params={ + "after": after, + "before": before, + "limit": limit, + "q": q, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListProjectsResponse, + parse_obj_as( + type_=ListProjectsResponse, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = False + _get_next = None + if _parsed_response.page_info is not None: + _parsed_next = _parsed_response.page_info.end_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + after=_parsed_next, + before=before, + limit=limit, + q=q, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def create( + self, + *, + name: str, + app_name: typing.Optional[str] = OMIT, + support_email: typing.Optional[str] = OMIT, + external_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Project]: + """ + Create a new project for the authenticated workspace + + Parameters + ---------- + name : str + Name of the project + + app_name : typing.Optional[str] + Display name for the Connect application + + support_email : typing.Optional[str] + Support email displayed to end users + + external_url : typing.Optional[str] + External URL for the project, if configured + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Project] + project created + """ + _response = self._client_wrapper.httpx_client.request( + "v1/connect/projects", + method="POST", + json={ + "name": name, + "app_name": app_name, + "support_email": support_email, + "external_url": external_url, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def retrieve(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Project]: + """ + Get the project details for a specific project + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Project] + project retrieved + """ + _response = self._client_wrapper.httpx_client.request( + f"v1/connect/projects/{jsonable_encoder(self._client_wrapper._project_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def delete(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Delete a project owned by the authenticated workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"v1/connect/projects/{jsonable_encoder(self._client_wrapper._project_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def update(self, *, logo: str, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Upload or replace the project logo + + Parameters + ---------- + logo : str + Data URI containing the new Base64 encoded image + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"v1/connect/projects/{jsonable_encoder(self._client_wrapper._project_id)}/logo", + method="POST", + json={ + "logo": logo, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def retrieve_info( self, *, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[ProjectInfoResponse]: @@ -52,9 +370,9 @@ def retrieve_info( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -69,6 +387,321 @@ class AsyncRawProjectsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper + async def list( + self, + *, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, + limit: typing.Optional[int] = None, + q: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Project, ListProjectsResponse]: + """ + List the projects that are available to the authenticated Connect client + + Parameters + ---------- + after : typing.Optional[str] + The cursor to start from for pagination + + before : typing.Optional[str] + The cursor to end before for pagination + + limit : typing.Optional[int] + The maximum number of results to return + + q : typing.Optional[str] + A search query to filter the projects + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Project, ListProjectsResponse] + projects listed + """ + _response = await self._client_wrapper.httpx_client.request( + "v1/connect/projects", + method="GET", + params={ + "after": after, + "before": before, + "limit": limit, + "q": q, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListProjectsResponse, + parse_obj_as( + type_=ListProjectsResponse, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = False + _get_next = None + if _parsed_response.page_info is not None: + _parsed_next = _parsed_response.page_info.end_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + after=_parsed_next, + before=before, + limit=limit, + q=q, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def create( + self, + *, + name: str, + app_name: typing.Optional[str] = OMIT, + support_email: typing.Optional[str] = OMIT, + external_url: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Project]: + """ + Create a new project for the authenticated workspace + + Parameters + ---------- + name : str + Name of the project + + app_name : typing.Optional[str] + Display name for the Connect application + + support_email : typing.Optional[str] + Support email displayed to end users + + external_url : typing.Optional[str] + External URL for the project, if configured + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Project] + project created + """ + _response = await self._client_wrapper.httpx_client.request( + "v1/connect/projects", + method="POST", + json={ + "name": name, + "app_name": app_name, + "support_email": support_email, + "external_url": external_url, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def retrieve(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[Project]: + """ + Get the project details for a specific project + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Project] + project retrieved + """ + _response = await self._client_wrapper.httpx_client.request( + f"v1/connect/projects/{jsonable_encoder(self._client_wrapper._project_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def delete(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[None]: + """ + Delete a project owned by the authenticated workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"v1/connect/projects/{jsonable_encoder(self._client_wrapper._project_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def update( + self, *, logo: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Upload or replace the project logo + + Parameters + ---------- + logo : str + Data URI containing the new Base64 encoded image + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"v1/connect/projects/{jsonable_encoder(self._client_wrapper._project_id)}/logo", + method="POST", + json={ + "logo": logo, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + async def retrieve_info( self, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[ProjectInfoResponse]: @@ -104,9 +737,9 @@ async def retrieve_info( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/tokens/client.py b/src/pipedream/tokens/client.py index cbe1c64..f37eccf 100644 --- a/src/pipedream/tokens/client.py +++ b/src/pipedream/tokens/client.py @@ -132,6 +132,7 @@ def validate( client.tokens.validate( ctok="ctok", app_id="app_id", + oauth_app_id="oauth_app_id", ) """ _response = self._raw_client.validate( @@ -272,6 +273,7 @@ async def main() -> None: await client.tokens.validate( ctok="ctok", app_id="app_id", + oauth_app_id="oauth_app_id", ) diff --git a/src/pipedream/tokens/raw_client.py b/src/pipedream/tokens/raw_client.py index 4370714..ff14f04 100644 --- a/src/pipedream/tokens/raw_client.py +++ b/src/pipedream/tokens/raw_client.py @@ -90,9 +90,9 @@ def create( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -154,9 +154,9 @@ def validate( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -239,9 +239,9 @@ async def create( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -303,9 +303,9 @@ async def validate( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/triggers/__init__.py b/src/pipedream/triggers/__init__.py index 5cde020..a2de887 100644 --- a/src/pipedream/triggers/__init__.py +++ b/src/pipedream/triggers/__init__.py @@ -2,3 +2,33 @@ # isort: skip_file +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import TriggersListRequestRegistry +_dynamic_imports: typing.Dict[str, str] = {"TriggersListRequestRegistry": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["TriggersListRequestRegistry"] diff --git a/src/pipedream/triggers/client.py b/src/pipedream/triggers/client.py index 17e518e..6c578cd 100644 --- a/src/pipedream/triggers/client.py +++ b/src/pipedream/triggers/client.py @@ -9,8 +9,10 @@ from ..types.configure_prop_response import ConfigurePropResponse from ..types.configured_props import ConfiguredProps from ..types.emitter import Emitter +from ..types.get_components_response import GetComponentsResponse from ..types.reload_props_response import ReloadPropsResponse from .raw_client import AsyncRawTriggersClient, RawTriggersClient +from .types.triggers_list_request_registry import TriggersListRequestRegistry # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -39,8 +41,9 @@ def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[TriggersListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Component]: + ) -> SyncPager[Component, GetComponentsResponse]: """ Retrieve available triggers with optional search and app filtering @@ -61,13 +64,16 @@ def list( app : typing.Optional[str] The ID or name slug of the app to filter the triggers + registry : typing.Optional[TriggersListRequestRegistry] + The registry to retrieve triggers from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncPager[Component] - triggers listed + SyncPager[Component, GetComponentsResponse] + behaves like registry=all Examples -------- @@ -79,7 +85,14 @@ def list( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) - response = client.triggers.list() + response = client.triggers.list( + after="after", + before="before", + limit=1, + q="q", + app="app", + registry="public", + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -87,7 +100,7 @@ def list( yield page """ return self._raw_client.list( - after=after, before=before, limit=limit, q=q, app=app, request_options=request_options + after=after, before=before, limit=limit, q=q, app=app, registry=registry, request_options=request_options ) def retrieve( @@ -145,7 +158,7 @@ def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ConfigurePropResponse: @@ -177,7 +190,7 @@ def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -390,8 +403,9 @@ async def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[TriggersListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Component]: + ) -> AsyncPager[Component, GetComponentsResponse]: """ Retrieve available triggers with optional search and app filtering @@ -412,13 +426,16 @@ async def list( app : typing.Optional[str] The ID or name slug of the app to filter the triggers + registry : typing.Optional[TriggersListRequestRegistry] + The registry to retrieve triggers from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncPager[Component] - triggers listed + AsyncPager[Component, GetComponentsResponse] + behaves like registry=all Examples -------- @@ -435,7 +452,14 @@ async def list( async def main() -> None: - response = await client.triggers.list() + response = await client.triggers.list( + after="after", + before="before", + limit=1, + q="q", + app="app", + registry="public", + ) async for item in response: yield item @@ -447,7 +471,7 @@ async def main() -> None: asyncio.run(main()) """ return await self._raw_client.list( - after=after, before=before, limit=limit, q=q, app=app, request_options=request_options + after=after, before=before, limit=limit, q=q, app=app, registry=registry, request_options=request_options ) async def retrieve( @@ -513,7 +537,7 @@ async def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ConfigurePropResponse: @@ -545,7 +569,7 @@ async def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] diff --git a/src/pipedream/triggers/raw_client.py b/src/pipedream/triggers/raw_client.py index 93ac6c6..85171f1 100644 --- a/src/pipedream/triggers/raw_client.py +++ b/src/pipedream/triggers/raw_client.py @@ -7,10 +7,11 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder -from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager +from ..core.pagination import AsyncPager, SyncPager from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata +from ..errors.bad_request_error import BadRequestError from ..errors.too_many_requests_error import TooManyRequestsError from ..types.component import Component from ..types.configure_prop_response import ConfigurePropResponse @@ -20,6 +21,7 @@ from ..types.get_component_response import GetComponentResponse from ..types.get_components_response import GetComponentsResponse from ..types.reload_props_response import ReloadPropsResponse +from .types.triggers_list_request_registry import TriggersListRequestRegistry # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -37,8 +39,9 @@ def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[TriggersListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Component]: + ) -> SyncPager[Component, GetComponentsResponse]: """ Retrieve available triggers with optional search and app filtering @@ -59,13 +62,16 @@ def list( app : typing.Optional[str] The ID or name slug of the app to filter the triggers + registry : typing.Optional[TriggersListRequestRegistry] + The registry to retrieve triggers from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncPager[Component] - triggers listed + SyncPager[Component, GetComponentsResponse] + behaves like registry=all """ _response = self._client_wrapper.httpx_client.request( f"v1/connect/{jsonable_encoder(self._client_wrapper._project_id)}/triggers", @@ -76,6 +82,7 @@ def list( "limit": limit, "q": q, "app": app, + "registry": registry, }, request_options=request_options, ) @@ -100,18 +107,28 @@ def list( limit=limit, q=q, app=app, + registry=registry, request_options=request_options, ) - return SyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), ) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -170,9 +187,9 @@ def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -193,7 +210,7 @@ def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[ConfigurePropResponse]: @@ -225,7 +242,7 @@ def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -276,9 +293,9 @@ def configure_prop( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -362,9 +379,9 @@ def reload_props( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -459,9 +476,9 @@ def deploy( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -484,8 +501,9 @@ async def list( limit: typing.Optional[int] = None, q: typing.Optional[str] = None, app: typing.Optional[str] = None, + registry: typing.Optional[TriggersListRequestRegistry] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Component]: + ) -> AsyncPager[Component, GetComponentsResponse]: """ Retrieve available triggers with optional search and app filtering @@ -506,13 +524,16 @@ async def list( app : typing.Optional[str] The ID or name slug of the app to filter the triggers + registry : typing.Optional[TriggersListRequestRegistry] + The registry to retrieve triggers from. Defaults to 'all' ('public', 'private', or 'all') + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncPager[Component] - triggers listed + AsyncPager[Component, GetComponentsResponse] + behaves like registry=all """ _response = await self._client_wrapper.httpx_client.request( f"v1/connect/{jsonable_encoder(self._client_wrapper._project_id)}/triggers", @@ -523,6 +544,7 @@ async def list( "limit": limit, "q": q, "app": app, + "registry": registry, }, request_options=request_options, ) @@ -549,19 +571,29 @@ async def _get_next(): limit=limit, q=q, app=app, + registry=registry, request_options=request_options, ) - return AsyncPager( - has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response) + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), ) if _response.status_code == 429: raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -620,9 +652,9 @@ async def retrieve( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -643,7 +675,7 @@ async def configure_prop( configured_props: typing.Optional[ConfiguredProps] = OMIT, dynamic_props_id: typing.Optional[str] = OMIT, page: typing.Optional[float] = OMIT, - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, query: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[ConfigurePropResponse]: @@ -675,7 +707,7 @@ async def configure_prop( page : typing.Optional[float] Page number for paginated results - prev_context : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + prev_context : typing.Optional[typing.Dict[str, typing.Any]] Previous context for pagination query : typing.Optional[str] @@ -726,9 +758,9 @@ async def configure_prop( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -812,9 +844,9 @@ async def reload_props( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -909,9 +941,9 @@ async def deploy( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/src/pipedream/triggers/types/__init__.py b/src/pipedream/triggers/types/__init__.py new file mode 100644 index 0000000..cd15c9e --- /dev/null +++ b/src/pipedream/triggers/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .triggers_list_request_registry import TriggersListRequestRegistry +_dynamic_imports: typing.Dict[str, str] = {"TriggersListRequestRegistry": ".triggers_list_request_registry"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["TriggersListRequestRegistry"] diff --git a/src/pipedream/triggers/types/triggers_list_request_registry.py b/src/pipedream/triggers/types/triggers_list_request_registry.py new file mode 100644 index 0000000..a8e8475 --- /dev/null +++ b/src/pipedream/triggers/types/triggers_list_request_registry.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TriggersListRequestRegistry = typing.Union[typing.Literal["public", "private", "all"], typing.Any] diff --git a/src/pipedream/types/__init__.py b/src/pipedream/types/__init__.py index 6063ee7..fe6d1c7 100644 --- a/src/pipedream/types/__init__.py +++ b/src/pipedream/types/__init__.py @@ -16,7 +16,29 @@ from .component import Component from .component_stash import ComponentStash from .component_type import ComponentType - from .configurable_prop import ConfigurableProp + from .configurable_prop import ( + ConfigurableProp, + ConfigurableProp_AirtableBaseId, + ConfigurableProp_AirtableFieldId, + ConfigurableProp_AirtableTableId, + ConfigurableProp_AirtableViewId, + ConfigurableProp_Alert, + ConfigurableProp_Any, + ConfigurableProp_App, + ConfigurableProp_Boolean, + ConfigurableProp_DiscordChannel, + ConfigurableProp_DiscordChannelArray, + ConfigurableProp_Integer, + ConfigurableProp_IntegerArray, + ConfigurableProp_InterfaceApphook, + ConfigurableProp_InterfaceHttp, + ConfigurableProp_InterfaceTimer, + ConfigurableProp_Object, + ConfigurableProp_ServiceDb, + ConfigurableProp_Sql, + ConfigurableProp_String, + ConfigurableProp_StringArray, + ) from .configurable_prop_airtable_base_id import ConfigurablePropAirtableBaseId from .configurable_prop_airtable_field_id import ConfigurablePropAirtableFieldId from .configurable_prop_airtable_table_id import ConfigurablePropAirtableTableId @@ -27,6 +49,7 @@ from .configurable_prop_any_options_item import ConfigurablePropAnyOptionsItem from .configurable_prop_app import ConfigurablePropApp from .configurable_prop_apphook import ConfigurablePropApphook + from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_boolean import ConfigurablePropBoolean from .configurable_prop_boolean_options_item import ConfigurablePropBooleanOptionsItem from .configurable_prop_db import ConfigurablePropDb @@ -51,7 +74,6 @@ from .configurable_prop_timer_default import ConfigurablePropTimerDefault from .configurable_prop_timer_option import ConfigurablePropTimerOption from .configurable_prop_timer_static import ConfigurablePropTimerStatic - from .configurable_prop_type import ConfigurablePropType from .configure_prop_options import ConfigurePropOptions from .configure_prop_options_item import ConfigurePropOptionsItem from .configure_prop_opts import ConfigurePropOpts @@ -92,9 +114,11 @@ from .list_accounts_response import ListAccountsResponse from .list_app_categories_response import ListAppCategoriesResponse from .list_apps_response import ListAppsResponse + from .list_projects_response import ListProjectsResponse from .observation import Observation from .observation_error import ObservationError from .page_info import PageInfo + from .project import Project from .project_environment import ProjectEnvironment from .project_info_response import ProjectInfoResponse from .project_info_response_app import ProjectInfoResponseApp @@ -102,7 +126,6 @@ from .prop_option_nested import PropOptionNested from .prop_option_value import PropOptionValue from .proxy_response import ProxyResponse - from .proxy_response_binary import ProxyResponseBinary from .reload_props_opts import ReloadPropsOpts from .reload_props_response import ReloadPropsResponse from .run_action_opts_stash_id import RunActionOptsStashId @@ -137,6 +160,7 @@ "ConfigurablePropAnyOptionsItem": ".configurable_prop_any_options_item", "ConfigurablePropApp": ".configurable_prop_app", "ConfigurablePropApphook": ".configurable_prop_apphook", + "ConfigurablePropBase": ".configurable_prop_base", "ConfigurablePropBoolean": ".configurable_prop_boolean", "ConfigurablePropBooleanOptionsItem": ".configurable_prop_boolean_options_item", "ConfigurablePropDb": ".configurable_prop_db", @@ -161,7 +185,26 @@ "ConfigurablePropTimerDefault": ".configurable_prop_timer_default", "ConfigurablePropTimerOption": ".configurable_prop_timer_option", "ConfigurablePropTimerStatic": ".configurable_prop_timer_static", - "ConfigurablePropType": ".configurable_prop_type", + "ConfigurableProp_AirtableBaseId": ".configurable_prop", + "ConfigurableProp_AirtableFieldId": ".configurable_prop", + "ConfigurableProp_AirtableTableId": ".configurable_prop", + "ConfigurableProp_AirtableViewId": ".configurable_prop", + "ConfigurableProp_Alert": ".configurable_prop", + "ConfigurableProp_Any": ".configurable_prop", + "ConfigurableProp_App": ".configurable_prop", + "ConfigurableProp_Boolean": ".configurable_prop", + "ConfigurableProp_DiscordChannel": ".configurable_prop", + "ConfigurableProp_DiscordChannelArray": ".configurable_prop", + "ConfigurableProp_Integer": ".configurable_prop", + "ConfigurableProp_IntegerArray": ".configurable_prop", + "ConfigurableProp_InterfaceApphook": ".configurable_prop", + "ConfigurableProp_InterfaceHttp": ".configurable_prop", + "ConfigurableProp_InterfaceTimer": ".configurable_prop", + "ConfigurableProp_Object": ".configurable_prop", + "ConfigurableProp_ServiceDb": ".configurable_prop", + "ConfigurableProp_Sql": ".configurable_prop", + "ConfigurableProp_String": ".configurable_prop", + "ConfigurableProp_StringArray": ".configurable_prop", "ConfigurePropOptions": ".configure_prop_options", "ConfigurePropOptionsItem": ".configure_prop_options_item", "ConfigurePropOpts": ".configure_prop_opts", @@ -205,9 +248,11 @@ "ListAccountsResponse": ".list_accounts_response", "ListAppCategoriesResponse": ".list_app_categories_response", "ListAppsResponse": ".list_apps_response", + "ListProjectsResponse": ".list_projects_response", "Observation": ".observation", "ObservationError": ".observation_error", "PageInfo": ".page_info", + "Project": ".project", "ProjectEnvironment": ".project_environment", "ProjectInfoResponse": ".project_info_response", "ProjectInfoResponseApp": ".project_info_response_app", @@ -215,7 +260,6 @@ "PropOptionNested": ".prop_option_nested", "PropOptionValue": ".prop_option_value", "ProxyResponse": ".proxy_response", - "ProxyResponseBinary": ".proxy_response_binary", "ReloadPropsOpts": ".reload_props_opts", "ReloadPropsResponse": ".reload_props_response", "RunActionOptsStashId": ".run_action_opts_stash_id", @@ -274,6 +318,7 @@ def __dir__(): "ConfigurablePropAnyOptionsItem", "ConfigurablePropApp", "ConfigurablePropApphook", + "ConfigurablePropBase", "ConfigurablePropBoolean", "ConfigurablePropBooleanOptionsItem", "ConfigurablePropDb", @@ -298,7 +343,26 @@ def __dir__(): "ConfigurablePropTimerDefault", "ConfigurablePropTimerOption", "ConfigurablePropTimerStatic", - "ConfigurablePropType", + "ConfigurableProp_AirtableBaseId", + "ConfigurableProp_AirtableFieldId", + "ConfigurableProp_AirtableTableId", + "ConfigurableProp_AirtableViewId", + "ConfigurableProp_Alert", + "ConfigurableProp_Any", + "ConfigurableProp_App", + "ConfigurableProp_Boolean", + "ConfigurableProp_DiscordChannel", + "ConfigurableProp_DiscordChannelArray", + "ConfigurableProp_Integer", + "ConfigurableProp_IntegerArray", + "ConfigurableProp_InterfaceApphook", + "ConfigurableProp_InterfaceHttp", + "ConfigurableProp_InterfaceTimer", + "ConfigurableProp_Object", + "ConfigurableProp_ServiceDb", + "ConfigurableProp_Sql", + "ConfigurableProp_String", + "ConfigurableProp_StringArray", "ConfigurePropOptions", "ConfigurePropOptionsItem", "ConfigurePropOpts", @@ -342,9 +406,11 @@ def __dir__(): "ListAccountsResponse", "ListAppCategoriesResponse", "ListAppsResponse", + "ListProjectsResponse", "Observation", "ObservationError", "PageInfo", + "Project", "ProjectEnvironment", "ProjectInfoResponse", "ProjectInfoResponseApp", @@ -352,7 +418,6 @@ def __dir__(): "PropOptionNested", "PropOptionValue", "ProxyResponse", - "ProxyResponseBinary", "ReloadPropsOpts", "ReloadPropsResponse", "RunActionOptsStashId", diff --git a/src/pipedream/types/account.py b/src/pipedream/types/account.py index 2627d08..ee8e428 100644 --- a/src/pipedream/types/account.py +++ b/src/pipedream/types/account.py @@ -46,7 +46,7 @@ class Account(UniversalBaseModel): The date and time the account was last updated, an ISO 8601 formatted string """ - credentials: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) + credentials: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ The credentials associated with the account, if the `include_credentials` parameter was set to true in the request """ diff --git a/src/pipedream/types/configurable_prop.py b/src/pipedream/types/configurable_prop.py index 5f5308b..911f8e6 100644 --- a/src/pipedream/types/configurable_prop.py +++ b/src/pipedream/types/configurable_prop.py @@ -1,78 +1,348 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import typing import pydantic import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .configurable_prop_type import ConfigurablePropType +from .configurable_prop_alert_type import ConfigurablePropAlertType +from .configurable_prop_any_options_item import ConfigurablePropAnyOptionsItem +from .configurable_prop_boolean_options_item import ConfigurablePropBooleanOptionsItem +from .configurable_prop_integer_array_options_item import ConfigurablePropIntegerArrayOptionsItem +from .configurable_prop_integer_options_item import ConfigurablePropIntegerOptionsItem +from .configurable_prop_object_options_item import ConfigurablePropObjectOptionsItem +from .configurable_prop_sql_auth import ConfigurablePropSqlAuth +from .configurable_prop_sql_options_item import ConfigurablePropSqlOptionsItem +from .configurable_prop_string_array_options_item import ConfigurablePropStringArrayOptionsItem +from .configurable_prop_string_options_item import ConfigurablePropStringOptionsItem +from .configurable_prop_timer_default import ConfigurablePropTimerDefault +from .configurable_prop_timer_option import ConfigurablePropTimerOption +from .configurable_prop_timer_static import ConfigurablePropTimerStatic +from .configured_prop_value_any import ConfiguredPropValueAny +from .configured_prop_value_boolean import ConfiguredPropValueBoolean +from .configured_prop_value_integer import ConfiguredPropValueInteger +from .configured_prop_value_object import ConfiguredPropValueObject +from .configured_prop_value_sql import ConfiguredPropValueSql +from .configured_prop_value_string import ConfiguredPropValueString -class ConfigurableProp(UniversalBaseModel): +class ConfigurableProp_Alert(UniversalBaseModel): """ - A configuration or input field for a component. + A configuration or input field for a component. This is a discriminated union based on the type field. """ - name: str = pydantic.Field() + type: typing.Literal["alert"] = "alert" + alert_type: typing_extensions.Annotated[ + typing.Optional[ConfigurablePropAlertType], FieldMetadata(alias="alertType") + ] = None + content: str + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_Any(UniversalBaseModel): """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value + A configuration or input field for a component. This is a discriminated union based on the type field. """ - type: ConfigurablePropType - label: typing.Optional[str] = pydantic.Field(default=None) + type: typing.Literal["any"] = "any" + default: typing.Optional[ConfiguredPropValueAny] = None + options: typing.Optional[typing.List[ConfigurablePropAnyOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_App(UniversalBaseModel): """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. + A configuration or input field for a component. This is a discriminated union based on the type field. """ - description: typing.Optional[str] = pydantic.Field(default=None) + type: typing.Literal["app"] = "app" + app: str + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_Boolean(UniversalBaseModel): """ - A description of the prop, shown to the user when configuring the component. + A configuration or input field for a component. This is a discriminated union based on the type field. """ - optional: typing.Optional[bool] = pydantic.Field(default=None) + type: typing.Literal["boolean"] = "boolean" + default: typing.Optional[ConfiguredPropValueBoolean] = None + options: typing.Optional[typing.List[ConfigurablePropBooleanOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_InterfaceTimer(UniversalBaseModel): """ - If true, this prop does not need to be specified. + A configuration or input field for a component. This is a discriminated union based on the type field. """ - disabled: typing.Optional[bool] = pydantic.Field(default=None) + type: typing.Literal["$.interface.timer"] = "$.interface.timer" + static: typing.Optional[ConfigurablePropTimerStatic] = None + default: typing.Optional[ConfigurablePropTimerDefault] = None + options: typing.Optional[typing.List[typing.Optional[ConfigurablePropTimerOption]]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_InterfaceApphook(UniversalBaseModel): """ - If true, this prop will be ignored. + A configuration or input field for a component. This is a discriminated union based on the type field. """ - hidden: typing.Optional[bool] = pydantic.Field(default=None) + type: typing.Literal["$.interface.apphook"] = "$.interface.apphook" + app_prop: typing_extensions.Annotated[str, FieldMetadata(alias="appProp")] + event_names: typing_extensions.Annotated[typing.Optional[typing.List[str]], FieldMetadata(alias="eventNames")] = ( + None + ) + remote: typing.Optional[bool] = None + static: typing.Optional[typing.List[typing.Any]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_IntegerArray(UniversalBaseModel): """ - If true, should not expose this prop to the user + A configuration or input field for a component. This is a discriminated union based on the type field. """ - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) + type: typing.Literal["integer[]"] = "integer[]" + min: typing.Optional[int] = None + max: typing.Optional[int] = None + default: typing.Optional[typing.List[ConfiguredPropValueInteger]] = None + options: typing.Optional[typing.List[ConfigurablePropIntegerArrayOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_InterfaceHttp(UniversalBaseModel): """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options + A configuration or input field for a component. This is a discriminated union based on the type field. """ - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) + type: typing.Literal["$.interface.http"] = "$.interface.http" + custom_response: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="customResponse")] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_ServiceDb(UniversalBaseModel): """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options + A configuration or input field for a component. This is a discriminated union based on the type field. """ - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) + type: typing.Literal["$.service.db"] = "$.service.db" + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_Sql(UniversalBaseModel): """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one + A configuration or input field for a component. This is a discriminated union based on the type field. """ - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) + type: typing.Literal["sql"] = "sql" + auth: typing.Optional[ConfigurablePropSqlAuth] = None + default: typing.Optional[ConfiguredPropValueSql] = None + options: typing.Optional[typing.List[ConfigurablePropSqlOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_AirtableBaseId(UniversalBaseModel): """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label + A configuration or input field for a component. This is a discriminated union based on the type field. """ + type: typing.Literal["$.airtable.baseId"] = "$.airtable.baseId" + app_prop: typing_extensions.Annotated[str, FieldMetadata(alias="appProp")] + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: @@ -81,3 +351,290 @@ class Config: frozen = True smart_union = True extra = pydantic.Extra.allow + + +class ConfigurableProp_AirtableTableId(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["$.airtable.tableId"] = "$.airtable.tableId" + base_id_prop: typing_extensions.Annotated[str, FieldMetadata(alias="baseIdProp")] + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_AirtableViewId(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["$.airtable.viewId"] = "$.airtable.viewId" + table_id_prop: typing_extensions.Annotated[str, FieldMetadata(alias="tableIdProp")] + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_AirtableFieldId(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["$.airtable.fieldId"] = "$.airtable.fieldId" + table_id_prop: typing_extensions.Annotated[str, FieldMetadata(alias="tableIdProp")] + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_DiscordChannel(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["$.discord.channel"] = "$.discord.channel" + app_prop: typing_extensions.Annotated[str, FieldMetadata(alias="appProp")] + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_DiscordChannelArray(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["$.discord.channel[]"] = "$.discord.channel[]" + app_prop: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="appProp")] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_Integer(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["integer"] = "integer" + min: typing.Optional[int] = None + max: typing.Optional[int] = None + default: typing.Optional[ConfiguredPropValueInteger] = None + options: typing.Optional[typing.List[ConfigurablePropIntegerOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_Object(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["object"] = "object" + default: typing.Optional[ConfiguredPropValueObject] = None + options: typing.Optional[typing.List[ConfigurablePropObjectOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_String(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["string"] = "string" + secret: typing.Optional[bool] = None + default: typing.Optional[ConfiguredPropValueString] = None + options: typing.Optional[typing.List[ConfigurablePropStringOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConfigurableProp_StringArray(UniversalBaseModel): + """ + A configuration or input field for a component. This is a discriminated union based on the type field. + """ + + type: typing.Literal["string[]"] = "string[]" + secret: typing.Optional[bool] = None + default: typing.Optional[typing.List[ConfiguredPropValueString]] = None + options: typing.Optional[typing.List[ConfigurablePropStringArrayOptionsItem]] = None + name: str + label: typing.Optional[str] = None + description: typing.Optional[str] = None + optional: typing.Optional[bool] = None + disabled: typing.Optional[bool] = None + hidden: typing.Optional[bool] = None + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = None + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = None + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = None + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +ConfigurableProp = typing_extensions.Annotated[ + typing.Union[ + ConfigurableProp_Alert, + ConfigurableProp_Any, + ConfigurableProp_App, + ConfigurableProp_Boolean, + ConfigurableProp_InterfaceTimer, + ConfigurableProp_InterfaceApphook, + ConfigurableProp_IntegerArray, + ConfigurableProp_InterfaceHttp, + ConfigurableProp_ServiceDb, + ConfigurableProp_Sql, + ConfigurableProp_AirtableBaseId, + ConfigurableProp_AirtableTableId, + ConfigurableProp_AirtableViewId, + ConfigurableProp_AirtableFieldId, + ConfigurableProp_DiscordChannel, + ConfigurableProp_DiscordChannelArray, + ConfigurableProp_Integer, + ConfigurableProp_Object, + ConfigurableProp_String, + ConfigurableProp_StringArray, + ], + pydantic.Field(discriminator="type"), +] diff --git a/src/pipedream/types/configurable_prop_airtable_base_id.py b/src/pipedream/types/configurable_prop_airtable_base_id.py index 45340cd..322ca16 100644 --- a/src/pipedream/types/configurable_prop_airtable_base_id.py +++ b/src/pipedream/types/configurable_prop_airtable_base_id.py @@ -4,75 +4,17 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropAirtableBaseId(UniversalBaseModel): - type: typing.Literal["$.airtable.baseId"] = "$.airtable.baseId" +class ConfigurablePropAirtableBaseId(ConfigurablePropBase): app_prop: typing_extensions.Annotated[str, FieldMetadata(alias="appProp")] = pydantic.Field() """ The name of the app prop that provides Airtable authentication """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_airtable_field_id.py b/src/pipedream/types/configurable_prop_airtable_field_id.py index f9bc3a3..a2fa882 100644 --- a/src/pipedream/types/configurable_prop_airtable_field_id.py +++ b/src/pipedream/types/configurable_prop_airtable_field_id.py @@ -4,75 +4,17 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropAirtableFieldId(UniversalBaseModel): - type: typing.Literal["$.airtable.fieldId"] = "$.airtable.fieldId" +class ConfigurablePropAirtableFieldId(ConfigurablePropBase): table_id_prop: typing_extensions.Annotated[str, FieldMetadata(alias="tableIdProp")] = pydantic.Field() """ The name of the prop that provides the Airtable table ID """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_airtable_table_id.py b/src/pipedream/types/configurable_prop_airtable_table_id.py index 4124a1e..a6be428 100644 --- a/src/pipedream/types/configurable_prop_airtable_table_id.py +++ b/src/pipedream/types/configurable_prop_airtable_table_id.py @@ -4,75 +4,17 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropAirtableTableId(UniversalBaseModel): - type: typing.Literal["$.airtable.tableId"] = "$.airtable.tableId" +class ConfigurablePropAirtableTableId(ConfigurablePropBase): base_id_prop: typing_extensions.Annotated[str, FieldMetadata(alias="baseIdProp")] = pydantic.Field() """ The name of the prop that provides the Airtable base ID """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_airtable_view_id.py b/src/pipedream/types/configurable_prop_airtable_view_id.py index 02bf679..7d265b8 100644 --- a/src/pipedream/types/configurable_prop_airtable_view_id.py +++ b/src/pipedream/types/configurable_prop_airtable_view_id.py @@ -4,75 +4,17 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropAirtableViewId(UniversalBaseModel): - type: typing.Literal["$.airtable.viewId"] = "$.airtable.viewId" +class ConfigurablePropAirtableViewId(ConfigurablePropBase): table_id_prop: typing_extensions.Annotated[str, FieldMetadata(alias="tableIdProp")] = pydantic.Field() """ The name of the prop that provides the Airtable table ID """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_alert.py b/src/pipedream/types/configurable_prop_alert.py index f0eff88..0e5e5fd 100644 --- a/src/pipedream/types/configurable_prop_alert.py +++ b/src/pipedream/types/configurable_prop_alert.py @@ -4,13 +4,13 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata from .configurable_prop_alert_type import ConfigurablePropAlertType +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropAlert(UniversalBaseModel): - type: typing.Literal["alert"] = "alert" +class ConfigurablePropAlert(ConfigurablePropBase): alert_type: typing_extensions.Annotated[ typing.Optional[ConfigurablePropAlertType], FieldMetadata(alias="alertType") ] = None @@ -19,64 +19,6 @@ class ConfigurablePropAlert(UniversalBaseModel): The content of the alert, which can include HTML or plain text. """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_any.py b/src/pipedream/types/configurable_prop_any.py index dff60a9..32e1eb7 100644 --- a/src/pipedream/types/configurable_prop_any.py +++ b/src/pipedream/types/configurable_prop_any.py @@ -3,74 +3,15 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from .configurable_prop_any_options_item import ConfigurablePropAnyOptionsItem +from .configurable_prop_base import ConfigurablePropBase from .configured_prop_value_any import ConfiguredPropValueAny -class ConfigurablePropAny(UniversalBaseModel): - type: typing.Literal["any"] = "any" +class ConfigurablePropAny(ConfigurablePropBase): default: typing.Optional[ConfiguredPropValueAny] = None options: typing.Optional[typing.List[ConfigurablePropAnyOptionsItem]] = None - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_app.py b/src/pipedream/types/configurable_prop_app.py index 514dbfa..f4cb42f 100644 --- a/src/pipedream/types/configurable_prop_app.py +++ b/src/pipedream/types/configurable_prop_app.py @@ -3,76 +3,16 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropApp(UniversalBaseModel): - type: typing.Literal["app"] = "app" +class ConfigurablePropApp(ConfigurablePropBase): app: str = pydantic.Field() """ The name slug of the app, e.g. 'github', 'slack', etc. This is used to identify the app for which the account is being configured. """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_apphook.py b/src/pipedream/types/configurable_prop_apphook.py index a2688c2..7847505 100644 --- a/src/pipedream/types/configurable_prop_apphook.py +++ b/src/pipedream/types/configurable_prop_apphook.py @@ -4,12 +4,12 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropApphook(UniversalBaseModel): - type: typing.Literal["$.interface.apphook"] = "$.interface.apphook" +class ConfigurablePropApphook(ConfigurablePropBase): app_prop: typing_extensions.Annotated[str, FieldMetadata(alias="appProp")] = pydantic.Field() """ The name of the app prop that this apphook depends on @@ -27,69 +27,11 @@ class ConfigurablePropApphook(UniversalBaseModel): Whether this apphook is remote """ - static: typing.Optional[typing.List[typing.Optional[typing.Any]]] = pydantic.Field(default=None) + static: typing.Optional[typing.List[typing.Any]] = pydantic.Field(default=None) """ Static configuration for the apphook """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_base.py b/src/pipedream/types/configurable_prop_base.py new file mode 100644 index 0000000..d0ccb3e --- /dev/null +++ b/src/pipedream/types/configurable_prop_base.py @@ -0,0 +1,81 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata + + +class ConfigurablePropBase(UniversalBaseModel): + """ + A configuration or input field for a component. + """ + + name: str = pydantic.Field() + """ + When building `configuredProps`, make sure to use this field as the key when setting the prop value + """ + + label: typing.Optional[str] = pydantic.Field(default=None) + """ + Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + A description of the prop, shown to the user when configuring the component. + """ + + optional: typing.Optional[bool] = pydantic.Field(default=None) + """ + If true, this prop does not need to be specified. + """ + + disabled: typing.Optional[bool] = pydantic.Field(default=None) + """ + If true, this prop will be ignored. + """ + + hidden: typing.Optional[bool] = pydantic.Field(default=None) + """ + If true, should not expose this prop to the user + """ + + remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( + pydantic.Field(default=None) + ) + """ + If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options + """ + + use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( + default=None + ) + """ + If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options + """ + + reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( + pydantic.Field(default=None) + ) + """ + If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one + """ + + with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( + default=None + ) + """ + If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/pipedream/types/configurable_prop_boolean.py b/src/pipedream/types/configurable_prop_boolean.py index 649ef51..4afc68e 100644 --- a/src/pipedream/types/configurable_prop_boolean.py +++ b/src/pipedream/types/configurable_prop_boolean.py @@ -3,74 +3,15 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_boolean_options_item import ConfigurablePropBooleanOptionsItem from .configured_prop_value_boolean import ConfiguredPropValueBoolean -class ConfigurablePropBoolean(UniversalBaseModel): - type: typing.Literal["boolean"] = "boolean" +class ConfigurablePropBoolean(ConfigurablePropBase): default: typing.Optional[ConfiguredPropValueBoolean] = None options: typing.Optional[typing.List[ConfigurablePropBooleanOptionsItem]] = None - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_db.py b/src/pipedream/types/configurable_prop_db.py index 63bbf57..268ce2b 100644 --- a/src/pipedream/types/configurable_prop_db.py +++ b/src/pipedream/types/configurable_prop_db.py @@ -3,71 +3,11 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropDb(UniversalBaseModel): - type: typing.Literal["$.service.db"] = "$.service.db" - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - +class ConfigurablePropDb(ConfigurablePropBase): if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_discord.py b/src/pipedream/types/configurable_prop_discord.py index a8e26a6..981159b 100644 --- a/src/pipedream/types/configurable_prop_discord.py +++ b/src/pipedream/types/configurable_prop_discord.py @@ -3,70 +3,12 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropDiscord(UniversalBaseModel): +class ConfigurablePropDiscord(ConfigurablePropBase): type: typing.Literal["$.discord.channel"] = "$.discord.channel" - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_discord_channel.py b/src/pipedream/types/configurable_prop_discord_channel.py index 358f0a7..e8c025f 100644 --- a/src/pipedream/types/configurable_prop_discord_channel.py +++ b/src/pipedream/types/configurable_prop_discord_channel.py @@ -4,75 +4,17 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropDiscordChannel(UniversalBaseModel): - type: typing.Literal["$.discord.channel"] = "$.discord.channel" +class ConfigurablePropDiscordChannel(ConfigurablePropBase): app_prop: typing_extensions.Annotated[str, FieldMetadata(alias="appProp")] = pydantic.Field() """ The name of the app prop that provides Discord authentication """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_discord_channel_array.py b/src/pipedream/types/configurable_prop_discord_channel_array.py index 3e3cb64..7c5af21 100644 --- a/src/pipedream/types/configurable_prop_discord_channel_array.py +++ b/src/pipedream/types/configurable_prop_discord_channel_array.py @@ -4,12 +4,12 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropDiscordChannelArray(UniversalBaseModel): - type: typing.Literal["$.discord.channel[]"] = "$.discord.channel[]" +class ConfigurablePropDiscordChannelArray(ConfigurablePropBase): app_prop: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="appProp")] = pydantic.Field( default=None ) @@ -17,64 +17,6 @@ class ConfigurablePropDiscordChannelArray(UniversalBaseModel): The name of the app prop that provides Discord authentication """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_http.py b/src/pipedream/types/configurable_prop_http.py index 18eb86d..c9276b6 100644 --- a/src/pipedream/types/configurable_prop_http.py +++ b/src/pipedream/types/configurable_prop_http.py @@ -4,12 +4,12 @@ import pydantic import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.serialization import FieldMetadata +from .configurable_prop_base import ConfigurablePropBase -class ConfigurablePropHttp(UniversalBaseModel): - type: typing.Literal["$.interface.http"] = "$.interface.http" +class ConfigurablePropHttp(ConfigurablePropBase): custom_response: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="customResponse")] = ( pydantic.Field(default=None) ) @@ -17,64 +17,6 @@ class ConfigurablePropHttp(UniversalBaseModel): Whether this HTTP interface allows custom responses """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_integer.py b/src/pipedream/types/configurable_prop_integer.py index a19f6f0..e7be86d 100644 --- a/src/pipedream/types/configurable_prop_integer.py +++ b/src/pipedream/types/configurable_prop_integer.py @@ -3,15 +3,13 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_integer_options_item import ConfigurablePropIntegerOptionsItem from .configured_prop_value_integer import ConfiguredPropValueInteger -class ConfigurablePropInteger(UniversalBaseModel): - type: typing.Literal["integer"] = "integer" +class ConfigurablePropInteger(ConfigurablePropBase): min: typing.Optional[int] = pydantic.Field(default=None) """ The minimum value for this integer prop. @@ -28,64 +26,6 @@ class ConfigurablePropInteger(UniversalBaseModel): Available integer options """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_integer_array.py b/src/pipedream/types/configurable_prop_integer_array.py index 4e39ff4..414079f 100644 --- a/src/pipedream/types/configurable_prop_integer_array.py +++ b/src/pipedream/types/configurable_prop_integer_array.py @@ -3,15 +3,13 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_integer_array_options_item import ConfigurablePropIntegerArrayOptionsItem from .configured_prop_value_integer import ConfiguredPropValueInteger -class ConfigurablePropIntegerArray(UniversalBaseModel): - type: typing.Literal["integer[]"] = "integer[]" +class ConfigurablePropIntegerArray(ConfigurablePropBase): min: typing.Optional[int] = pydantic.Field(default=None) """ The minimum value for integers in this array @@ -32,64 +30,6 @@ class ConfigurablePropIntegerArray(UniversalBaseModel): Available options for the integer array """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_object.py b/src/pipedream/types/configurable_prop_object.py index 5be13ea..6a283ca 100644 --- a/src/pipedream/types/configurable_prop_object.py +++ b/src/pipedream/types/configurable_prop_object.py @@ -3,74 +3,15 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_object_options_item import ConfigurablePropObjectOptionsItem from .configured_prop_value_object import ConfiguredPropValueObject -class ConfigurablePropObject(UniversalBaseModel): - type: typing.Literal["object"] = "object" +class ConfigurablePropObject(ConfigurablePropBase): default: typing.Optional[ConfiguredPropValueObject] = None options: typing.Optional[typing.List[ConfigurablePropObjectOptionsItem]] = None - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_sql.py b/src/pipedream/types/configurable_prop_sql.py index 6c13cf3..2cdd4a6 100644 --- a/src/pipedream/types/configurable_prop_sql.py +++ b/src/pipedream/types/configurable_prop_sql.py @@ -3,76 +3,17 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_sql_auth import ConfigurablePropSqlAuth from .configurable_prop_sql_options_item import ConfigurablePropSqlOptionsItem from .configured_prop_value_sql import ConfiguredPropValueSql -class ConfigurablePropSql(UniversalBaseModel): - type: typing.Literal["sql"] = "sql" +class ConfigurablePropSql(ConfigurablePropBase): auth: typing.Optional[ConfigurablePropSqlAuth] = None default: typing.Optional[ConfiguredPropValueSql] = None options: typing.Optional[typing.List[ConfigurablePropSqlOptionsItem]] = None - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_string.py b/src/pipedream/types/configurable_prop_string.py index dee9dc5..76f4680 100644 --- a/src/pipedream/types/configurable_prop_string.py +++ b/src/pipedream/types/configurable_prop_string.py @@ -3,15 +3,13 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_string_options_item import ConfigurablePropStringOptionsItem from .configured_prop_value_string import ConfiguredPropValueString -class ConfigurablePropString(UniversalBaseModel): - type: typing.Literal["string"] = "string" +class ConfigurablePropString(ConfigurablePropBase): secret: typing.Optional[bool] = pydantic.Field(default=None) """ If true, this prop is a secret and should not be displayed in plain text. @@ -19,63 +17,6 @@ class ConfigurablePropString(UniversalBaseModel): default: typing.Optional[ConfiguredPropValueString] = None options: typing.Optional[typing.List[ConfigurablePropStringOptionsItem]] = None - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_string_array.py b/src/pipedream/types/configurable_prop_string_array.py index e6a1302..9b00d4b 100644 --- a/src/pipedream/types/configurable_prop_string_array.py +++ b/src/pipedream/types/configurable_prop_string_array.py @@ -3,15 +3,13 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_string_array_options_item import ConfigurablePropStringArrayOptionsItem from .configured_prop_value_string import ConfiguredPropValueString -class ConfigurablePropStringArray(UniversalBaseModel): - type: typing.Literal["string[]"] = "string[]" +class ConfigurablePropStringArray(ConfigurablePropBase): secret: typing.Optional[bool] = pydantic.Field(default=None) """ If true, this prop is a secret and should not be displayed in plain text. @@ -23,63 +21,6 @@ class ConfigurablePropStringArray(UniversalBaseModel): """ options: typing.Optional[typing.List[ConfigurablePropStringArrayOptionsItem]] = None - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/pipedream/types/configurable_prop_timer.py b/src/pipedream/types/configurable_prop_timer.py index 34da42c..4a6f37d 100644 --- a/src/pipedream/types/configurable_prop_timer.py +++ b/src/pipedream/types/configurable_prop_timer.py @@ -3,16 +3,14 @@ import typing import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .configurable_prop_base import ConfigurablePropBase from .configurable_prop_timer_default import ConfigurablePropTimerDefault from .configurable_prop_timer_option import ConfigurablePropTimerOption from .configurable_prop_timer_static import ConfigurablePropTimerStatic -class ConfigurablePropTimer(UniversalBaseModel): - type: typing.Literal["$.interface.timer"] = "$.interface.timer" +class ConfigurablePropTimer(ConfigurablePropBase): static: typing.Optional[ConfigurablePropTimerStatic] = None default: typing.Optional[ConfigurablePropTimerDefault] = None options: typing.Optional[typing.List[typing.Optional[ConfigurablePropTimerOption]]] = pydantic.Field(default=None) @@ -20,64 +18,6 @@ class ConfigurablePropTimer(UniversalBaseModel): Available timer configuration options """ - name: str = pydantic.Field() - """ - When building `configuredProps`, make sure to use this field as the key when setting the prop value - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - Value to use as an input label. In cases where `type` is "app", should load the app via `getApp`, etc. and show `app.name` instead. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - A description of the prop, shown to the user when configuring the component. - """ - - optional: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop does not need to be specified. - """ - - disabled: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, this prop will be ignored. - """ - - hidden: typing.Optional[bool] = pydantic.Field(default=None) - """ - If true, should not expose this prop to the user - """ - - remote_options: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="remoteOptions")] = ( - pydantic.Field(default=None) - ) - """ - If true, call `configureComponent` for this prop to load remote options. It is safe, and preferred, given a returned list of { label: string; value: any } objects to set the prop value to { __lv: { label: string; value: any } }. This way, on load, you can access label for the value without necessarily reloading these options - """ - - use_query: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="useQuery")] = pydantic.Field( - default=None - ) - """ - If true, calls to `configureComponent` for this prop support receiving a `query` parameter to filter remote options - """ - - reload_props: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="reloadProps")] = ( - pydantic.Field(default=None) - ) - """ - If true, after setting a value for this prop, a call to `reloadComponentProps` is required as the component has dynamic configurable props dependent on this one - """ - - with_label: typing_extensions.Annotated[typing.Optional[bool], FieldMetadata(alias="withLabel")] = pydantic.Field( - default=None - ) - """ - If true, you must save the configured prop value as a "label-value" object which should look like: { __lv: { label: string; value: any } } because the execution needs to access the label - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/pipedream/types/configurable_prop_type.py b/src/pipedream/types/configurable_prop_type.py deleted file mode 100644 index 408276a..0000000 --- a/src/pipedream/types/configurable_prop_type.py +++ /dev/null @@ -1,32 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ConfigurablePropType = typing.Union[ - typing.Literal[ - "$.airtable.baseId", - "$.airtable.fieldId", - "$.airtable.tableId", - "$.airtable.viewId", - "$.discord.channel", - "$.discord.channel[]", - "$.interface.apphook", - "$.interface.http", - "$.interface.timer", - "$.service.db", - "alert", - "any", - "app", - "boolean", - "data_store", - "dir", - "http_request", - "integer", - "integer[]", - "object", - "sql", - "string", - "string[]", - ], - typing.Any, -] diff --git a/src/pipedream/types/configure_prop_opts.py b/src/pipedream/types/configure_prop_opts.py index bee3f7c..1712ac7 100644 --- a/src/pipedream/types/configure_prop_opts.py +++ b/src/pipedream/types/configure_prop_opts.py @@ -48,7 +48,7 @@ class ConfigurePropOpts(UniversalBaseModel): Page number for paginated results """ - prev_context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) + prev_context: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ Previous context for pagination """ diff --git a/src/pipedream/types/configure_prop_response.py b/src/pipedream/types/configure_prop_response.py index 188645d..7bb1134 100644 --- a/src/pipedream/types/configure_prop_response.py +++ b/src/pipedream/types/configure_prop_response.py @@ -20,7 +20,7 @@ class ConfigurePropResponse(UniversalBaseModel): """ observations: typing.Optional[typing.List[Observation]] = None - context: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) + context: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ New context after configuring the prop """ diff --git a/src/pipedream/types/configured_prop_value_any.py b/src/pipedream/types/configured_prop_value_any.py index 98361e0..180403c 100644 --- a/src/pipedream/types/configured_prop_value_any.py +++ b/src/pipedream/types/configured_prop_value_any.py @@ -2,4 +2,4 @@ import typing -ConfiguredPropValueAny = typing.Optional[typing.Any] +ConfiguredPropValueAny = typing.Any diff --git a/src/pipedream/types/configured_prop_value_object.py b/src/pipedream/types/configured_prop_value_object.py index 3cab1eb..b77485b 100644 --- a/src/pipedream/types/configured_prop_value_object.py +++ b/src/pipedream/types/configured_prop_value_object.py @@ -2,4 +2,4 @@ import typing -ConfiguredPropValueObject = typing.Dict[str, typing.Optional[typing.Any]] +ConfiguredPropValueObject = typing.Dict[str, typing.Any] diff --git a/src/pipedream/types/deployed_component.py b/src/pipedream/types/deployed_component.py index b628be7..65e60a9 100644 --- a/src/pipedream/types/deployed_component.py +++ b/src/pipedream/types/deployed_component.py @@ -64,7 +64,7 @@ class DeployedComponent(UniversalBaseModel): The name slug of the deployed component """ - callback_observations: typing.Optional[typing.Optional[typing.Any]] = pydantic.Field(default=None) + callback_observations: typing.Optional[typing.Any] = pydantic.Field(default=None) """ Callback observations for the deployed component """ diff --git a/src/pipedream/types/emitted_event.py b/src/pipedream/types/emitted_event.py index 633bbeb..0ce4f3d 100644 --- a/src/pipedream/types/emitted_event.py +++ b/src/pipedream/types/emitted_event.py @@ -11,7 +11,7 @@ class EmittedEvent(UniversalBaseModel): An event emitted by a trigger """ - e: typing.Dict[str, typing.Optional[typing.Any]] = pydantic.Field() + e: typing.Dict[str, typing.Any] = pydantic.Field() """ The event's payload """ diff --git a/src/pipedream/types/emitter.py b/src/pipedream/types/emitter.py index 9f1c407..b17e466 100644 --- a/src/pipedream/types/emitter.py +++ b/src/pipedream/types/emitter.py @@ -5,6 +5,7 @@ import typing import pydantic +import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .configurable_prop import ConfigurableProp from .configured_props import ConfiguredProps @@ -27,7 +28,7 @@ class Emitter_DeployedComponent(UniversalBaseModel): updated_at: int name: str name_slug: str - callback_observations: typing.Optional[typing.Optional[typing.Any]] = None + callback_observations: typing.Optional[typing.Any] = None emit_on_deploy: typing.Optional[bool] = None if IS_PYDANTIC_V2: @@ -87,4 +88,7 @@ class Config: extra = pydantic.Extra.allow -Emitter = typing.Union[Emitter_DeployedComponent, Emitter_HttpInterface, Emitter_TimerInterface] +Emitter = typing_extensions.Annotated[ + typing.Union[Emitter_DeployedComponent, Emitter_HttpInterface, Emitter_TimerInterface], + pydantic.Field(discriminator="type"), +] diff --git a/src/pipedream/types/error_response.py b/src/pipedream/types/error_response.py index ce5a708..62e35d4 100644 --- a/src/pipedream/types/error_response.py +++ b/src/pipedream/types/error_response.py @@ -21,7 +21,7 @@ class ErrorResponse(UniversalBaseModel): The error code """ - details: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) + details: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ Additional error details """ diff --git a/src/pipedream/types/list_projects_response.py b/src/pipedream/types/list_projects_response.py new file mode 100644 index 0000000..4094ff3 --- /dev/null +++ b/src/pipedream/types/list_projects_response.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .page_info import PageInfo +from .project import Project + + +class ListProjectsResponse(UniversalBaseModel): + """ + Response received when listing Connect projects + """ + + data: typing.List[Project] + page_info: PageInfo + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/pipedream/types/project.py b/src/pipedream/types/project.py new file mode 100644 index 0000000..020e95e --- /dev/null +++ b/src/pipedream/types/project.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class Project(UniversalBaseModel): + """ + Project that can be accessed via the Connect API + """ + + id: str = pydantic.Field() + """ + Hash ID for the project + """ + + name: str = pydantic.Field() + """ + Display name of the project + """ + + app_name: typing.Optional[str] = pydantic.Field(default=None) + """ + App name shown to Connect users + """ + + support_email: typing.Optional[str] = pydantic.Field(default=None) + """ + Support email configured for the project + """ + + external_url: typing.Optional[str] = pydantic.Field(default=None) + """ + External URL for the project, if configured + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/pipedream/types/proxy_response.py b/src/pipedream/types/proxy_response.py index 902ac7d..6d7a139 100644 --- a/src/pipedream/types/proxy_response.py +++ b/src/pipedream/types/proxy_response.py @@ -2,4 +2,4 @@ import typing -ProxyResponse = typing.Optional[typing.Any] +ProxyResponse = typing.Any diff --git a/src/pipedream/types/proxy_response_binary.py b/src/pipedream/types/proxy_response_binary.py deleted file mode 100644 index e799429..0000000 --- a/src/pipedream/types/proxy_response_binary.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -ProxyResponseBinary = str diff --git a/src/pipedream/types/run_action_response.py b/src/pipedream/types/run_action_response.py index e223088..844175f 100644 --- a/src/pipedream/types/run_action_response.py +++ b/src/pipedream/types/run_action_response.py @@ -12,17 +12,17 @@ class RunActionResponse(UniversalBaseModel): The response received after running an action. See https://pipedream.com/docs/components/api#returning-data-from-steps for more details. """ - exports: typing.Optional[typing.Optional[typing.Any]] = pydantic.Field(default=None) + exports: typing.Optional[typing.Any] = pydantic.Field(default=None) """ The key-value pairs resulting from calls to `$.export` """ - os: typing.Optional[typing.Optional[typing.Any]] = pydantic.Field(default=None) + os: typing.Optional[typing.Any] = pydantic.Field(default=None) """ Any logs produced during the execution of the action """ - ret: typing.Optional[typing.Optional[typing.Any]] = pydantic.Field(default=None) + ret: typing.Optional[typing.Any] = pydantic.Field(default=None) """ The value returned by the action """ diff --git a/src/pipedream/users/raw_client.py b/src/pipedream/users/raw_client.py index 168f71b..4239355 100644 --- a/src/pipedream/users/raw_client.py +++ b/src/pipedream/users/raw_client.py @@ -45,9 +45,9 @@ def delete_external_user( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), @@ -91,9 +91,9 @@ async def delete_external_user( raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + typing.Any, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.Any, # type: ignore object_=_response.json(), ), ), diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py index c9ccb3b..50f2759 100644 --- a/tests/utils/test_http_client.py +++ b/tests/utils/test_http_client.py @@ -1,6 +1,6 @@ # This file was auto-generated by Fern from our API Definition. -from pipedream.core.http_client import get_request_body +from pipedream.core.http_client import get_request_body, remove_none_from_dict from pipedream.core.request_options import RequestOptions @@ -8,6 +8,10 @@ def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} +def get_request_options_with_none() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later", "optional": None}} + + def test_get_json_request_body() -> None: json_body, data_body = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=None) assert json_body == {"hello": "world"} @@ -59,3 +63,47 @@ def test_get_empty_json_request_body() -> None: assert json_body_extras is None assert data_body_extras is None + + +def test_json_body_preserves_none_values() -> None: + """Test that JSON bodies preserve None values (they become JSON null).""" + json_body, data_body = get_request_body( + json={"hello": "world", "optional": None}, data=None, request_options=None, omit=None + ) + # JSON bodies should preserve None values + assert json_body == {"hello": "world", "optional": None} + assert data_body is None + + +def test_data_body_preserves_none_values_without_multipart() -> None: + """Test that data bodies preserve None values when not using multipart. + + The filtering of None values happens in HttpClient.request/stream methods, + not in get_request_body. This test verifies get_request_body doesn't filter None. + """ + json_body, data_body = get_request_body( + json=None, data={"hello": "world", "optional": None}, request_options=None, omit=None + ) + # get_request_body should preserve None values in data body + # The filtering happens later in HttpClient.request when multipart is detected + assert data_body == {"hello": "world", "optional": None} + assert json_body is None + + +def test_remove_none_from_dict_filters_none_values() -> None: + """Test that remove_none_from_dict correctly filters out None values.""" + original = {"hello": "world", "optional": None, "another": "value", "also_none": None} + filtered = remove_none_from_dict(original) + assert filtered == {"hello": "world", "another": "value"} + # Original should not be modified + assert original == {"hello": "world", "optional": None, "another": "value", "also_none": None} + + +def test_remove_none_from_dict_empty_dict() -> None: + """Test that remove_none_from_dict handles empty dict.""" + assert remove_none_from_dict({}) == {} + + +def test_remove_none_from_dict_all_none() -> None: + """Test that remove_none_from_dict handles dict with all None values.""" + assert remove_none_from_dict({"a": None, "b": None}) == {}