diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5e2e170..347cfcb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,7 +21,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/droidrun-cloud-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Rye
run: |
@@ -46,7 +46,7 @@ jobs:
id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/droidrun-cloud-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Rye
run: |
@@ -67,7 +67,7 @@ jobs:
github.repository == 'stainless-sdks/droidrun-cloud-python' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
- uses: actions/github-script@v8
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: core.setOutput('github_token', await core.getIDToken());
@@ -87,7 +87,7 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/droidrun-cloud-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Rye
run: |
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index 965bfe5..112bd43 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Rye
run: |
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 9f5c3c4..ba61562 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -12,7 +12,7 @@ jobs:
if: github.repository == 'droidrun/mobilerun-sdk-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check release environment
run: |
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index e0dc500..1f73031 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.1.0"
+ ".": "3.2.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 1578830..20e38d9 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 89
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/droidrun%2Fdroidrun-cloud-5354e1e393f7a2c470fba288fd927027d7f8ab0f76350be330392a32d61321d7.yml
-openapi_spec_hash: 61d176c5697051a52251e67cc2a143b7
-config_hash: 2e5f796057a879ad2efd58b3ec8289cf
+configured_endpoints: 104
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/droidrun/droidrun-cloud-9c5542353ace880640fb7e8946c5c8d008480abb8d1952595ba2ee9323aed724.yml
+openapi_spec_hash: afa528f0921063291f66c56c6093f2ab
+config_hash: 27cfddf59d8227e89667fa012952335c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b66e4c..91e1a63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,43 @@
# Changelog
+## 3.2.0 (2026-05-21)
+
+Full Changelog: [v3.1.0...v3.2.0](https://github.com/droidrun/mobilerun-sdk-python/compare/v3.1.0...v3.2.0)
+
+### Features
+
+* **api:** add back devices.state.time ([d6f0634](https://github.com/droidrun/mobilerun-sdk-python/commit/d6f0634aa89816215469ab0140d06256e32e3874))
+* **api:** add missing device tools ([b11b8a9](https://github.com/droidrun/mobilerun-sdk-python/commit/b11b8a95ca84a2cc657f63b6e0a97182a698bfbb))
+* **api:** api update ([2791087](https://github.com/droidrun/mobilerun-sdk-python/commit/279108770b917a87a17b62a9855233133b7d651c))
+* **api:** api update ([c77e4cb](https://github.com/droidrun/mobilerun-sdk-python/commit/c77e4cbf5e12b8d69f71a64e0e1b6e5e2570b027))
+* **api:** api update ([713c27d](https://github.com/droidrun/mobilerun-sdk-python/commit/713c27da8f2a496f57b68bb2465f9e8ebd13ede0))
+* **api:** api update ([1e39fcb](https://github.com/droidrun/mobilerun-sdk-python/commit/1e39fcbdc514e9ef20feda52cd0232c8f6b568aa))
+* **api:** api update ([8b8b8b1](https://github.com/droidrun/mobilerun-sdk-python/commit/8b8b8b1d56db10de229123323ccdcb28b3903086))
+* **api:** api update ([75bbc88](https://github.com/droidrun/mobilerun-sdk-python/commit/75bbc88550c6f53e16c25c692ea0febc2ed07acd))
+* **api:** device timezone api like location ([2dc5cdd](https://github.com/droidrun/mobilerun-sdk-python/commit/2dc5cdd19fe9f7c40a66c0e39d4c2e015e982c4d))
+* **api:** labels for objects ([0b8ee8c](https://github.com/droidrun/mobilerun-sdk-python/commit/0b8ee8c2cd690761a249fb97ac22c393a574f8f3))
+* **api:** manual updates ([7861088](https://github.com/droidrun/mobilerun-sdk-python/commit/786108846d965ae14d619615b1d991d0cf937cb1))
+* **api:** manual updates ([4579552](https://github.com/droidrun/mobilerun-sdk-python/commit/4579552f608c8de6aeab1731f512509ff049b6a4))
+* **internal/types:** support eagerly validating pydantic iterators ([8c7fc10](https://github.com/droidrun/mobilerun-sdk-python/commit/8c7fc10dae7a32f182a614733b3ea7a79455b288))
+* support setting headers via env ([273abbc](https://github.com/droidrun/mobilerun-sdk-python/commit/273abbc5339e39f57f59d885a9bfdf5df5d2e4fe))
+
+
+### Bug Fixes
+
+* **client:** add missing f-string prefix in file type error message ([8d073c2](https://github.com/droidrun/mobilerun-sdk-python/commit/8d073c20fc48f1ae04718591bcd2b7f8805c9c3c))
+* use correct field name format for multipart file arrays ([7ad94ce](https://github.com/droidrun/mobilerun-sdk-python/commit/7ad94ce83e73be25a9dc55c91aabe36065594480))
+
+
+### Performance Improvements
+
+* **client:** optimize file structure copying in multipart requests ([b6562d3](https://github.com/droidrun/mobilerun-sdk-python/commit/b6562d3c3c02a73d9e7e36f26bfd6d86728993d2))
+
+
+### Chores
+
+* **internal:** more robust bootstrap script ([d8ed809](https://github.com/droidrun/mobilerun-sdk-python/commit/d8ed809f6aec7556e96cd697a8b517120cd4ebff))
+* **internal:** reformat pyproject.toml ([d108c45](https://github.com/droidrun/mobilerun-sdk-python/commit/d108c45430e484b935c7ee5bd17138c2bc998d0f))
+
## 3.1.0 (2026-04-16)
Full Changelog: [v3.0.0...v3.1.0](https://github.com/droidrun/mobilerun-sdk-python/compare/v3.0.0...v3.1.0)
diff --git a/README.md b/README.md
index 748a763..73b8b1d 100644
--- a/README.md
+++ b/README.md
@@ -124,11 +124,17 @@ from mobilerun_sdk import Mobilerun
client = Mobilerun()
-profile = client.profiles.create(
- name="x",
- spec={},
+device = client.devices.create(
+ carrier={
+ "gsm_operator_alpha": "GsmOperatorAlpha",
+ "gsm_operator_numeric": 0,
+ "gsm_sim_operator_alpha": "GsmSimOperatorAlpha",
+ "gsm_sim_operator_iso_country": "GsmSimOperatorIsoCountry",
+ "gsm_sim_operator_numeric": 0,
+ "persist_sys_timezone": "PersistSysTimezone",
+ },
)
-print(profile.spec)
+print(device.carrier)
```
## File uploads
diff --git a/api.md b/api.md
index dfe1856..ccda2c7 100644
--- a/api.md
+++ b/api.md
@@ -5,145 +5,148 @@ from mobilerun_sdk.types import (
DeviceCarrier,
DeviceIdentifiers,
DeviceSpec,
+ Location,
Meta,
Pagination,
PaginationMeta,
PermissionSet,
+ Socks5,
)
```
-# Tasks
+# Agents
Types:
```python
-from mobilerun_sdk.types import (
- PackageCredentials,
- Task,
- TaskStatus,
- UsageResult,
- TaskRetrieveResponse,
- TaskListResponse,
- TaskGetStatusResponse,
- TaskGetTrajectoryResponse,
- TaskRunResponse,
- TaskSendMessageResponse,
- TaskStopResponse,
-)
+from mobilerun_sdk.types import AgentListResponse
```
Methods:
-- client.tasks.retrieve(task_id) -> TaskRetrieveResponse
-- client.tasks.list(\*\*params) -> TaskListResponse
-- client.tasks.attach(task_id) -> None
-- client.tasks.get_status(task_id) -> TaskGetStatusResponse
-- client.tasks.get_trajectory(task_id) -> TaskGetTrajectoryResponse
-- client.tasks.run(\*\*params) -> TaskRunResponse
-- client.tasks.run_streamed(\*\*params) -> object
-- client.tasks.send_message(task_id, \*\*params) -> TaskSendMessageResponse
-- client.tasks.stop(task_id) -> TaskStopResponse
+- client.agents.list() -> AgentListResponse
-## Screenshots
+# Apps
Types:
```python
-from mobilerun_sdk.types.tasks import MediaResponse, ScreenshotListResponse
+from mobilerun_sdk.types import (
+ AppRetrieveResponse,
+ AppListResponse,
+ AppDeleteResponse,
+ AppConfirmUploadResponse,
+ AppCreateSignedUploadURLResponse,
+ AppMarkFailedResponse,
+)
```
Methods:
-- client.tasks.screenshots.retrieve(index, \*, task_id) -> MediaResponse
-- client.tasks.screenshots.list(task_id) -> ScreenshotListResponse
+- client.apps.retrieve(id) -> AppRetrieveResponse
+- client.apps.list(\*\*params) -> AppListResponse
+- client.apps.delete(id) -> AppDeleteResponse
+- client.apps.confirm_upload(id) -> AppConfirmUploadResponse
+- client.apps.create_signed_upload_url(\*\*params) -> AppCreateSignedUploadURLResponse
+- client.apps.mark_failed(id) -> AppMarkFailedResponse
-## UiStates
+# Carriers
Types:
```python
-from mobilerun_sdk.types.tasks import UiStateListResponse
+from mobilerun_sdk.types import (
+ CarrierCreateResponse,
+ CarrierRetrieveResponse,
+ CarrierUpdateResponse,
+ CarrierListResponse,
+ CarrierDeleteResponse,
+ CarrierLookupResponse,
+)
```
Methods:
-- client.tasks.ui_states.retrieve(index, \*, task_id) -> MediaResponse
-- client.tasks.ui_states.list(task_id) -> UiStateListResponse
+- client.carriers.create(\*\*params) -> CarrierCreateResponse
+- client.carriers.retrieve(carrier_id) -> CarrierRetrieveResponse
+- client.carriers.update(carrier_id, \*\*params) -> CarrierUpdateResponse
+- client.carriers.list(\*\*params) -> CarrierListResponse
+- client.carriers.delete(carrier_id) -> CarrierDeleteResponse
+- client.carriers.lookup(\*\*params) -> CarrierLookupResponse
-# Agents
+# Credentials
Types:
```python
-from mobilerun_sdk.types import AgentListResponse
+from mobilerun_sdk.types import CredentialListResponse
```
Methods:
-- client.agents.list() -> AgentListResponse
+- client.credentials.list(\*\*params) -> CredentialListResponse
-# Proxies
+## Packages
Types:
```python
-from mobilerun_sdk.types import (
- ProxyConfig,
- ProxyCreateResponse,
- ProxyRetrieveResponse,
- ProxyUpdateResponse,
- ProxyListResponse,
- ProxyDeleteResponse,
-)
+from mobilerun_sdk.types.credentials import PackageCreateResponse, PackageListResponse
```
Methods:
-- client.proxies.create(\*\*params) -> ProxyCreateResponse
-- client.proxies.retrieve(proxy_id) -> ProxyRetrieveResponse
-- client.proxies.update(proxy_id, \*\*params) -> ProxyUpdateResponse
-- client.proxies.list(\*\*params) -> ProxyListResponse
-- client.proxies.delete(proxy_id) -> ProxyDeleteResponse
+- client.credentials.packages.create(\*\*params) -> PackageCreateResponse
+- client.credentials.packages.list(package_name) -> PackageListResponse
-# Carriers
+### Credentials
Types:
```python
-from mobilerun_sdk.types import Carrier, CarrierListResponse, CarrierDeleteResponse
+from mobilerun_sdk.types.credentials.packages import (
+ Credential,
+ CredentialCreateResponse,
+ CredentialRetrieveResponse,
+ CredentialDeleteResponse,
+)
```
Methods:
-- client.carriers.create(\*\*params) -> Carrier
-- client.carriers.retrieve(carrier_id) -> Carrier
-- client.carriers.update(carrier_id, \*\*params) -> Carrier
-- client.carriers.list(\*\*params) -> CarrierListResponse
-- client.carriers.delete(carrier_id) -> CarrierDeleteResponse
-- client.carriers.lookup(\*\*params) -> Carrier
+- client.credentials.packages.credentials.create(package_name, \*\*params) -> CredentialCreateResponse
+- client.credentials.packages.credentials.retrieve(credential_name, \*, package_name) -> CredentialRetrieveResponse
+- client.credentials.packages.credentials.delete(credential_name, \*, package_name) -> CredentialDeleteResponse
-# Profiles
+#### Fields
Types:
```python
-from mobilerun_sdk.types import Profile, ProfileListResponse, ProfileDeleteResponse
+from mobilerun_sdk.types.credentials.packages.credentials import (
+ FieldCreateResponse,
+ FieldUpdateResponse,
+ FieldDeleteResponse,
+)
```
Methods:
-- client.profiles.create(\*\*params) -> Profile
-- client.profiles.retrieve(profile_id) -> Profile
-- client.profiles.update(profile_id, \*\*params) -> Profile
-- client.profiles.list(\*\*params) -> ProfileListResponse
-- client.profiles.delete(profile_id) -> ProfileDeleteResponse
+- client.credentials.packages.credentials.fields.create(credential_name, \*, package_name, \*\*params) -> FieldCreateResponse
+- client.credentials.packages.credentials.fields.update(field_type, \*, package_name, credential_name, \*\*params) -> FieldUpdateResponse
+- client.credentials.packages.credentials.fields.delete(field_type, \*, package_name, credential_name) -> FieldDeleteResponse
# Devices
Types:
```python
-from mobilerun_sdk.types import Device, DeviceListResponse, DeviceCountResponse
+from mobilerun_sdk.types import (
+ Device,
+ DeviceListResponse,
+ DeviceCountResponse,
+ DeviceFingerprintResponse,
+)
```
Methods:
@@ -152,136 +155,156 @@ Methods:
- client.devices.retrieve(device_id) -> Device
- client.devices.list(\*\*params) -> DeviceListResponse
- client.devices.count() -> DeviceCountResponse
+- client.devices.fingerprint(device_id) -> DeviceFingerprintResponse
+- client.devices.reboot(device_id) -> None
+- client.devices.reset(device_id) -> None
- client.devices.set_name(device_id, \*\*params) -> Device
- client.devices.terminate(device_id, \*\*params) -> None
- client.devices.wait_ready(device_id) -> Device
-## Time
+## Actions
Types:
```python
-from mobilerun_sdk.types.devices import TimeTimeResponse, TimeTimezoneResponse
+from mobilerun_sdk.types.devices import ActionOverlayVisibleResponse
```
Methods:
-- client.devices.time.set_timezone(device_id, \*\*params) -> None
-- client.devices.time.time(device_id) -> str
-- client.devices.time.timezone(device_id) -> TimeTimezoneResponse
-
-## Profile
-
-Methods:
-
-- client.devices.profile.update(device_id, \*\*params) -> None
+- client.devices.actions.global\_(device_id, \*\*params) -> None
+- client.devices.actions.overlay_visible(device_id) -> ActionOverlayVisibleResponse
+- client.devices.actions.set_overlay_visible(device_id, \*\*params) -> None
+- client.devices.actions.swipe(device_id, \*\*params) -> None
+- client.devices.actions.tap(device_id, \*\*params) -> None
-## Files
+## Apps
Types:
```python
-from mobilerun_sdk.types.devices import FileInfo, FileListResponse, FileDownloadResponse
+from mobilerun_sdk.types.devices import AppListResponse
```
Methods:
-- client.devices.files.list(device_id, \*\*params) -> FileListResponse
-- client.devices.files.delete(device_id, \*\*params) -> None
-- client.devices.files.download(device_id, \*\*params) -> str
-- client.devices.files.upload(device_id, \*\*params) -> None
+- client.devices.apps.update(package_name, \*, device_id) -> None
+- client.devices.apps.list(device_id, \*\*params) -> Optional[AppListResponse]
+- client.devices.apps.delete(package_name, \*, device_id) -> None
+- client.devices.apps.install(device_id, \*\*params) -> None
+- client.devices.apps.start(package_name, \*, device_id, \*\*params) -> None
-## Proxy
+## Esim
Types:
```python
-from mobilerun_sdk.types.devices import ProxyStatusResponse
+from mobilerun_sdk.types.devices import EsimListResponse, EsimActivateResponse, EsimStatusResponse
```
Methods:
-- client.devices.proxy.connect(device_id, \*\*params) -> None
-- client.devices.proxy.disconnect(device_id) -> None
-- client.devices.proxy.status(device_id) -> ProxyStatusResponse
+- client.devices.esim.list(device_id) -> Optional[EsimListResponse]
+- client.devices.esim.activate(device_id, \*\*params) -> EsimActivateResponse
+- client.devices.esim.enable(device_id, \*\*params) -> None
+- client.devices.esim.remove(device_id, \*\*params) -> None
+- client.devices.esim.set_roaming(device_id, \*\*params) -> None
+- client.devices.esim.status(device_id) -> Optional[EsimStatusResponse]
-## Location
+### Apn
Types:
```python
-from mobilerun_sdk.types.devices import LocationGetResponse
+from mobilerun_sdk.types.devices.esim import ApnListResponse
```
Methods:
-- client.devices.location.get(device_id) -> LocationGetResponse
-- client.devices.location.set(device_id, \*\*params) -> None
+- client.devices.esim.apn.create(device_id, \*\*params) -> None
+- client.devices.esim.apn.list(device_id) -> Optional[ApnListResponse]
+- client.devices.esim.apn.select(device_id, \*\*params) -> None
-## Actions
+## Files
Types:
```python
-from mobilerun_sdk.types.devices import ActionOverlayVisibleResponse
+from mobilerun_sdk.types.devices import FileInfo, FileListResponse, FileDownloadResponse
```
Methods:
-- client.devices.actions.global\_(device_id, \*\*params) -> None
-- client.devices.actions.overlay_visible(device_id) -> ActionOverlayVisibleResponse
-- client.devices.actions.set_overlay_visible(device_id, \*\*params) -> None
-- client.devices.actions.swipe(device_id, \*\*params) -> None
-- client.devices.actions.tap(device_id, \*\*params) -> None
+- client.devices.files.list(device_id, \*\*params) -> FileListResponse
+- client.devices.files.delete(device_id, \*\*params) -> None
+- client.devices.files.download(device_id, \*\*params) -> str
+- client.devices.files.upload(device_id, \*\*params) -> None
-## State
+## Keyboard
-Types:
+Methods:
-```python
-from mobilerun_sdk.types.devices import Rect, StateScreenshotResponse, StateUiResponse
-```
+- client.devices.keyboard.clear(device_id) -> None
+- client.devices.keyboard.key(device_id, \*\*params) -> None
+- client.devices.keyboard.write(device_id, \*\*params) -> None
+
+## Location
Methods:
-- client.devices.state.screenshot(device_id, \*\*params) -> str
-- client.devices.state.ui(device_id, \*\*params) -> StateUiResponse
+- client.devices.location.get(device_id) -> Location
+- client.devices.location.set(device_id, \*\*params) -> None
-## Apps
+## Packages
Types:
```python
-from mobilerun_sdk.types.devices import AppListResponse
+from mobilerun_sdk.types.devices import PackageListResponse
```
Methods:
-- client.devices.apps.update(package_name, \*, device_id) -> None
-- client.devices.apps.list(device_id, \*\*params) -> Optional[AppListResponse]
-- client.devices.apps.delete(package_name, \*, device_id) -> None
-- client.devices.apps.install(device_id, \*\*params) -> None
-- client.devices.apps.start(package_name, \*, device_id, \*\*params) -> None
+- client.devices.packages.list(device_id, \*\*params) -> Optional[PackageListResponse]
-## Packages
+## Profile
+
+Methods:
+
+- client.devices.profile.update(device_id, \*\*params) -> None
+
+## Proxy
Types:
```python
-from mobilerun_sdk.types.devices import PackageListResponse
+from mobilerun_sdk.types.devices import ProxyStatusResponse
```
Methods:
-- client.devices.packages.list(device_id, \*\*params) -> Optional[PackageListResponse]
+- client.devices.proxy.connect(device_id, \*\*params) -> None
+- client.devices.proxy.disconnect(device_id) -> None
+- client.devices.proxy.status(device_id) -> ProxyStatusResponse
-## Keyboard
+## State
+
+Types:
+
+```python
+from mobilerun_sdk.types.devices import (
+ Rect,
+ StateScreenshotResponse,
+ StateTimeResponse,
+ StateUiResponse,
+)
+```
Methods:
-- client.devices.keyboard.clear(device_id) -> None
-- client.devices.keyboard.key(device_id, \*\*params) -> None
-- client.devices.keyboard.write(device_id, \*\*params) -> None
+- client.devices.state.screenshot(device_id, \*\*params) -> str
+- client.devices.state.time(device_id) -> str
+- client.devices.state.ui(device_id, \*\*params) -> StateUiResponse
## Tasks
@@ -295,129 +318,163 @@ Methods:
- client.devices.tasks.list(device_id, \*\*params) -> TaskListResponse
-## Esim
+## Timezone
Types:
```python
-from mobilerun_sdk.types.devices import EsimListResponse, EsimActivateResponse
+from mobilerun_sdk.types.devices import TimezoneGetResponse
```
Methods:
-- client.devices.esim.list(device_id) -> Optional[EsimListResponse]
-- client.devices.esim.activate(device_id, \*\*params) -> EsimActivateResponse
-- client.devices.esim.enable(device_id, \*\*params) -> None
-- client.devices.esim.remove(device_id, \*\*params) -> None
+- client.devices.timezone.get(device_id) -> TimezoneGetResponse
+- client.devices.timezone.set(device_id, \*\*params) -> None
-# Apps
+## Language
Types:
```python
-from mobilerun_sdk.types import AppListResponse
+from mobilerun_sdk.types.devices import LanguageGetResponse
```
Methods:
-- client.apps.list(\*\*params) -> AppListResponse
+- client.devices.language.get(device_id) -> LanguageGetResponse
+- client.devices.language.set(device_id, \*\*params) -> None
-# Credentials
+# Hooks
Types:
```python
-from mobilerun_sdk.types import CredentialListResponse
+from mobilerun_sdk.types import (
+ HookRetrieveResponse,
+ HookUpdateResponse,
+ HookListResponse,
+ HookGetSampleDataResponse,
+ HookPerformResponse,
+ HookSubscribeResponse,
+ HookUnsubscribeResponse,
+)
```
Methods:
-- client.credentials.list(\*\*params) -> CredentialListResponse
+- client.hooks.retrieve(hook_id) -> HookRetrieveResponse
+- client.hooks.update(hook_id, \*\*params) -> HookUpdateResponse
+- client.hooks.list(\*\*params) -> HookListResponse
+- client.hooks.get_sample_data() -> HookGetSampleDataResponse
+- client.hooks.perform(\*\*params) -> HookPerformResponse
+- client.hooks.subscribe(\*\*params) -> HookSubscribeResponse
+- client.hooks.unsubscribe(hook_id) -> HookUnsubscribeResponse
-## Packages
+# Models
Types:
```python
-from mobilerun_sdk.types.credentials import PackageCreateResponse, PackageListResponse
+from mobilerun_sdk.types import ModelListResponse
```
Methods:
-- client.credentials.packages.create(\*\*params) -> PackageCreateResponse
-- client.credentials.packages.list(package_name) -> PackageListResponse
+- client.models.list() -> ModelListResponse
-### Credentials
+# Profiles
Types:
```python
-from mobilerun_sdk.types.credentials.packages import (
- Credential,
- CredentialCreateResponse,
- CredentialRetrieveResponse,
- CredentialDeleteResponse,
-)
+from mobilerun_sdk.types import Profile, ProfileListResponse, ProfileDeleteResponse
```
Methods:
-- client.credentials.packages.credentials.create(package_name, \*\*params) -> CredentialCreateResponse
-- client.credentials.packages.credentials.retrieve(credential_name, \*, package_name) -> CredentialRetrieveResponse
-- client.credentials.packages.credentials.delete(credential_name, \*, package_name) -> CredentialDeleteResponse
+- client.profiles.create(\*\*params) -> Profile
+- client.profiles.retrieve(profile_id) -> Profile
+- client.profiles.update(profile_id, \*\*params) -> Profile
+- client.profiles.list(\*\*params) -> ProfileListResponse
+- client.profiles.delete(profile_id) -> ProfileDeleteResponse
-#### Fields
+# Proxies
Types:
```python
-from mobilerun_sdk.types.credentials.packages.credentials import (
- FieldCreateResponse,
- FieldUpdateResponse,
- FieldDeleteResponse,
+from mobilerun_sdk.types import (
+ ProxyConfig,
+ ProxyCreateResponse,
+ ProxyRetrieveResponse,
+ ProxyUpdateResponse,
+ ProxyListResponse,
+ ProxyDeleteResponse,
)
```
Methods:
-- client.credentials.packages.credentials.fields.create(credential_name, \*, package_name, \*\*params) -> FieldCreateResponse
-- client.credentials.packages.credentials.fields.update(field_type, \*, package_name, credential_name, \*\*params) -> FieldUpdateResponse
-- client.credentials.packages.credentials.fields.delete(field_type, \*, package_name, credential_name) -> FieldDeleteResponse
+- client.proxies.create(\*\*params) -> ProxyCreateResponse
+- client.proxies.retrieve(proxy_id) -> ProxyRetrieveResponse
+- client.proxies.update(proxy_id, \*\*params) -> ProxyUpdateResponse
+- client.proxies.list(\*\*params) -> ProxyListResponse
+- client.proxies.delete(proxy_id) -> ProxyDeleteResponse
-# Hooks
+# Tasks
Types:
```python
from mobilerun_sdk.types import (
- HookRetrieveResponse,
- HookUpdateResponse,
- HookListResponse,
- HookGetSampleDataResponse,
- HookPerformResponse,
- HookSubscribeResponse,
- HookUnsubscribeResponse,
+ PackageCredentials,
+ Task,
+ TaskStatus,
+ UsageResult,
+ TaskRetrieveResponse,
+ TaskListResponse,
+ TaskGetStatusResponse,
+ TaskGetTrajectoryResponse,
+ TaskRunResponse,
+ TaskSendMessageResponse,
+ TaskStopResponse,
)
```
Methods:
-- client.hooks.retrieve(hook_id) -> HookRetrieveResponse
-- client.hooks.update(hook_id, \*\*params) -> HookUpdateResponse
-- client.hooks.list(\*\*params) -> HookListResponse
-- client.hooks.get_sample_data() -> HookGetSampleDataResponse
-- client.hooks.perform(\*\*params) -> HookPerformResponse
-- client.hooks.subscribe(\*\*params) -> HookSubscribeResponse
-- client.hooks.unsubscribe(hook_id) -> HookUnsubscribeResponse
+- client.tasks.retrieve(task_id) -> TaskRetrieveResponse
+- client.tasks.list(\*\*params) -> TaskListResponse
+- client.tasks.attach(task_id) -> None
+- client.tasks.get_status(task_id) -> TaskGetStatusResponse
+- client.tasks.get_trajectory(task_id) -> TaskGetTrajectoryResponse
+- client.tasks.run(\*\*params) -> TaskRunResponse
+- client.tasks.run_streamed(\*\*params) -> object
+- client.tasks.send_message(task_id, \*\*params) -> TaskSendMessageResponse
+- client.tasks.stop(task_id) -> TaskStopResponse
-# Models
+## Screenshots
Types:
```python
-from mobilerun_sdk.types import ModelListResponse
+from mobilerun_sdk.types.tasks import MediaResponse, ScreenshotListResponse
```
Methods:
-- client.models.list() -> ModelListResponse
+- client.tasks.screenshots.retrieve(index, \*, task_id) -> MediaResponse
+- client.tasks.screenshots.list(task_id) -> ScreenshotListResponse
+
+## UiStates
+
+Types:
+
+```python
+from mobilerun_sdk.types.tasks import UiStateListResponse
+```
+
+Methods:
+
+- client.tasks.ui_states.retrieve(index, \*, task_id) -> MediaResponse
+- client.tasks.ui_states.list(task_id) -> UiStateListResponse
diff --git a/pyproject.toml b/pyproject.toml
index 4a1ba0a..b097c23 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "mobilerun-sdk"
-version = "3.1.0"
+version = "3.2.0"
description = "The official Python library for the mobilerun API"
dynamic = ["readme"]
license = "Apache-2.0"
@@ -168,7 +168,7 @@ show_error_codes = true
#
# We also exclude our `tests` as mypy doesn't always infer
# types correctly and Pyright will still catch any type errors.
-exclude = ['src/mobilerun_sdk/_files.py', '_dev/.*.py', 'tests/.*']
+exclude = ["src/mobilerun_sdk/_files.py", "_dev/.*.py", "tests/.*"]
strict_equality = true
implicit_reexport = true
diff --git a/scripts/bootstrap b/scripts/bootstrap
index b430fee..fe8451e 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,7 +4,7 @@ set -e
cd "$(dirname "$0")/.."
-if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then
brew bundle check >/dev/null 2>&1 || {
echo -n "==> Install Homebrew dependencies? (y/N): "
read -r response
diff --git a/src/mobilerun_sdk/_client.py b/src/mobilerun_sdk/_client.py
index 09cae30..28b10f0 100644
--- a/src/mobilerun_sdk/_client.py
+++ b/src/mobilerun_sdk/_client.py
@@ -20,7 +20,11 @@
RequestOptions,
not_given,
)
-from ._utils import is_given, get_async_library
+from ._utils import (
+ is_given,
+ is_mapping_t,
+ get_async_library,
+)
from ._compat import cached_property
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
@@ -96,6 +100,15 @@ def __init__(
if base_url is None:
base_url = f"https://api.mobilerun.ai/v1"
+ custom_headers_env = os.environ.get("MOBILERUN_CUSTOM_HEADERS")
+ if custom_headers_env is not None:
+ parsed: dict[str, str] = {}
+ for line in custom_headers_env.split("\n"):
+ colon = line.find(":")
+ if colon >= 0:
+ parsed[line[:colon].strip()] = line[colon + 1 :].strip()
+ default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
+
super().__init__(
version=__version__,
base_url=base_url,
@@ -107,13 +120,6 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- @cached_property
- def tasks(self) -> TasksResource:
- """Tasks API"""
- from .resources.tasks import TasksResource
-
- return TasksResource(self)
-
@cached_property
def agents(self) -> AgentsResource:
"""Agents API"""
@@ -122,22 +128,25 @@ def agents(self) -> AgentsResource:
return AgentsResource(self)
@cached_property
- def proxies(self) -> ProxiesResource:
- from .resources.proxies import ProxiesResource
+ def apps(self) -> AppsResource:
+ """App Management"""
+ from .resources.apps import AppsResource
- return ProxiesResource(self)
+ return AppsResource(self)
@cached_property
def carriers(self) -> CarriersResource:
+ """Mobile Carriers"""
from .resources.carriers import CarriersResource
return CarriersResource(self)
@cached_property
- def profiles(self) -> ProfilesResource:
- from .resources.profiles import ProfilesResource
+ def credentials(self) -> CredentialsResource:
+ """Vault & Secrets"""
+ from .resources.credentials import CredentialsResource
- return ProfilesResource(self)
+ return CredentialsResource(self)
@cached_property
def devices(self) -> DevicesResource:
@@ -145,31 +154,39 @@ def devices(self) -> DevicesResource:
return DevicesResource(self)
- @cached_property
- def apps(self) -> AppsResource:
- from .resources.apps import AppsResource
-
- return AppsResource(self)
-
- @cached_property
- def credentials(self) -> CredentialsResource:
- from .resources.credentials import CredentialsResource
-
- return CredentialsResource(self)
-
@cached_property
def hooks(self) -> HooksResource:
- """Webhooks API"""
from .resources.hooks import HooksResource
return HooksResource(self)
@cached_property
def models(self) -> ModelsResource:
+ """LLM Models"""
from .resources.models import ModelsResource
return ModelsResource(self)
+ @cached_property
+ def profiles(self) -> ProfilesResource:
+ from .resources.profiles import ProfilesResource
+
+ return ProfilesResource(self)
+
+ @cached_property
+ def proxies(self) -> ProxiesResource:
+ """Network Proxies"""
+ from .resources.proxies import ProxiesResource
+
+ return ProxiesResource(self)
+
+ @cached_property
+ def tasks(self) -> TasksResource:
+ """Tasks API"""
+ from .resources.tasks import TasksResource
+
+ return TasksResource(self)
+
@cached_property
def with_raw_response(self) -> MobilerunWithRawResponse:
return MobilerunWithRawResponse(self)
@@ -334,6 +351,15 @@ def __init__(
if base_url is None:
base_url = f"https://api.mobilerun.ai/v1"
+ custom_headers_env = os.environ.get("MOBILERUN_CUSTOM_HEADERS")
+ if custom_headers_env is not None:
+ parsed: dict[str, str] = {}
+ for line in custom_headers_env.split("\n"):
+ colon = line.find(":")
+ if colon >= 0:
+ parsed[line[:colon].strip()] = line[colon + 1 :].strip()
+ default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
+
super().__init__(
version=__version__,
base_url=base_url,
@@ -345,13 +371,6 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- @cached_property
- def tasks(self) -> AsyncTasksResource:
- """Tasks API"""
- from .resources.tasks import AsyncTasksResource
-
- return AsyncTasksResource(self)
-
@cached_property
def agents(self) -> AsyncAgentsResource:
"""Agents API"""
@@ -360,22 +379,25 @@ def agents(self) -> AsyncAgentsResource:
return AsyncAgentsResource(self)
@cached_property
- def proxies(self) -> AsyncProxiesResource:
- from .resources.proxies import AsyncProxiesResource
+ def apps(self) -> AsyncAppsResource:
+ """App Management"""
+ from .resources.apps import AsyncAppsResource
- return AsyncProxiesResource(self)
+ return AsyncAppsResource(self)
@cached_property
def carriers(self) -> AsyncCarriersResource:
+ """Mobile Carriers"""
from .resources.carriers import AsyncCarriersResource
return AsyncCarriersResource(self)
@cached_property
- def profiles(self) -> AsyncProfilesResource:
- from .resources.profiles import AsyncProfilesResource
+ def credentials(self) -> AsyncCredentialsResource:
+ """Vault & Secrets"""
+ from .resources.credentials import AsyncCredentialsResource
- return AsyncProfilesResource(self)
+ return AsyncCredentialsResource(self)
@cached_property
def devices(self) -> AsyncDevicesResource:
@@ -383,31 +405,39 @@ def devices(self) -> AsyncDevicesResource:
return AsyncDevicesResource(self)
- @cached_property
- def apps(self) -> AsyncAppsResource:
- from .resources.apps import AsyncAppsResource
-
- return AsyncAppsResource(self)
-
- @cached_property
- def credentials(self) -> AsyncCredentialsResource:
- from .resources.credentials import AsyncCredentialsResource
-
- return AsyncCredentialsResource(self)
-
@cached_property
def hooks(self) -> AsyncHooksResource:
- """Webhooks API"""
from .resources.hooks import AsyncHooksResource
return AsyncHooksResource(self)
@cached_property
def models(self) -> AsyncModelsResource:
+ """LLM Models"""
from .resources.models import AsyncModelsResource
return AsyncModelsResource(self)
+ @cached_property
+ def profiles(self) -> AsyncProfilesResource:
+ from .resources.profiles import AsyncProfilesResource
+
+ return AsyncProfilesResource(self)
+
+ @cached_property
+ def proxies(self) -> AsyncProxiesResource:
+ """Network Proxies"""
+ from .resources.proxies import AsyncProxiesResource
+
+ return AsyncProxiesResource(self)
+
+ @cached_property
+ def tasks(self) -> AsyncTasksResource:
+ """Tasks API"""
+ from .resources.tasks import AsyncTasksResource
+
+ return AsyncTasksResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncMobilerunWithRawResponse:
return AsyncMobilerunWithRawResponse(self)
@@ -538,13 +568,6 @@ class MobilerunWithRawResponse:
def __init__(self, client: Mobilerun) -> None:
self._client = client
- @cached_property
- def tasks(self) -> tasks.TasksResourceWithRawResponse:
- """Tasks API"""
- from .resources.tasks import TasksResourceWithRawResponse
-
- return TasksResourceWithRawResponse(self._client.tasks)
-
@cached_property
def agents(self) -> agents.AgentsResourceWithRawResponse:
"""Agents API"""
@@ -553,22 +576,25 @@ def agents(self) -> agents.AgentsResourceWithRawResponse:
return AgentsResourceWithRawResponse(self._client.agents)
@cached_property
- def proxies(self) -> proxies.ProxiesResourceWithRawResponse:
- from .resources.proxies import ProxiesResourceWithRawResponse
+ def apps(self) -> apps.AppsResourceWithRawResponse:
+ """App Management"""
+ from .resources.apps import AppsResourceWithRawResponse
- return ProxiesResourceWithRawResponse(self._client.proxies)
+ return AppsResourceWithRawResponse(self._client.apps)
@cached_property
def carriers(self) -> carriers.CarriersResourceWithRawResponse:
+ """Mobile Carriers"""
from .resources.carriers import CarriersResourceWithRawResponse
return CarriersResourceWithRawResponse(self._client.carriers)
@cached_property
- def profiles(self) -> profiles.ProfilesResourceWithRawResponse:
- from .resources.profiles import ProfilesResourceWithRawResponse
+ def credentials(self) -> credentials.CredentialsResourceWithRawResponse:
+ """Vault & Secrets"""
+ from .resources.credentials import CredentialsResourceWithRawResponse
- return ProfilesResourceWithRawResponse(self._client.profiles)
+ return CredentialsResourceWithRawResponse(self._client.credentials)
@cached_property
def devices(self) -> devices.DevicesResourceWithRawResponse:
@@ -576,44 +602,45 @@ def devices(self) -> devices.DevicesResourceWithRawResponse:
return DevicesResourceWithRawResponse(self._client.devices)
- @cached_property
- def apps(self) -> apps.AppsResourceWithRawResponse:
- from .resources.apps import AppsResourceWithRawResponse
-
- return AppsResourceWithRawResponse(self._client.apps)
-
- @cached_property
- def credentials(self) -> credentials.CredentialsResourceWithRawResponse:
- from .resources.credentials import CredentialsResourceWithRawResponse
-
- return CredentialsResourceWithRawResponse(self._client.credentials)
-
@cached_property
def hooks(self) -> hooks.HooksResourceWithRawResponse:
- """Webhooks API"""
from .resources.hooks import HooksResourceWithRawResponse
return HooksResourceWithRawResponse(self._client.hooks)
@cached_property
def models(self) -> models.ModelsResourceWithRawResponse:
+ """LLM Models"""
from .resources.models import ModelsResourceWithRawResponse
return ModelsResourceWithRawResponse(self._client.models)
+ @cached_property
+ def profiles(self) -> profiles.ProfilesResourceWithRawResponse:
+ from .resources.profiles import ProfilesResourceWithRawResponse
-class AsyncMobilerunWithRawResponse:
- _client: AsyncMobilerun
+ return ProfilesResourceWithRawResponse(self._client.profiles)
- def __init__(self, client: AsyncMobilerun) -> None:
- self._client = client
+ @cached_property
+ def proxies(self) -> proxies.ProxiesResourceWithRawResponse:
+ """Network Proxies"""
+ from .resources.proxies import ProxiesResourceWithRawResponse
+
+ return ProxiesResourceWithRawResponse(self._client.proxies)
@cached_property
- def tasks(self) -> tasks.AsyncTasksResourceWithRawResponse:
+ def tasks(self) -> tasks.TasksResourceWithRawResponse:
"""Tasks API"""
- from .resources.tasks import AsyncTasksResourceWithRawResponse
+ from .resources.tasks import TasksResourceWithRawResponse
- return AsyncTasksResourceWithRawResponse(self._client.tasks)
+ return TasksResourceWithRawResponse(self._client.tasks)
+
+
+class AsyncMobilerunWithRawResponse:
+ _client: AsyncMobilerun
+
+ def __init__(self, client: AsyncMobilerun) -> None:
+ self._client = client
@cached_property
def agents(self) -> agents.AsyncAgentsResourceWithRawResponse:
@@ -623,22 +650,25 @@ def agents(self) -> agents.AsyncAgentsResourceWithRawResponse:
return AsyncAgentsResourceWithRawResponse(self._client.agents)
@cached_property
- def proxies(self) -> proxies.AsyncProxiesResourceWithRawResponse:
- from .resources.proxies import AsyncProxiesResourceWithRawResponse
+ def apps(self) -> apps.AsyncAppsResourceWithRawResponse:
+ """App Management"""
+ from .resources.apps import AsyncAppsResourceWithRawResponse
- return AsyncProxiesResourceWithRawResponse(self._client.proxies)
+ return AsyncAppsResourceWithRawResponse(self._client.apps)
@cached_property
def carriers(self) -> carriers.AsyncCarriersResourceWithRawResponse:
+ """Mobile Carriers"""
from .resources.carriers import AsyncCarriersResourceWithRawResponse
return AsyncCarriersResourceWithRawResponse(self._client.carriers)
@cached_property
- def profiles(self) -> profiles.AsyncProfilesResourceWithRawResponse:
- from .resources.profiles import AsyncProfilesResourceWithRawResponse
+ def credentials(self) -> credentials.AsyncCredentialsResourceWithRawResponse:
+ """Vault & Secrets"""
+ from .resources.credentials import AsyncCredentialsResourceWithRawResponse
- return AsyncProfilesResourceWithRawResponse(self._client.profiles)
+ return AsyncCredentialsResourceWithRawResponse(self._client.credentials)
@cached_property
def devices(self) -> devices.AsyncDevicesResourceWithRawResponse:
@@ -646,44 +676,45 @@ def devices(self) -> devices.AsyncDevicesResourceWithRawResponse:
return AsyncDevicesResourceWithRawResponse(self._client.devices)
- @cached_property
- def apps(self) -> apps.AsyncAppsResourceWithRawResponse:
- from .resources.apps import AsyncAppsResourceWithRawResponse
-
- return AsyncAppsResourceWithRawResponse(self._client.apps)
-
- @cached_property
- def credentials(self) -> credentials.AsyncCredentialsResourceWithRawResponse:
- from .resources.credentials import AsyncCredentialsResourceWithRawResponse
-
- return AsyncCredentialsResourceWithRawResponse(self._client.credentials)
-
@cached_property
def hooks(self) -> hooks.AsyncHooksResourceWithRawResponse:
- """Webhooks API"""
from .resources.hooks import AsyncHooksResourceWithRawResponse
return AsyncHooksResourceWithRawResponse(self._client.hooks)
@cached_property
def models(self) -> models.AsyncModelsResourceWithRawResponse:
+ """LLM Models"""
from .resources.models import AsyncModelsResourceWithRawResponse
return AsyncModelsResourceWithRawResponse(self._client.models)
+ @cached_property
+ def profiles(self) -> profiles.AsyncProfilesResourceWithRawResponse:
+ from .resources.profiles import AsyncProfilesResourceWithRawResponse
-class MobilerunWithStreamedResponse:
- _client: Mobilerun
+ return AsyncProfilesResourceWithRawResponse(self._client.profiles)
- def __init__(self, client: Mobilerun) -> None:
- self._client = client
+ @cached_property
+ def proxies(self) -> proxies.AsyncProxiesResourceWithRawResponse:
+ """Network Proxies"""
+ from .resources.proxies import AsyncProxiesResourceWithRawResponse
+
+ return AsyncProxiesResourceWithRawResponse(self._client.proxies)
@cached_property
- def tasks(self) -> tasks.TasksResourceWithStreamingResponse:
+ def tasks(self) -> tasks.AsyncTasksResourceWithRawResponse:
"""Tasks API"""
- from .resources.tasks import TasksResourceWithStreamingResponse
+ from .resources.tasks import AsyncTasksResourceWithRawResponse
- return TasksResourceWithStreamingResponse(self._client.tasks)
+ return AsyncTasksResourceWithRawResponse(self._client.tasks)
+
+
+class MobilerunWithStreamedResponse:
+ _client: Mobilerun
+
+ def __init__(self, client: Mobilerun) -> None:
+ self._client = client
@cached_property
def agents(self) -> agents.AgentsResourceWithStreamingResponse:
@@ -693,22 +724,25 @@ def agents(self) -> agents.AgentsResourceWithStreamingResponse:
return AgentsResourceWithStreamingResponse(self._client.agents)
@cached_property
- def proxies(self) -> proxies.ProxiesResourceWithStreamingResponse:
- from .resources.proxies import ProxiesResourceWithStreamingResponse
+ def apps(self) -> apps.AppsResourceWithStreamingResponse:
+ """App Management"""
+ from .resources.apps import AppsResourceWithStreamingResponse
- return ProxiesResourceWithStreamingResponse(self._client.proxies)
+ return AppsResourceWithStreamingResponse(self._client.apps)
@cached_property
def carriers(self) -> carriers.CarriersResourceWithStreamingResponse:
+ """Mobile Carriers"""
from .resources.carriers import CarriersResourceWithStreamingResponse
return CarriersResourceWithStreamingResponse(self._client.carriers)
@cached_property
- def profiles(self) -> profiles.ProfilesResourceWithStreamingResponse:
- from .resources.profiles import ProfilesResourceWithStreamingResponse
+ def credentials(self) -> credentials.CredentialsResourceWithStreamingResponse:
+ """Vault & Secrets"""
+ from .resources.credentials import CredentialsResourceWithStreamingResponse
- return ProfilesResourceWithStreamingResponse(self._client.profiles)
+ return CredentialsResourceWithStreamingResponse(self._client.credentials)
@cached_property
def devices(self) -> devices.DevicesResourceWithStreamingResponse:
@@ -716,44 +750,45 @@ def devices(self) -> devices.DevicesResourceWithStreamingResponse:
return DevicesResourceWithStreamingResponse(self._client.devices)
- @cached_property
- def apps(self) -> apps.AppsResourceWithStreamingResponse:
- from .resources.apps import AppsResourceWithStreamingResponse
-
- return AppsResourceWithStreamingResponse(self._client.apps)
-
- @cached_property
- def credentials(self) -> credentials.CredentialsResourceWithStreamingResponse:
- from .resources.credentials import CredentialsResourceWithStreamingResponse
-
- return CredentialsResourceWithStreamingResponse(self._client.credentials)
-
@cached_property
def hooks(self) -> hooks.HooksResourceWithStreamingResponse:
- """Webhooks API"""
from .resources.hooks import HooksResourceWithStreamingResponse
return HooksResourceWithStreamingResponse(self._client.hooks)
@cached_property
def models(self) -> models.ModelsResourceWithStreamingResponse:
+ """LLM Models"""
from .resources.models import ModelsResourceWithStreamingResponse
return ModelsResourceWithStreamingResponse(self._client.models)
+ @cached_property
+ def profiles(self) -> profiles.ProfilesResourceWithStreamingResponse:
+ from .resources.profiles import ProfilesResourceWithStreamingResponse
-class AsyncMobilerunWithStreamedResponse:
- _client: AsyncMobilerun
+ return ProfilesResourceWithStreamingResponse(self._client.profiles)
- def __init__(self, client: AsyncMobilerun) -> None:
- self._client = client
+ @cached_property
+ def proxies(self) -> proxies.ProxiesResourceWithStreamingResponse:
+ """Network Proxies"""
+ from .resources.proxies import ProxiesResourceWithStreamingResponse
+
+ return ProxiesResourceWithStreamingResponse(self._client.proxies)
@cached_property
- def tasks(self) -> tasks.AsyncTasksResourceWithStreamingResponse:
+ def tasks(self) -> tasks.TasksResourceWithStreamingResponse:
"""Tasks API"""
- from .resources.tasks import AsyncTasksResourceWithStreamingResponse
+ from .resources.tasks import TasksResourceWithStreamingResponse
- return AsyncTasksResourceWithStreamingResponse(self._client.tasks)
+ return TasksResourceWithStreamingResponse(self._client.tasks)
+
+
+class AsyncMobilerunWithStreamedResponse:
+ _client: AsyncMobilerun
+
+ def __init__(self, client: AsyncMobilerun) -> None:
+ self._client = client
@cached_property
def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse:
@@ -763,22 +798,25 @@ def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse:
return AsyncAgentsResourceWithStreamingResponse(self._client.agents)
@cached_property
- def proxies(self) -> proxies.AsyncProxiesResourceWithStreamingResponse:
- from .resources.proxies import AsyncProxiesResourceWithStreamingResponse
+ def apps(self) -> apps.AsyncAppsResourceWithStreamingResponse:
+ """App Management"""
+ from .resources.apps import AsyncAppsResourceWithStreamingResponse
- return AsyncProxiesResourceWithStreamingResponse(self._client.proxies)
+ return AsyncAppsResourceWithStreamingResponse(self._client.apps)
@cached_property
def carriers(self) -> carriers.AsyncCarriersResourceWithStreamingResponse:
+ """Mobile Carriers"""
from .resources.carriers import AsyncCarriersResourceWithStreamingResponse
return AsyncCarriersResourceWithStreamingResponse(self._client.carriers)
@cached_property
- def profiles(self) -> profiles.AsyncProfilesResourceWithStreamingResponse:
- from .resources.profiles import AsyncProfilesResourceWithStreamingResponse
+ def credentials(self) -> credentials.AsyncCredentialsResourceWithStreamingResponse:
+ """Vault & Secrets"""
+ from .resources.credentials import AsyncCredentialsResourceWithStreamingResponse
- return AsyncProfilesResourceWithStreamingResponse(self._client.profiles)
+ return AsyncCredentialsResourceWithStreamingResponse(self._client.credentials)
@cached_property
def devices(self) -> devices.AsyncDevicesResourceWithStreamingResponse:
@@ -786,31 +824,39 @@ def devices(self) -> devices.AsyncDevicesResourceWithStreamingResponse:
return AsyncDevicesResourceWithStreamingResponse(self._client.devices)
- @cached_property
- def apps(self) -> apps.AsyncAppsResourceWithStreamingResponse:
- from .resources.apps import AsyncAppsResourceWithStreamingResponse
-
- return AsyncAppsResourceWithStreamingResponse(self._client.apps)
-
- @cached_property
- def credentials(self) -> credentials.AsyncCredentialsResourceWithStreamingResponse:
- from .resources.credentials import AsyncCredentialsResourceWithStreamingResponse
-
- return AsyncCredentialsResourceWithStreamingResponse(self._client.credentials)
-
@cached_property
def hooks(self) -> hooks.AsyncHooksResourceWithStreamingResponse:
- """Webhooks API"""
from .resources.hooks import AsyncHooksResourceWithStreamingResponse
return AsyncHooksResourceWithStreamingResponse(self._client.hooks)
@cached_property
def models(self) -> models.AsyncModelsResourceWithStreamingResponse:
+ """LLM Models"""
from .resources.models import AsyncModelsResourceWithStreamingResponse
return AsyncModelsResourceWithStreamingResponse(self._client.models)
+ @cached_property
+ def profiles(self) -> profiles.AsyncProfilesResourceWithStreamingResponse:
+ from .resources.profiles import AsyncProfilesResourceWithStreamingResponse
+
+ return AsyncProfilesResourceWithStreamingResponse(self._client.profiles)
+
+ @cached_property
+ def proxies(self) -> proxies.AsyncProxiesResourceWithStreamingResponse:
+ """Network Proxies"""
+ from .resources.proxies import AsyncProxiesResourceWithStreamingResponse
+
+ return AsyncProxiesResourceWithStreamingResponse(self._client.proxies)
+
+ @cached_property
+ def tasks(self) -> tasks.AsyncTasksResourceWithStreamingResponse:
+ """Tasks API"""
+ from .resources.tasks import AsyncTasksResourceWithStreamingResponse
+
+ return AsyncTasksResourceWithStreamingResponse(self._client.tasks)
+
Client = Mobilerun
diff --git a/src/mobilerun_sdk/_files.py b/src/mobilerun_sdk/_files.py
index 0f0f150..22855ba 100644
--- a/src/mobilerun_sdk/_files.py
+++ b/src/mobilerun_sdk/_files.py
@@ -3,8 +3,8 @@
import io
import os
import pathlib
-from typing import overload
-from typing_extensions import TypeGuard
+from typing import Sequence, cast, overload
+from typing_extensions import TypeVar, TypeGuard
import anyio
@@ -17,7 +17,9 @@
HttpxFileContent,
HttpxRequestFiles,
)
-from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
+from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t
+
+_T = TypeVar("_T")
def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
@@ -97,7 +99,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles
elif is_sequence_t(files):
files = [(key, await _async_transform_file(file)) for key, file in files]
else:
- raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence")
+ raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence")
return files
@@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent:
return await anyio.Path(file).read_bytes()
return file
+
+
+def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T:
+ """Copy only the containers along the given paths.
+
+ Used to guard against mutation by extract_files without copying the entire structure.
+ Only dicts and lists that lie on a path are copied; everything else
+ is returned by reference.
+
+ For example, given paths=[["foo", "files", "file"]] and the structure:
+ {
+ "foo": {
+ "bar": {"baz": {}},
+ "files": {"file": }
+ }
+ }
+ The root dict, "foo", and "files" are copied (they lie on the path).
+ "bar" and "baz" are returned by reference (off the path).
+ """
+ return _deepcopy_with_paths(item, paths, 0)
+
+
+def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T:
+ if not paths:
+ return item
+ if is_mapping(item):
+ key_to_paths: dict[str, list[Sequence[str]]] = {}
+ for path in paths:
+ if index < len(path):
+ key_to_paths.setdefault(path[index], []).append(path)
+
+ # if no path continues through this mapping, it won't be mutated and copying it is redundant
+ if not key_to_paths:
+ return item
+
+ result = dict(item)
+ for key, subpaths in key_to_paths.items():
+ if key in result:
+ result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1)
+ return cast(_T, result)
+ if is_list(item):
+ array_paths = [path for path in paths if index < len(path) and path[index] == ""]
+
+ # if no path expects a list here, nothing will be mutated inside it - return by reference
+ if not array_paths:
+ return cast(_T, item)
+ return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item])
+ return item
diff --git a/src/mobilerun_sdk/_models.py b/src/mobilerun_sdk/_models.py
index 29070e0..8c5ab26 100644
--- a/src/mobilerun_sdk/_models.py
+++ b/src/mobilerun_sdk/_models.py
@@ -25,7 +25,9 @@
ClassVar,
Protocol,
Required,
+ Annotated,
ParamSpec,
+ TypeAlias,
TypedDict,
TypeGuard,
final,
@@ -79,7 +81,15 @@
from ._constants import RAW_RESPONSE_HEADER
if TYPE_CHECKING:
+ from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler
+ from pydantic_core import CoreSchema, core_schema
from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema
+else:
+ try:
+ from pydantic_core import CoreSchema, core_schema
+ except ImportError:
+ CoreSchema = None
+ core_schema = None
__all__ = ["BaseModel", "GenericModel"]
@@ -396,6 +406,76 @@ def model_dump_json(
)
+class _EagerIterable(list[_T], Generic[_T]):
+ """
+ Accepts any Iterable[T] input (including generators), consumes it
+ eagerly, and validates all items upfront.
+
+ Validation preserves the original container type where possible
+ (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON)
+ always emits a list — round-tripping through model_dump() will not
+ restore the original container type.
+ """
+
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls,
+ source_type: Any,
+ handler: GetCoreSchemaHandler,
+ ) -> CoreSchema:
+ (item_type,) = get_args(source_type) or (Any,)
+ item_schema: CoreSchema = handler.generate_schema(item_type)
+ list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema)
+
+ return core_schema.no_info_wrap_validator_function(
+ cls._validate,
+ list_of_items_schema,
+ serialization=core_schema.plain_serializer_function_ser_schema(
+ cls._serialize,
+ info_arg=False,
+ ),
+ )
+
+ @staticmethod
+ def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any:
+ original_type: type[Any] = type(v)
+
+ # Normalize to list so list_schema can validate each item
+ if isinstance(v, list):
+ items: list[_T] = v
+ else:
+ try:
+ items = list(v)
+ except TypeError as e:
+ raise TypeError("Value is not iterable") from e
+
+ # Validate items against the inner schema
+ validated: list[_T] = handler(items)
+
+ # Reconstruct original container type
+ if original_type is list:
+ return validated
+ # str(list) produces the list's repr, not a string built from items,
+ # so skip reconstruction for str and its subclasses.
+ if issubclass(original_type, str):
+ return validated
+ try:
+ return original_type(validated)
+ except (TypeError, ValueError):
+ # If the type cannot be reconstructed, just return the validated list
+ return validated
+
+ @staticmethod
+ def _serialize(v: Iterable[_T]) -> list[_T]:
+ """Always serialize as a list so Pydantic's JSON encoder is happy."""
+ if isinstance(v, list):
+ return v
+ return list(v)
+
+
+EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable]
+
+
def _construct_field(value: object, field: FieldInfo, key: str) -> object:
if value is None:
return field_get_default(field)
diff --git a/src/mobilerun_sdk/_qs.py b/src/mobilerun_sdk/_qs.py
index de8c99b..4127c19 100644
--- a/src/mobilerun_sdk/_qs.py
+++ b/src/mobilerun_sdk/_qs.py
@@ -2,17 +2,13 @@
from typing import Any, List, Tuple, Union, Mapping, TypeVar
from urllib.parse import parse_qs, urlencode
-from typing_extensions import Literal, get_args
+from typing_extensions import get_args
-from ._types import NotGiven, not_given
+from ._types import NotGiven, ArrayFormat, NestedFormat, not_given
from ._utils import flatten
_T = TypeVar("_T")
-
-ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
-NestedFormat = Literal["dots", "brackets"]
-
PrimitiveData = Union[str, int, float, bool, None]
# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"]
# https://github.com/microsoft/pyright/issues/3555
diff --git a/src/mobilerun_sdk/_types.py b/src/mobilerun_sdk/_types.py
index 0f64915..c41c84a 100644
--- a/src/mobilerun_sdk/_types.py
+++ b/src/mobilerun_sdk/_types.py
@@ -47,6 +47,9 @@
ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
_T = TypeVar("_T")
+ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
+NestedFormat = Literal["dots", "brackets"]
+
# Approximates httpx internal ProxiesTypes and RequestFiles types
# while adding support for `PathLike` instances
diff --git a/src/mobilerun_sdk/_utils/__init__.py b/src/mobilerun_sdk/_utils/__init__.py
index 10cb66d..1c090e5 100644
--- a/src/mobilerun_sdk/_utils/__init__.py
+++ b/src/mobilerun_sdk/_utils/__init__.py
@@ -24,7 +24,6 @@
coerce_integer as coerce_integer,
file_from_path as file_from_path,
strip_not_given as strip_not_given,
- deepcopy_minimal as deepcopy_minimal,
get_async_library as get_async_library,
maybe_coerce_float as maybe_coerce_float,
get_required_header as get_required_header,
diff --git a/src/mobilerun_sdk/_utils/_utils.py b/src/mobilerun_sdk/_utils/_utils.py
index 63b8cd6..199cd23 100644
--- a/src/mobilerun_sdk/_utils/_utils.py
+++ b/src/mobilerun_sdk/_utils/_utils.py
@@ -17,11 +17,11 @@
)
from pathlib import Path
from datetime import date, datetime
-from typing_extensions import TypeGuard
+from typing_extensions import TypeGuard, get_args
import sniffio
-from .._types import Omit, NotGiven, FileTypes, HeadersLike
+from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike
_T = TypeVar("_T")
_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
@@ -40,25 +40,45 @@ def extract_files(
query: Mapping[str, object],
*,
paths: Sequence[Sequence[str]],
+ array_format: ArrayFormat = "brackets",
) -> list[tuple[str, FileTypes]]:
"""Recursively extract files from the given dictionary based on specified paths.
A path may look like this ['foo', 'files', '', 'data'].
+ ``array_format`` controls how ```` segments contribute to the emitted
+ field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and
+ ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``).
+
Note: this mutates the given dictionary.
"""
files: list[tuple[str, FileTypes]] = []
for path in paths:
- files.extend(_extract_items(query, path, index=0, flattened_key=None))
+ files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format))
return files
+def _array_suffix(array_format: ArrayFormat, array_index: int) -> str:
+ if array_format == "brackets":
+ return "[]"
+ if array_format == "indices":
+ return f"[{array_index}]"
+ if array_format == "repeat" or array_format == "comma":
+ # Both repeat the bare field name for each file part; there is no
+ # meaningful way to comma-join binary parts.
+ return ""
+ raise NotImplementedError(
+ f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}"
+ )
+
+
def _extract_items(
obj: object,
path: Sequence[str],
*,
index: int,
flattened_key: str | None,
+ array_format: ArrayFormat,
) -> list[tuple[str, FileTypes]]:
try:
key = path[index]
@@ -75,9 +95,11 @@ def _extract_items(
if is_list(obj):
files: list[tuple[str, FileTypes]] = []
- for entry in obj:
- assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
- files.append((flattened_key + "[]", cast(FileTypes, entry)))
+ for array_index, entry in enumerate(obj):
+ suffix = _array_suffix(array_format, array_index)
+ emitted_key = (flattened_key + suffix) if flattened_key else suffix
+ assert_is_file_content(entry, key=emitted_key)
+ files.append((emitted_key, cast(FileTypes, entry)))
return files
assert_is_file_content(obj, key=flattened_key)
@@ -106,6 +128,7 @@ def _extract_items(
path,
index=index,
flattened_key=flattened_key,
+ array_format=array_format,
)
elif is_list(obj):
if key != "":
@@ -117,9 +140,12 @@ def _extract_items(
item,
path,
index=index,
- flattened_key=flattened_key + "[]" if flattened_key is not None else "[]",
+ flattened_key=(
+ (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index)
+ ),
+ array_format=array_format,
)
- for item in obj
+ for array_index, item in enumerate(obj)
]
)
@@ -177,21 +203,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]:
return isinstance(obj, Iterable)
-def deepcopy_minimal(item: _T) -> _T:
- """Minimal reimplementation of copy.deepcopy() that will only copy certain object types:
-
- - mappings, e.g. `dict`
- - list
-
- This is done for performance reasons.
- """
- if is_mapping(item):
- return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()})
- if is_list(item):
- return cast(_T, [deepcopy_minimal(entry) for entry in item])
- return item
-
-
# copied from https://github.com/Rapptz/RoboDanny
def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str:
size = len(seq)
diff --git a/src/mobilerun_sdk/_version.py b/src/mobilerun_sdk/_version.py
index f3f6069..a3beadb 100644
--- a/src/mobilerun_sdk/_version.py
+++ b/src/mobilerun_sdk/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "mobilerun_sdk"
-__version__ = "3.1.0" # x-release-please-version
+__version__ = "3.2.0" # x-release-please-version
diff --git a/src/mobilerun_sdk/resources/__init__.py b/src/mobilerun_sdk/resources/__init__.py
index 2ded3d8..21f473c 100644
--- a/src/mobilerun_sdk/resources/__init__.py
+++ b/src/mobilerun_sdk/resources/__init__.py
@@ -82,54 +82,36 @@
)
__all__ = [
- "TasksResource",
- "AsyncTasksResource",
- "TasksResourceWithRawResponse",
- "AsyncTasksResourceWithRawResponse",
- "TasksResourceWithStreamingResponse",
- "AsyncTasksResourceWithStreamingResponse",
"AgentsResource",
"AsyncAgentsResource",
"AgentsResourceWithRawResponse",
"AsyncAgentsResourceWithRawResponse",
"AgentsResourceWithStreamingResponse",
"AsyncAgentsResourceWithStreamingResponse",
- "ProxiesResource",
- "AsyncProxiesResource",
- "ProxiesResourceWithRawResponse",
- "AsyncProxiesResourceWithRawResponse",
- "ProxiesResourceWithStreamingResponse",
- "AsyncProxiesResourceWithStreamingResponse",
- "CarriersResource",
- "AsyncCarriersResource",
- "CarriersResourceWithRawResponse",
- "AsyncCarriersResourceWithRawResponse",
- "CarriersResourceWithStreamingResponse",
- "AsyncCarriersResourceWithStreamingResponse",
- "ProfilesResource",
- "AsyncProfilesResource",
- "ProfilesResourceWithRawResponse",
- "AsyncProfilesResourceWithRawResponse",
- "ProfilesResourceWithStreamingResponse",
- "AsyncProfilesResourceWithStreamingResponse",
- "DevicesResource",
- "AsyncDevicesResource",
- "DevicesResourceWithRawResponse",
- "AsyncDevicesResourceWithRawResponse",
- "DevicesResourceWithStreamingResponse",
- "AsyncDevicesResourceWithStreamingResponse",
"AppsResource",
"AsyncAppsResource",
"AppsResourceWithRawResponse",
"AsyncAppsResourceWithRawResponse",
"AppsResourceWithStreamingResponse",
"AsyncAppsResourceWithStreamingResponse",
+ "CarriersResource",
+ "AsyncCarriersResource",
+ "CarriersResourceWithRawResponse",
+ "AsyncCarriersResourceWithRawResponse",
+ "CarriersResourceWithStreamingResponse",
+ "AsyncCarriersResourceWithStreamingResponse",
"CredentialsResource",
"AsyncCredentialsResource",
"CredentialsResourceWithRawResponse",
"AsyncCredentialsResourceWithRawResponse",
"CredentialsResourceWithStreamingResponse",
"AsyncCredentialsResourceWithStreamingResponse",
+ "DevicesResource",
+ "AsyncDevicesResource",
+ "DevicesResourceWithRawResponse",
+ "AsyncDevicesResourceWithRawResponse",
+ "DevicesResourceWithStreamingResponse",
+ "AsyncDevicesResourceWithStreamingResponse",
"HooksResource",
"AsyncHooksResource",
"HooksResourceWithRawResponse",
@@ -142,4 +124,22 @@
"AsyncModelsResourceWithRawResponse",
"ModelsResourceWithStreamingResponse",
"AsyncModelsResourceWithStreamingResponse",
+ "ProfilesResource",
+ "AsyncProfilesResource",
+ "ProfilesResourceWithRawResponse",
+ "AsyncProfilesResourceWithRawResponse",
+ "ProfilesResourceWithStreamingResponse",
+ "AsyncProfilesResourceWithStreamingResponse",
+ "ProxiesResource",
+ "AsyncProxiesResource",
+ "ProxiesResourceWithRawResponse",
+ "AsyncProxiesResourceWithRawResponse",
+ "ProxiesResourceWithStreamingResponse",
+ "AsyncProxiesResourceWithStreamingResponse",
+ "TasksResource",
+ "AsyncTasksResource",
+ "TasksResourceWithRawResponse",
+ "AsyncTasksResourceWithRawResponse",
+ "TasksResourceWithStreamingResponse",
+ "AsyncTasksResourceWithStreamingResponse",
]
diff --git a/src/mobilerun_sdk/resources/apps.py b/src/mobilerun_sdk/resources/apps.py
index 1b80d91..2587290 100644
--- a/src/mobilerun_sdk/resources/apps.py
+++ b/src/mobilerun_sdk/resources/apps.py
@@ -2,13 +2,14 @@
from __future__ import annotations
+from typing import Iterable
from typing_extensions import Literal
import httpx
-from ..types import app_list_params
+from ..types import app_list_params, app_create_signed_upload_url_params
from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from .._utils import maybe_transform, async_maybe_transform
+from .._utils import path_template, maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -19,11 +20,18 @@
)
from .._base_client import make_request_options
from ..types.app_list_response import AppListResponse
+from ..types.app_delete_response import AppDeleteResponse
+from ..types.app_retrieve_response import AppRetrieveResponse
+from ..types.app_mark_failed_response import AppMarkFailedResponse
+from ..types.app_confirm_upload_response import AppConfirmUploadResponse
+from ..types.app_create_signed_upload_url_response import AppCreateSignedUploadURLResponse
__all__ = ["AppsResource", "AsyncAppsResource"]
class AppsResource(SyncAPIResource):
+ """App Management"""
+
@cached_property
def with_raw_response(self) -> AppsResourceWithRawResponse:
"""
@@ -43,15 +51,49 @@ def with_streaming_response(self) -> AppsResourceWithStreamingResponse:
"""
return AppsResourceWithStreamingResponse(self)
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppRetrieveResponse:
+ """
+ Retrieves an app by its ID
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ path_template("/apps/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppRetrieveResponse,
+ )
+
def list(
self,
*,
order: Literal["asc", "desc"] | Omit = omit,
page: int | Omit = omit,
page_size: int | Omit = omit,
+ platform: Literal["all", "android", "ios"] | Omit = omit,
query: str | Omit = omit,
sort_by: Literal["createdAt", "name"] | Omit = omit,
- source: Literal["all", "uploaded", "store", "queued"] | Omit = omit,
+ status: Literal["all", "queued", "available", "failed"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -83,9 +125,10 @@ def list(
"order": order,
"page": page,
"page_size": page_size,
+ "platform": platform,
"query": query,
"sort_by": sort_by,
- "source": source,
+ "status": status,
},
app_list_params.AppListParams,
),
@@ -93,8 +136,173 @@ def list(
cast_to=AppListResponse,
)
+ def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppDeleteResponse:
+ """Deletes an uploaded app by ID.
+
+ Removes files from R2 storage and the database
+ entry.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._delete(
+ path_template("/apps/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppDeleteResponse,
+ )
+
+ def confirm_upload(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppConfirmUploadResponse:
+ """
+ Verifies the APK file exists in R2 and sets the app status to available.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ path_template("/apps/{id}/confirm-upload", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppConfirmUploadResponse,
+ )
+
+ def create_signed_upload_url(
+ self,
+ *,
+ bundle_id: str,
+ display_name: str,
+ files: Iterable[app_create_signed_upload_url_params.File],
+ size_bytes: float,
+ version_code: float,
+ version_name: str,
+ country: str | Omit = omit,
+ description: str | Omit = omit,
+ developer_name: str | Omit = omit,
+ icon_url: str | Omit = omit,
+ platform: Literal["android", "ios"] | Omit = omit,
+ target_sdk: float | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppCreateSignedUploadURLResponse:
+ """
+ Creates or updates an app and returns pre-signed Cloudflare R2 upload URLs for
+ each file
+
+ Args:
+ country: Country code for Search Results
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/apps/create-signed-upload-url",
+ body=maybe_transform(
+ {
+ "bundle_id": bundle_id,
+ "display_name": display_name,
+ "files": files,
+ "size_bytes": size_bytes,
+ "version_code": version_code,
+ "version_name": version_name,
+ "country": country,
+ "description": description,
+ "developer_name": developer_name,
+ "icon_url": icon_url,
+ "platform": platform,
+ "target_sdk": target_sdk,
+ },
+ app_create_signed_upload_url_params.AppCreateSignedUploadURLParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppCreateSignedUploadURLResponse,
+ )
+
+ def mark_failed(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppMarkFailedResponse:
+ """
+ Sets the app status to failed.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ path_template("/apps/{id}/mark-failed", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppMarkFailedResponse,
+ )
+
class AsyncAppsResource(AsyncAPIResource):
+ """App Management"""
+
@cached_property
def with_raw_response(self) -> AsyncAppsResourceWithRawResponse:
"""
@@ -114,15 +322,49 @@ def with_streaming_response(self) -> AsyncAppsResourceWithStreamingResponse:
"""
return AsyncAppsResourceWithStreamingResponse(self)
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppRetrieveResponse:
+ """
+ Retrieves an app by its ID
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ path_template("/apps/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppRetrieveResponse,
+ )
+
async def list(
self,
*,
order: Literal["asc", "desc"] | Omit = omit,
page: int | Omit = omit,
page_size: int | Omit = omit,
+ platform: Literal["all", "android", "ios"] | Omit = omit,
query: str | Omit = omit,
sort_by: Literal["createdAt", "name"] | Omit = omit,
- source: Literal["all", "uploaded", "store", "queued"] | Omit = omit,
+ status: Literal["all", "queued", "available", "failed"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -154,9 +396,10 @@ async def list(
"order": order,
"page": page,
"page_size": page_size,
+ "platform": platform,
"query": query,
"sort_by": sort_by,
- "source": source,
+ "status": status,
},
app_list_params.AppListParams,
),
@@ -164,38 +407,261 @@ async def list(
cast_to=AppListResponse,
)
+ async def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppDeleteResponse:
+ """Deletes an uploaded app by ID.
+
+ Removes files from R2 storage and the database
+ entry.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._delete(
+ path_template("/apps/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppDeleteResponse,
+ )
+
+ async def confirm_upload(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppConfirmUploadResponse:
+ """
+ Verifies the APK file exists in R2 and sets the app status to available.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ path_template("/apps/{id}/confirm-upload", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppConfirmUploadResponse,
+ )
+
+ async def create_signed_upload_url(
+ self,
+ *,
+ bundle_id: str,
+ display_name: str,
+ files: Iterable[app_create_signed_upload_url_params.File],
+ size_bytes: float,
+ version_code: float,
+ version_name: str,
+ country: str | Omit = omit,
+ description: str | Omit = omit,
+ developer_name: str | Omit = omit,
+ icon_url: str | Omit = omit,
+ platform: Literal["android", "ios"] | Omit = omit,
+ target_sdk: float | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppCreateSignedUploadURLResponse:
+ """
+ Creates or updates an app and returns pre-signed Cloudflare R2 upload URLs for
+ each file
+
+ Args:
+ country: Country code for Search Results
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/apps/create-signed-upload-url",
+ body=await async_maybe_transform(
+ {
+ "bundle_id": bundle_id,
+ "display_name": display_name,
+ "files": files,
+ "size_bytes": size_bytes,
+ "version_code": version_code,
+ "version_name": version_name,
+ "country": country,
+ "description": description,
+ "developer_name": developer_name,
+ "icon_url": icon_url,
+ "platform": platform,
+ "target_sdk": target_sdk,
+ },
+ app_create_signed_upload_url_params.AppCreateSignedUploadURLParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppCreateSignedUploadURLResponse,
+ )
+
+ async def mark_failed(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppMarkFailedResponse:
+ """
+ Sets the app status to failed.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ path_template("/apps/{id}/mark-failed", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppMarkFailedResponse,
+ )
+
class AppsResourceWithRawResponse:
def __init__(self, apps: AppsResource) -> None:
self._apps = apps
+ self.retrieve = to_raw_response_wrapper(
+ apps.retrieve,
+ )
self.list = to_raw_response_wrapper(
apps.list,
)
+ self.delete = to_raw_response_wrapper(
+ apps.delete,
+ )
+ self.confirm_upload = to_raw_response_wrapper(
+ apps.confirm_upload,
+ )
+ self.create_signed_upload_url = to_raw_response_wrapper(
+ apps.create_signed_upload_url,
+ )
+ self.mark_failed = to_raw_response_wrapper(
+ apps.mark_failed,
+ )
class AsyncAppsResourceWithRawResponse:
def __init__(self, apps: AsyncAppsResource) -> None:
self._apps = apps
+ self.retrieve = async_to_raw_response_wrapper(
+ apps.retrieve,
+ )
self.list = async_to_raw_response_wrapper(
apps.list,
)
+ self.delete = async_to_raw_response_wrapper(
+ apps.delete,
+ )
+ self.confirm_upload = async_to_raw_response_wrapper(
+ apps.confirm_upload,
+ )
+ self.create_signed_upload_url = async_to_raw_response_wrapper(
+ apps.create_signed_upload_url,
+ )
+ self.mark_failed = async_to_raw_response_wrapper(
+ apps.mark_failed,
+ )
class AppsResourceWithStreamingResponse:
def __init__(self, apps: AppsResource) -> None:
self._apps = apps
+ self.retrieve = to_streamed_response_wrapper(
+ apps.retrieve,
+ )
self.list = to_streamed_response_wrapper(
apps.list,
)
+ self.delete = to_streamed_response_wrapper(
+ apps.delete,
+ )
+ self.confirm_upload = to_streamed_response_wrapper(
+ apps.confirm_upload,
+ )
+ self.create_signed_upload_url = to_streamed_response_wrapper(
+ apps.create_signed_upload_url,
+ )
+ self.mark_failed = to_streamed_response_wrapper(
+ apps.mark_failed,
+ )
class AsyncAppsResourceWithStreamingResponse:
def __init__(self, apps: AsyncAppsResource) -> None:
self._apps = apps
+ self.retrieve = async_to_streamed_response_wrapper(
+ apps.retrieve,
+ )
self.list = async_to_streamed_response_wrapper(
apps.list,
)
+ self.delete = async_to_streamed_response_wrapper(
+ apps.delete,
+ )
+ self.confirm_upload = async_to_streamed_response_wrapper(
+ apps.confirm_upload,
+ )
+ self.create_signed_upload_url = async_to_streamed_response_wrapper(
+ apps.create_signed_upload_url,
+ )
+ self.mark_failed = async_to_streamed_response_wrapper(
+ apps.mark_failed,
+ )
diff --git a/src/mobilerun_sdk/resources/carriers.py b/src/mobilerun_sdk/resources/carriers.py
index 642493e..d627159 100644
--- a/src/mobilerun_sdk/resources/carriers.py
+++ b/src/mobilerun_sdk/resources/carriers.py
@@ -18,14 +18,19 @@
async_to_streamed_response_wrapper,
)
from .._base_client import make_request_options
-from ..types.carrier import Carrier
from ..types.carrier_list_response import CarrierListResponse
+from ..types.carrier_create_response import CarrierCreateResponse
from ..types.carrier_delete_response import CarrierDeleteResponse
+from ..types.carrier_lookup_response import CarrierLookupResponse
+from ..types.carrier_update_response import CarrierUpdateResponse
+from ..types.carrier_retrieve_response import CarrierRetrieveResponse
__all__ = ["CarriersResource", "AsyncCarriersResource"]
class CarriersResource(SyncAPIResource):
+ """Mobile Carriers"""
+
@cached_property
def with_raw_response(self) -> CarriersResourceWithRawResponse:
"""
@@ -70,7 +75,7 @@ def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierCreateResponse:
"""
Create a new carrier
@@ -141,7 +146,7 @@ def create(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Carrier,
+ cast_to=CarrierCreateResponse,
)
def retrieve(
@@ -154,7 +159,7 @@ def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierRetrieveResponse:
"""
Get carrier by ID
@@ -174,7 +179,7 @@ def retrieve(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Carrier,
+ cast_to=CarrierRetrieveResponse,
)
def update(
@@ -201,7 +206,7 @@ def update(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierUpdateResponse:
"""
Update a carrier
@@ -268,7 +273,7 @@ def update(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Carrier,
+ cast_to=CarrierUpdateResponse,
)
def list(
@@ -377,7 +382,7 @@ def lookup(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierLookupResponse:
"""
Get carrier by MCC and MNC
@@ -409,11 +414,13 @@ def lookup(
carrier_lookup_params.CarrierLookupParams,
),
),
- cast_to=Carrier,
+ cast_to=CarrierLookupResponse,
)
class AsyncCarriersResource(AsyncAPIResource):
+ """Mobile Carriers"""
+
@cached_property
def with_raw_response(self) -> AsyncCarriersResourceWithRawResponse:
"""
@@ -458,7 +465,7 @@ async def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierCreateResponse:
"""
Create a new carrier
@@ -529,7 +536,7 @@ async def create(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Carrier,
+ cast_to=CarrierCreateResponse,
)
async def retrieve(
@@ -542,7 +549,7 @@ async def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierRetrieveResponse:
"""
Get carrier by ID
@@ -562,7 +569,7 @@ async def retrieve(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Carrier,
+ cast_to=CarrierRetrieveResponse,
)
async def update(
@@ -589,7 +596,7 @@ async def update(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierUpdateResponse:
"""
Update a carrier
@@ -656,7 +663,7 @@ async def update(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Carrier,
+ cast_to=CarrierUpdateResponse,
)
async def list(
@@ -765,7 +772,7 @@ async def lookup(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> Carrier:
+ ) -> CarrierLookupResponse:
"""
Get carrier by MCC and MNC
@@ -797,7 +804,7 @@ async def lookup(
carrier_lookup_params.CarrierLookupParams,
),
),
- cast_to=Carrier,
+ cast_to=CarrierLookupResponse,
)
diff --git a/src/mobilerun_sdk/resources/credentials/credentials.py b/src/mobilerun_sdk/resources/credentials/credentials.py
index 43d30f4..9276d9f 100644
--- a/src/mobilerun_sdk/resources/credentials/credentials.py
+++ b/src/mobilerun_sdk/resources/credentials/credentials.py
@@ -30,8 +30,11 @@
class CredentialsResource(SyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def packages(self) -> PackagesResource:
+ """Vault & Secrets"""
return PackagesResource(self._client)
@cached_property
@@ -97,8 +100,11 @@ def list(
class AsyncCredentialsResource(AsyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def packages(self) -> AsyncPackagesResource:
+ """Vault & Secrets"""
return AsyncPackagesResource(self._client)
@cached_property
@@ -173,6 +179,7 @@ def __init__(self, credentials: CredentialsResource) -> None:
@cached_property
def packages(self) -> PackagesResourceWithRawResponse:
+ """Vault & Secrets"""
return PackagesResourceWithRawResponse(self._credentials.packages)
@@ -186,6 +193,7 @@ def __init__(self, credentials: AsyncCredentialsResource) -> None:
@cached_property
def packages(self) -> AsyncPackagesResourceWithRawResponse:
+ """Vault & Secrets"""
return AsyncPackagesResourceWithRawResponse(self._credentials.packages)
@@ -199,6 +207,7 @@ def __init__(self, credentials: CredentialsResource) -> None:
@cached_property
def packages(self) -> PackagesResourceWithStreamingResponse:
+ """Vault & Secrets"""
return PackagesResourceWithStreamingResponse(self._credentials.packages)
@@ -212,4 +221,5 @@ def __init__(self, credentials: AsyncCredentialsResource) -> None:
@cached_property
def packages(self) -> AsyncPackagesResourceWithStreamingResponse:
+ """Vault & Secrets"""
return AsyncPackagesResourceWithStreamingResponse(self._credentials.packages)
diff --git a/src/mobilerun_sdk/resources/credentials/packages/credentials/credentials.py b/src/mobilerun_sdk/resources/credentials/packages/credentials/credentials.py
index 1802563..df12c15 100644
--- a/src/mobilerun_sdk/resources/credentials/packages/credentials/credentials.py
+++ b/src/mobilerun_sdk/resources/credentials/packages/credentials/credentials.py
@@ -34,8 +34,11 @@
class CredentialsResource(SyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def fields(self) -> FieldsResource:
+ """Vault & Secrets"""
return FieldsResource(self._client)
@cached_property
@@ -181,8 +184,11 @@ def delete(
class AsyncCredentialsResource(AsyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def fields(self) -> AsyncFieldsResource:
+ """Vault & Secrets"""
return AsyncFieldsResource(self._client)
@cached_property
@@ -343,6 +349,7 @@ def __init__(self, credentials: CredentialsResource) -> None:
@cached_property
def fields(self) -> FieldsResourceWithRawResponse:
+ """Vault & Secrets"""
return FieldsResourceWithRawResponse(self._credentials.fields)
@@ -362,6 +369,7 @@ def __init__(self, credentials: AsyncCredentialsResource) -> None:
@cached_property
def fields(self) -> AsyncFieldsResourceWithRawResponse:
+ """Vault & Secrets"""
return AsyncFieldsResourceWithRawResponse(self._credentials.fields)
@@ -381,6 +389,7 @@ def __init__(self, credentials: CredentialsResource) -> None:
@cached_property
def fields(self) -> FieldsResourceWithStreamingResponse:
+ """Vault & Secrets"""
return FieldsResourceWithStreamingResponse(self._credentials.fields)
@@ -400,4 +409,5 @@ def __init__(self, credentials: AsyncCredentialsResource) -> None:
@cached_property
def fields(self) -> AsyncFieldsResourceWithStreamingResponse:
+ """Vault & Secrets"""
return AsyncFieldsResourceWithStreamingResponse(self._credentials.fields)
diff --git a/src/mobilerun_sdk/resources/credentials/packages/credentials/fields.py b/src/mobilerun_sdk/resources/credentials/packages/credentials/fields.py
index 77940d6..38ebfd9 100644
--- a/src/mobilerun_sdk/resources/credentials/packages/credentials/fields.py
+++ b/src/mobilerun_sdk/resources/credentials/packages/credentials/fields.py
@@ -26,6 +26,8 @@
class FieldsResource(SyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def with_raw_response(self) -> FieldsResourceWithRawResponse:
"""
@@ -192,6 +194,8 @@ def delete(
class AsyncFieldsResource(AsyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def with_raw_response(self) -> AsyncFieldsResourceWithRawResponse:
"""
diff --git a/src/mobilerun_sdk/resources/credentials/packages/packages.py b/src/mobilerun_sdk/resources/credentials/packages/packages.py
index f871c41..135ccdd 100644
--- a/src/mobilerun_sdk/resources/credentials/packages/packages.py
+++ b/src/mobilerun_sdk/resources/credentials/packages/packages.py
@@ -31,8 +31,11 @@
class PackagesResource(SyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def credentials(self) -> CredentialsResource:
+ """Vault & Secrets"""
return CredentialsResource(self._client)
@cached_property
@@ -121,8 +124,11 @@ def list(
class AsyncPackagesResource(AsyncAPIResource):
+ """Vault & Secrets"""
+
@cached_property
def credentials(self) -> AsyncCredentialsResource:
+ """Vault & Secrets"""
return AsyncCredentialsResource(self._client)
@cached_property
@@ -223,6 +229,7 @@ def __init__(self, packages: PackagesResource) -> None:
@cached_property
def credentials(self) -> CredentialsResourceWithRawResponse:
+ """Vault & Secrets"""
return CredentialsResourceWithRawResponse(self._packages.credentials)
@@ -239,6 +246,7 @@ def __init__(self, packages: AsyncPackagesResource) -> None:
@cached_property
def credentials(self) -> AsyncCredentialsResourceWithRawResponse:
+ """Vault & Secrets"""
return AsyncCredentialsResourceWithRawResponse(self._packages.credentials)
@@ -255,6 +263,7 @@ def __init__(self, packages: PackagesResource) -> None:
@cached_property
def credentials(self) -> CredentialsResourceWithStreamingResponse:
+ """Vault & Secrets"""
return CredentialsResourceWithStreamingResponse(self._packages.credentials)
@@ -271,4 +280,5 @@ def __init__(self, packages: AsyncPackagesResource) -> None:
@cached_property
def credentials(self) -> AsyncCredentialsResourceWithStreamingResponse:
+ """Vault & Secrets"""
return AsyncCredentialsResourceWithStreamingResponse(self._packages.credentials)
diff --git a/src/mobilerun_sdk/resources/devices/__init__.py b/src/mobilerun_sdk/resources/devices/__init__.py
index e361ca9..77ef45e 100644
--- a/src/mobilerun_sdk/resources/devices/__init__.py
+++ b/src/mobilerun_sdk/resources/devices/__init__.py
@@ -16,14 +16,6 @@
EsimResourceWithStreamingResponse,
AsyncEsimResourceWithStreamingResponse,
)
-from .time import (
- TimeResource,
- AsyncTimeResource,
- TimeResourceWithRawResponse,
- AsyncTimeResourceWithRawResponse,
- TimeResourceWithStreamingResponse,
- AsyncTimeResourceWithStreamingResponse,
-)
from .files import (
FilesResource,
AsyncFilesResource,
@@ -88,6 +80,14 @@
KeyboardResourceWithStreamingResponse,
AsyncKeyboardResourceWithStreamingResponse,
)
+from .language import (
+ LanguageResource,
+ AsyncLanguageResource,
+ LanguageResourceWithRawResponse,
+ AsyncLanguageResourceWithRawResponse,
+ LanguageResourceWithStreamingResponse,
+ AsyncLanguageResourceWithStreamingResponse,
+)
from .location import (
LocationResource,
AsyncLocationResource,
@@ -104,80 +104,94 @@
PackagesResourceWithStreamingResponse,
AsyncPackagesResourceWithStreamingResponse,
)
+from .timezone import (
+ TimezoneResource,
+ AsyncTimezoneResource,
+ TimezoneResourceWithRawResponse,
+ AsyncTimezoneResourceWithRawResponse,
+ TimezoneResourceWithStreamingResponse,
+ AsyncTimezoneResourceWithStreamingResponse,
+)
__all__ = [
- "TimeResource",
- "AsyncTimeResource",
- "TimeResourceWithRawResponse",
- "AsyncTimeResourceWithRawResponse",
- "TimeResourceWithStreamingResponse",
- "AsyncTimeResourceWithStreamingResponse",
- "ProfileResource",
- "AsyncProfileResource",
- "ProfileResourceWithRawResponse",
- "AsyncProfileResourceWithRawResponse",
- "ProfileResourceWithStreamingResponse",
- "AsyncProfileResourceWithStreamingResponse",
- "FilesResource",
- "AsyncFilesResource",
- "FilesResourceWithRawResponse",
- "AsyncFilesResourceWithRawResponse",
- "FilesResourceWithStreamingResponse",
- "AsyncFilesResourceWithStreamingResponse",
- "ProxyResource",
- "AsyncProxyResource",
- "ProxyResourceWithRawResponse",
- "AsyncProxyResourceWithRawResponse",
- "ProxyResourceWithStreamingResponse",
- "AsyncProxyResourceWithStreamingResponse",
- "LocationResource",
- "AsyncLocationResource",
- "LocationResourceWithRawResponse",
- "AsyncLocationResourceWithRawResponse",
- "LocationResourceWithStreamingResponse",
- "AsyncLocationResourceWithStreamingResponse",
"ActionsResource",
"AsyncActionsResource",
"ActionsResourceWithRawResponse",
"AsyncActionsResourceWithRawResponse",
"ActionsResourceWithStreamingResponse",
"AsyncActionsResourceWithStreamingResponse",
- "StateResource",
- "AsyncStateResource",
- "StateResourceWithRawResponse",
- "AsyncStateResourceWithRawResponse",
- "StateResourceWithStreamingResponse",
- "AsyncStateResourceWithStreamingResponse",
"AppsResource",
"AsyncAppsResource",
"AppsResourceWithRawResponse",
"AsyncAppsResourceWithRawResponse",
"AppsResourceWithStreamingResponse",
"AsyncAppsResourceWithStreamingResponse",
- "PackagesResource",
- "AsyncPackagesResource",
- "PackagesResourceWithRawResponse",
- "AsyncPackagesResourceWithRawResponse",
- "PackagesResourceWithStreamingResponse",
- "AsyncPackagesResourceWithStreamingResponse",
+ "EsimResource",
+ "AsyncEsimResource",
+ "EsimResourceWithRawResponse",
+ "AsyncEsimResourceWithRawResponse",
+ "EsimResourceWithStreamingResponse",
+ "AsyncEsimResourceWithStreamingResponse",
+ "FilesResource",
+ "AsyncFilesResource",
+ "FilesResourceWithRawResponse",
+ "AsyncFilesResourceWithRawResponse",
+ "FilesResourceWithStreamingResponse",
+ "AsyncFilesResourceWithStreamingResponse",
"KeyboardResource",
"AsyncKeyboardResource",
"KeyboardResourceWithRawResponse",
"AsyncKeyboardResourceWithRawResponse",
"KeyboardResourceWithStreamingResponse",
"AsyncKeyboardResourceWithStreamingResponse",
+ "LocationResource",
+ "AsyncLocationResource",
+ "LocationResourceWithRawResponse",
+ "AsyncLocationResourceWithRawResponse",
+ "LocationResourceWithStreamingResponse",
+ "AsyncLocationResourceWithStreamingResponse",
+ "PackagesResource",
+ "AsyncPackagesResource",
+ "PackagesResourceWithRawResponse",
+ "AsyncPackagesResourceWithRawResponse",
+ "PackagesResourceWithStreamingResponse",
+ "AsyncPackagesResourceWithStreamingResponse",
+ "ProfileResource",
+ "AsyncProfileResource",
+ "ProfileResourceWithRawResponse",
+ "AsyncProfileResourceWithRawResponse",
+ "ProfileResourceWithStreamingResponse",
+ "AsyncProfileResourceWithStreamingResponse",
+ "ProxyResource",
+ "AsyncProxyResource",
+ "ProxyResourceWithRawResponse",
+ "AsyncProxyResourceWithRawResponse",
+ "ProxyResourceWithStreamingResponse",
+ "AsyncProxyResourceWithStreamingResponse",
+ "StateResource",
+ "AsyncStateResource",
+ "StateResourceWithRawResponse",
+ "AsyncStateResourceWithRawResponse",
+ "StateResourceWithStreamingResponse",
+ "AsyncStateResourceWithStreamingResponse",
"TasksResource",
"AsyncTasksResource",
"TasksResourceWithRawResponse",
"AsyncTasksResourceWithRawResponse",
"TasksResourceWithStreamingResponse",
"AsyncTasksResourceWithStreamingResponse",
- "EsimResource",
- "AsyncEsimResource",
- "EsimResourceWithRawResponse",
- "AsyncEsimResourceWithRawResponse",
- "EsimResourceWithStreamingResponse",
- "AsyncEsimResourceWithStreamingResponse",
+ "TimezoneResource",
+ "AsyncTimezoneResource",
+ "TimezoneResourceWithRawResponse",
+ "AsyncTimezoneResourceWithRawResponse",
+ "TimezoneResourceWithStreamingResponse",
+ "AsyncTimezoneResourceWithStreamingResponse",
+ "LanguageResource",
+ "AsyncLanguageResource",
+ "LanguageResourceWithRawResponse",
+ "AsyncLanguageResourceWithRawResponse",
+ "LanguageResourceWithStreamingResponse",
+ "AsyncLanguageResourceWithStreamingResponse",
"DevicesResource",
"AsyncDevicesResource",
"DevicesResourceWithRawResponse",
diff --git a/src/mobilerun_sdk/resources/devices/apps.py b/src/mobilerun_sdk/resources/devices/apps.py
index 4ffb89d..4541bb5 100644
--- a/src/mobilerun_sdk/resources/devices/apps.py
+++ b/src/mobilerun_sdk/resources/devices/apps.py
@@ -3,11 +3,12 @@
from __future__ import annotations
from typing import Optional
+from typing_extensions import overload
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, path_template, required_args, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -183,11 +184,13 @@ def delete(
cast_to=NoneType,
)
+ @overload
def install(
self,
device_id: str,
*,
- package_name: str,
+ bundle_id: str,
+ package_name: str | Omit = omit,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -196,10 +199,49 @@ def install(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
+ """Install app
+
+ Args:
+ bundle_id: iOS bundle identifier (e.g.
+
+ com.example.app)
+
+ package_name: Android package name (e.g. com.example.app)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
"""
- Install app
+ ...
+
+ @overload
+ def install(
+ self,
+ device_id: str,
+ *,
+ package_name: str,
+ bundle_id: str | Omit = omit,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """Install app
Args:
+ package_name: Android package name (e.g.
+
+ com.example.app)
+
+ bundle_id: iOS bundle identifier (e.g. com.example.app)
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -208,6 +250,23 @@ def install(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ ...
+
+ @required_args(["bundle_id"], ["package_name"])
+ def install(
+ self,
+ device_id: str,
+ *,
+ bundle_id: str | Omit = omit,
+ package_name: str | Omit = omit,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
if not device_id:
raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
@@ -219,7 +278,13 @@ def install(
}
return self._post(
path_template("/devices/{device_id}/apps", device_id=device_id),
- body=maybe_transform({"package_name": package_name}, app_install_params.AppInstallParams),
+ body=maybe_transform(
+ {
+ "bundle_id": bundle_id,
+ "package_name": package_name,
+ },
+ app_install_params.AppInstallParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -433,11 +498,13 @@ async def delete(
cast_to=NoneType,
)
+ @overload
async def install(
self,
device_id: str,
*,
- package_name: str,
+ bundle_id: str,
+ package_name: str | Omit = omit,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -446,10 +513,49 @@ async def install(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
+ """Install app
+
+ Args:
+ bundle_id: iOS bundle identifier (e.g.
+
+ com.example.app)
+
+ package_name: Android package name (e.g. com.example.app)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
"""
- Install app
+ ...
+
+ @overload
+ async def install(
+ self,
+ device_id: str,
+ *,
+ package_name: str,
+ bundle_id: str | Omit = omit,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """Install app
Args:
+ package_name: Android package name (e.g.
+
+ com.example.app)
+
+ bundle_id: iOS bundle identifier (e.g. com.example.app)
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -458,6 +564,23 @@ async def install(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ ...
+
+ @required_args(["bundle_id"], ["package_name"])
+ async def install(
+ self,
+ device_id: str,
+ *,
+ bundle_id: str | Omit = omit,
+ package_name: str | Omit = omit,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
if not device_id:
raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
@@ -469,7 +592,13 @@ async def install(
}
return await self._post(
path_template("/devices/{device_id}/apps", device_id=device_id),
- body=await async_maybe_transform({"package_name": package_name}, app_install_params.AppInstallParams),
+ body=await async_maybe_transform(
+ {
+ "bundle_id": bundle_id,
+ "package_name": package_name,
+ },
+ app_install_params.AppInstallParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/mobilerun_sdk/resources/devices/devices.py b/src/mobilerun_sdk/resources/devices/devices.py
index b6743a9..31c41ed 100644
--- a/src/mobilerun_sdk/resources/devices/devices.py
+++ b/src/mobilerun_sdk/resources/devices/devices.py
@@ -16,22 +16,6 @@
AppsResourceWithStreamingResponse,
AsyncAppsResourceWithStreamingResponse,
)
-from .esim import (
- EsimResource,
- AsyncEsimResource,
- EsimResourceWithRawResponse,
- AsyncEsimResourceWithRawResponse,
- EsimResourceWithStreamingResponse,
- AsyncEsimResourceWithStreamingResponse,
-)
-from .time import (
- TimeResource,
- AsyncTimeResource,
- TimeResourceWithRawResponse,
- AsyncTimeResourceWithRawResponse,
- TimeResourceWithStreamingResponse,
- AsyncTimeResourceWithStreamingResponse,
-)
from .files import (
FilesResource,
AsyncFilesResource,
@@ -82,7 +66,7 @@
AsyncProfileResourceWithStreamingResponse,
)
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
-from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
from .keyboard import (
KeyboardResource,
AsyncKeyboardResource,
@@ -91,6 +75,14 @@
KeyboardResourceWithStreamingResponse,
AsyncKeyboardResourceWithStreamingResponse,
)
+from .language import (
+ LanguageResource,
+ AsyncLanguageResource,
+ LanguageResourceWithRawResponse,
+ AsyncLanguageResourceWithRawResponse,
+ LanguageResourceWithStreamingResponse,
+ AsyncLanguageResourceWithStreamingResponse,
+)
from .location import (
LocationResource,
AsyncLocationResource,
@@ -107,7 +99,23 @@
PackagesResourceWithStreamingResponse,
AsyncPackagesResourceWithStreamingResponse,
)
+from .timezone import (
+ TimezoneResource,
+ AsyncTimezoneResource,
+ TimezoneResourceWithRawResponse,
+ AsyncTimezoneResourceWithRawResponse,
+ TimezoneResourceWithStreamingResponse,
+ AsyncTimezoneResourceWithStreamingResponse,
+)
from ..._compat import cached_property
+from .esim.esim import (
+ EsimResource,
+ AsyncEsimResource,
+ EsimResourceWithRawResponse,
+ AsyncEsimResourceWithRawResponse,
+ EsimResourceWithStreamingResponse,
+ AsyncEsimResourceWithStreamingResponse,
+)
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
to_raw_response_wrapper,
@@ -119,6 +127,8 @@
from ...types.device import Device
from ...types.device_list_response import DeviceListResponse
from ...types.device_count_response import DeviceCountResponse
+from ...types.shared_params.location import Location
+from ...types.device_fingerprint_response import DeviceFingerprintResponse
from ...types.shared_params.device_carrier import DeviceCarrier
from ...types.shared_params.device_identifiers import DeviceIdentifiers
@@ -127,52 +137,57 @@
class DevicesResource(SyncAPIResource):
@cached_property
- def time(self) -> TimeResource:
- return TimeResource(self._client)
+ def actions(self) -> ActionsResource:
+ return ActionsResource(self._client)
@cached_property
- def profile(self) -> ProfileResource:
- return ProfileResource(self._client)
+ def apps(self) -> AppsResource:
+ return AppsResource(self._client)
+
+ @cached_property
+ def esim(self) -> EsimResource:
+ return EsimResource(self._client)
@cached_property
def files(self) -> FilesResource:
return FilesResource(self._client)
@cached_property
- def proxy(self) -> ProxyResource:
- return ProxyResource(self._client)
+ def keyboard(self) -> KeyboardResource:
+ return KeyboardResource(self._client)
@cached_property
def location(self) -> LocationResource:
return LocationResource(self._client)
@cached_property
- def actions(self) -> ActionsResource:
- return ActionsResource(self._client)
-
- @cached_property
- def state(self) -> StateResource:
- return StateResource(self._client)
+ def packages(self) -> PackagesResource:
+ return PackagesResource(self._client)
@cached_property
- def apps(self) -> AppsResource:
- return AppsResource(self._client)
+ def profile(self) -> ProfileResource:
+ return ProfileResource(self._client)
@cached_property
- def packages(self) -> PackagesResource:
- return PackagesResource(self._client)
+ def proxy(self) -> ProxyResource:
+ return ProxyResource(self._client)
@cached_property
- def keyboard(self) -> KeyboardResource:
- return KeyboardResource(self._client)
+ def state(self) -> StateResource:
+ return StateResource(self._client)
@cached_property
def tasks(self) -> TasksResource:
+ """Device Management"""
return TasksResource(self._client)
@cached_property
- def esim(self) -> EsimResource:
- return EsimResource(self._client)
+ def timezone(self) -> TimezoneResource:
+ return TimezoneResource(self._client)
+
+ @cached_property
+ def language(self) -> LanguageResource:
+ return LanguageResource(self._client)
@cached_property
def with_raw_response(self) -> DevicesResourceWithRawResponse:
@@ -196,16 +211,21 @@ def with_streaming_response(self) -> DevicesResourceWithStreamingResponse:
def create(
self,
*,
- device_type: Literal[
- "dedicated_physical_device", "dedicated_premium_device", "dedicated_emulated_device", "dedicated_ios_device"
- ]
+ query_country: str | Omit = omit,
+ device_type: Literal["dedicated_physical_device", "dedicated_premium_device", "dedicated_ios_device"]
| Omit = omit,
+ profile_id: str | Omit = omit,
+ android_version: int | Omit = omit,
apps: Optional[SequenceNotStr[str]] | Omit = omit,
carrier: DeviceCarrier | Omit = omit,
+ body_country: str | Omit = omit,
files: Optional[SequenceNotStr[str]] | Omit = omit,
identifiers: DeviceIdentifiers | Omit = omit,
+ locale: str | Omit = omit,
+ location: Location | Omit = omit,
name: str | Omit = omit,
proxy: device_create_params.Proxy | Omit = omit,
+ timezone: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -213,10 +233,16 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Device:
- """
- Provision a new device
+ """Provision a new device
Args:
+ query_country: ISO 3166-1 alpha-2 country code.
+
+ If omitted the system picks the country with
+ the most availability.
+
+ profile_id: Profile ID to use as device spec
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -229,12 +255,17 @@ def create(
"/devices",
body=maybe_transform(
{
+ "android_version": android_version,
"apps": apps,
"carrier": carrier,
+ "body_country": body_country,
"files": files,
"identifiers": identifiers,
+ "locale": locale,
+ "location": location,
"name": name,
"proxy": proxy,
+ "timezone": timezone,
},
device_create_params.DeviceCreateParams,
),
@@ -243,7 +274,14 @@ def create(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=maybe_transform({"device_type": device_type}, device_create_params.DeviceCreateParams),
+ query=maybe_transform(
+ {
+ "query_country": query_country,
+ "device_type": device_type,
+ "profile_id": profile_id,
+ },
+ device_create_params.DeviceCreateParams,
+ ),
),
cast_to=Device,
)
@@ -294,15 +332,20 @@ def list(
state: Optional[
List[
Literal[
- "creating", "assigned", "ready", "rebooting", "migrating", "terminated", "maintenance", "unknown"
+ "creating",
+ "assigned",
+ "ready",
+ "rebooting",
+ "migrating",
+ "resetting",
+ "terminated",
+ "maintenance",
+ "unknown",
]
]
]
| Omit = omit,
- type: Literal[
- "dedicated_physical_device", "dedicated_premium_device", "dedicated_emulated_device", "dedicated_ios_device"
- ]
- | Omit = omit,
+ type: Literal["dedicated_physical_device", "dedicated_premium_device", "dedicated_ios_device"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -366,6 +409,115 @@ def count(
cast_to=DeviceCountResponse,
)
+ def fingerprint(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DeviceFingerprintResponse:
+ """
+ Device fingerprint snapshot
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._get(
+ path_template("/devices/{device_id}/fingerprint", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DeviceFingerprintResponse,
+ )
+
+ def reboot(
+ self,
+ device_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Reboot a device
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ path_template("/devices/{device_id}/reboot", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def reset(
+ self,
+ device_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Reset a device to a fresh state (VMOS one-click new device; non-VMOS providers
+ return 404)
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ path_template("/devices/{device_id}/reset", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
def set_name(
self,
device_id: str,
@@ -480,52 +632,57 @@ def wait_ready(
class AsyncDevicesResource(AsyncAPIResource):
@cached_property
- def time(self) -> AsyncTimeResource:
- return AsyncTimeResource(self._client)
+ def actions(self) -> AsyncActionsResource:
+ return AsyncActionsResource(self._client)
@cached_property
- def profile(self) -> AsyncProfileResource:
- return AsyncProfileResource(self._client)
+ def apps(self) -> AsyncAppsResource:
+ return AsyncAppsResource(self._client)
+
+ @cached_property
+ def esim(self) -> AsyncEsimResource:
+ return AsyncEsimResource(self._client)
@cached_property
def files(self) -> AsyncFilesResource:
return AsyncFilesResource(self._client)
@cached_property
- def proxy(self) -> AsyncProxyResource:
- return AsyncProxyResource(self._client)
+ def keyboard(self) -> AsyncKeyboardResource:
+ return AsyncKeyboardResource(self._client)
@cached_property
def location(self) -> AsyncLocationResource:
return AsyncLocationResource(self._client)
@cached_property
- def actions(self) -> AsyncActionsResource:
- return AsyncActionsResource(self._client)
-
- @cached_property
- def state(self) -> AsyncStateResource:
- return AsyncStateResource(self._client)
+ def packages(self) -> AsyncPackagesResource:
+ return AsyncPackagesResource(self._client)
@cached_property
- def apps(self) -> AsyncAppsResource:
- return AsyncAppsResource(self._client)
+ def profile(self) -> AsyncProfileResource:
+ return AsyncProfileResource(self._client)
@cached_property
- def packages(self) -> AsyncPackagesResource:
- return AsyncPackagesResource(self._client)
+ def proxy(self) -> AsyncProxyResource:
+ return AsyncProxyResource(self._client)
@cached_property
- def keyboard(self) -> AsyncKeyboardResource:
- return AsyncKeyboardResource(self._client)
+ def state(self) -> AsyncStateResource:
+ return AsyncStateResource(self._client)
@cached_property
def tasks(self) -> AsyncTasksResource:
+ """Device Management"""
return AsyncTasksResource(self._client)
@cached_property
- def esim(self) -> AsyncEsimResource:
- return AsyncEsimResource(self._client)
+ def timezone(self) -> AsyncTimezoneResource:
+ return AsyncTimezoneResource(self._client)
+
+ @cached_property
+ def language(self) -> AsyncLanguageResource:
+ return AsyncLanguageResource(self._client)
@cached_property
def with_raw_response(self) -> AsyncDevicesResourceWithRawResponse:
@@ -549,16 +706,21 @@ def with_streaming_response(self) -> AsyncDevicesResourceWithStreamingResponse:
async def create(
self,
*,
- device_type: Literal[
- "dedicated_physical_device", "dedicated_premium_device", "dedicated_emulated_device", "dedicated_ios_device"
- ]
+ query_country: str | Omit = omit,
+ device_type: Literal["dedicated_physical_device", "dedicated_premium_device", "dedicated_ios_device"]
| Omit = omit,
+ profile_id: str | Omit = omit,
+ android_version: int | Omit = omit,
apps: Optional[SequenceNotStr[str]] | Omit = omit,
carrier: DeviceCarrier | Omit = omit,
+ body_country: str | Omit = omit,
files: Optional[SequenceNotStr[str]] | Omit = omit,
identifiers: DeviceIdentifiers | Omit = omit,
+ locale: str | Omit = omit,
+ location: Location | Omit = omit,
name: str | Omit = omit,
proxy: device_create_params.Proxy | Omit = omit,
+ timezone: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -566,10 +728,16 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Device:
- """
- Provision a new device
+ """Provision a new device
Args:
+ query_country: ISO 3166-1 alpha-2 country code.
+
+ If omitted the system picks the country with
+ the most availability.
+
+ profile_id: Profile ID to use as device spec
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -582,12 +750,17 @@ async def create(
"/devices",
body=await async_maybe_transform(
{
+ "android_version": android_version,
"apps": apps,
"carrier": carrier,
+ "body_country": body_country,
"files": files,
"identifiers": identifiers,
+ "locale": locale,
+ "location": location,
"name": name,
"proxy": proxy,
+ "timezone": timezone,
},
device_create_params.DeviceCreateParams,
),
@@ -597,7 +770,12 @@ async def create(
extra_body=extra_body,
timeout=timeout,
query=await async_maybe_transform(
- {"device_type": device_type}, device_create_params.DeviceCreateParams
+ {
+ "query_country": query_country,
+ "device_type": device_type,
+ "profile_id": profile_id,
+ },
+ device_create_params.DeviceCreateParams,
),
),
cast_to=Device,
@@ -649,15 +827,20 @@ async def list(
state: Optional[
List[
Literal[
- "creating", "assigned", "ready", "rebooting", "migrating", "terminated", "maintenance", "unknown"
+ "creating",
+ "assigned",
+ "ready",
+ "rebooting",
+ "migrating",
+ "resetting",
+ "terminated",
+ "maintenance",
+ "unknown",
]
]
]
| Omit = omit,
- type: Literal[
- "dedicated_physical_device", "dedicated_premium_device", "dedicated_emulated_device", "dedicated_ios_device"
- ]
- | Omit = omit,
+ type: Literal["dedicated_physical_device", "dedicated_premium_device", "dedicated_ios_device"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -721,6 +904,115 @@ async def count(
cast_to=DeviceCountResponse,
)
+ async def fingerprint(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DeviceFingerprintResponse:
+ """
+ Device fingerprint snapshot
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._get(
+ path_template("/devices/{device_id}/fingerprint", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DeviceFingerprintResponse,
+ )
+
+ async def reboot(
+ self,
+ device_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Reboot a device
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ path_template("/devices/{device_id}/reboot", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def reset(
+ self,
+ device_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Reset a device to a fresh state (VMOS one-click new device; non-VMOS providers
+ return 404)
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ path_template("/devices/{device_id}/reset", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
async def set_name(
self,
device_id: str,
@@ -849,6 +1141,15 @@ def __init__(self, devices: DevicesResource) -> None:
self.count = to_raw_response_wrapper(
devices.count,
)
+ self.fingerprint = to_raw_response_wrapper(
+ devices.fingerprint,
+ )
+ self.reboot = to_raw_response_wrapper(
+ devices.reboot,
+ )
+ self.reset = to_raw_response_wrapper(
+ devices.reset,
+ )
self.set_name = to_raw_response_wrapper(
devices.set_name,
)
@@ -860,52 +1161,57 @@ def __init__(self, devices: DevicesResource) -> None:
)
@cached_property
- def time(self) -> TimeResourceWithRawResponse:
- return TimeResourceWithRawResponse(self._devices.time)
+ def actions(self) -> ActionsResourceWithRawResponse:
+ return ActionsResourceWithRawResponse(self._devices.actions)
@cached_property
- def profile(self) -> ProfileResourceWithRawResponse:
- return ProfileResourceWithRawResponse(self._devices.profile)
+ def apps(self) -> AppsResourceWithRawResponse:
+ return AppsResourceWithRawResponse(self._devices.apps)
+
+ @cached_property
+ def esim(self) -> EsimResourceWithRawResponse:
+ return EsimResourceWithRawResponse(self._devices.esim)
@cached_property
def files(self) -> FilesResourceWithRawResponse:
return FilesResourceWithRawResponse(self._devices.files)
@cached_property
- def proxy(self) -> ProxyResourceWithRawResponse:
- return ProxyResourceWithRawResponse(self._devices.proxy)
+ def keyboard(self) -> KeyboardResourceWithRawResponse:
+ return KeyboardResourceWithRawResponse(self._devices.keyboard)
@cached_property
def location(self) -> LocationResourceWithRawResponse:
return LocationResourceWithRawResponse(self._devices.location)
@cached_property
- def actions(self) -> ActionsResourceWithRawResponse:
- return ActionsResourceWithRawResponse(self._devices.actions)
-
- @cached_property
- def state(self) -> StateResourceWithRawResponse:
- return StateResourceWithRawResponse(self._devices.state)
+ def packages(self) -> PackagesResourceWithRawResponse:
+ return PackagesResourceWithRawResponse(self._devices.packages)
@cached_property
- def apps(self) -> AppsResourceWithRawResponse:
- return AppsResourceWithRawResponse(self._devices.apps)
+ def profile(self) -> ProfileResourceWithRawResponse:
+ return ProfileResourceWithRawResponse(self._devices.profile)
@cached_property
- def packages(self) -> PackagesResourceWithRawResponse:
- return PackagesResourceWithRawResponse(self._devices.packages)
+ def proxy(self) -> ProxyResourceWithRawResponse:
+ return ProxyResourceWithRawResponse(self._devices.proxy)
@cached_property
- def keyboard(self) -> KeyboardResourceWithRawResponse:
- return KeyboardResourceWithRawResponse(self._devices.keyboard)
+ def state(self) -> StateResourceWithRawResponse:
+ return StateResourceWithRawResponse(self._devices.state)
@cached_property
def tasks(self) -> TasksResourceWithRawResponse:
+ """Device Management"""
return TasksResourceWithRawResponse(self._devices.tasks)
@cached_property
- def esim(self) -> EsimResourceWithRawResponse:
- return EsimResourceWithRawResponse(self._devices.esim)
+ def timezone(self) -> TimezoneResourceWithRawResponse:
+ return TimezoneResourceWithRawResponse(self._devices.timezone)
+
+ @cached_property
+ def language(self) -> LanguageResourceWithRawResponse:
+ return LanguageResourceWithRawResponse(self._devices.language)
class AsyncDevicesResourceWithRawResponse:
@@ -924,6 +1230,15 @@ def __init__(self, devices: AsyncDevicesResource) -> None:
self.count = async_to_raw_response_wrapper(
devices.count,
)
+ self.fingerprint = async_to_raw_response_wrapper(
+ devices.fingerprint,
+ )
+ self.reboot = async_to_raw_response_wrapper(
+ devices.reboot,
+ )
+ self.reset = async_to_raw_response_wrapper(
+ devices.reset,
+ )
self.set_name = async_to_raw_response_wrapper(
devices.set_name,
)
@@ -935,52 +1250,57 @@ def __init__(self, devices: AsyncDevicesResource) -> None:
)
@cached_property
- def time(self) -> AsyncTimeResourceWithRawResponse:
- return AsyncTimeResourceWithRawResponse(self._devices.time)
+ def actions(self) -> AsyncActionsResourceWithRawResponse:
+ return AsyncActionsResourceWithRawResponse(self._devices.actions)
@cached_property
- def profile(self) -> AsyncProfileResourceWithRawResponse:
- return AsyncProfileResourceWithRawResponse(self._devices.profile)
+ def apps(self) -> AsyncAppsResourceWithRawResponse:
+ return AsyncAppsResourceWithRawResponse(self._devices.apps)
+
+ @cached_property
+ def esim(self) -> AsyncEsimResourceWithRawResponse:
+ return AsyncEsimResourceWithRawResponse(self._devices.esim)
@cached_property
def files(self) -> AsyncFilesResourceWithRawResponse:
return AsyncFilesResourceWithRawResponse(self._devices.files)
@cached_property
- def proxy(self) -> AsyncProxyResourceWithRawResponse:
- return AsyncProxyResourceWithRawResponse(self._devices.proxy)
+ def keyboard(self) -> AsyncKeyboardResourceWithRawResponse:
+ return AsyncKeyboardResourceWithRawResponse(self._devices.keyboard)
@cached_property
def location(self) -> AsyncLocationResourceWithRawResponse:
return AsyncLocationResourceWithRawResponse(self._devices.location)
@cached_property
- def actions(self) -> AsyncActionsResourceWithRawResponse:
- return AsyncActionsResourceWithRawResponse(self._devices.actions)
-
- @cached_property
- def state(self) -> AsyncStateResourceWithRawResponse:
- return AsyncStateResourceWithRawResponse(self._devices.state)
+ def packages(self) -> AsyncPackagesResourceWithRawResponse:
+ return AsyncPackagesResourceWithRawResponse(self._devices.packages)
@cached_property
- def apps(self) -> AsyncAppsResourceWithRawResponse:
- return AsyncAppsResourceWithRawResponse(self._devices.apps)
+ def profile(self) -> AsyncProfileResourceWithRawResponse:
+ return AsyncProfileResourceWithRawResponse(self._devices.profile)
@cached_property
- def packages(self) -> AsyncPackagesResourceWithRawResponse:
- return AsyncPackagesResourceWithRawResponse(self._devices.packages)
+ def proxy(self) -> AsyncProxyResourceWithRawResponse:
+ return AsyncProxyResourceWithRawResponse(self._devices.proxy)
@cached_property
- def keyboard(self) -> AsyncKeyboardResourceWithRawResponse:
- return AsyncKeyboardResourceWithRawResponse(self._devices.keyboard)
+ def state(self) -> AsyncStateResourceWithRawResponse:
+ return AsyncStateResourceWithRawResponse(self._devices.state)
@cached_property
def tasks(self) -> AsyncTasksResourceWithRawResponse:
+ """Device Management"""
return AsyncTasksResourceWithRawResponse(self._devices.tasks)
@cached_property
- def esim(self) -> AsyncEsimResourceWithRawResponse:
- return AsyncEsimResourceWithRawResponse(self._devices.esim)
+ def timezone(self) -> AsyncTimezoneResourceWithRawResponse:
+ return AsyncTimezoneResourceWithRawResponse(self._devices.timezone)
+
+ @cached_property
+ def language(self) -> AsyncLanguageResourceWithRawResponse:
+ return AsyncLanguageResourceWithRawResponse(self._devices.language)
class DevicesResourceWithStreamingResponse:
@@ -999,6 +1319,15 @@ def __init__(self, devices: DevicesResource) -> None:
self.count = to_streamed_response_wrapper(
devices.count,
)
+ self.fingerprint = to_streamed_response_wrapper(
+ devices.fingerprint,
+ )
+ self.reboot = to_streamed_response_wrapper(
+ devices.reboot,
+ )
+ self.reset = to_streamed_response_wrapper(
+ devices.reset,
+ )
self.set_name = to_streamed_response_wrapper(
devices.set_name,
)
@@ -1010,52 +1339,57 @@ def __init__(self, devices: DevicesResource) -> None:
)
@cached_property
- def time(self) -> TimeResourceWithStreamingResponse:
- return TimeResourceWithStreamingResponse(self._devices.time)
+ def actions(self) -> ActionsResourceWithStreamingResponse:
+ return ActionsResourceWithStreamingResponse(self._devices.actions)
+
+ @cached_property
+ def apps(self) -> AppsResourceWithStreamingResponse:
+ return AppsResourceWithStreamingResponse(self._devices.apps)
@cached_property
- def profile(self) -> ProfileResourceWithStreamingResponse:
- return ProfileResourceWithStreamingResponse(self._devices.profile)
+ def esim(self) -> EsimResourceWithStreamingResponse:
+ return EsimResourceWithStreamingResponse(self._devices.esim)
@cached_property
def files(self) -> FilesResourceWithStreamingResponse:
return FilesResourceWithStreamingResponse(self._devices.files)
@cached_property
- def proxy(self) -> ProxyResourceWithStreamingResponse:
- return ProxyResourceWithStreamingResponse(self._devices.proxy)
+ def keyboard(self) -> KeyboardResourceWithStreamingResponse:
+ return KeyboardResourceWithStreamingResponse(self._devices.keyboard)
@cached_property
def location(self) -> LocationResourceWithStreamingResponse:
return LocationResourceWithStreamingResponse(self._devices.location)
@cached_property
- def actions(self) -> ActionsResourceWithStreamingResponse:
- return ActionsResourceWithStreamingResponse(self._devices.actions)
-
- @cached_property
- def state(self) -> StateResourceWithStreamingResponse:
- return StateResourceWithStreamingResponse(self._devices.state)
+ def packages(self) -> PackagesResourceWithStreamingResponse:
+ return PackagesResourceWithStreamingResponse(self._devices.packages)
@cached_property
- def apps(self) -> AppsResourceWithStreamingResponse:
- return AppsResourceWithStreamingResponse(self._devices.apps)
+ def profile(self) -> ProfileResourceWithStreamingResponse:
+ return ProfileResourceWithStreamingResponse(self._devices.profile)
@cached_property
- def packages(self) -> PackagesResourceWithStreamingResponse:
- return PackagesResourceWithStreamingResponse(self._devices.packages)
+ def proxy(self) -> ProxyResourceWithStreamingResponse:
+ return ProxyResourceWithStreamingResponse(self._devices.proxy)
@cached_property
- def keyboard(self) -> KeyboardResourceWithStreamingResponse:
- return KeyboardResourceWithStreamingResponse(self._devices.keyboard)
+ def state(self) -> StateResourceWithStreamingResponse:
+ return StateResourceWithStreamingResponse(self._devices.state)
@cached_property
def tasks(self) -> TasksResourceWithStreamingResponse:
+ """Device Management"""
return TasksResourceWithStreamingResponse(self._devices.tasks)
@cached_property
- def esim(self) -> EsimResourceWithStreamingResponse:
- return EsimResourceWithStreamingResponse(self._devices.esim)
+ def timezone(self) -> TimezoneResourceWithStreamingResponse:
+ return TimezoneResourceWithStreamingResponse(self._devices.timezone)
+
+ @cached_property
+ def language(self) -> LanguageResourceWithStreamingResponse:
+ return LanguageResourceWithStreamingResponse(self._devices.language)
class AsyncDevicesResourceWithStreamingResponse:
@@ -1074,6 +1408,15 @@ def __init__(self, devices: AsyncDevicesResource) -> None:
self.count = async_to_streamed_response_wrapper(
devices.count,
)
+ self.fingerprint = async_to_streamed_response_wrapper(
+ devices.fingerprint,
+ )
+ self.reboot = async_to_streamed_response_wrapper(
+ devices.reboot,
+ )
+ self.reset = async_to_streamed_response_wrapper(
+ devices.reset,
+ )
self.set_name = async_to_streamed_response_wrapper(
devices.set_name,
)
@@ -1085,49 +1428,54 @@ def __init__(self, devices: AsyncDevicesResource) -> None:
)
@cached_property
- def time(self) -> AsyncTimeResourceWithStreamingResponse:
- return AsyncTimeResourceWithStreamingResponse(self._devices.time)
+ def actions(self) -> AsyncActionsResourceWithStreamingResponse:
+ return AsyncActionsResourceWithStreamingResponse(self._devices.actions)
@cached_property
- def profile(self) -> AsyncProfileResourceWithStreamingResponse:
- return AsyncProfileResourceWithStreamingResponse(self._devices.profile)
+ def apps(self) -> AsyncAppsResourceWithStreamingResponse:
+ return AsyncAppsResourceWithStreamingResponse(self._devices.apps)
+
+ @cached_property
+ def esim(self) -> AsyncEsimResourceWithStreamingResponse:
+ return AsyncEsimResourceWithStreamingResponse(self._devices.esim)
@cached_property
def files(self) -> AsyncFilesResourceWithStreamingResponse:
return AsyncFilesResourceWithStreamingResponse(self._devices.files)
@cached_property
- def proxy(self) -> AsyncProxyResourceWithStreamingResponse:
- return AsyncProxyResourceWithStreamingResponse(self._devices.proxy)
+ def keyboard(self) -> AsyncKeyboardResourceWithStreamingResponse:
+ return AsyncKeyboardResourceWithStreamingResponse(self._devices.keyboard)
@cached_property
def location(self) -> AsyncLocationResourceWithStreamingResponse:
return AsyncLocationResourceWithStreamingResponse(self._devices.location)
@cached_property
- def actions(self) -> AsyncActionsResourceWithStreamingResponse:
- return AsyncActionsResourceWithStreamingResponse(self._devices.actions)
-
- @cached_property
- def state(self) -> AsyncStateResourceWithStreamingResponse:
- return AsyncStateResourceWithStreamingResponse(self._devices.state)
+ def packages(self) -> AsyncPackagesResourceWithStreamingResponse:
+ return AsyncPackagesResourceWithStreamingResponse(self._devices.packages)
@cached_property
- def apps(self) -> AsyncAppsResourceWithStreamingResponse:
- return AsyncAppsResourceWithStreamingResponse(self._devices.apps)
+ def profile(self) -> AsyncProfileResourceWithStreamingResponse:
+ return AsyncProfileResourceWithStreamingResponse(self._devices.profile)
@cached_property
- def packages(self) -> AsyncPackagesResourceWithStreamingResponse:
- return AsyncPackagesResourceWithStreamingResponse(self._devices.packages)
+ def proxy(self) -> AsyncProxyResourceWithStreamingResponse:
+ return AsyncProxyResourceWithStreamingResponse(self._devices.proxy)
@cached_property
- def keyboard(self) -> AsyncKeyboardResourceWithStreamingResponse:
- return AsyncKeyboardResourceWithStreamingResponse(self._devices.keyboard)
+ def state(self) -> AsyncStateResourceWithStreamingResponse:
+ return AsyncStateResourceWithStreamingResponse(self._devices.state)
@cached_property
def tasks(self) -> AsyncTasksResourceWithStreamingResponse:
+ """Device Management"""
return AsyncTasksResourceWithStreamingResponse(self._devices.tasks)
@cached_property
- def esim(self) -> AsyncEsimResourceWithStreamingResponse:
- return AsyncEsimResourceWithStreamingResponse(self._devices.esim)
+ def timezone(self) -> AsyncTimezoneResourceWithStreamingResponse:
+ return AsyncTimezoneResourceWithStreamingResponse(self._devices.timezone)
+
+ @cached_property
+ def language(self) -> AsyncLanguageResourceWithStreamingResponse:
+ return AsyncLanguageResourceWithStreamingResponse(self._devices.language)
diff --git a/src/mobilerun_sdk/resources/devices/esim/__init__.py b/src/mobilerun_sdk/resources/devices/esim/__init__.py
new file mode 100644
index 0000000..12056c7
--- /dev/null
+++ b/src/mobilerun_sdk/resources/devices/esim/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .apn import (
+ ApnResource,
+ AsyncApnResource,
+ ApnResourceWithRawResponse,
+ AsyncApnResourceWithRawResponse,
+ ApnResourceWithStreamingResponse,
+ AsyncApnResourceWithStreamingResponse,
+)
+from .esim import (
+ EsimResource,
+ AsyncEsimResource,
+ EsimResourceWithRawResponse,
+ AsyncEsimResourceWithRawResponse,
+ EsimResourceWithStreamingResponse,
+ AsyncEsimResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "ApnResource",
+ "AsyncApnResource",
+ "ApnResourceWithRawResponse",
+ "AsyncApnResourceWithRawResponse",
+ "ApnResourceWithStreamingResponse",
+ "AsyncApnResourceWithStreamingResponse",
+ "EsimResource",
+ "AsyncEsimResource",
+ "EsimResourceWithRawResponse",
+ "AsyncEsimResourceWithRawResponse",
+ "EsimResourceWithStreamingResponse",
+ "AsyncEsimResourceWithStreamingResponse",
+]
diff --git a/src/mobilerun_sdk/resources/devices/time.py b/src/mobilerun_sdk/resources/devices/esim/apn.py
similarity index 65%
rename from src/mobilerun_sdk/resources/devices/time.py
rename to src/mobilerun_sdk/resources/devices/esim/apn.py
index daab6c6..c732e52 100644
--- a/src/mobilerun_sdk/resources/devices/time.py
+++ b/src/mobilerun_sdk/resources/devices/esim/apn.py
@@ -2,50 +2,59 @@
from __future__ import annotations
+from typing import Optional
+
import httpx
-from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
-from ..._compat import cached_property
-from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import (
+from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
to_raw_response_wrapper,
to_streamed_response_wrapper,
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ..._base_client import make_request_options
-from ...types.devices import time_set_timezone_params
-from ...types.devices.time_timezone_response import TimeTimezoneResponse
+from ...._base_client import make_request_options
+from ....types.devices.esim import apn_create_params, apn_select_params
+from ....types.devices.esim.apn_list_response import ApnListResponse
-__all__ = ["TimeResource", "AsyncTimeResource"]
+__all__ = ["ApnResource", "AsyncApnResource"]
-class TimeResource(SyncAPIResource):
+class ApnResource(SyncAPIResource):
@cached_property
- def with_raw_response(self) -> TimeResourceWithRawResponse:
+ def with_raw_response(self) -> ApnResourceWithRawResponse:
"""
This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers
"""
- return TimeResourceWithRawResponse(self)
+ return ApnResourceWithRawResponse(self)
@cached_property
- def with_streaming_response(self) -> TimeResourceWithStreamingResponse:
+ def with_streaming_response(self) -> ApnResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response
"""
- return TimeResourceWithStreamingResponse(self)
+ return ApnResourceWithStreamingResponse(self)
- def set_timezone(
+ def create(
self,
device_id: str,
*,
- timezone: str,
+ apn: str,
+ mcc: str,
+ mnc: str,
+ name: str,
+ protocol: str,
+ roaming_protocol: str,
+ sub_id: int,
+ type: str,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -55,7 +64,7 @@ def set_timezone(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
"""
- Set device timezone
+ Create and set an APN for an eSIM subscription
Args:
extra_headers: Send extra headers
@@ -76,15 +85,27 @@ def set_timezone(
**(extra_headers or {}),
}
return self._post(
- path_template("/devices/{device_id}/timezone", device_id=device_id),
- body=maybe_transform({"timezone": timezone}, time_set_timezone_params.TimeSetTimezoneParams),
+ path_template("/devices/{device_id}/esim/apn", device_id=device_id),
+ body=maybe_transform(
+ {
+ "apn": apn,
+ "mcc": mcc,
+ "mnc": mnc,
+ "name": name,
+ "protocol": protocol,
+ "roaming_protocol": roaming_protocol,
+ "sub_id": sub_id,
+ "type": type,
+ },
+ apn_create_params.ApnCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=NoneType,
)
- def time(
+ def list(
self,
device_id: str,
*,
@@ -95,9 +116,9 @@ def time(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> str:
+ ) -> Optional[ApnListResponse]:
"""
- Device time
+ List APNs for active subscriptions
Args:
extra_headers: Send extra headers
@@ -117,17 +138,19 @@ def time(
**(extra_headers or {}),
}
return self._get(
- path_template("/devices/{device_id}/time", device_id=device_id),
+ path_template("/devices/{device_id}/esim/apn", device_id=device_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=str,
+ cast_to=ApnListResponse,
)
- def timezone(
+ def select(
self,
device_id: str,
*,
+ apn_id: int,
+ sub_id: int,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -135,9 +158,9 @@ def timezone(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> TimeTimezoneResponse:
+ ) -> None:
"""
- Get device timezone
+ Select an existing APN as preferred
Args:
extra_headers: Send extra headers
@@ -150,46 +173,61 @@ def timezone(
"""
if not device_id:
raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {
**strip_not_given(
{"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
),
**(extra_headers or {}),
}
- return self._get(
- path_template("/devices/{device_id}/timezone", device_id=device_id),
+ return self._put(
+ path_template("/devices/{device_id}/esim/apn", device_id=device_id),
+ body=maybe_transform(
+ {
+ "apn_id": apn_id,
+ "sub_id": sub_id,
+ },
+ apn_select_params.ApnSelectParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=TimeTimezoneResponse,
+ cast_to=NoneType,
)
-class AsyncTimeResource(AsyncAPIResource):
+class AsyncApnResource(AsyncAPIResource):
@cached_property
- def with_raw_response(self) -> AsyncTimeResourceWithRawResponse:
+ def with_raw_response(self) -> AsyncApnResourceWithRawResponse:
"""
This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers
"""
- return AsyncTimeResourceWithRawResponse(self)
+ return AsyncApnResourceWithRawResponse(self)
@cached_property
- def with_streaming_response(self) -> AsyncTimeResourceWithStreamingResponse:
+ def with_streaming_response(self) -> AsyncApnResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response
"""
- return AsyncTimeResourceWithStreamingResponse(self)
+ return AsyncApnResourceWithStreamingResponse(self)
- async def set_timezone(
+ async def create(
self,
device_id: str,
*,
- timezone: str,
+ apn: str,
+ mcc: str,
+ mnc: str,
+ name: str,
+ protocol: str,
+ roaming_protocol: str,
+ sub_id: int,
+ type: str,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -199,7 +237,7 @@ async def set_timezone(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
"""
- Set device timezone
+ Create and set an APN for an eSIM subscription
Args:
extra_headers: Send extra headers
@@ -220,15 +258,27 @@ async def set_timezone(
**(extra_headers or {}),
}
return await self._post(
- path_template("/devices/{device_id}/timezone", device_id=device_id),
- body=await async_maybe_transform({"timezone": timezone}, time_set_timezone_params.TimeSetTimezoneParams),
+ path_template("/devices/{device_id}/esim/apn", device_id=device_id),
+ body=await async_maybe_transform(
+ {
+ "apn": apn,
+ "mcc": mcc,
+ "mnc": mnc,
+ "name": name,
+ "protocol": protocol,
+ "roaming_protocol": roaming_protocol,
+ "sub_id": sub_id,
+ "type": type,
+ },
+ apn_create_params.ApnCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=NoneType,
)
- async def time(
+ async def list(
self,
device_id: str,
*,
@@ -239,9 +289,9 @@ async def time(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> str:
+ ) -> Optional[ApnListResponse]:
"""
- Device time
+ List APNs for active subscriptions
Args:
extra_headers: Send extra headers
@@ -261,17 +311,19 @@ async def time(
**(extra_headers or {}),
}
return await self._get(
- path_template("/devices/{device_id}/time", device_id=device_id),
+ path_template("/devices/{device_id}/esim/apn", device_id=device_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=str,
+ cast_to=ApnListResponse,
)
- async def timezone(
+ async def select(
self,
device_id: str,
*,
+ apn_id: int,
+ sub_id: int,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -279,9 +331,9 @@ async def timezone(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> TimeTimezoneResponse:
+ ) -> None:
"""
- Get device timezone
+ Select an existing APN as preferred
Args:
extra_headers: Send extra headers
@@ -294,76 +346,84 @@ async def timezone(
"""
if not device_id:
raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {
**strip_not_given(
{"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
),
**(extra_headers or {}),
}
- return await self._get(
- path_template("/devices/{device_id}/timezone", device_id=device_id),
+ return await self._put(
+ path_template("/devices/{device_id}/esim/apn", device_id=device_id),
+ body=await async_maybe_transform(
+ {
+ "apn_id": apn_id,
+ "sub_id": sub_id,
+ },
+ apn_select_params.ApnSelectParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=TimeTimezoneResponse,
+ cast_to=NoneType,
)
-class TimeResourceWithRawResponse:
- def __init__(self, time: TimeResource) -> None:
- self._time = time
+class ApnResourceWithRawResponse:
+ def __init__(self, apn: ApnResource) -> None:
+ self._apn = apn
- self.set_timezone = to_raw_response_wrapper(
- time.set_timezone,
+ self.create = to_raw_response_wrapper(
+ apn.create,
)
- self.time = to_raw_response_wrapper(
- time.time,
+ self.list = to_raw_response_wrapper(
+ apn.list,
)
- self.timezone = to_raw_response_wrapper(
- time.timezone,
+ self.select = to_raw_response_wrapper(
+ apn.select,
)
-class AsyncTimeResourceWithRawResponse:
- def __init__(self, time: AsyncTimeResource) -> None:
- self._time = time
+class AsyncApnResourceWithRawResponse:
+ def __init__(self, apn: AsyncApnResource) -> None:
+ self._apn = apn
- self.set_timezone = async_to_raw_response_wrapper(
- time.set_timezone,
+ self.create = async_to_raw_response_wrapper(
+ apn.create,
)
- self.time = async_to_raw_response_wrapper(
- time.time,
+ self.list = async_to_raw_response_wrapper(
+ apn.list,
)
- self.timezone = async_to_raw_response_wrapper(
- time.timezone,
+ self.select = async_to_raw_response_wrapper(
+ apn.select,
)
-class TimeResourceWithStreamingResponse:
- def __init__(self, time: TimeResource) -> None:
- self._time = time
+class ApnResourceWithStreamingResponse:
+ def __init__(self, apn: ApnResource) -> None:
+ self._apn = apn
- self.set_timezone = to_streamed_response_wrapper(
- time.set_timezone,
+ self.create = to_streamed_response_wrapper(
+ apn.create,
)
- self.time = to_streamed_response_wrapper(
- time.time,
+ self.list = to_streamed_response_wrapper(
+ apn.list,
)
- self.timezone = to_streamed_response_wrapper(
- time.timezone,
+ self.select = to_streamed_response_wrapper(
+ apn.select,
)
-class AsyncTimeResourceWithStreamingResponse:
- def __init__(self, time: AsyncTimeResource) -> None:
- self._time = time
+class AsyncApnResourceWithStreamingResponse:
+ def __init__(self, apn: AsyncApnResource) -> None:
+ self._apn = apn
- self.set_timezone = async_to_streamed_response_wrapper(
- time.set_timezone,
+ self.create = async_to_streamed_response_wrapper(
+ apn.create,
)
- self.time = async_to_streamed_response_wrapper(
- time.time,
+ self.list = async_to_streamed_response_wrapper(
+ apn.list,
)
- self.timezone = async_to_streamed_response_wrapper(
- time.timezone,
+ self.select = async_to_streamed_response_wrapper(
+ apn.select,
)
diff --git a/src/mobilerun_sdk/resources/devices/esim.py b/src/mobilerun_sdk/resources/devices/esim/esim.py
similarity index 64%
rename from src/mobilerun_sdk/resources/devices/esim.py
rename to src/mobilerun_sdk/resources/devices/esim/esim.py
index 3cf9c6e..9693e2e 100644
--- a/src/mobilerun_sdk/resources/devices/esim.py
+++ b/src/mobilerun_sdk/resources/devices/esim/esim.py
@@ -6,25 +6,38 @@
import httpx
-from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
-from ..._compat import cached_property
-from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import (
+from .apn import (
+ ApnResource,
+ AsyncApnResource,
+ ApnResourceWithRawResponse,
+ AsyncApnResourceWithRawResponse,
+ ApnResourceWithStreamingResponse,
+ AsyncApnResourceWithStreamingResponse,
+)
+from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
to_raw_response_wrapper,
to_streamed_response_wrapper,
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ..._base_client import make_request_options
-from ...types.devices import esim_enable_params, esim_remove_params, esim_activate_params
-from ...types.devices.esim_list_response import EsimListResponse
-from ...types.devices.esim_activate_response import EsimActivateResponse
+from ...._base_client import make_request_options
+from ....types.devices import esim_enable_params, esim_remove_params, esim_activate_params, esim_set_roaming_params
+from ....types.devices.esim_list_response import EsimListResponse
+from ....types.devices.esim_status_response import EsimStatusResponse
+from ....types.devices.esim_activate_response import EsimActivateResponse
__all__ = ["EsimResource", "AsyncEsimResource"]
class EsimResource(SyncAPIResource):
+ @cached_property
+ def apn(self) -> ApnResource:
+ return ApnResource(self._client)
+
@cached_property
def with_raw_response(self) -> EsimResourceWithRawResponse:
"""
@@ -89,8 +102,9 @@ def activate(
device_id: str,
*,
enable: bool,
- matching_id: str,
sm_dp_addr: str,
+ confirmation_code: str | Omit = omit,
+ matching_id: str | Omit = omit,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -103,6 +117,10 @@ def activate(
Configure eSIM (download profile and/or enable subscription)
Args:
+ confirmation_code: Optional carrier-issued confirmation code (the 4th LPA segment). Required only
+ for plans whose SM-DP+ challenges the device for one. Requires matchingId — the
+ LPA spec only interprets segment 4 when segment 3 is present.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -124,8 +142,9 @@ def activate(
body=maybe_transform(
{
"enable": enable,
- "matching_id": matching_id,
"sm_dp_addr": sm_dp_addr,
+ "confirmation_code": confirmation_code,
+ "matching_id": matching_id,
},
esim_activate_params.EsimActivateParams,
),
@@ -224,8 +243,95 @@ def remove(
cast_to=NoneType,
)
+ def set_roaming(
+ self,
+ device_id: str,
+ *,
+ enabled: bool,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Toggle eSIM data roaming
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._put(
+ path_template("/devices/{device_id}/esim/roaming", device_id=device_id),
+ body=maybe_transform({"enabled": enabled}, esim_set_roaming_params.EsimSetRoamingParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def status(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Optional[EsimStatusResponse]:
+ """
+ Get eSIM connectivity status
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._get(
+ path_template("/devices/{device_id}/esim/status", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EsimStatusResponse,
+ )
+
class AsyncEsimResource(AsyncAPIResource):
+ @cached_property
+ def apn(self) -> AsyncApnResource:
+ return AsyncApnResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncEsimResourceWithRawResponse:
"""
@@ -290,8 +396,9 @@ async def activate(
device_id: str,
*,
enable: bool,
- matching_id: str,
sm_dp_addr: str,
+ confirmation_code: str | Omit = omit,
+ matching_id: str | Omit = omit,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -304,6 +411,10 @@ async def activate(
Configure eSIM (download profile and/or enable subscription)
Args:
+ confirmation_code: Optional carrier-issued confirmation code (the 4th LPA segment). Required only
+ for plans whose SM-DP+ challenges the device for one. Requires matchingId — the
+ LPA spec only interprets segment 4 when segment 3 is present.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -325,8 +436,9 @@ async def activate(
body=await async_maybe_transform(
{
"enable": enable,
- "matching_id": matching_id,
"sm_dp_addr": sm_dp_addr,
+ "confirmation_code": confirmation_code,
+ "matching_id": matching_id,
},
esim_activate_params.EsimActivateParams,
),
@@ -425,6 +537,89 @@ async def remove(
cast_to=NoneType,
)
+ async def set_roaming(
+ self,
+ device_id: str,
+ *,
+ enabled: bool,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Toggle eSIM data roaming
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._put(
+ path_template("/devices/{device_id}/esim/roaming", device_id=device_id),
+ body=await async_maybe_transform({"enabled": enabled}, esim_set_roaming_params.EsimSetRoamingParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def status(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Optional[EsimStatusResponse]:
+ """
+ Get eSIM connectivity status
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._get(
+ path_template("/devices/{device_id}/esim/status", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EsimStatusResponse,
+ )
+
class EsimResourceWithRawResponse:
def __init__(self, esim: EsimResource) -> None:
@@ -442,6 +637,16 @@ def __init__(self, esim: EsimResource) -> None:
self.remove = to_raw_response_wrapper(
esim.remove,
)
+ self.set_roaming = to_raw_response_wrapper(
+ esim.set_roaming,
+ )
+ self.status = to_raw_response_wrapper(
+ esim.status,
+ )
+
+ @cached_property
+ def apn(self) -> ApnResourceWithRawResponse:
+ return ApnResourceWithRawResponse(self._esim.apn)
class AsyncEsimResourceWithRawResponse:
@@ -460,6 +665,16 @@ def __init__(self, esim: AsyncEsimResource) -> None:
self.remove = async_to_raw_response_wrapper(
esim.remove,
)
+ self.set_roaming = async_to_raw_response_wrapper(
+ esim.set_roaming,
+ )
+ self.status = async_to_raw_response_wrapper(
+ esim.status,
+ )
+
+ @cached_property
+ def apn(self) -> AsyncApnResourceWithRawResponse:
+ return AsyncApnResourceWithRawResponse(self._esim.apn)
class EsimResourceWithStreamingResponse:
@@ -478,6 +693,16 @@ def __init__(self, esim: EsimResource) -> None:
self.remove = to_streamed_response_wrapper(
esim.remove,
)
+ self.set_roaming = to_streamed_response_wrapper(
+ esim.set_roaming,
+ )
+ self.status = to_streamed_response_wrapper(
+ esim.status,
+ )
+
+ @cached_property
+ def apn(self) -> ApnResourceWithStreamingResponse:
+ return ApnResourceWithStreamingResponse(self._esim.apn)
class AsyncEsimResourceWithStreamingResponse:
@@ -496,3 +721,13 @@ def __init__(self, esim: AsyncEsimResource) -> None:
self.remove = async_to_streamed_response_wrapper(
esim.remove,
)
+ self.set_roaming = async_to_streamed_response_wrapper(
+ esim.set_roaming,
+ )
+ self.status = async_to_streamed_response_wrapper(
+ esim.status,
+ )
+
+ @cached_property
+ def apn(self) -> AsyncApnResourceWithStreamingResponse:
+ return AsyncApnResourceWithStreamingResponse(self._esim.apn)
diff --git a/src/mobilerun_sdk/resources/devices/files.py b/src/mobilerun_sdk/resources/devices/files.py
index 0b20330..ffaba39 100644
--- a/src/mobilerun_sdk/resources/devices/files.py
+++ b/src/mobilerun_sdk/resources/devices/files.py
@@ -6,16 +6,9 @@
import httpx
+from ..._files import deepcopy_with_paths
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given
-from ..._utils import (
- is_given,
- extract_files,
- path_template,
- maybe_transform,
- strip_not_given,
- deepcopy_minimal,
- async_maybe_transform,
-)
+from ..._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -222,7 +215,7 @@ def upload(
),
**(extra_headers or {}),
}
- body = deepcopy_minimal({"file": file})
+ body = deepcopy_with_paths({"file": file}, [["file"]])
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
# It should be noted that the actual Content-Type header that will be
# sent to the server will contain a `boundary` parameter, e.g.
@@ -434,7 +427,7 @@ async def upload(
),
**(extra_headers or {}),
}
- body = deepcopy_minimal({"file": file})
+ body = deepcopy_with_paths({"file": file}, [["file"]])
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
# It should be noted that the actual Content-Type header that will be
# sent to the server will contain a `boundary` parameter, e.g.
diff --git a/src/mobilerun_sdk/resources/devices/keyboard.py b/src/mobilerun_sdk/resources/devices/keyboard.py
index 66c14c6..97e2695 100644
--- a/src/mobilerun_sdk/resources/devices/keyboard.py
+++ b/src/mobilerun_sdk/resources/devices/keyboard.py
@@ -130,6 +130,7 @@ def write(
*,
text: str,
clear: bool | Omit = omit,
+ error_rate: float | Omit = omit,
stealth: bool | Omit = omit,
wpm: int | Omit = omit,
x_device_display_id: int | Omit = omit,
@@ -143,9 +144,11 @@ def write(
"""Input text
Args:
- wpm: Words per minute for stealth typing.
+ error_rate: Per-character mistake rate for humantouch typing.
- 0 uses portal default.
+ -1 uses server default.
+
+ wpm: Words per minute for stealth typing. 0 uses portal default.
extra_headers: Send extra headers
@@ -170,6 +173,7 @@ def write(
{
"text": text,
"clear": clear,
+ "error_rate": error_rate,
"stealth": stealth,
"wpm": wpm,
},
@@ -292,6 +296,7 @@ async def write(
*,
text: str,
clear: bool | Omit = omit,
+ error_rate: float | Omit = omit,
stealth: bool | Omit = omit,
wpm: int | Omit = omit,
x_device_display_id: int | Omit = omit,
@@ -305,9 +310,11 @@ async def write(
"""Input text
Args:
- wpm: Words per minute for stealth typing.
+ error_rate: Per-character mistake rate for humantouch typing.
+
+ -1 uses server default.
- 0 uses portal default.
+ wpm: Words per minute for stealth typing. 0 uses portal default.
extra_headers: Send extra headers
@@ -332,6 +339,7 @@ async def write(
{
"text": text,
"clear": clear,
+ "error_rate": error_rate,
"stealth": stealth,
"wpm": wpm,
},
diff --git a/src/mobilerun_sdk/resources/devices/language.py b/src/mobilerun_sdk/resources/devices/language.py
new file mode 100644
index 0000000..498c78f
--- /dev/null
+++ b/src/mobilerun_sdk/resources/devices/language.py
@@ -0,0 +1,303 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.devices import language_set_params
+from ...types.devices.language_get_response import LanguageGetResponse
+
+__all__ = ["LanguageResource", "AsyncLanguageResource"]
+
+
+class LanguageResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> LanguageResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return LanguageResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LanguageResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response
+ """
+ return LanguageResourceWithStreamingResponse(self)
+
+ def get(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LanguageGetResponse:
+ """
+ Get device language/locale
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._get(
+ path_template("/devices/{device_id}/language", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LanguageGetResponse,
+ )
+
+ def set(
+ self,
+ device_id: str,
+ *,
+ locale: str,
+ restart: bool | Omit = omit,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Set device language/locale
+
+ Args:
+ locale: BCP-47 locale: a 2–3 letter language tag, optionally followed by a 4-letter
+ script and/or a 2-letter region (e.g. en-US, de-DE, ja-JP, zh-Hans-CN).
+
+ restart: Restart zygote so the locale change takes full effect immediately. Without it,
+ the locale is written but won't fully apply until the next reboot.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._post(
+ path_template("/devices/{device_id}/language", device_id=device_id),
+ body=maybe_transform(
+ {
+ "locale": locale,
+ "restart": restart,
+ },
+ language_set_params.LanguageSetParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncLanguageResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncLanguageResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncLanguageResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLanguageResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response
+ """
+ return AsyncLanguageResourceWithStreamingResponse(self)
+
+ async def get(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LanguageGetResponse:
+ """
+ Get device language/locale
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._get(
+ path_template("/devices/{device_id}/language", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LanguageGetResponse,
+ )
+
+ async def set(
+ self,
+ device_id: str,
+ *,
+ locale: str,
+ restart: bool | Omit = omit,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Set device language/locale
+
+ Args:
+ locale: BCP-47 locale: a 2–3 letter language tag, optionally followed by a 4-letter
+ script and/or a 2-letter region (e.g. en-US, de-DE, ja-JP, zh-Hans-CN).
+
+ restart: Restart zygote so the locale change takes full effect immediately. Without it,
+ the locale is written but won't fully apply until the next reboot.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._post(
+ path_template("/devices/{device_id}/language", device_id=device_id),
+ body=await async_maybe_transform(
+ {
+ "locale": locale,
+ "restart": restart,
+ },
+ language_set_params.LanguageSetParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class LanguageResourceWithRawResponse:
+ def __init__(self, language: LanguageResource) -> None:
+ self._language = language
+
+ self.get = to_raw_response_wrapper(
+ language.get,
+ )
+ self.set = to_raw_response_wrapper(
+ language.set,
+ )
+
+
+class AsyncLanguageResourceWithRawResponse:
+ def __init__(self, language: AsyncLanguageResource) -> None:
+ self._language = language
+
+ self.get = async_to_raw_response_wrapper(
+ language.get,
+ )
+ self.set = async_to_raw_response_wrapper(
+ language.set,
+ )
+
+
+class LanguageResourceWithStreamingResponse:
+ def __init__(self, language: LanguageResource) -> None:
+ self._language = language
+
+ self.get = to_streamed_response_wrapper(
+ language.get,
+ )
+ self.set = to_streamed_response_wrapper(
+ language.set,
+ )
+
+
+class AsyncLanguageResourceWithStreamingResponse:
+ def __init__(self, language: AsyncLanguageResource) -> None:
+ self._language = language
+
+ self.get = async_to_streamed_response_wrapper(
+ language.get,
+ )
+ self.set = async_to_streamed_response_wrapper(
+ language.set,
+ )
diff --git a/src/mobilerun_sdk/resources/devices/location.py b/src/mobilerun_sdk/resources/devices/location.py
index 799ac2c..f9469fc 100644
--- a/src/mobilerun_sdk/resources/devices/location.py
+++ b/src/mobilerun_sdk/resources/devices/location.py
@@ -16,7 +16,7 @@
)
from ..._base_client import make_request_options
from ...types.devices import location_set_params
-from ...types.devices.location_get_response import LocationGetResponse
+from ...types.shared.location import Location
__all__ = ["LocationResource", "AsyncLocationResource"]
@@ -52,7 +52,7 @@ def get(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> LocationGetResponse:
+ ) -> Location:
"""
Get device location
@@ -78,7 +78,7 @@ def get(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=LocationGetResponse,
+ cast_to=Location,
)
def set(
@@ -163,7 +163,7 @@ async def get(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> LocationGetResponse:
+ ) -> Location:
"""
Get device location
@@ -189,7 +189,7 @@ async def get(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=LocationGetResponse,
+ cast_to=Location,
)
async def set(
diff --git a/src/mobilerun_sdk/resources/devices/proxy.py b/src/mobilerun_sdk/resources/devices/proxy.py
index 5cf46cb..08e0c04 100644
--- a/src/mobilerun_sdk/resources/devices/proxy.py
+++ b/src/mobilerun_sdk/resources/devices/proxy.py
@@ -52,7 +52,6 @@ def connect(
smart_ip: bool | Omit = omit,
socks5: proxy_connect_params.Socks5 | Omit = omit,
user: str | Omit = omit,
- wireguard: str | Omit = omit,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -65,12 +64,10 @@ def connect(
Connect proxy
Args:
- name: Proxy name (used for wireguard tunnel name)
+ name: Proxy name
socks5: SOCKS5 proxy configuration (required for socks5).
- wireguard: WireGuard tunnel configuration file content (required for wireguard).
-
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -99,7 +96,6 @@ def connect(
"smart_ip": smart_ip,
"socks5": socks5,
"user": user,
- "wireguard": wireguard,
},
proxy_connect_params.ProxyConnectParams,
),
@@ -222,7 +218,6 @@ async def connect(
smart_ip: bool | Omit = omit,
socks5: proxy_connect_params.Socks5 | Omit = omit,
user: str | Omit = omit,
- wireguard: str | Omit = omit,
x_device_display_id: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -235,12 +230,10 @@ async def connect(
Connect proxy
Args:
- name: Proxy name (used for wireguard tunnel name)
+ name: Proxy name
socks5: SOCKS5 proxy configuration (required for socks5).
- wireguard: WireGuard tunnel configuration file content (required for wireguard).
-
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -269,7 +262,6 @@ async def connect(
"smart_ip": smart_ip,
"socks5": socks5,
"user": user,
- "wireguard": wireguard,
},
proxy_connect_params.ProxyConnectParams,
),
diff --git a/src/mobilerun_sdk/resources/devices/state.py b/src/mobilerun_sdk/resources/devices/state.py
index 032c8c4..789665e 100644
--- a/src/mobilerun_sdk/resources/devices/state.py
+++ b/src/mobilerun_sdk/resources/devices/state.py
@@ -86,6 +86,46 @@ def screenshot(
cast_to=str,
)
+ def time(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> str:
+ """
+ Device time
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._get(
+ path_template("/devices/{device_id}/time", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=str,
+ )
+
def ui(
self,
device_id: str,
@@ -199,6 +239,46 @@ async def screenshot(
cast_to=str,
)
+ async def time(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> str:
+ """
+ Device time
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._get(
+ path_template("/devices/{device_id}/time", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=str,
+ )
+
async def ui(
self,
device_id: str,
@@ -252,6 +332,9 @@ def __init__(self, state: StateResource) -> None:
self.screenshot = to_raw_response_wrapper(
state.screenshot,
)
+ self.time = to_raw_response_wrapper(
+ state.time,
+ )
self.ui = to_raw_response_wrapper(
state.ui,
)
@@ -264,6 +347,9 @@ def __init__(self, state: AsyncStateResource) -> None:
self.screenshot = async_to_raw_response_wrapper(
state.screenshot,
)
+ self.time = async_to_raw_response_wrapper(
+ state.time,
+ )
self.ui = async_to_raw_response_wrapper(
state.ui,
)
@@ -276,6 +362,9 @@ def __init__(self, state: StateResource) -> None:
self.screenshot = to_streamed_response_wrapper(
state.screenshot,
)
+ self.time = to_streamed_response_wrapper(
+ state.time,
+ )
self.ui = to_streamed_response_wrapper(
state.ui,
)
@@ -288,6 +377,9 @@ def __init__(self, state: AsyncStateResource) -> None:
self.screenshot = async_to_streamed_response_wrapper(
state.screenshot,
)
+ self.time = async_to_streamed_response_wrapper(
+ state.time,
+ )
self.ui = async_to_streamed_response_wrapper(
state.ui,
)
diff --git a/src/mobilerun_sdk/resources/devices/tasks.py b/src/mobilerun_sdk/resources/devices/tasks.py
index 62c8d6f..061d2ca 100644
--- a/src/mobilerun_sdk/resources/devices/tasks.py
+++ b/src/mobilerun_sdk/resources/devices/tasks.py
@@ -24,6 +24,8 @@
class TasksResource(SyncAPIResource):
+ """Device Management"""
+
@cached_property
def with_raw_response(self) -> TasksResourceWithRawResponse:
"""
@@ -94,6 +96,8 @@ def list(
class AsyncTasksResource(AsyncAPIResource):
+ """Device Management"""
+
@cached_property
def with_raw_response(self) -> AsyncTasksResourceWithRawResponse:
"""
diff --git a/src/mobilerun_sdk/resources/devices/timezone.py b/src/mobilerun_sdk/resources/devices/timezone.py
new file mode 100644
index 0000000..a136b2f
--- /dev/null
+++ b/src/mobilerun_sdk/resources/devices/timezone.py
@@ -0,0 +1,277 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.devices import timezone_set_params
+from ...types.devices.timezone_get_response import TimezoneGetResponse
+
+__all__ = ["TimezoneResource", "AsyncTimezoneResource"]
+
+
+class TimezoneResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> TimezoneResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return TimezoneResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TimezoneResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response
+ """
+ return TimezoneResourceWithStreamingResponse(self)
+
+ def get(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TimezoneGetResponse:
+ """
+ Get device timezone
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._get(
+ path_template("/devices/{device_id}/timezone", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TimezoneGetResponse,
+ )
+
+ def set(
+ self,
+ device_id: str,
+ *,
+ timezone: str,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Set device timezone
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._post(
+ path_template("/devices/{device_id}/timezone", device_id=device_id),
+ body=maybe_transform({"timezone": timezone}, timezone_set_params.TimezoneSetParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncTimezoneResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncTimezoneResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTimezoneResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTimezoneResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response
+ """
+ return AsyncTimezoneResourceWithStreamingResponse(self)
+
+ async def get(
+ self,
+ device_id: str,
+ *,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TimezoneGetResponse:
+ """
+ Get device timezone
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._get(
+ path_template("/devices/{device_id}/timezone", device_id=device_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TimezoneGetResponse,
+ )
+
+ async def set(
+ self,
+ device_id: str,
+ *,
+ timezone: str,
+ x_device_display_id: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Set device timezone
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not device_id:
+ raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {
+ **strip_not_given(
+ {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._post(
+ path_template("/devices/{device_id}/timezone", device_id=device_id),
+ body=await async_maybe_transform({"timezone": timezone}, timezone_set_params.TimezoneSetParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class TimezoneResourceWithRawResponse:
+ def __init__(self, timezone: TimezoneResource) -> None:
+ self._timezone = timezone
+
+ self.get = to_raw_response_wrapper(
+ timezone.get,
+ )
+ self.set = to_raw_response_wrapper(
+ timezone.set,
+ )
+
+
+class AsyncTimezoneResourceWithRawResponse:
+ def __init__(self, timezone: AsyncTimezoneResource) -> None:
+ self._timezone = timezone
+
+ self.get = async_to_raw_response_wrapper(
+ timezone.get,
+ )
+ self.set = async_to_raw_response_wrapper(
+ timezone.set,
+ )
+
+
+class TimezoneResourceWithStreamingResponse:
+ def __init__(self, timezone: TimezoneResource) -> None:
+ self._timezone = timezone
+
+ self.get = to_streamed_response_wrapper(
+ timezone.get,
+ )
+ self.set = to_streamed_response_wrapper(
+ timezone.set,
+ )
+
+
+class AsyncTimezoneResourceWithStreamingResponse:
+ def __init__(self, timezone: AsyncTimezoneResource) -> None:
+ self._timezone = timezone
+
+ self.get = async_to_streamed_response_wrapper(
+ timezone.get,
+ )
+ self.set = async_to_streamed_response_wrapper(
+ timezone.set,
+ )
diff --git a/src/mobilerun_sdk/resources/hooks.py b/src/mobilerun_sdk/resources/hooks.py
index c86af7d..185b05e 100644
--- a/src/mobilerun_sdk/resources/hooks.py
+++ b/src/mobilerun_sdk/resources/hooks.py
@@ -31,8 +31,6 @@
class HooksResource(SyncAPIResource):
- """Webhooks API"""
-
@cached_property
def with_raw_response(self) -> HooksResourceWithRawResponse:
"""
@@ -316,8 +314,6 @@ def unsubscribe(
class AsyncHooksResource(AsyncAPIResource):
- """Webhooks API"""
-
@cached_property
def with_raw_response(self) -> AsyncHooksResourceWithRawResponse:
"""
diff --git a/src/mobilerun_sdk/resources/models.py b/src/mobilerun_sdk/resources/models.py
index d1edbdd..4bb2764 100644
--- a/src/mobilerun_sdk/resources/models.py
+++ b/src/mobilerun_sdk/resources/models.py
@@ -20,6 +20,8 @@
class ModelsResource(SyncAPIResource):
+ """LLM Models"""
+
@cached_property
def with_raw_response(self) -> ModelsResourceWithRawResponse:
"""
@@ -60,6 +62,8 @@ def list(
class AsyncModelsResource(AsyncAPIResource):
+ """LLM Models"""
+
@cached_property
def with_raw_response(self) -> AsyncModelsResourceWithRawResponse:
"""
diff --git a/src/mobilerun_sdk/resources/proxies.py b/src/mobilerun_sdk/resources/proxies.py
index b3cf10c..cd4235f 100644
--- a/src/mobilerun_sdk/resources/proxies.py
+++ b/src/mobilerun_sdk/resources/proxies.py
@@ -28,6 +28,8 @@
class ProxiesResource(SyncAPIResource):
+ """Network Proxies"""
+
@cached_property
def with_raw_response(self) -> ProxiesResourceWithRawResponse:
"""
@@ -349,6 +351,8 @@ def delete(
class AsyncProxiesResource(AsyncAPIResource):
+ """Network Proxies"""
+
@cached_property
def with_raw_response(self) -> AsyncProxiesResourceWithRawResponse:
"""
diff --git a/src/mobilerun_sdk/types/__init__.py b/src/mobilerun_sdk/types/__init__.py
index 6a946aa..ba767ee 100644
--- a/src/mobilerun_sdk/types/__init__.py
+++ b/src/mobilerun_sdk/types/__init__.py
@@ -6,6 +6,8 @@
from .device import Device as Device
from .shared import (
Meta as Meta,
+ Socks5 as Socks5,
+ Location as Location,
DeviceSpec as DeviceSpec,
Pagination as Pagination,
DeviceCarrier as DeviceCarrier,
@@ -13,7 +15,6 @@
PaginationMeta as PaginationMeta,
DeviceIdentifiers as DeviceIdentifiers,
)
-from .carrier import Carrier as Carrier
from .profile import Profile as Profile
from .task_status import TaskStatus as TaskStatus
from .proxy_config import ProxyConfig as ProxyConfig
@@ -31,6 +32,7 @@
from .task_list_response import TaskListResponse as TaskListResponse
from .task_stop_response import TaskStopResponse as TaskStopResponse
from .agent_list_response import AgentListResponse as AgentListResponse
+from .app_delete_response import AppDeleteResponse as AppDeleteResponse
from .carrier_list_params import CarrierListParams as CarrierListParams
from .hook_perform_params import HookPerformParams as HookPerformParams
from .model_list_response import ModelListResponse as ModelListResponse
@@ -42,6 +44,7 @@
from .device_create_params import DeviceCreateParams as DeviceCreateParams
from .device_list_response import DeviceListResponse as DeviceListResponse
from .hook_update_response import HookUpdateResponse as HookUpdateResponse
+from .app_retrieve_response import AppRetrieveResponse as AppRetrieveResponse
from .carrier_create_params import CarrierCreateParams as CarrierCreateParams
from .carrier_list_response import CarrierListResponse as CarrierListResponse
from .carrier_lookup_params import CarrierLookupParams as CarrierLookupParams
@@ -59,17 +62,26 @@
from .device_set_name_params import DeviceSetNameParams as DeviceSetNameParams
from .hook_retrieve_response import HookRetrieveResponse as HookRetrieveResponse
from .task_retrieve_response import TaskRetrieveResponse as TaskRetrieveResponse
+from .carrier_create_response import CarrierCreateResponse as CarrierCreateResponse
from .carrier_delete_response import CarrierDeleteResponse as CarrierDeleteResponse
+from .carrier_lookup_response import CarrierLookupResponse as CarrierLookupResponse
+from .carrier_update_response import CarrierUpdateResponse as CarrierUpdateResponse
from .device_terminate_params import DeviceTerminateParams as DeviceTerminateParams
from .hook_subscribe_response import HookSubscribeResponse as HookSubscribeResponse
from .profile_delete_response import ProfileDeleteResponse as ProfileDeleteResponse
from .proxy_retrieve_response import ProxyRetrieveResponse as ProxyRetrieveResponse
+from .app_mark_failed_response import AppMarkFailedResponse as AppMarkFailedResponse
from .credential_list_response import CredentialListResponse as CredentialListResponse
from .task_get_status_response import TaskGetStatusResponse as TaskGetStatusResponse
from .task_run_streamed_params import TaskRunStreamedParams as TaskRunStreamedParams
from .task_send_message_params import TaskSendMessageParams as TaskSendMessageParams
+from .carrier_retrieve_response import CarrierRetrieveResponse as CarrierRetrieveResponse
from .hook_unsubscribe_response import HookUnsubscribeResponse as HookUnsubscribeResponse
from .package_credentials_param import PackageCredentialsParam as PackageCredentialsParam
from .task_send_message_response import TaskSendMessageResponse as TaskSendMessageResponse
+from .app_confirm_upload_response import AppConfirmUploadResponse as AppConfirmUploadResponse
+from .device_fingerprint_response import DeviceFingerprintResponse as DeviceFingerprintResponse
from .task_get_trajectory_response import TaskGetTrajectoryResponse as TaskGetTrajectoryResponse
from .hook_get_sample_data_response import HookGetSampleDataResponse as HookGetSampleDataResponse
+from .app_create_signed_upload_url_params import AppCreateSignedUploadURLParams as AppCreateSignedUploadURLParams
+from .app_create_signed_upload_url_response import AppCreateSignedUploadURLResponse as AppCreateSignedUploadURLResponse
diff --git a/src/mobilerun_sdk/types/app_confirm_upload_response.py b/src/mobilerun_sdk/types/app_confirm_upload_response.py
new file mode 100644
index 0000000..da579dc
--- /dev/null
+++ b/src/mobilerun_sdk/types/app_confirm_upload_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["AppConfirmUploadResponse"]
+
+
+class AppConfirmUploadResponse(BaseModel):
+ message: str
+
+ success: Literal[True]
diff --git a/src/mobilerun_sdk/types/app_create_signed_upload_url_params.py b/src/mobilerun_sdk/types/app_create_signed_upload_url_params.py
new file mode 100644
index 0000000..a374c4b
--- /dev/null
+++ b/src/mobilerun_sdk/types/app_create_signed_upload_url_params.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["AppCreateSignedUploadURLParams", "File"]
+
+
+class AppCreateSignedUploadURLParams(TypedDict, total=False):
+ bundle_id: Required[Annotated[str, PropertyInfo(alias="bundleId")]]
+
+ display_name: Required[Annotated[str, PropertyInfo(alias="displayName")]]
+
+ files: Required[Iterable[File]]
+
+ size_bytes: Required[Annotated[float, PropertyInfo(alias="sizeBytes")]]
+
+ version_code: Required[Annotated[float, PropertyInfo(alias="versionCode")]]
+
+ version_name: Required[Annotated[str, PropertyInfo(alias="versionName")]]
+
+ country: str
+ """Country code for Search Results"""
+
+ description: str
+
+ developer_name: Annotated[str, PropertyInfo(alias="developerName")]
+
+ icon_url: Annotated[str, PropertyInfo(alias="iconURL")]
+
+ platform: Literal["android", "ios"]
+
+ target_sdk: Annotated[float, PropertyInfo(alias="targetSdk")]
+
+
+class File(TypedDict, total=False):
+ content_type: Required[Annotated[str, PropertyInfo(alias="contentType")]]
+
+ file_name: Required[Annotated[str, PropertyInfo(alias="fileName")]]
diff --git a/src/mobilerun_sdk/types/app_create_signed_upload_url_response.py b/src/mobilerun_sdk/types/app_create_signed_upload_url_response.py
new file mode 100644
index 0000000..8d15581
--- /dev/null
+++ b/src/mobilerun_sdk/types/app_create_signed_upload_url_response.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["AppCreateSignedUploadURLResponse", "R2UploadURL"]
+
+
+class R2UploadURL(BaseModel):
+ file_name: str = FieldInfo(alias="fileName")
+
+ r2_upload_url: str = FieldInfo(alias="r2UploadUrl")
+
+
+class AppCreateSignedUploadURLResponse(BaseModel):
+ app_id: str = FieldInfo(alias="appId")
+ """App ID in the database"""
+
+ r2_upload_urls: List[R2UploadURL] = FieldInfo(alias="r2UploadUrls")
+ """Pre-signed Cloudflare R2 URLs for uploading app files"""
+
+ version_id: str = FieldInfo(alias="versionId")
+ """App version ID in the database"""
diff --git a/src/mobilerun_sdk/types/app_delete_response.py b/src/mobilerun_sdk/types/app_delete_response.py
new file mode 100644
index 0000000..5dce5fa
--- /dev/null
+++ b/src/mobilerun_sdk/types/app_delete_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["AppDeleteResponse"]
+
+
+class AppDeleteResponse(BaseModel):
+ message: str
+
+ success: Literal[True]
diff --git a/src/mobilerun_sdk/types/app_list_params.py b/src/mobilerun_sdk/types/app_list_params.py
index 01fe905..319c937 100644
--- a/src/mobilerun_sdk/types/app_list_params.py
+++ b/src/mobilerun_sdk/types/app_list_params.py
@@ -16,8 +16,10 @@ class AppListParams(TypedDict, total=False):
page_size: Annotated[int, PropertyInfo(alias="pageSize")]
+ platform: Literal["all", "android", "ios"]
+
query: str
sort_by: Annotated[Literal["createdAt", "name"], PropertyInfo(alias="sortBy")]
- source: Literal["all", "uploaded", "store", "queued"]
+ status: Literal["all", "queued", "available", "failed"]
diff --git a/src/mobilerun_sdk/types/app_list_response.py b/src/mobilerun_sdk/types/app_list_response.py
index e211771..f7e2394 100644
--- a/src/mobilerun_sdk/types/app_list_response.py
+++ b/src/mobilerun_sdk/types/app_list_response.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Dict, List, Union, Optional
+from typing import List, Optional
from datetime import datetime
from typing_extensions import Literal
@@ -9,25 +9,23 @@
from .._models import BaseModel
from .shared.pagination import Pagination
-__all__ = ["AppListResponse", "Count", "Item"]
+__all__ = ["AppListResponse", "Count", "Item", "ItemVersion"]
class Count(BaseModel):
available_count: float = FieldInfo(alias="availableCount")
- queued_count: float = FieldInfo(alias="queuedCount")
+ failed_count: float = FieldInfo(alias="failedCount")
- store_count: float = FieldInfo(alias="storeCount")
+ queued_count: float = FieldInfo(alias="queuedCount")
total_count: float = FieldInfo(alias="totalCount")
- upload_count: float = FieldInfo(alias="uploadCount")
-
-class Item(BaseModel):
+class ItemVersion(BaseModel):
id: str
- category_name: Optional[str] = FieldInfo(alias="categoryName", default=None)
+ app_id: str = FieldInfo(alias="appId")
country: Literal[
"AF",
@@ -286,47 +284,45 @@ class Item(BaseModel):
created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None)
- description: Optional[str] = None
+ queued_at: Optional[datetime] = FieldInfo(alias="queuedAt", default=None)
- developer_name: Optional[str] = FieldInfo(alias="developerName", default=None)
+ size_bytes: Optional[int] = FieldInfo(alias="sizeBytes", default=None)
- display_name: str = FieldInfo(alias="displayName")
+ source: Literal["user", "system", "portal"]
- expected_files: Union[str, float, bool, Dict[str, Optional[object]], List[Optional[object]], None] = FieldInfo(
- alias="expectedFiles", default=None
- )
+ status: Literal["queued", "available", "failed"]
- icon_url: str = FieldInfo(alias="iconURL")
+ target_sdk: Optional[int] = FieldInfo(alias="targetSdk", default=None)
- package_name: str = FieldInfo(alias="packageName")
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
- privacy_policy_url: Optional[str] = FieldInfo(alias="privacyPolicyUrl", default=None)
+ user_id: Optional[str] = FieldInfo(alias="userId", default=None)
- queued_at: Optional[datetime] = FieldInfo(alias="queuedAt", default=None)
+ version_code: int = FieldInfo(alias="versionCode")
- rating_count: Optional[int] = FieldInfo(alias="ratingCount", default=None)
+ version_name: str = FieldInfo(alias="versionName")
- rating_score: Optional[str] = FieldInfo(alias="ratingScore", default=None)
- size_bytes: Optional[int] = FieldInfo(alias="sizeBytes", default=None)
+class Item(BaseModel):
+ id: str
- source: Literal["uploaded", "store"]
+ bundle_id: str = FieldInfo(alias="bundleId")
- status: Literal["queued", "available", "failed"]
+ created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None)
- stealth_tier: Optional[Literal["tier1", "tier2", "tier3"]] = FieldInfo(alias="stealthTier", default=None)
+ description: Optional[str] = None
- target_sdk: Optional[int] = FieldInfo(alias="targetSdk", default=None)
+ developer_name: Optional[str] = FieldInfo(alias="developerName", default=None)
- type: Literal["android", "ios"]
+ display_name: str = FieldInfo(alias="displayName")
- updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
+ icon_url: str = FieldInfo(alias="iconURL")
- user_id: Optional[str] = FieldInfo(alias="userId", default=None)
+ platform: Literal["android", "ios"]
- version_code: int = FieldInfo(alias="versionCode")
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
- version_name: str = FieldInfo(alias="versionName")
+ version: ItemVersion
class AppListResponse(BaseModel):
diff --git a/src/mobilerun_sdk/types/app_mark_failed_response.py b/src/mobilerun_sdk/types/app_mark_failed_response.py
new file mode 100644
index 0000000..4df598b
--- /dev/null
+++ b/src/mobilerun_sdk/types/app_mark_failed_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["AppMarkFailedResponse"]
+
+
+class AppMarkFailedResponse(BaseModel):
+ message: str
+
+ success: Literal[True]
diff --git a/src/mobilerun_sdk/types/app_retrieve_response.py b/src/mobilerun_sdk/types/app_retrieve_response.py
new file mode 100644
index 0000000..57fd551
--- /dev/null
+++ b/src/mobilerun_sdk/types/app_retrieve_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["AppRetrieveResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+
+ bundle_id: str = FieldInfo(alias="bundleId")
+
+ created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None)
+
+ description: Optional[str] = None
+
+ developer_name: Optional[str] = FieldInfo(alias="developerName", default=None)
+
+ display_name: str = FieldInfo(alias="displayName")
+
+ icon_url: str = FieldInfo(alias="iconURL")
+
+ platform: Literal["android", "ios"]
+
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
+
+
+class AppRetrieveResponse(BaseModel):
+ data: Data
diff --git a/src/mobilerun_sdk/types/carrier.py b/src/mobilerun_sdk/types/carrier_create_response.py
similarity index 90%
rename from src/mobilerun_sdk/types/carrier.py
rename to src/mobilerun_sdk/types/carrier_create_response.py
index 7289eec..643be23 100644
--- a/src/mobilerun_sdk/types/carrier.py
+++ b/src/mobilerun_sdk/types/carrier_create_response.py
@@ -7,10 +7,10 @@
from .._models import BaseModel
-__all__ = ["Carrier"]
+__all__ = ["CarrierCreateResponse"]
-class Carrier(BaseModel):
+class CarrierCreateResponse(BaseModel):
id: int
company: str
diff --git a/src/mobilerun_sdk/types/carrier_list_response.py b/src/mobilerun_sdk/types/carrier_list_response.py
index 8d6b97b..098754a 100644
--- a/src/mobilerun_sdk/types/carrier_list_response.py
+++ b/src/mobilerun_sdk/types/carrier_list_response.py
@@ -1,18 +1,59 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from typing import List, Optional
+from datetime import datetime
from pydantic import Field as FieldInfo
-from .carrier import Carrier
from .._models import BaseModel
from .shared.meta import Meta
-__all__ = ["CarrierListResponse"]
+__all__ = ["CarrierListResponse", "Item"]
+
+
+class Item(BaseModel):
+ id: int
+
+ company: str
+
+ country: str
+
+ country_code: str
+
+ country_iso: str
+
+ created_at: datetime
+
+ detail_url: str
+
+ gsm_bands: str
+
+ lte_bands: str
+
+ mcc: str
+
+ mnc: str
+
+ mobile_prefix: str
+
+ nsn_size: str
+
+ number_format: str
+
+ operator: str
+
+ protocols: str
+
+ umts_bands: str
+
+ website: str
+
+ schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
+ """A URL to the JSON Schema for this object."""
class CarrierListResponse(BaseModel):
- items: Optional[List[Carrier]] = None
+ items: Optional[List[Item]] = None
pagination: Meta
diff --git a/src/mobilerun_sdk/types/carrier_lookup_response.py b/src/mobilerun_sdk/types/carrier_lookup_response.py
new file mode 100644
index 0000000..925e0d6
--- /dev/null
+++ b/src/mobilerun_sdk/types/carrier_lookup_response.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CarrierLookupResponse"]
+
+
+class CarrierLookupResponse(BaseModel):
+ id: int
+
+ company: str
+
+ country: str
+
+ country_code: str
+
+ country_iso: str
+
+ created_at: datetime
+
+ detail_url: str
+
+ gsm_bands: str
+
+ lte_bands: str
+
+ mcc: str
+
+ mnc: str
+
+ mobile_prefix: str
+
+ nsn_size: str
+
+ number_format: str
+
+ operator: str
+
+ protocols: str
+
+ umts_bands: str
+
+ website: str
+
+ schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
+ """A URL to the JSON Schema for this object."""
diff --git a/src/mobilerun_sdk/types/carrier_retrieve_response.py b/src/mobilerun_sdk/types/carrier_retrieve_response.py
new file mode 100644
index 0000000..f4bf58c
--- /dev/null
+++ b/src/mobilerun_sdk/types/carrier_retrieve_response.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CarrierRetrieveResponse"]
+
+
+class CarrierRetrieveResponse(BaseModel):
+ id: int
+
+ company: str
+
+ country: str
+
+ country_code: str
+
+ country_iso: str
+
+ created_at: datetime
+
+ detail_url: str
+
+ gsm_bands: str
+
+ lte_bands: str
+
+ mcc: str
+
+ mnc: str
+
+ mobile_prefix: str
+
+ nsn_size: str
+
+ number_format: str
+
+ operator: str
+
+ protocols: str
+
+ umts_bands: str
+
+ website: str
+
+ schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
+ """A URL to the JSON Schema for this object."""
diff --git a/src/mobilerun_sdk/types/carrier_update_response.py b/src/mobilerun_sdk/types/carrier_update_response.py
new file mode 100644
index 0000000..ba0d7ef
--- /dev/null
+++ b/src/mobilerun_sdk/types/carrier_update_response.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CarrierUpdateResponse"]
+
+
+class CarrierUpdateResponse(BaseModel):
+ id: int
+
+ company: str
+
+ country: str
+
+ country_code: str
+
+ country_iso: str
+
+ created_at: datetime
+
+ detail_url: str
+
+ gsm_bands: str
+
+ lte_bands: str
+
+ mcc: str
+
+ mnc: str
+
+ mobile_prefix: str
+
+ nsn_size: str
+
+ number_format: str
+
+ operator: str
+
+ protocols: str
+
+ umts_bands: str
+
+ website: str
+
+ schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
+ """A URL to the JSON Schema for this object."""
diff --git a/src/mobilerun_sdk/types/device_create_params.py b/src/mobilerun_sdk/types/device_create_params.py
index 751f16e..a631b9c 100644
--- a/src/mobilerun_sdk/types/device_create_params.py
+++ b/src/mobilerun_sdk/types/device_create_params.py
@@ -3,45 +3,54 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Literal, Required, Annotated, TypedDict
+from typing_extensions import Literal, Annotated, TypedDict
from .._types import SequenceNotStr
from .._utils import PropertyInfo
+from .shared_params.socks5 import Socks5
+from .shared_params.location import Location
from .shared_params.device_carrier import DeviceCarrier
from .shared_params.device_identifiers import DeviceIdentifiers
-__all__ = ["DeviceCreateParams", "Proxy", "ProxySocks5"]
+__all__ = ["DeviceCreateParams", "Proxy"]
class DeviceCreateParams(TypedDict, total=False):
+ query_country: Annotated[str, PropertyInfo(alias="country")]
+ """ISO 3166-1 alpha-2 country code.
+
+ If omitted the system picks the country with the most availability.
+ """
+
device_type: Annotated[
- Literal[
- "dedicated_physical_device", "dedicated_premium_device", "dedicated_emulated_device", "dedicated_ios_device"
- ],
+ Literal["dedicated_physical_device", "dedicated_premium_device", "dedicated_ios_device"],
PropertyInfo(alias="deviceType"),
]
+ profile_id: Annotated[str, PropertyInfo(alias="profileId")]
+ """Profile ID to use as device spec"""
+
+ android_version: Annotated[int, PropertyInfo(alias="androidVersion")]
+
apps: Optional[SequenceNotStr[str]]
carrier: DeviceCarrier
+ body_country: Annotated[str, PropertyInfo(alias="country")]
+
files: Optional[SequenceNotStr[str]]
identifiers: DeviceIdentifiers
- name: str
-
- proxy: Proxy
-
+ locale: str
-class ProxySocks5(TypedDict, total=False):
- host: Required[str]
+ location: Location
- password: Required[str]
+ name: str
- port: Required[int]
+ proxy: Proxy
- user: Required[str]
+ timezone: str
class Proxy(TypedDict, total=False):
@@ -49,6 +58,4 @@ class Proxy(TypedDict, total=False):
smart_ip: Annotated[bool, PropertyInfo(alias="smartIp")]
- socks5: ProxySocks5
-
- wireguard: str
+ socks5: Socks5
diff --git a/src/mobilerun_sdk/types/device_fingerprint_response.py b/src/mobilerun_sdk/types/device_fingerprint_response.py
new file mode 100644
index 0000000..4f8f410
--- /dev/null
+++ b/src/mobilerun_sdk/types/device_fingerprint_response.py
@@ -0,0 +1,48 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .shared.device_carrier import DeviceCarrier
+from .shared.device_identifiers import DeviceIdentifiers
+
+__all__ = ["DeviceFingerprintResponse", "Display", "Model"]
+
+
+class Display(BaseModel):
+ density_dpi: Optional[int] = FieldInfo(alias="densityDpi", default=None)
+
+ height: Optional[int] = None
+
+ width: Optional[int] = None
+
+
+class Model(BaseModel):
+ aosp_version: Optional[str] = FieldInfo(alias="aospVersion", default=None)
+
+ brand: Optional[str] = None
+
+ device: Optional[str] = None
+
+ hardware: Optional[str] = None
+
+ image_repository: Optional[str] = FieldInfo(alias="imageRepository", default=None)
+
+ manufacturer: Optional[str] = None
+
+ model: Optional[str] = None
+
+
+class DeviceFingerprintResponse(BaseModel):
+ carrier: DeviceCarrier
+
+ display: Display
+
+ identifiers: DeviceIdentifiers
+
+ model: Model
+
+ schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
+ """A URL to the JSON Schema for this object."""
diff --git a/src/mobilerun_sdk/types/device_list_params.py b/src/mobilerun_sdk/types/device_list_params.py
index 037f565..fcec1bb 100644
--- a/src/mobilerun_sdk/types/device_list_params.py
+++ b/src/mobilerun_sdk/types/device_list_params.py
@@ -26,9 +26,19 @@ class DeviceListParams(TypedDict, total=False):
provider_id: Annotated[str, PropertyInfo(alias="providerId")]
state: Optional[
- List[Literal["creating", "assigned", "ready", "rebooting", "migrating", "terminated", "maintenance", "unknown"]]
+ List[
+ Literal[
+ "creating",
+ "assigned",
+ "ready",
+ "rebooting",
+ "migrating",
+ "resetting",
+ "terminated",
+ "maintenance",
+ "unknown",
+ ]
+ ]
]
- type: Literal[
- "dedicated_physical_device", "dedicated_premium_device", "dedicated_emulated_device", "dedicated_ios_device"
- ]
+ type: Literal["dedicated_physical_device", "dedicated_premium_device", "dedicated_ios_device"]
diff --git a/src/mobilerun_sdk/types/devices/__init__.py b/src/mobilerun_sdk/types/devices/__init__.py
index c5f89e4..ea98848 100644
--- a/src/mobilerun_sdk/types/devices/__init__.py
+++ b/src/mobilerun_sdk/types/devices/__init__.py
@@ -20,25 +20,28 @@
from .file_list_response import FileListResponse as FileListResponse
from .file_upload_params import FileUploadParams as FileUploadParams
from .task_list_response import TaskListResponse as TaskListResponse
-from .time_time_response import TimeTimeResponse as TimeTimeResponse
from .action_swipe_params import ActionSwipeParams as ActionSwipeParams
from .keyboard_key_params import KeyboardKeyParams as KeyboardKeyParams
+from .language_set_params import LanguageSetParams as LanguageSetParams
from .location_set_params import LocationSetParams as LocationSetParams
from .package_list_params import PackageListParams as PackageListParams
+from .state_time_response import StateTimeResponse as StateTimeResponse
+from .timezone_set_params import TimezoneSetParams as TimezoneSetParams
from .action_global_params import ActionGlobalParams as ActionGlobalParams
from .esim_activate_params import EsimActivateParams as EsimActivateParams
+from .esim_status_response import EsimStatusResponse as EsimStatusResponse
from .file_download_params import FileDownloadParams as FileDownloadParams
from .proxy_connect_params import ProxyConnectParams as ProxyConnectParams
from .keyboard_write_params import KeyboardWriteParams as KeyboardWriteParams
-from .location_get_response import LocationGetResponse as LocationGetResponse
+from .language_get_response import LanguageGetResponse as LanguageGetResponse
from .package_list_response import PackageListResponse as PackageListResponse
from .profile_update_params import ProfileUpdateParams as ProfileUpdateParams
from .proxy_status_response import ProxyStatusResponse as ProxyStatusResponse
+from .timezone_get_response import TimezoneGetResponse as TimezoneGetResponse
from .esim_activate_response import EsimActivateResponse as EsimActivateResponse
from .file_download_response import FileDownloadResponse as FileDownloadResponse
-from .time_timezone_response import TimeTimezoneResponse as TimeTimezoneResponse
+from .esim_set_roaming_params import EsimSetRoamingParams as EsimSetRoamingParams
from .state_screenshot_params import StateScreenshotParams as StateScreenshotParams
-from .time_set_timezone_params import TimeSetTimezoneParams as TimeSetTimezoneParams
from .state_screenshot_response import StateScreenshotResponse as StateScreenshotResponse
from .action_overlay_visible_response import ActionOverlayVisibleResponse as ActionOverlayVisibleResponse
from .action_set_overlay_visible_params import ActionSetOverlayVisibleParams as ActionSetOverlayVisibleParams
diff --git a/src/mobilerun_sdk/types/devices/app_install_params.py b/src/mobilerun_sdk/types/devices/app_install_params.py
index 915231c..8db67fe 100644
--- a/src/mobilerun_sdk/types/devices/app_install_params.py
+++ b/src/mobilerun_sdk/types/devices/app_install_params.py
@@ -2,14 +2,32 @@
from __future__ import annotations
-from typing_extensions import Required, Annotated, TypedDict
+from typing import Union
+from typing_extensions import Required, Annotated, TypeAlias, TypedDict
from ..._utils import PropertyInfo
-__all__ = ["AppInstallParams"]
+__all__ = ["AppInstallParams", "Variant0", "Variant1"]
-class AppInstallParams(TypedDict, total=False):
+class Variant0(TypedDict, total=False):
+ bundle_id: Required[Annotated[str, PropertyInfo(alias="bundleId")]]
+ """iOS bundle identifier (e.g. com.example.app)"""
+
+ package_name: Annotated[str, PropertyInfo(alias="packageName")]
+ """Android package name (e.g. com.example.app)"""
+
+ x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
+
+
+class Variant1(TypedDict, total=False):
package_name: Required[Annotated[str, PropertyInfo(alias="packageName")]]
+ """Android package name (e.g. com.example.app)"""
+
+ bundle_id: Annotated[str, PropertyInfo(alias="bundleId")]
+ """iOS bundle identifier (e.g. com.example.app)"""
x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
+
+
+AppInstallParams: TypeAlias = Union[Variant0, Variant1]
diff --git a/src/mobilerun_sdk/types/devices/esim/__init__.py b/src/mobilerun_sdk/types/devices/esim/__init__.py
new file mode 100644
index 0000000..7fb2236
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/esim/__init__.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .apn_create_params import ApnCreateParams as ApnCreateParams
+from .apn_list_response import ApnListResponse as ApnListResponse
+from .apn_select_params import ApnSelectParams as ApnSelectParams
diff --git a/src/mobilerun_sdk/types/devices/esim/apn_create_params.py b/src/mobilerun_sdk/types/devices/esim/apn_create_params.py
new file mode 100644
index 0000000..ffb2434
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/esim/apn_create_params.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["ApnCreateParams"]
+
+
+class ApnCreateParams(TypedDict, total=False):
+ apn: Required[str]
+
+ mcc: Required[str]
+
+ mnc: Required[str]
+
+ name: Required[str]
+
+ protocol: Required[str]
+
+ roaming_protocol: Required[Annotated[str, PropertyInfo(alias="roamingProtocol")]]
+
+ sub_id: Required[Annotated[int, PropertyInfo(alias="subId")]]
+
+ type: Required[str]
+
+ x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/devices/esim/apn_list_response.py b/src/mobilerun_sdk/types/devices/esim/apn_list_response.py
new file mode 100644
index 0000000..41bd145
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/esim/apn_list_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = ["ApnListResponse", "ApnListResponseItem"]
+
+
+class ApnListResponseItem(BaseModel):
+ id: int
+
+ apn: str
+
+ is_preferred: bool = FieldInfo(alias="isPreferred")
+
+ mcc: str
+
+ mnc: str
+
+ name: str
+
+ protocol: str
+
+ roaming_protocol: str = FieldInfo(alias="roamingProtocol")
+
+ sub_id: int = FieldInfo(alias="subId")
+
+ type: str
+
+
+ApnListResponse: TypeAlias = List[ApnListResponseItem]
diff --git a/src/mobilerun_sdk/types/devices/esim/apn_select_params.py b/src/mobilerun_sdk/types/devices/esim/apn_select_params.py
new file mode 100644
index 0000000..ca44100
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/esim/apn_select_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["ApnSelectParams"]
+
+
+class ApnSelectParams(TypedDict, total=False):
+ apn_id: Required[Annotated[int, PropertyInfo(alias="apnId")]]
+
+ sub_id: Required[Annotated[int, PropertyInfo(alias="subId")]]
+
+ x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/devices/esim_activate_params.py b/src/mobilerun_sdk/types/devices/esim_activate_params.py
index 643eebc..e5c9e74 100644
--- a/src/mobilerun_sdk/types/devices/esim_activate_params.py
+++ b/src/mobilerun_sdk/types/devices/esim_activate_params.py
@@ -12,8 +12,15 @@
class EsimActivateParams(TypedDict, total=False):
enable: Required[bool]
- matching_id: Required[Annotated[str, PropertyInfo(alias="matchingId")]]
-
sm_dp_addr: Required[Annotated[str, PropertyInfo(alias="smDpAddr")]]
+ confirmation_code: Annotated[str, PropertyInfo(alias="confirmationCode")]
+ """Optional carrier-issued confirmation code (the 4th LPA segment).
+
+ Required only for plans whose SM-DP+ challenges the device for one. Requires
+ matchingId — the LPA spec only interprets segment 4 when segment 3 is present.
+ """
+
+ matching_id: Annotated[str, PropertyInfo(alias="matchingId")]
+
x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/devices/esim_set_roaming_params.py b/src/mobilerun_sdk/types/devices/esim_set_roaming_params.py
new file mode 100644
index 0000000..218544c
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/esim_set_roaming_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["EsimSetRoamingParams"]
+
+
+class EsimSetRoamingParams(TypedDict, total=False):
+ enabled: Required[bool]
+
+ x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/devices/esim_status_response.py b/src/mobilerun_sdk/types/devices/esim_status_response.py
new file mode 100644
index 0000000..9a42b6e
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/esim_status_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["EsimStatusResponse", "EsimStatusResponseItem"]
+
+
+class EsimStatusResponseItem(BaseModel):
+ carrier: str
+
+ data_roaming_enabled: bool = FieldInfo(alias="dataRoamingEnabled")
+
+ data_state: str = FieldInfo(alias="dataState")
+
+ is_roaming: bool = FieldInfo(alias="isRoaming")
+
+ mobile_data_enabled: bool = FieldInfo(alias="mobileDataEnabled")
+
+ network_type: str = FieldInfo(alias="networkType")
+
+ operator: str
+
+ phone_type: str = FieldInfo(alias="phoneType")
+
+ sim_state: str = FieldInfo(alias="simState")
+
+ sub_id: int = FieldInfo(alias="subId")
+
+
+EsimStatusResponse: TypeAlias = List[EsimStatusResponseItem]
diff --git a/src/mobilerun_sdk/types/devices/keyboard_write_params.py b/src/mobilerun_sdk/types/devices/keyboard_write_params.py
index f0fa72f..c1340d7 100644
--- a/src/mobilerun_sdk/types/devices/keyboard_write_params.py
+++ b/src/mobilerun_sdk/types/devices/keyboard_write_params.py
@@ -14,6 +14,9 @@ class KeyboardWriteParams(TypedDict, total=False):
clear: bool
+ error_rate: Annotated[float, PropertyInfo(alias="errorRate")]
+ """Per-character mistake rate for humantouch typing. -1 uses server default."""
+
stealth: bool
wpm: int
diff --git a/src/mobilerun_sdk/types/devices/language_get_response.py b/src/mobilerun_sdk/types/devices/language_get_response.py
new file mode 100644
index 0000000..22c2c78
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/language_get_response.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["LanguageGetResponse"]
+
+
+class LanguageGetResponse(BaseModel):
+ locale: str
+
+ schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
+ """A URL to the JSON Schema for this object."""
diff --git a/src/mobilerun_sdk/types/devices/language_set_params.py b/src/mobilerun_sdk/types/devices/language_set_params.py
new file mode 100644
index 0000000..1cfb643
--- /dev/null
+++ b/src/mobilerun_sdk/types/devices/language_set_params.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["LanguageSetParams"]
+
+
+class LanguageSetParams(TypedDict, total=False):
+ locale: Required[str]
+ """
+ BCP-47 locale: a 2–3 letter language tag, optionally followed by a 4-letter
+ script and/or a 2-letter region (e.g. en-US, de-DE, ja-JP, zh-Hans-CN).
+ """
+
+ restart: bool
+ """Restart zygote so the locale change takes full effect immediately.
+
+ Without it, the locale is written but won't fully apply until the next reboot.
+ """
+
+ x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/devices/proxy_connect_params.py b/src/mobilerun_sdk/types/devices/proxy_connect_params.py
index d177e00..5afc504 100644
--- a/src/mobilerun_sdk/types/devices/proxy_connect_params.py
+++ b/src/mobilerun_sdk/types/devices/proxy_connect_params.py
@@ -13,7 +13,7 @@ class ProxyConnectParams(TypedDict, total=False):
host: str
name: str
- """Proxy name (used for wireguard tunnel name)"""
+ """Proxy name"""
password: str
@@ -26,9 +26,6 @@ class ProxyConnectParams(TypedDict, total=False):
user: str
- wireguard: str
- """WireGuard tunnel configuration file content (required for wireguard)."""
-
x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/devices/proxy_status_response.py b/src/mobilerun_sdk/types/devices/proxy_status_response.py
index b457c9d..668f612 100644
--- a/src/mobilerun_sdk/types/devices/proxy_status_response.py
+++ b/src/mobilerun_sdk/types/devices/proxy_status_response.py
@@ -16,7 +16,7 @@ class ProxyStatusResponse(BaseModel):
"""Active proxy name"""
protocol: Optional[str] = None
- """Active proxy protocol (socks5 or wireguard)."""
+ """Active proxy protocol (socks5)."""
schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
"""A URL to the JSON Schema for this object."""
diff --git a/src/mobilerun_sdk/types/devices/time_time_response.py b/src/mobilerun_sdk/types/devices/state_time_response.py
similarity index 65%
rename from src/mobilerun_sdk/types/devices/time_time_response.py
rename to src/mobilerun_sdk/types/devices/state_time_response.py
index 3851347..6aed1eb 100644
--- a/src/mobilerun_sdk/types/devices/time_time_response.py
+++ b/src/mobilerun_sdk/types/devices/state_time_response.py
@@ -2,6 +2,6 @@
from typing_extensions import TypeAlias
-__all__ = ["TimeTimeResponse"]
+__all__ = ["StateTimeResponse"]
-TimeTimeResponse: TypeAlias = str
+StateTimeResponse: TypeAlias = str
diff --git a/src/mobilerun_sdk/types/devices/state_ui_response.py b/src/mobilerun_sdk/types/devices/state_ui_response.py
index 7a695f3..bdddd0c 100644
--- a/src/mobilerun_sdk/types/devices/state_ui_response.py
+++ b/src/mobilerun_sdk/types/devices/state_ui_response.py
@@ -76,3 +76,5 @@ class StateUiResponse(BaseModel):
schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
"""A URL to the JSON Schema for this object."""
+
+ ime_tree: Optional[object] = None
diff --git a/src/mobilerun_sdk/types/devices/time_timezone_response.py b/src/mobilerun_sdk/types/devices/timezone_get_response.py
similarity index 82%
rename from src/mobilerun_sdk/types/devices/time_timezone_response.py
rename to src/mobilerun_sdk/types/devices/timezone_get_response.py
index 7c5340f..2eaa30d 100644
--- a/src/mobilerun_sdk/types/devices/time_timezone_response.py
+++ b/src/mobilerun_sdk/types/devices/timezone_get_response.py
@@ -6,10 +6,10 @@
from ..._models import BaseModel
-__all__ = ["TimeTimezoneResponse"]
+__all__ = ["TimezoneGetResponse"]
-class TimeTimezoneResponse(BaseModel):
+class TimezoneGetResponse(BaseModel):
timezone: Optional[str] = None
schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
diff --git a/src/mobilerun_sdk/types/devices/time_set_timezone_params.py b/src/mobilerun_sdk/types/devices/timezone_set_params.py
similarity index 79%
rename from src/mobilerun_sdk/types/devices/time_set_timezone_params.py
rename to src/mobilerun_sdk/types/devices/timezone_set_params.py
index 24cd94c..87172dc 100644
--- a/src/mobilerun_sdk/types/devices/time_set_timezone_params.py
+++ b/src/mobilerun_sdk/types/devices/timezone_set_params.py
@@ -6,10 +6,10 @@
from ..._utils import PropertyInfo
-__all__ = ["TimeSetTimezoneParams"]
+__all__ = ["TimezoneSetParams"]
-class TimeSetTimezoneParams(TypedDict, total=False):
+class TimezoneSetParams(TypedDict, total=False):
timezone: Required[str]
x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")]
diff --git a/src/mobilerun_sdk/types/shared/__init__.py b/src/mobilerun_sdk/types/shared/__init__.py
index 1c5496b..7d0f116 100644
--- a/src/mobilerun_sdk/types/shared/__init__.py
+++ b/src/mobilerun_sdk/types/shared/__init__.py
@@ -1,6 +1,8 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from .meta import Meta as Meta
+from .socks5 import Socks5 as Socks5
+from .location import Location as Location
from .pagination import Pagination as Pagination
from .device_spec import DeviceSpec as DeviceSpec
from .device_carrier import DeviceCarrier as DeviceCarrier
diff --git a/src/mobilerun_sdk/types/shared/device_spec.py b/src/mobilerun_sdk/types/shared/device_spec.py
index 33e54cc..64614c9 100644
--- a/src/mobilerun_sdk/types/shared/device_spec.py
+++ b/src/mobilerun_sdk/types/shared/device_spec.py
@@ -4,21 +4,13 @@
from pydantic import Field as FieldInfo
+from .socks5 import Socks5
+from .location import Location
from ..._models import BaseModel
from .device_carrier import DeviceCarrier
from .device_identifiers import DeviceIdentifiers
-__all__ = ["DeviceSpec", "Proxy", "ProxySocks5"]
-
-
-class ProxySocks5(BaseModel):
- host: str
-
- password: str
-
- port: int
-
- user: str
+__all__ = ["DeviceSpec", "Proxy"]
class Proxy(BaseModel):
@@ -26,23 +18,31 @@ class Proxy(BaseModel):
smart_ip: Optional[bool] = FieldInfo(alias="smartIp", default=None)
- socks5: Optional[ProxySocks5] = None
-
- wireguard: Optional[str] = None
+ socks5: Optional[Socks5] = None
class DeviceSpec(BaseModel):
schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
"""A URL to the JSON Schema for this object."""
+ android_version: Optional[int] = FieldInfo(alias="androidVersion", default=None)
+
apps: Optional[List[str]] = None
carrier: Optional[DeviceCarrier] = None
+ country: Optional[str] = None
+
files: Optional[List[str]] = None
identifiers: Optional[DeviceIdentifiers] = None
+ locale: Optional[str] = None
+
+ location: Optional[Location] = None
+
name: Optional[str] = None
proxy: Optional[Proxy] = None
+
+ timezone: Optional[str] = None
diff --git a/src/mobilerun_sdk/types/devices/location_get_response.py b/src/mobilerun_sdk/types/shared/location.py
similarity index 83%
rename from src/mobilerun_sdk/types/devices/location_get_response.py
rename to src/mobilerun_sdk/types/shared/location.py
index 27f711a..bf0a32a 100644
--- a/src/mobilerun_sdk/types/devices/location_get_response.py
+++ b/src/mobilerun_sdk/types/shared/location.py
@@ -6,10 +6,10 @@
from ..._models import BaseModel
-__all__ = ["LocationGetResponse"]
+__all__ = ["Location"]
-class LocationGetResponse(BaseModel):
+class Location(BaseModel):
latitude: float
longitude: float
diff --git a/src/mobilerun_sdk/types/shared/socks5.py b/src/mobilerun_sdk/types/shared/socks5.py
new file mode 100644
index 0000000..36791bd
--- /dev/null
+++ b/src/mobilerun_sdk/types/shared/socks5.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["Socks5"]
+
+
+class Socks5(BaseModel):
+ host: str
+
+ password: str
+
+ port: int
+
+ user: str
diff --git a/src/mobilerun_sdk/types/shared_params/__init__.py b/src/mobilerun_sdk/types/shared_params/__init__.py
index 28eabc9..0b4dbfb 100644
--- a/src/mobilerun_sdk/types/shared_params/__init__.py
+++ b/src/mobilerun_sdk/types/shared_params/__init__.py
@@ -1,5 +1,7 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from .socks5 import Socks5 as Socks5
+from .location import Location as Location
from .device_spec import DeviceSpec as DeviceSpec
from .device_carrier import DeviceCarrier as DeviceCarrier
from .device_identifiers import DeviceIdentifiers as DeviceIdentifiers
diff --git a/src/mobilerun_sdk/types/shared_params/device_spec.py b/src/mobilerun_sdk/types/shared_params/device_spec.py
index 60bd226..7d1a43e 100644
--- a/src/mobilerun_sdk/types/shared_params/device_spec.py
+++ b/src/mobilerun_sdk/types/shared_params/device_spec.py
@@ -3,24 +3,16 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Required, Annotated, TypedDict
+from typing_extensions import Annotated, TypedDict
+from .socks5 import Socks5
from ..._types import SequenceNotStr
from ..._utils import PropertyInfo
+from .location import Location
from .device_carrier import DeviceCarrier
from .device_identifiers import DeviceIdentifiers
-__all__ = ["DeviceSpec", "Proxy", "ProxySocks5"]
-
-
-class ProxySocks5(TypedDict, total=False):
- host: Required[str]
-
- password: Required[str]
-
- port: Required[int]
-
- user: Required[str]
+__all__ = ["DeviceSpec", "Proxy"]
class Proxy(TypedDict, total=False):
@@ -28,20 +20,28 @@ class Proxy(TypedDict, total=False):
smart_ip: Annotated[bool, PropertyInfo(alias="smartIp")]
- socks5: ProxySocks5
-
- wireguard: str
+ socks5: Socks5
class DeviceSpec(TypedDict, total=False):
+ android_version: Annotated[int, PropertyInfo(alias="androidVersion")]
+
apps: Optional[SequenceNotStr[str]]
carrier: DeviceCarrier
+ country: str
+
files: Optional[SequenceNotStr[str]]
identifiers: DeviceIdentifiers
+ locale: str
+
+ location: Location
+
name: str
proxy: Proxy
+
+ timezone: str
diff --git a/src/mobilerun_sdk/types/shared_params/location.py b/src/mobilerun_sdk/types/shared_params/location.py
new file mode 100644
index 0000000..49198dc
--- /dev/null
+++ b/src/mobilerun_sdk/types/shared_params/location.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["Location"]
+
+
+class Location(TypedDict, total=False):
+ latitude: Required[float]
+
+ longitude: Required[float]
diff --git a/src/mobilerun_sdk/types/shared_params/socks5.py b/src/mobilerun_sdk/types/shared_params/socks5.py
new file mode 100644
index 0000000..0e56d16
--- /dev/null
+++ b/src/mobilerun_sdk/types/shared_params/socks5.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["Socks5"]
+
+
+class Socks5(TypedDict, total=False):
+ host: Required[str]
+
+ password: Required[str]
+
+ port: Required[int]
+
+ user: Required[str]
diff --git a/src/mobilerun_sdk/types/task.py b/src/mobilerun_sdk/types/task.py
index d436f7b..4a0f115 100644
--- a/src/mobilerun_sdk/types/task.py
+++ b/src/mobilerun_sdk/types/task.py
@@ -39,6 +39,8 @@ class Task(BaseModel):
credentials: Optional[List[PackageCredentials]] = None
+ credits_used: Optional[float] = FieldInfo(alias="creditsUsed", default=None)
+
dispatched_at: Optional[datetime] = FieldInfo(alias="dispatchedAt", default=None)
display_id: Optional[int] = FieldInfo(alias="displayId", default=None)
diff --git a/src/mobilerun_sdk/types/task_list_response.py b/src/mobilerun_sdk/types/task_list_response.py
index 9dfed40..16c8d1d 100644
--- a/src/mobilerun_sdk/types/task_list_response.py
+++ b/src/mobilerun_sdk/types/task_list_response.py
@@ -1,16 +1,100 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
-from .task import Task
from .._models import BaseModel
+from .task_status import TaskStatus
+from .package_credentials import PackageCredentials
from .shared.pagination_meta import PaginationMeta
-__all__ = ["TaskListResponse"]
+__all__ = ["TaskListResponse", "Item"]
+
+
+class Item(BaseModel):
+ """Task representation for list endpoints — omits the large trajectory field."""
+
+ id: str
+
+ device_id: str = FieldInfo(alias="deviceId")
+
+ display_id: int = FieldInfo(alias="displayId")
+
+ llm_model: str = FieldInfo(alias="llmModel")
+ """The LLM model identifier to use for the task (e.g. 'gemini/gemini-2.5-flash')"""
+
+ status: TaskStatus
+
+ task: str
+
+ tmp_device: bool = FieldInfo(alias="tmpDevice")
+
+ user_id: str = FieldInfo(alias="userId")
+
+ agent_id: Optional[int] = FieldInfo(alias="agentId", default=None)
+
+ apps: Optional[List[str]] = None
+
+ cancel_requested_at: Optional[datetime] = FieldInfo(alias="cancelRequestedAt", default=None)
+
+ claimed_at: Optional[datetime] = FieldInfo(alias="claimedAt", default=None)
+
+ continue_on_failure: Optional[bool] = FieldInfo(alias="continueOnFailure", default=None)
+
+ created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None)
+
+ credentials: Optional[List[PackageCredentials]] = None
+
+ credits_used: Optional[float] = FieldInfo(alias="creditsUsed", default=None)
+
+ dispatched_at: Optional[datetime] = FieldInfo(alias="dispatchedAt", default=None)
+
+ execution_timeout: Optional[int] = FieldInfo(alias="executionTimeout", default=None)
+
+ files: Optional[List[str]] = None
+
+ finished_at: Optional[datetime] = FieldInfo(alias="finishedAt", default=None)
+
+ max_steps: Optional[int] = FieldInfo(alias="maxSteps", default=None)
+
+ memory_namespace: Optional[str] = FieldInfo(alias="memoryNamespace", default=None)
+ """Memory namespace for cross-task personalization"""
+
+ message: Optional[str] = None
+
+ output: Optional[Dict[str, object]] = None
+
+ output_schema: Optional[Dict[str, object]] = FieldInfo(alias="outputSchema", default=None)
+
+ reasoning: Optional[bool] = None
+
+ stealth: Optional[bool] = None
+
+ steps: Optional[int] = None
+
+ stream_url: Optional[str] = FieldInfo(alias="streamUrl", default=None)
+
+ subagent_model: Optional[str] = FieldInfo(alias="subagentModel", default=None)
+ """LLM model used by sub-agent roles: executor, app_opener, structured_output"""
+
+ succeeded: Optional[bool] = None
+
+ temperature: Optional[float] = None
+
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
+
+ vision: Optional[bool] = None
+
+ vpn_country: Optional[Literal["US", "BR", "FR", "DE", "IN", "JP", "KR", "ZA"]] = FieldInfo(
+ alias="vpnCountry", default=None
+ )
class TaskListResponse(BaseModel):
- items: List[Task]
+ items: List[Item]
"""The paginated items"""
pagination: PaginationMeta
diff --git a/tests/api_resources/devices/esim/__init__.py b/tests/api_resources/devices/esim/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/devices/esim/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/devices/esim/test_apn.py b/tests/api_resources/devices/esim/test_apn.py
new file mode 100644
index 0000000..d538cf7
--- /dev/null
+++ b/tests/api_resources/devices/esim/test_apn.py
@@ -0,0 +1,430 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, Optional, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from mobilerun_sdk import Mobilerun, AsyncMobilerun
+from mobilerun_sdk.types.devices.esim import ApnListResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestApn:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Mobilerun) -> None:
+ apn = client.devices.esim.apn.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
+ apn = client.devices.esim.apn.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ x_device_display_id=0,
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Mobilerun) -> None:
+ response = client.devices.esim.apn.with_raw_response.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ apn = response.parse()
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Mobilerun) -> None:
+ with client.devices.esim.apn.with_streaming_response.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ apn = response.parse()
+ assert apn is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_create(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.esim.apn.with_raw_response.create(
+ device_id="",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Mobilerun) -> None:
+ apn = client.devices.esim.apn.list(
+ device_id="deviceId",
+ )
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mobilerun) -> None:
+ apn = client.devices.esim.apn.list(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Mobilerun) -> None:
+ response = client.devices.esim.apn.with_raw_response.list(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ apn = response.parse()
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Mobilerun) -> None:
+ with client.devices.esim.apn.with_streaming_response.list(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ apn = response.parse()
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_list(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.esim.apn.with_raw_response.list(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_select(self, client: Mobilerun) -> None:
+ apn = client.devices.esim.apn.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_select_with_all_params(self, client: Mobilerun) -> None:
+ apn = client.devices.esim.apn.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ x_device_display_id=0,
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_select(self, client: Mobilerun) -> None:
+ response = client.devices.esim.apn.with_raw_response.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ apn = response.parse()
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_select(self, client: Mobilerun) -> None:
+ with client.devices.esim.apn.with_streaming_response.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ apn = response.parse()
+ assert apn is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_select(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.esim.apn.with_raw_response.select(
+ device_id="",
+ apn_id=0,
+ sub_id=0,
+ )
+
+
+class TestAsyncApn:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncMobilerun) -> None:
+ apn = await async_client.devices.esim.apn.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ apn = await async_client.devices.esim.apn.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ x_device_display_id=0,
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.esim.apn.with_raw_response.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ apn = await response.parse()
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.esim.apn.with_streaming_response.create(
+ device_id="deviceId",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ apn = await response.parse()
+ assert apn is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.esim.apn.with_raw_response.create(
+ device_id="",
+ apn="apn",
+ mcc="mcc",
+ mnc="mnc",
+ name="name",
+ protocol="protocol",
+ roaming_protocol="roamingProtocol",
+ sub_id=0,
+ type="type",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMobilerun) -> None:
+ apn = await async_client.devices.esim.apn.list(
+ device_id="deviceId",
+ )
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ apn = await async_client.devices.esim.apn.list(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.esim.apn.with_raw_response.list(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ apn = await response.parse()
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.esim.apn.with_streaming_response.list(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ apn = await response.parse()
+ assert_matches_type(Optional[ApnListResponse], apn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.esim.apn.with_raw_response.list(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_select(self, async_client: AsyncMobilerun) -> None:
+ apn = await async_client.devices.esim.apn.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_select_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ apn = await async_client.devices.esim.apn.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ x_device_display_id=0,
+ )
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_select(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.esim.apn.with_raw_response.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ apn = await response.parse()
+ assert apn is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_select(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.esim.apn.with_streaming_response.select(
+ device_id="deviceId",
+ apn_id=0,
+ sub_id=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ apn = await response.parse()
+ assert apn is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_select(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.esim.apn.with_raw_response.select(
+ device_id="",
+ apn_id=0,
+ sub_id=0,
+ )
diff --git a/tests/api_resources/devices/test_apps.py b/tests/api_resources/devices/test_apps.py
index 946ac64..87c426d 100644
--- a/tests/api_resources/devices/test_apps.py
+++ b/tests/api_resources/devices/test_apps.py
@@ -196,29 +196,30 @@ def test_path_params_delete(self, client: Mobilerun) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_install(self, client: Mobilerun) -> None:
+ def test_method_install_overload_1(self, client: Mobilerun) -> None:
app = client.devices.apps.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
)
assert app is None
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_install_with_all_params(self, client: Mobilerun) -> None:
+ def test_method_install_with_all_params_overload_1(self, client: Mobilerun) -> None:
app = client.devices.apps.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
+ package_name="x",
x_device_display_id=0,
)
assert app is None
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_install(self, client: Mobilerun) -> None:
+ def test_raw_response_install_overload_1(self, client: Mobilerun) -> None:
response = client.devices.apps.with_raw_response.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
)
assert response.is_closed is True
@@ -228,10 +229,10 @@ def test_raw_response_install(self, client: Mobilerun) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_install(self, client: Mobilerun) -> None:
+ def test_streaming_response_install_overload_1(self, client: Mobilerun) -> None:
with client.devices.apps.with_streaming_response.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -243,11 +244,68 @@ def test_streaming_response_install(self, client: Mobilerun) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_path_params_install(self, client: Mobilerun) -> None:
+ def test_path_params_install_overload_1(self, client: Mobilerun) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
client.devices.apps.with_raw_response.install(
device_id="",
- package_name="packageName",
+ bundle_id="x",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_install_overload_2(self, client: Mobilerun) -> None:
+ app = client.devices.apps.install(
+ device_id="deviceId",
+ package_name="x",
+ )
+ assert app is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_install_with_all_params_overload_2(self, client: Mobilerun) -> None:
+ app = client.devices.apps.install(
+ device_id="deviceId",
+ package_name="x",
+ bundle_id="x",
+ x_device_display_id=0,
+ )
+ assert app is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_install_overload_2(self, client: Mobilerun) -> None:
+ response = client.devices.apps.with_raw_response.install(
+ device_id="deviceId",
+ package_name="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert app is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_install_overload_2(self, client: Mobilerun) -> None:
+ with client.devices.apps.with_streaming_response.install(
+ device_id="deviceId",
+ package_name="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert app is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_install_overload_2(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.apps.with_raw_response.install(
+ device_id="",
+ package_name="x",
)
@pytest.mark.skip(reason="Mock server tests are disabled")
@@ -498,29 +556,30 @@ async def test_path_params_delete(self, async_client: AsyncMobilerun) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_install(self, async_client: AsyncMobilerun) -> None:
+ async def test_method_install_overload_1(self, async_client: AsyncMobilerun) -> None:
app = await async_client.devices.apps.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
)
assert app is None
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_install_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ async def test_method_install_with_all_params_overload_1(self, async_client: AsyncMobilerun) -> None:
app = await async_client.devices.apps.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
+ package_name="x",
x_device_display_id=0,
)
assert app is None
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_install(self, async_client: AsyncMobilerun) -> None:
+ async def test_raw_response_install_overload_1(self, async_client: AsyncMobilerun) -> None:
response = await async_client.devices.apps.with_raw_response.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
)
assert response.is_closed is True
@@ -530,10 +589,10 @@ async def test_raw_response_install(self, async_client: AsyncMobilerun) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_install(self, async_client: AsyncMobilerun) -> None:
+ async def test_streaming_response_install_overload_1(self, async_client: AsyncMobilerun) -> None:
async with async_client.devices.apps.with_streaming_response.install(
device_id="deviceId",
- package_name="packageName",
+ bundle_id="x",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -545,11 +604,68 @@ async def test_streaming_response_install(self, async_client: AsyncMobilerun) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_path_params_install(self, async_client: AsyncMobilerun) -> None:
+ async def test_path_params_install_overload_1(self, async_client: AsyncMobilerun) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
await async_client.devices.apps.with_raw_response.install(
device_id="",
- package_name="packageName",
+ bundle_id="x",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_install_overload_2(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.devices.apps.install(
+ device_id="deviceId",
+ package_name="x",
+ )
+ assert app is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_install_with_all_params_overload_2(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.devices.apps.install(
+ device_id="deviceId",
+ package_name="x",
+ bundle_id="x",
+ x_device_display_id=0,
+ )
+ assert app is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_install_overload_2(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.apps.with_raw_response.install(
+ device_id="deviceId",
+ package_name="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert app is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_install_overload_2(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.apps.with_streaming_response.install(
+ device_id="deviceId",
+ package_name="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert app is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_install_overload_2(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.apps.with_raw_response.install(
+ device_id="",
+ package_name="x",
)
@pytest.mark.skip(reason="Mock server tests are disabled")
diff --git a/tests/api_resources/devices/test_esim.py b/tests/api_resources/devices/test_esim.py
index 84cf406..82541c2 100644
--- a/tests/api_resources/devices/test_esim.py
+++ b/tests/api_resources/devices/test_esim.py
@@ -11,6 +11,7 @@
from mobilerun_sdk import Mobilerun, AsyncMobilerun
from mobilerun_sdk.types.devices import (
EsimListResponse,
+ EsimStatusResponse,
EsimActivateResponse,
)
@@ -77,7 +78,6 @@ def test_method_activate(self, client: Mobilerun) -> None:
esim = client.devices.esim.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
)
assert_matches_type(EsimActivateResponse, esim, path=["response"])
@@ -88,8 +88,9 @@ def test_method_activate_with_all_params(self, client: Mobilerun) -> None:
esim = client.devices.esim.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
+ confirmation_code="confirmationCode",
+ matching_id="matchingId",
x_device_display_id=0,
)
assert_matches_type(EsimActivateResponse, esim, path=["response"])
@@ -100,7 +101,6 @@ def test_raw_response_activate(self, client: Mobilerun) -> None:
response = client.devices.esim.with_raw_response.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
)
@@ -115,7 +115,6 @@ def test_streaming_response_activate(self, client: Mobilerun) -> None:
with client.devices.esim.with_streaming_response.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
) as response:
assert not response.is_closed
@@ -133,7 +132,6 @@ def test_path_params_activate(self, client: Mobilerun) -> None:
client.devices.esim.with_raw_response.activate(
device_id="",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
)
@@ -249,6 +247,113 @@ def test_path_params_remove(self, client: Mobilerun) -> None:
sub_id=0,
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_set_roaming(self, client: Mobilerun) -> None:
+ esim = client.devices.esim.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ )
+ assert esim is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_set_roaming_with_all_params(self, client: Mobilerun) -> None:
+ esim = client.devices.esim.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ x_device_display_id=0,
+ )
+ assert esim is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_set_roaming(self, client: Mobilerun) -> None:
+ response = client.devices.esim.with_raw_response.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ esim = response.parse()
+ assert esim is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_set_roaming(self, client: Mobilerun) -> None:
+ with client.devices.esim.with_streaming_response.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ esim = response.parse()
+ assert esim is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_set_roaming(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.esim.with_raw_response.set_roaming(
+ device_id="",
+ enabled=True,
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_status(self, client: Mobilerun) -> None:
+ esim = client.devices.esim.status(
+ device_id="deviceId",
+ )
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_status_with_all_params(self, client: Mobilerun) -> None:
+ esim = client.devices.esim.status(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_status(self, client: Mobilerun) -> None:
+ response = client.devices.esim.with_raw_response.status(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ esim = response.parse()
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_status(self, client: Mobilerun) -> None:
+ with client.devices.esim.with_streaming_response.status(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ esim = response.parse()
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_status(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.esim.with_raw_response.status(
+ device_id="",
+ )
+
class TestAsyncEsim:
parametrize = pytest.mark.parametrize(
@@ -312,7 +417,6 @@ async def test_method_activate(self, async_client: AsyncMobilerun) -> None:
esim = await async_client.devices.esim.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
)
assert_matches_type(EsimActivateResponse, esim, path=["response"])
@@ -323,8 +427,9 @@ async def test_method_activate_with_all_params(self, async_client: AsyncMobileru
esim = await async_client.devices.esim.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
+ confirmation_code="confirmationCode",
+ matching_id="matchingId",
x_device_display_id=0,
)
assert_matches_type(EsimActivateResponse, esim, path=["response"])
@@ -335,7 +440,6 @@ async def test_raw_response_activate(self, async_client: AsyncMobilerun) -> None
response = await async_client.devices.esim.with_raw_response.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
)
@@ -350,7 +454,6 @@ async def test_streaming_response_activate(self, async_client: AsyncMobilerun) -
async with async_client.devices.esim.with_streaming_response.activate(
device_id="deviceId",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
) as response:
assert not response.is_closed
@@ -368,7 +471,6 @@ async def test_path_params_activate(self, async_client: AsyncMobilerun) -> None:
await async_client.devices.esim.with_raw_response.activate(
device_id="",
enable=True,
- matching_id="matchingId",
sm_dp_addr="smDpAddr",
)
@@ -483,3 +585,110 @@ async def test_path_params_remove(self, async_client: AsyncMobilerun) -> None:
device_id="",
sub_id=0,
)
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_set_roaming(self, async_client: AsyncMobilerun) -> None:
+ esim = await async_client.devices.esim.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ )
+ assert esim is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_set_roaming_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ esim = await async_client.devices.esim.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ x_device_display_id=0,
+ )
+ assert esim is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_set_roaming(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.esim.with_raw_response.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ esim = await response.parse()
+ assert esim is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_set_roaming(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.esim.with_streaming_response.set_roaming(
+ device_id="deviceId",
+ enabled=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ esim = await response.parse()
+ assert esim is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_set_roaming(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.esim.with_raw_response.set_roaming(
+ device_id="",
+ enabled=True,
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_status(self, async_client: AsyncMobilerun) -> None:
+ esim = await async_client.devices.esim.status(
+ device_id="deviceId",
+ )
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_status_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ esim = await async_client.devices.esim.status(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_status(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.esim.with_raw_response.status(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ esim = await response.parse()
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_status(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.esim.with_streaming_response.status(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ esim = await response.parse()
+ assert_matches_type(Optional[EsimStatusResponse], esim, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_status(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.esim.with_raw_response.status(
+ device_id="",
+ )
diff --git a/tests/api_resources/devices/test_keyboard.py b/tests/api_resources/devices/test_keyboard.py
index ba20e76..766a63b 100644
--- a/tests/api_resources/devices/test_keyboard.py
+++ b/tests/api_resources/devices/test_keyboard.py
@@ -138,6 +138,7 @@ def test_method_write_with_all_params(self, client: Mobilerun) -> None:
device_id="deviceId",
text="text",
clear=True,
+ error_rate=0,
stealth=True,
wpm=0,
x_device_display_id=0,
@@ -310,6 +311,7 @@ async def test_method_write_with_all_params(self, async_client: AsyncMobilerun)
device_id="deviceId",
text="text",
clear=True,
+ error_rate=0,
stealth=True,
wpm=0,
x_device_display_id=0,
diff --git a/tests/api_resources/devices/test_language.py b/tests/api_resources/devices/test_language.py
new file mode 100644
index 0000000..c38f4dd
--- /dev/null
+++ b/tests/api_resources/devices/test_language.py
@@ -0,0 +1,240 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from mobilerun_sdk import Mobilerun, AsyncMobilerun
+from mobilerun_sdk.types.devices import LanguageGetResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLanguage:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_get(self, client: Mobilerun) -> None:
+ language = client.devices.language.get(
+ device_id="deviceId",
+ )
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_get_with_all_params(self, client: Mobilerun) -> None:
+ language = client.devices.language.get(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_get(self, client: Mobilerun) -> None:
+ response = client.devices.language.with_raw_response.get(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ language = response.parse()
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_get(self, client: Mobilerun) -> None:
+ with client.devices.language.with_streaming_response.get(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ language = response.parse()
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_get(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.language.with_raw_response.get(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_set(self, client: Mobilerun) -> None:
+ language = client.devices.language.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ )
+ assert language is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_set_with_all_params(self, client: Mobilerun) -> None:
+ language = client.devices.language.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ restart=True,
+ x_device_display_id=0,
+ )
+ assert language is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_set(self, client: Mobilerun) -> None:
+ response = client.devices.language.with_raw_response.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ language = response.parse()
+ assert language is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_set(self, client: Mobilerun) -> None:
+ with client.devices.language.with_streaming_response.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ language = response.parse()
+ assert language is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_set(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.language.with_raw_response.set(
+ device_id="",
+ locale="sqf-Kkif-BB",
+ )
+
+
+class TestAsyncLanguage:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_get(self, async_client: AsyncMobilerun) -> None:
+ language = await async_client.devices.language.get(
+ device_id="deviceId",
+ )
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_get_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ language = await async_client.devices.language.get(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.language.with_raw_response.get(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ language = await response.parse()
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.language.with_streaming_response.get(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ language = await response.parse()
+ assert_matches_type(LanguageGetResponse, language, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.language.with_raw_response.get(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_set(self, async_client: AsyncMobilerun) -> None:
+ language = await async_client.devices.language.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ )
+ assert language is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_set_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ language = await async_client.devices.language.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ restart=True,
+ x_device_display_id=0,
+ )
+ assert language is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_set(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.language.with_raw_response.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ language = await response.parse()
+ assert language is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_set(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.language.with_streaming_response.set(
+ device_id="deviceId",
+ locale="sqf-Kkif-BB",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ language = await response.parse()
+ assert language is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_set(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.language.with_raw_response.set(
+ device_id="",
+ locale="sqf-Kkif-BB",
+ )
diff --git a/tests/api_resources/devices/test_location.py b/tests/api_resources/devices/test_location.py
index 2d8b007..2ec5cb0 100644
--- a/tests/api_resources/devices/test_location.py
+++ b/tests/api_resources/devices/test_location.py
@@ -9,7 +9,7 @@
from tests.utils import assert_matches_type
from mobilerun_sdk import Mobilerun, AsyncMobilerun
-from mobilerun_sdk.types.devices import LocationGetResponse
+from mobilerun_sdk.types.shared import Location
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -23,7 +23,7 @@ def test_method_get(self, client: Mobilerun) -> None:
location = client.devices.location.get(
device_id="deviceId",
)
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -32,7 +32,7 @@ def test_method_get_with_all_params(self, client: Mobilerun) -> None:
device_id="deviceId",
x_device_display_id=0,
)
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -44,7 +44,7 @@ def test_raw_response_get(self, client: Mobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
location = response.parse()
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -56,7 +56,7 @@ def test_streaming_response_get(self, client: Mobilerun) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
location = response.parse()
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -141,7 +141,7 @@ async def test_method_get(self, async_client: AsyncMobilerun) -> None:
location = await async_client.devices.location.get(
device_id="deviceId",
)
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -150,7 +150,7 @@ async def test_method_get_with_all_params(self, async_client: AsyncMobilerun) ->
device_id="deviceId",
x_device_display_id=0,
)
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -162,7 +162,7 @@ async def test_raw_response_get(self, async_client: AsyncMobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
location = await response.parse()
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -174,7 +174,7 @@ async def test_streaming_response_get(self, async_client: AsyncMobilerun) -> Non
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
location = await response.parse()
- assert_matches_type(LocationGetResponse, location, path=["response"])
+ assert_matches_type(Location, location, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/devices/test_proxy.py b/tests/api_resources/devices/test_proxy.py
index eb9d960..914b3e3 100644
--- a/tests/api_resources/devices/test_proxy.py
+++ b/tests/api_resources/devices/test_proxy.py
@@ -42,7 +42,6 @@ def test_method_connect_with_all_params(self, client: Mobilerun) -> None:
"user": "user",
},
user="user",
- wireguard="wireguard",
x_device_display_id=0,
)
assert proxy is None
@@ -214,7 +213,6 @@ async def test_method_connect_with_all_params(self, async_client: AsyncMobilerun
"user": "user",
},
user="user",
- wireguard="wireguard",
x_device_display_id=0,
)
assert proxy is None
diff --git a/tests/api_resources/devices/test_state.py b/tests/api_resources/devices/test_state.py
index 9ef60ae..64d94ec 100644
--- a/tests/api_resources/devices/test_state.py
+++ b/tests/api_resources/devices/test_state.py
@@ -71,6 +71,57 @@ def test_path_params_screenshot(self, client: Mobilerun) -> None:
device_id="",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_time(self, client: Mobilerun) -> None:
+ state = client.devices.state.time(
+ device_id="deviceId",
+ )
+ assert_matches_type(str, state, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_time_with_all_params(self, client: Mobilerun) -> None:
+ state = client.devices.state.time(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(str, state, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_time(self, client: Mobilerun) -> None:
+ response = client.devices.state.with_raw_response.time(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ state = response.parse()
+ assert_matches_type(str, state, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_time(self, client: Mobilerun) -> None:
+ with client.devices.state.with_streaming_response.time(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ state = response.parse()
+ assert_matches_type(str, state, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_time(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.state.with_raw_response.time(
+ device_id="",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_ui(self, client: Mobilerun) -> None:
@@ -181,6 +232,57 @@ async def test_path_params_screenshot(self, async_client: AsyncMobilerun) -> Non
device_id="",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_time(self, async_client: AsyncMobilerun) -> None:
+ state = await async_client.devices.state.time(
+ device_id="deviceId",
+ )
+ assert_matches_type(str, state, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_time_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ state = await async_client.devices.state.time(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(str, state, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_time(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.state.with_raw_response.time(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ state = await response.parse()
+ assert_matches_type(str, state, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_time(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.state.with_streaming_response.time(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ state = await response.parse()
+ assert_matches_type(str, state, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_time(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.state.with_raw_response.time(
+ device_id="",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_ui(self, async_client: AsyncMobilerun) -> None:
diff --git a/tests/api_resources/devices/test_time.py b/tests/api_resources/devices/test_time.py
deleted file mode 100644
index 6427488..0000000
--- a/tests/api_resources/devices/test_time.py
+++ /dev/null
@@ -1,340 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import pytest
-
-from tests.utils import assert_matches_type
-from mobilerun_sdk import Mobilerun, AsyncMobilerun
-from mobilerun_sdk.types.devices import TimeTimezoneResponse
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestTime:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_set_timezone(self, client: Mobilerun) -> None:
- time = client.devices.time.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- )
- assert time is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_set_timezone_with_all_params(self, client: Mobilerun) -> None:
- time = client.devices.time.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- x_device_display_id=0,
- )
- assert time is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_raw_response_set_timezone(self, client: Mobilerun) -> None:
- response = client.devices.time.with_raw_response.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- time = response.parse()
- assert time is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_streaming_response_set_timezone(self, client: Mobilerun) -> None:
- with client.devices.time.with_streaming_response.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- time = response.parse()
- assert time is None
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_path_params_set_timezone(self, client: Mobilerun) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
- client.devices.time.with_raw_response.set_timezone(
- device_id="",
- timezone="timezone",
- )
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_time(self, client: Mobilerun) -> None:
- time = client.devices.time.time(
- device_id="deviceId",
- )
- assert_matches_type(str, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_time_with_all_params(self, client: Mobilerun) -> None:
- time = client.devices.time.time(
- device_id="deviceId",
- x_device_display_id=0,
- )
- assert_matches_type(str, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_raw_response_time(self, client: Mobilerun) -> None:
- response = client.devices.time.with_raw_response.time(
- device_id="deviceId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- time = response.parse()
- assert_matches_type(str, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_streaming_response_time(self, client: Mobilerun) -> None:
- with client.devices.time.with_streaming_response.time(
- device_id="deviceId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- time = response.parse()
- assert_matches_type(str, time, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_path_params_time(self, client: Mobilerun) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
- client.devices.time.with_raw_response.time(
- device_id="",
- )
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_timezone(self, client: Mobilerun) -> None:
- time = client.devices.time.timezone(
- device_id="deviceId",
- )
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_timezone_with_all_params(self, client: Mobilerun) -> None:
- time = client.devices.time.timezone(
- device_id="deviceId",
- x_device_display_id=0,
- )
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_raw_response_timezone(self, client: Mobilerun) -> None:
- response = client.devices.time.with_raw_response.timezone(
- device_id="deviceId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- time = response.parse()
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_streaming_response_timezone(self, client: Mobilerun) -> None:
- with client.devices.time.with_streaming_response.timezone(
- device_id="deviceId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- time = response.parse()
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_path_params_timezone(self, client: Mobilerun) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
- client.devices.time.with_raw_response.timezone(
- device_id="",
- )
-
-
-class TestAsyncTime:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_set_timezone(self, async_client: AsyncMobilerun) -> None:
- time = await async_client.devices.time.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- )
- assert time is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_set_timezone_with_all_params(self, async_client: AsyncMobilerun) -> None:
- time = await async_client.devices.time.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- x_device_display_id=0,
- )
- assert time is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_set_timezone(self, async_client: AsyncMobilerun) -> None:
- response = await async_client.devices.time.with_raw_response.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- time = await response.parse()
- assert time is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_set_timezone(self, async_client: AsyncMobilerun) -> None:
- async with async_client.devices.time.with_streaming_response.set_timezone(
- device_id="deviceId",
- timezone="timezone",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- time = await response.parse()
- assert time is None
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_path_params_set_timezone(self, async_client: AsyncMobilerun) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
- await async_client.devices.time.with_raw_response.set_timezone(
- device_id="",
- timezone="timezone",
- )
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_time(self, async_client: AsyncMobilerun) -> None:
- time = await async_client.devices.time.time(
- device_id="deviceId",
- )
- assert_matches_type(str, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_time_with_all_params(self, async_client: AsyncMobilerun) -> None:
- time = await async_client.devices.time.time(
- device_id="deviceId",
- x_device_display_id=0,
- )
- assert_matches_type(str, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_time(self, async_client: AsyncMobilerun) -> None:
- response = await async_client.devices.time.with_raw_response.time(
- device_id="deviceId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- time = await response.parse()
- assert_matches_type(str, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_time(self, async_client: AsyncMobilerun) -> None:
- async with async_client.devices.time.with_streaming_response.time(
- device_id="deviceId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- time = await response.parse()
- assert_matches_type(str, time, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_path_params_time(self, async_client: AsyncMobilerun) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
- await async_client.devices.time.with_raw_response.time(
- device_id="",
- )
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_timezone(self, async_client: AsyncMobilerun) -> None:
- time = await async_client.devices.time.timezone(
- device_id="deviceId",
- )
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_timezone_with_all_params(self, async_client: AsyncMobilerun) -> None:
- time = await async_client.devices.time.timezone(
- device_id="deviceId",
- x_device_display_id=0,
- )
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_timezone(self, async_client: AsyncMobilerun) -> None:
- response = await async_client.devices.time.with_raw_response.timezone(
- device_id="deviceId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- time = await response.parse()
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_timezone(self, async_client: AsyncMobilerun) -> None:
- async with async_client.devices.time.with_streaming_response.timezone(
- device_id="deviceId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- time = await response.parse()
- assert_matches_type(TimeTimezoneResponse, time, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_path_params_timezone(self, async_client: AsyncMobilerun) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
- await async_client.devices.time.with_raw_response.timezone(
- device_id="",
- )
diff --git a/tests/api_resources/devices/test_timezone.py b/tests/api_resources/devices/test_timezone.py
new file mode 100644
index 0000000..6902ac4
--- /dev/null
+++ b/tests/api_resources/devices/test_timezone.py
@@ -0,0 +1,238 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from mobilerun_sdk import Mobilerun, AsyncMobilerun
+from mobilerun_sdk.types.devices import TimezoneGetResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTimezone:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_get(self, client: Mobilerun) -> None:
+ timezone = client.devices.timezone.get(
+ device_id="deviceId",
+ )
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_get_with_all_params(self, client: Mobilerun) -> None:
+ timezone = client.devices.timezone.get(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_get(self, client: Mobilerun) -> None:
+ response = client.devices.timezone.with_raw_response.get(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ timezone = response.parse()
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_get(self, client: Mobilerun) -> None:
+ with client.devices.timezone.with_streaming_response.get(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ timezone = response.parse()
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_get(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.timezone.with_raw_response.get(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_set(self, client: Mobilerun) -> None:
+ timezone = client.devices.timezone.set(
+ device_id="deviceId",
+ timezone="timezone",
+ )
+ assert timezone is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_set_with_all_params(self, client: Mobilerun) -> None:
+ timezone = client.devices.timezone.set(
+ device_id="deviceId",
+ timezone="timezone",
+ x_device_display_id=0,
+ )
+ assert timezone is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_set(self, client: Mobilerun) -> None:
+ response = client.devices.timezone.with_raw_response.set(
+ device_id="deviceId",
+ timezone="timezone",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ timezone = response.parse()
+ assert timezone is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_set(self, client: Mobilerun) -> None:
+ with client.devices.timezone.with_streaming_response.set(
+ device_id="deviceId",
+ timezone="timezone",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ timezone = response.parse()
+ assert timezone is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_set(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.timezone.with_raw_response.set(
+ device_id="",
+ timezone="timezone",
+ )
+
+
+class TestAsyncTimezone:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_get(self, async_client: AsyncMobilerun) -> None:
+ timezone = await async_client.devices.timezone.get(
+ device_id="deviceId",
+ )
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_get_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ timezone = await async_client.devices.timezone.get(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.timezone.with_raw_response.get(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ timezone = await response.parse()
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.timezone.with_streaming_response.get(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ timezone = await response.parse()
+ assert_matches_type(TimezoneGetResponse, timezone, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.timezone.with_raw_response.get(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_set(self, async_client: AsyncMobilerun) -> None:
+ timezone = await async_client.devices.timezone.set(
+ device_id="deviceId",
+ timezone="timezone",
+ )
+ assert timezone is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_set_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ timezone = await async_client.devices.timezone.set(
+ device_id="deviceId",
+ timezone="timezone",
+ x_device_display_id=0,
+ )
+ assert timezone is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_set(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.timezone.with_raw_response.set(
+ device_id="deviceId",
+ timezone="timezone",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ timezone = await response.parse()
+ assert timezone is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_set(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.timezone.with_streaming_response.set(
+ device_id="deviceId",
+ timezone="timezone",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ timezone = await response.parse()
+ assert timezone is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_set(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.timezone.with_raw_response.set(
+ device_id="",
+ timezone="timezone",
+ )
diff --git a/tests/api_resources/test_apps.py b/tests/api_resources/test_apps.py
index 0015cdc..887a5ee 100644
--- a/tests/api_resources/test_apps.py
+++ b/tests/api_resources/test_apps.py
@@ -9,7 +9,14 @@
from tests.utils import assert_matches_type
from mobilerun_sdk import Mobilerun, AsyncMobilerun
-from mobilerun_sdk.types import AppListResponse
+from mobilerun_sdk.types import (
+ AppListResponse,
+ AppDeleteResponse,
+ AppRetrieveResponse,
+ AppMarkFailedResponse,
+ AppConfirmUploadResponse,
+ AppCreateSignedUploadURLResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -17,6 +24,48 @@
class TestApps:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Mobilerun) -> None:
+ app = client.apps.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppRetrieveResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mobilerun) -> None:
+ response = client.apps.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert_matches_type(AppRetrieveResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mobilerun) -> None:
+ with client.apps.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert_matches_type(AppRetrieveResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.apps.with_raw_response.retrieve(
+ "",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_list(self, client: Mobilerun) -> None:
@@ -30,9 +79,10 @@ def test_method_list_with_all_params(self, client: Mobilerun) -> None:
order="asc",
page=1,
page_size=1,
+ platform="all",
query="query",
sort_by="createdAt",
- source="all",
+ status="all",
)
assert_matches_type(AppListResponse, app, path=["response"])
@@ -58,12 +108,268 @@ def test_streaming_response_list(self, client: Mobilerun) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Mobilerun) -> None:
+ app = client.apps.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppDeleteResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Mobilerun) -> None:
+ response = client.apps.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert_matches_type(AppDeleteResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Mobilerun) -> None:
+ with client.apps.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert_matches_type(AppDeleteResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.apps.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_confirm_upload(self, client: Mobilerun) -> None:
+ app = client.apps.confirm_upload(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppConfirmUploadResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_confirm_upload(self, client: Mobilerun) -> None:
+ response = client.apps.with_raw_response.confirm_upload(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert_matches_type(AppConfirmUploadResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_confirm_upload(self, client: Mobilerun) -> None:
+ with client.apps.with_streaming_response.confirm_upload(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert_matches_type(AppConfirmUploadResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_confirm_upload(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.apps.with_raw_response.confirm_upload(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_signed_upload_url(self, client: Mobilerun) -> None:
+ app = client.apps.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ )
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_signed_upload_url_with_all_params(self, client: Mobilerun) -> None:
+ app = client.apps.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ country="US",
+ description="description",
+ developer_name="developerName",
+ icon_url="iconURL",
+ platform="android",
+ target_sdk=0,
+ )
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create_signed_upload_url(self, client: Mobilerun) -> None:
+ response = client.apps.with_raw_response.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create_signed_upload_url(self, client: Mobilerun) -> None:
+ with client.apps.with_streaming_response.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_mark_failed(self, client: Mobilerun) -> None:
+ app = client.apps.mark_failed(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppMarkFailedResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_mark_failed(self, client: Mobilerun) -> None:
+ response = client.apps.with_raw_response.mark_failed(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert_matches_type(AppMarkFailedResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_mark_failed(self, client: Mobilerun) -> None:
+ with client.apps.with_streaming_response.mark_failed(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert_matches_type(AppMarkFailedResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_mark_failed(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.apps.with_raw_response.mark_failed(
+ "",
+ )
+
class TestAsyncApps:
parametrize = pytest.mark.parametrize(
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.apps.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppRetrieveResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.apps.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert_matches_type(AppRetrieveResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.apps.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert_matches_type(AppRetrieveResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.apps.with_raw_response.retrieve(
+ "",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_list(self, async_client: AsyncMobilerun) -> None:
@@ -77,9 +383,10 @@ async def test_method_list_with_all_params(self, async_client: AsyncMobilerun) -
order="asc",
page=1,
page_size=1,
+ platform="all",
query="query",
sort_by="createdAt",
- source="all",
+ status="all",
)
assert_matches_type(AppListResponse, app, path=["response"])
@@ -104,3 +411,217 @@ async def test_streaming_response_list(self, async_client: AsyncMobilerun) -> No
assert_matches_type(AppListResponse, app, path=["response"])
assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.apps.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppDeleteResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.apps.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert_matches_type(AppDeleteResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.apps.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert_matches_type(AppDeleteResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.apps.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_confirm_upload(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.apps.confirm_upload(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppConfirmUploadResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_confirm_upload(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.apps.with_raw_response.confirm_upload(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert_matches_type(AppConfirmUploadResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_confirm_upload(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.apps.with_streaming_response.confirm_upload(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert_matches_type(AppConfirmUploadResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_confirm_upload(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.apps.with_raw_response.confirm_upload(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_signed_upload_url(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.apps.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ )
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_signed_upload_url_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.apps.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ country="US",
+ description="description",
+ developer_name="developerName",
+ icon_url="iconURL",
+ platform="android",
+ target_sdk=0,
+ )
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create_signed_upload_url(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.apps.with_raw_response.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create_signed_upload_url(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.apps.with_streaming_response.create_signed_upload_url(
+ bundle_id="x",
+ display_name="x",
+ files=[
+ {
+ "content_type": "x",
+ "file_name": "x",
+ }
+ ],
+ size_bytes=0,
+ version_code=0,
+ version_name="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert_matches_type(AppCreateSignedUploadURLResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_mark_failed(self, async_client: AsyncMobilerun) -> None:
+ app = await async_client.apps.mark_failed(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AppMarkFailedResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_mark_failed(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.apps.with_raw_response.mark_failed(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert_matches_type(AppMarkFailedResponse, app, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_mark_failed(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.apps.with_streaming_response.mark_failed(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert_matches_type(AppMarkFailedResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_mark_failed(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.apps.with_raw_response.mark_failed(
+ "",
+ )
diff --git a/tests/api_resources/test_carriers.py b/tests/api_resources/test_carriers.py
index 429c77d..537784a 100644
--- a/tests/api_resources/test_carriers.py
+++ b/tests/api_resources/test_carriers.py
@@ -10,9 +10,12 @@
from tests.utils import assert_matches_type
from mobilerun_sdk import Mobilerun, AsyncMobilerun
from mobilerun_sdk.types import (
- Carrier,
CarrierListResponse,
+ CarrierCreateResponse,
CarrierDeleteResponse,
+ CarrierLookupResponse,
+ CarrierUpdateResponse,
+ CarrierRetrieveResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -30,7 +33,7 @@ def test_method_create(self, client: Mobilerun) -> None:
mnc="x",
operator="x",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -53,7 +56,7 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
umts_bands="umts_bands",
website="website",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -68,7 +71,7 @@ def test_raw_response_create(self, client: Mobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -83,7 +86,7 @@ def test_streaming_response_create(self, client: Mobilerun) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -93,7 +96,7 @@ def test_method_retrieve(self, client: Mobilerun) -> None:
carrier = client.carriers.retrieve(
1,
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierRetrieveResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -105,7 +108,7 @@ def test_raw_response_retrieve(self, client: Mobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierRetrieveResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -117,7 +120,7 @@ def test_streaming_response_retrieve(self, client: Mobilerun) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierRetrieveResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -127,7 +130,7 @@ def test_method_update(self, client: Mobilerun) -> None:
carrier = client.carriers.update(
carrier_id=1,
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -149,7 +152,7 @@ def test_method_update_with_all_params(self, client: Mobilerun) -> None:
umts_bands="umts_bands",
website="website",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -161,7 +164,7 @@ def test_raw_response_update(self, client: Mobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -173,7 +176,7 @@ def test_streaming_response_update(self, client: Mobilerun) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -259,7 +262,7 @@ def test_method_lookup(self, client: Mobilerun) -> None:
mcc="x",
mnc="x",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierLookupResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -272,7 +275,7 @@ def test_raw_response_lookup(self, client: Mobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierLookupResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -285,7 +288,7 @@ def test_streaming_response_lookup(self, client: Mobilerun) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierLookupResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -304,7 +307,7 @@ async def test_method_create(self, async_client: AsyncMobilerun) -> None:
mnc="x",
operator="x",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -327,7 +330,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
umts_bands="umts_bands",
website="website",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -342,7 +345,7 @@ async def test_raw_response_create(self, async_client: AsyncMobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -357,7 +360,7 @@ async def test_streaming_response_create(self, async_client: AsyncMobilerun) ->
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierCreateResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -367,7 +370,7 @@ async def test_method_retrieve(self, async_client: AsyncMobilerun) -> None:
carrier = await async_client.carriers.retrieve(
1,
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierRetrieveResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -379,7 +382,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncMobilerun) -> None
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierRetrieveResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -391,7 +394,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncMobilerun) -
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierRetrieveResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -401,7 +404,7 @@ async def test_method_update(self, async_client: AsyncMobilerun) -> None:
carrier = await async_client.carriers.update(
carrier_id=1,
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -423,7 +426,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncMobilerun)
umts_bands="umts_bands",
website="website",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -435,7 +438,7 @@ async def test_raw_response_update(self, async_client: AsyncMobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -447,7 +450,7 @@ async def test_streaming_response_update(self, async_client: AsyncMobilerun) ->
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierUpdateResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -533,7 +536,7 @@ async def test_method_lookup(self, async_client: AsyncMobilerun) -> None:
mcc="x",
mnc="x",
)
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierLookupResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -546,7 +549,7 @@ async def test_raw_response_lookup(self, async_client: AsyncMobilerun) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierLookupResponse, carrier, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -559,6 +562,6 @@ async def test_streaming_response_lookup(self, async_client: AsyncMobilerun) ->
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
carrier = await response.parse()
- assert_matches_type(Carrier, carrier, path=["response"])
+ assert_matches_type(CarrierLookupResponse, carrier, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_devices.py b/tests/api_resources/test_devices.py
index 975ae92..c70afb3 100644
--- a/tests/api_resources/test_devices.py
+++ b/tests/api_resources/test_devices.py
@@ -13,6 +13,7 @@
Device,
DeviceListResponse,
DeviceCountResponse,
+ DeviceFingerprintResponse,
)
from mobilerun_sdk._utils import parse_datetime
@@ -32,7 +33,10 @@ def test_method_create(self, client: Mobilerun) -> None:
@parametrize
def test_method_create_with_all_params(self, client: Mobilerun) -> None:
device = client.devices.create(
+ query_country="country",
device_type="dedicated_physical_device",
+ profile_id="profileId",
+ android_version=0,
apps=["string"],
carrier={
"gsm_operator_alpha": "GsmOperatorAlpha",
@@ -42,6 +46,7 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
"gsm_sim_operator_numeric": 0,
"persist_sys_timezone": "PersistSysTimezone",
},
+ body_country="country",
files=["string"],
identifiers={
"bootloader_serial_number": "BootloaderSerialNumber",
@@ -60,6 +65,11 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
"identifier_wifi_mac": "IdentifierWifiMAC",
"serial_number": "SerialNumber",
},
+ locale="locale",
+ location={
+ "latitude": 0,
+ "longitude": 0,
+ },
name="name",
proxy={
"name": "name",
@@ -70,8 +80,8 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
"port": 0,
"user": "user",
},
- "wireguard": "wireguard",
},
+ timezone="timezone",
)
assert_matches_type(Device, device, path=["response"])
@@ -211,6 +221,141 @@ def test_streaming_response_count(self, client: Mobilerun) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_fingerprint(self, client: Mobilerun) -> None:
+ device = client.devices.fingerprint(
+ device_id="deviceId",
+ )
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_fingerprint_with_all_params(self, client: Mobilerun) -> None:
+ device = client.devices.fingerprint(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_fingerprint(self, client: Mobilerun) -> None:
+ response = client.devices.with_raw_response.fingerprint(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ device = response.parse()
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_fingerprint(self, client: Mobilerun) -> None:
+ with client.devices.with_streaming_response.fingerprint(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ device = response.parse()
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_fingerprint(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.with_raw_response.fingerprint(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_reboot(self, client: Mobilerun) -> None:
+ device = client.devices.reboot(
+ "deviceId",
+ )
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_reboot(self, client: Mobilerun) -> None:
+ response = client.devices.with_raw_response.reboot(
+ "deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ device = response.parse()
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_reboot(self, client: Mobilerun) -> None:
+ with client.devices.with_streaming_response.reboot(
+ "deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ device = response.parse()
+ assert device is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_reboot(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.with_raw_response.reboot(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_reset(self, client: Mobilerun) -> None:
+ device = client.devices.reset(
+ "deviceId",
+ )
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_reset(self, client: Mobilerun) -> None:
+ response = client.devices.with_raw_response.reset(
+ "deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ device = response.parse()
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_reset(self, client: Mobilerun) -> None:
+ with client.devices.with_streaming_response.reset(
+ "deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ device = response.parse()
+ assert device is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_reset(self, client: Mobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ client.devices.with_raw_response.reset(
+ "",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_set_name(self, client: Mobilerun) -> None:
@@ -367,7 +512,10 @@ async def test_method_create(self, async_client: AsyncMobilerun) -> None:
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncMobilerun) -> None:
device = await async_client.devices.create(
+ query_country="country",
device_type="dedicated_physical_device",
+ profile_id="profileId",
+ android_version=0,
apps=["string"],
carrier={
"gsm_operator_alpha": "GsmOperatorAlpha",
@@ -377,6 +525,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
"gsm_sim_operator_numeric": 0,
"persist_sys_timezone": "PersistSysTimezone",
},
+ body_country="country",
files=["string"],
identifiers={
"bootloader_serial_number": "BootloaderSerialNumber",
@@ -395,6 +544,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
"identifier_wifi_mac": "IdentifierWifiMAC",
"serial_number": "SerialNumber",
},
+ locale="locale",
+ location={
+ "latitude": 0,
+ "longitude": 0,
+ },
name="name",
proxy={
"name": "name",
@@ -405,8 +559,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
"port": 0,
"user": "user",
},
- "wireguard": "wireguard",
},
+ timezone="timezone",
)
assert_matches_type(Device, device, path=["response"])
@@ -546,6 +700,141 @@ async def test_streaming_response_count(self, async_client: AsyncMobilerun) -> N
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_fingerprint(self, async_client: AsyncMobilerun) -> None:
+ device = await async_client.devices.fingerprint(
+ device_id="deviceId",
+ )
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_fingerprint_with_all_params(self, async_client: AsyncMobilerun) -> None:
+ device = await async_client.devices.fingerprint(
+ device_id="deviceId",
+ x_device_display_id=0,
+ )
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_fingerprint(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.with_raw_response.fingerprint(
+ device_id="deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ device = await response.parse()
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_fingerprint(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.with_streaming_response.fingerprint(
+ device_id="deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ device = await response.parse()
+ assert_matches_type(DeviceFingerprintResponse, device, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_fingerprint(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.with_raw_response.fingerprint(
+ device_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_reboot(self, async_client: AsyncMobilerun) -> None:
+ device = await async_client.devices.reboot(
+ "deviceId",
+ )
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_reboot(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.with_raw_response.reboot(
+ "deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ device = await response.parse()
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_reboot(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.with_streaming_response.reboot(
+ "deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ device = await response.parse()
+ assert device is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_reboot(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.with_raw_response.reboot(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_reset(self, async_client: AsyncMobilerun) -> None:
+ device = await async_client.devices.reset(
+ "deviceId",
+ )
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_reset(self, async_client: AsyncMobilerun) -> None:
+ response = await async_client.devices.with_raw_response.reset(
+ "deviceId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ device = await response.parse()
+ assert device is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_reset(self, async_client: AsyncMobilerun) -> None:
+ async with async_client.devices.with_streaming_response.reset(
+ "deviceId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ device = await response.parse()
+ assert device is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_reset(self, async_client: AsyncMobilerun) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"):
+ await async_client.devices.with_raw_response.reset(
+ "",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_set_name(self, async_client: AsyncMobilerun) -> None:
diff --git a/tests/api_resources/test_profiles.py b/tests/api_resources/test_profiles.py
index 6c557de..404e8d8 100644
--- a/tests/api_resources/test_profiles.py
+++ b/tests/api_resources/test_profiles.py
@@ -36,6 +36,7 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
profile = client.profiles.create(
name="x",
spec={
+ "android_version": 0,
"apps": ["string"],
"carrier": {
"gsm_operator_alpha": "GsmOperatorAlpha",
@@ -45,6 +46,7 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
"gsm_sim_operator_numeric": 0,
"persist_sys_timezone": "PersistSysTimezone",
},
+ "country": "country",
"files": ["string"],
"identifiers": {
"bootloader_serial_number": "BootloaderSerialNumber",
@@ -63,6 +65,11 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
"identifier_wifi_mac": "IdentifierWifiMAC",
"serial_number": "SerialNumber",
},
+ "locale": "locale",
+ "location": {
+ "latitude": 0,
+ "longitude": 0,
+ },
"name": "name",
"proxy": {
"name": "name",
@@ -73,8 +80,8 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None:
"port": 0,
"user": "user",
},
- "wireguard": "wireguard",
},
+ "timezone": "timezone",
},
)
assert_matches_type(Profile, profile, path=["response"])
@@ -166,6 +173,7 @@ def test_method_update_with_all_params(self, client: Mobilerun) -> None:
profile_id="profileId",
name="x",
spec={
+ "android_version": 0,
"apps": ["string"],
"carrier": {
"gsm_operator_alpha": "GsmOperatorAlpha",
@@ -175,6 +183,7 @@ def test_method_update_with_all_params(self, client: Mobilerun) -> None:
"gsm_sim_operator_numeric": 0,
"persist_sys_timezone": "PersistSysTimezone",
},
+ "country": "country",
"files": ["string"],
"identifiers": {
"bootloader_serial_number": "BootloaderSerialNumber",
@@ -193,6 +202,11 @@ def test_method_update_with_all_params(self, client: Mobilerun) -> None:
"identifier_wifi_mac": "IdentifierWifiMAC",
"serial_number": "SerialNumber",
},
+ "locale": "locale",
+ "location": {
+ "latitude": 0,
+ "longitude": 0,
+ },
"name": "name",
"proxy": {
"name": "name",
@@ -203,8 +217,8 @@ def test_method_update_with_all_params(self, client: Mobilerun) -> None:
"port": 0,
"user": "user",
},
- "wireguard": "wireguard",
},
+ "timezone": "timezone",
},
)
assert_matches_type(Profile, profile, path=["response"])
@@ -352,6 +366,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
profile = await async_client.profiles.create(
name="x",
spec={
+ "android_version": 0,
"apps": ["string"],
"carrier": {
"gsm_operator_alpha": "GsmOperatorAlpha",
@@ -361,6 +376,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
"gsm_sim_operator_numeric": 0,
"persist_sys_timezone": "PersistSysTimezone",
},
+ "country": "country",
"files": ["string"],
"identifiers": {
"bootloader_serial_number": "BootloaderSerialNumber",
@@ -379,6 +395,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
"identifier_wifi_mac": "IdentifierWifiMAC",
"serial_number": "SerialNumber",
},
+ "locale": "locale",
+ "location": {
+ "latitude": 0,
+ "longitude": 0,
+ },
"name": "name",
"proxy": {
"name": "name",
@@ -389,8 +410,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun)
"port": 0,
"user": "user",
},
- "wireguard": "wireguard",
},
+ "timezone": "timezone",
},
)
assert_matches_type(Profile, profile, path=["response"])
@@ -482,6 +503,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncMobilerun)
profile_id="profileId",
name="x",
spec={
+ "android_version": 0,
"apps": ["string"],
"carrier": {
"gsm_operator_alpha": "GsmOperatorAlpha",
@@ -491,6 +513,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncMobilerun)
"gsm_sim_operator_numeric": 0,
"persist_sys_timezone": "PersistSysTimezone",
},
+ "country": "country",
"files": ["string"],
"identifiers": {
"bootloader_serial_number": "BootloaderSerialNumber",
@@ -509,6 +532,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncMobilerun)
"identifier_wifi_mac": "IdentifierWifiMAC",
"serial_number": "SerialNumber",
},
+ "locale": "locale",
+ "location": {
+ "latitude": 0,
+ "longitude": 0,
+ },
"name": "name",
"proxy": {
"name": "name",
@@ -519,8 +547,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncMobilerun)
"port": 0,
"user": "user",
},
- "wireguard": "wireguard",
},
+ "timezone": "timezone",
},
)
assert_matches_type(Profile, profile, path=["response"])
diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py
deleted file mode 100644
index db8149c..0000000
--- a/tests/test_deepcopy.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from mobilerun_sdk._utils import deepcopy_minimal
-
-
-def assert_different_identities(obj1: object, obj2: object) -> None:
- assert obj1 == obj2
- assert id(obj1) != id(obj2)
-
-
-def test_simple_dict() -> None:
- obj1 = {"foo": "bar"}
- obj2 = deepcopy_minimal(obj1)
- assert_different_identities(obj1, obj2)
-
-
-def test_nested_dict() -> None:
- obj1 = {"foo": {"bar": True}}
- obj2 = deepcopy_minimal(obj1)
- assert_different_identities(obj1, obj2)
- assert_different_identities(obj1["foo"], obj2["foo"])
-
-
-def test_complex_nested_dict() -> None:
- obj1 = {"foo": {"bar": [{"hello": "world"}]}}
- obj2 = deepcopy_minimal(obj1)
- assert_different_identities(obj1, obj2)
- assert_different_identities(obj1["foo"], obj2["foo"])
- assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"])
- assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0])
-
-
-def test_simple_list() -> None:
- obj1 = ["a", "b", "c"]
- obj2 = deepcopy_minimal(obj1)
- assert_different_identities(obj1, obj2)
-
-
-def test_nested_list() -> None:
- obj1 = ["a", [1, 2, 3]]
- obj2 = deepcopy_minimal(obj1)
- assert_different_identities(obj1, obj2)
- assert_different_identities(obj1[1], obj2[1])
-
-
-class MyObject: ...
-
-
-def test_ignores_other_types() -> None:
- # custom classes
- my_obj = MyObject()
- obj1 = {"foo": my_obj}
- obj2 = deepcopy_minimal(obj1)
- assert_different_identities(obj1, obj2)
- assert obj1["foo"] is my_obj
-
- # tuples
- obj3 = ("a", "b")
- obj4 = deepcopy_minimal(obj3)
- assert obj3 is obj4
diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py
index 5792e00..bd4e31b 100644
--- a/tests/test_extract_files.py
+++ b/tests/test_extract_files.py
@@ -4,7 +4,7 @@
import pytest
-from mobilerun_sdk._types import FileTypes
+from mobilerun_sdk._types import FileTypes, ArrayFormat
from mobilerun_sdk._utils import extract_files
@@ -37,10 +37,7 @@ def test_multiple_files() -> None:
def test_top_level_file_array() -> None:
query = {"files": [b"file one", b"file two"], "title": "hello"}
- assert extract_files(query, paths=[["files", ""]]) == [
- ("files[]", b"file one"),
- ("files[]", b"file two"),
- ]
+ assert extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")]
assert query == {"title": "hello"}
@@ -71,3 +68,24 @@ def test_ignores_incorrect_paths(
expected: list[tuple[str, FileTypes]],
) -> None:
assert extract_files(query, paths=paths) == expected
+
+
+@pytest.mark.parametrize(
+ "array_format,expected_top_level,expected_nested",
+ [
+ ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]),
+ ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]),
+ ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]),
+ ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]),
+ ],
+)
+def test_array_format_controls_file_field_names(
+ array_format: ArrayFormat,
+ expected_top_level: list[tuple[str, FileTypes]],
+ expected_nested: list[tuple[str, FileTypes]],
+) -> None:
+ top_level = {"files": [b"a", b"b"]}
+ assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level
+
+ nested = {"items": [{"file": b"a"}, {"file": b"b"}]}
+ assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested
diff --git a/tests/test_files.py b/tests/test_files.py
index 7d4e3e7..97fe070 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -4,7 +4,8 @@
import pytest
from dirty_equals import IsDict, IsList, IsBytes, IsTuple
-from mobilerun_sdk._files import to_httpx_files, async_to_httpx_files
+from mobilerun_sdk._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files
+from mobilerun_sdk._utils import extract_files
readme_path = Path(__file__).parent.parent.joinpath("README.md")
@@ -49,3 +50,99 @@ def test_string_not_allowed() -> None:
"file": "foo", # type: ignore
}
)
+
+
+def assert_different_identities(obj1: object, obj2: object) -> None:
+ assert obj1 == obj2
+ assert obj1 is not obj2
+
+
+class TestDeepcopyWithPaths:
+ def test_copies_top_level_dict(self) -> None:
+ original = {"file": b"data", "other": "value"}
+ result = deepcopy_with_paths(original, [["file"]])
+ assert_different_identities(result, original)
+
+ def test_file_value_is_same_reference(self) -> None:
+ file_bytes = b"contents"
+ original = {"file": file_bytes}
+ result = deepcopy_with_paths(original, [["file"]])
+ assert_different_identities(result, original)
+ assert result["file"] is file_bytes
+
+ def test_list_popped_wholesale(self) -> None:
+ files = [b"f1", b"f2"]
+ original = {"files": files, "title": "t"}
+ result = deepcopy_with_paths(original, [["files", ""]])
+ assert_different_identities(result, original)
+ result_files = result["files"]
+ assert isinstance(result_files, list)
+ assert_different_identities(result_files, files)
+
+ def test_nested_array_path_copies_list_and_elements(self) -> None:
+ elem1 = {"file": b"f1", "extra": 1}
+ elem2 = {"file": b"f2", "extra": 2}
+ original = {"items": [elem1, elem2]}
+ result = deepcopy_with_paths(original, [["items", "", "file"]])
+ assert_different_identities(result, original)
+ result_items = result["items"]
+ assert isinstance(result_items, list)
+ assert_different_identities(result_items, original["items"])
+ assert_different_identities(result_items[0], elem1)
+ assert_different_identities(result_items[1], elem2)
+
+ def test_empty_paths_returns_same_object(self) -> None:
+ original = {"foo": "bar"}
+ result = deepcopy_with_paths(original, [])
+ assert result is original
+
+ def test_multiple_paths(self) -> None:
+ f1 = b"file1"
+ f2 = b"file2"
+ original = {"a": f1, "b": f2, "c": "unchanged"}
+ result = deepcopy_with_paths(original, [["a"], ["b"]])
+ assert_different_identities(result, original)
+ assert result["a"] is f1
+ assert result["b"] is f2
+ assert result["c"] is original["c"]
+
+ def test_extract_files_does_not_mutate_original_top_level(self) -> None:
+ file_bytes = b"contents"
+ original = {"file": file_bytes, "other": "value"}
+
+ copied = deepcopy_with_paths(original, [["file"]])
+ extracted = extract_files(copied, paths=[["file"]])
+
+ assert extracted == [("file", file_bytes)]
+ assert original == {"file": file_bytes, "other": "value"}
+ assert copied == {"other": "value"}
+
+ def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None:
+ file1 = b"f1"
+ file2 = b"f2"
+ original = {
+ "items": [
+ {"file": file1, "extra": 1},
+ {"file": file2, "extra": 2},
+ ],
+ "title": "example",
+ }
+
+ copied = deepcopy_with_paths(original, [["items", "", "file"]])
+ extracted = extract_files(copied, paths=[["items", "", "file"]])
+
+ assert [entry for _, entry in extracted] == [file1, file2]
+ assert original == {
+ "items": [
+ {"file": file1, "extra": 1},
+ {"file": file2, "extra": 2},
+ ],
+ "title": "example",
+ }
+ assert copied == {
+ "items": [
+ {"extra": 1},
+ {"extra": 2},
+ ],
+ "title": "example",
+ }
diff --git a/tests/test_models.py b/tests/test_models.py
index 36abb13..231e44e 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,7 +1,8 @@
import json
-from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast
from datetime import datetime, timezone
-from typing_extensions import Literal, Annotated, TypeAliasType
+from collections import deque
+from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType
import pytest
import pydantic
@@ -9,7 +10,7 @@
from mobilerun_sdk._utils import PropertyInfo
from mobilerun_sdk._compat import PYDANTIC_V1, parse_obj, model_dump, model_json
-from mobilerun_sdk._models import DISCRIMINATOR_CACHE, BaseModel, construct_type
+from mobilerun_sdk._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type
class BasicModel(BaseModel):
@@ -961,3 +962,56 @@ def __getattr__(self, attr: str) -> Item: ...
assert model.a.prop == 1
assert isinstance(model.a, Item)
assert model.other == "foo"
+
+
+# NOTE: Workaround for Pydantic Iterable behavior.
+# Iterable fields are replaced with a ValidatorIterator and may be consumed
+# during serialization, which can cause subsequent dumps to return empty data.
+# See: https://github.com/pydantic/pydantic/issues/9541
+@pytest.mark.parametrize(
+ "data, expected_validated",
+ [
+ ([1, 2, 3], [1, 2, 3]),
+ ((1, 2, 3), (1, 2, 3)),
+ (set([1, 2, 3]), set([1, 2, 3])),
+ (iter([1, 2, 3]), [1, 2, 3]),
+ ([], []),
+ ((x for x in [1, 2, 3]), [1, 2, 3]),
+ (map(lambda x: x, [1, 2, 3]), [1, 2, 3]),
+ (frozenset([1, 2, 3]), frozenset([1, 2, 3])),
+ (deque([1, 2, 3]), deque([1, 2, 3])),
+ ],
+ ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"],
+)
+@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2")
+def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None:
+ class TypeWithIterable(TypedDict):
+ items: EagerIterable[int]
+
+ class Model(BaseModel):
+ data: TypeWithIterable
+
+ m = Model.model_validate({"data": {"items": data}})
+ assert m.data["items"] == expected_validated
+
+ # Verify repeated dumps don't lose data (the original bug)
+ assert m.model_dump()["data"]["items"] == list(expected_validated)
+ assert m.model_dump()["data"]["items"] == list(expected_validated)
+
+
+@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2")
+def test_iterable_construction_str_falls_back_to_list() -> None:
+ # str is iterable (over chars), but str(list_of_chars) produces the list's repr
+ # rather than reconstructing a string from items. We special-case str to fall
+ # back to list instead of attempting reconstruction.
+ class TypeWithIterable(TypedDict):
+ items: EagerIterable[str]
+
+ class Model(BaseModel):
+ data: TypeWithIterable
+
+ m = Model.model_validate({"data": {"items": "hello"}})
+
+ # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"])
+ assert m.data["items"] == ["h", "e", "l", "l", "o"]
+ assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"]