diff --git a/.vscode/settings.json b/.vscode/settings.json index c3a0ab0..19f2214 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.venvFolders": ["/home/seve/w/seam/seamapi-python/.venv"] + // "python.venvFolders": ["/home/seve/w/seam/seamapi-python/.venv"] } diff --git a/README.md b/README.md index b6ad589..02e2fa9 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ seam.locks.lock_door(some_lock) This project uses [poetry](https://github.com/python-poetry/poetry) +- To setup the project and install dependencies run `poetry install` - To run tests, run `poetry run pytest` - To build the project for publishing, run `poetry build` - To publish the project run `poetry build` @@ -39,3 +40,10 @@ Our tests use a seam sandbox environment given by the environment variables `SEAM_SANDBOX_API_KEY`. If you want to run the tests, you should first create a sandbox workspace [on your dashboard](https://dashboard.getseam.com) then create a sandbox workspace. + +> NOTE: For installation on m1 mac, you may need to export the following lines +> prior to `poetry install`... +> +> `export CPPFLAGS="-I/opt/homebrew/opt/openssl@1.1/include"` +> +> `export LDFLAGS="-L/opt/homebrew/opt/openssl@1.1/lib -L${HOME}/.pyenv/versions/3.8.10/lib"` diff --git a/seamapi/connect_webviews.py b/seamapi/connect_webviews.py index bef4ab0..564fdf8 100644 --- a/seamapi/connect_webviews.py +++ b/seamapi/connect_webviews.py @@ -34,7 +34,7 @@ def list(self) -> List[ConnectWebview]: ] def get(self, connect_webview_id: str) -> ConnectWebview: - res = requests.post( + res = requests.get( f"{self.seam.api_url}/connect_webviews/get", headers={"Authorization": f"Bearer {self.seam.api_key}"}, params={"connect_webview_id": connect_webview_id}, diff --git a/seamapi/devices.py b/seamapi/devices.py index c049a19..6dc16f0 100644 --- a/seamapi/devices.py +++ b/seamapi/devices.py @@ -35,12 +35,20 @@ def list( devices = res.json()["devices"] return [Device.from_dict(d) for d in devices] - def get(self, device: Union[DeviceId, Device]) -> Device: - device_id = to_device_id(device) + def get( + self, + device: Optional[Union[DeviceId, Device]] = None, + name: Optional[str] = None, + ) -> Device: + params = {} + if device: + params["device_id"] = to_device_id(device) + if name: + params["name"] = name res = requests.get( f"{self.seam.api_url}/devices/get", headers={"Authorization": f"Bearer {self.seam.api_key}"}, - params={"device_id": device_id}, + params=params, ) if not res.ok: raise Exception(res.text) diff --git a/seamapi/locks.py b/seamapi/locks.py index b95d520..db5a0a7 100644 --- a/seamapi/locks.py +++ b/seamapi/locks.py @@ -43,12 +43,20 @@ def list( json_locks = res.json()["devices"] return [Device.from_dict(d) for d in json_locks] - def get(self, device: Union[DeviceId, Device]) -> Device: - device_id = to_device_id(device) + def get( + self, + device: Optional[Union[DeviceId, Device]] = None, + name: Optional[str] = None, + ) -> Device: + params = {} + if device: + params["device_id"] = to_device_id(device) + if name: + params["name"] = name res = requests.post( f"{self.seam.api_url}/locks/get", headers={"Authorization": f"Bearer {self.seam.api_key}"}, - params={"device_id": device_id}, + params=params, ) if not res.ok: raise Exception(res.text) diff --git a/seamapi/types.py b/seamapi/types.py index 512f06b..e0fcaaa 100644 --- a/seamapi/types.py +++ b/seamapi/types.py @@ -12,7 +12,6 @@ AcceptedProvider = str # e.g. august or noiseaware -@dataclass_json @dataclass class Device: device_id: DeviceId @@ -151,7 +150,11 @@ def list(self) -> List[Device]: raise NotImplementedError @abc.abstractmethod - def get(self, device: Union[DeviceId, Device]) -> Device: + def get( + self, + device: Optional[Union[DeviceId, Device]] = None, + name: Optional[str] = None, + ) -> Device: raise NotImplementedError diff --git a/tests/conftest.py b/tests/conftest.py index 39bcdcb..c383d41 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ from dotenv import load_dotenv from typing import Any from dataclasses import dataclass +import sys from testcontainers.postgres import PostgresContainer from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs @@ -21,7 +22,7 @@ class SeamBackend: sandbox_api_key: str -@pytest.fixture +@pytest.fixture(scope="session") def seam_backend(): with PostgresContainer("postgres:13", dbname="postgres") as pg: db_host = "host.docker.internal" if sys.platform == "darwin" else "172.17.0.1" @@ -34,18 +35,18 @@ def seam_backend(): ).with_env("DATABASE_NAME", "seam_api").with_env("NODE_ENV", "test").with_env( "POSTGRES_HOST", db_host ).with_env( - "SERVER_BASE_URL", "http://localhost:3021" + "SERVER_BASE_URL", "http://localhost:3000" ).with_env( "SEAMTEAM_ADMIN_PASSWORD", "1234" ).with_bind_ports( - 3000, 4020 + 3000, 3000 ).with_command( "start:for-integration-testing" ) as sc_container: wait_for_logs(sc_container, r"started server", timeout=20) - requests.get("http://localhost:4020/health") + requests.get("http://localhost:3000/health") yield SeamBackend( - url="http://localhost:4020", + url="http://localhost:3000", sandbox_api_key="seam_sandykey_0000000000000000000sand", ) diff --git a/tests/connect_webviews/test_connect_webviews.py b/tests/connect_webviews/test_connect_webviews.py new file mode 100644 index 0000000..c02ad26 --- /dev/null +++ b/tests/connect_webviews/test_connect_webviews.py @@ -0,0 +1,7 @@ +from seamapi import Seam + + +def test_connect_webviews(seam: Seam): + webview = seam.connect_webviews.create(accepted_providers=["schlage"]) + + assert webview.url is not None diff --git a/tests/devices/test_devices.py b/tests/devices/test_devices.py new file mode 100644 index 0000000..718b4b0 --- /dev/null +++ b/tests/devices/test_devices.py @@ -0,0 +1,13 @@ +from seamapi import Seam +from tests.fixtures.login_via_schlage import login_via_schlage +from seamapi.utils.deep_attr_dict import DeepAttrDict + + +def test_devices(seam: Seam): + login_via_schlage(seam) + + devices = seam.devices.list() + assert len(devices) > 0 + + some_device = seam.devices.get(name="FRONT DOOR") + assert some_device.properties.name == "FRONT DOOR" diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 0000000..e0ccd91 --- /dev/null +++ b/tests/fixtures/__init__.py @@ -0,0 +1 @@ +from .login_via_schlage import login_via_schlage diff --git a/tests/fixtures/login_via_schlage.py b/tests/fixtures/login_via_schlage.py new file mode 100644 index 0000000..d1d9dcc --- /dev/null +++ b/tests/fixtures/login_via_schlage.py @@ -0,0 +1,22 @@ +from seamapi import Seam +import time +import requests + + +def login_via_schlage(seam: Seam): + webview = seam.connect_webviews.create(accepted_providers=["schlage"]) + + # This is an internal endpoint that will be removed, don't use it, see how + # it says "internal" there? It's not going to stick around. + schlage_login_res = requests.post( + f"{seam.api_url}/internal/schlage/login", + json={ + "email": "jane@example.com", + "password": "1234", + "connect_webview_id": webview.connect_webview_id, + }, + ).json() + + # We've completed a webview login, which will load devices into this + # workspace + pass diff --git a/tests/test_init_seam.py b/tests/test_init_seam.py index 2696f48..b13e836 100644 --- a/tests/test_init_seam.py +++ b/tests/test_init_seam.py @@ -1,13 +1,7 @@ from seamapi import Seam -# def test_init_seam(): -# seam = Seam() -# assert seam.api_key -# assert seam.api_url - - def test_init_seam_with_fixture(seam: Seam): assert seam.api_key assert seam.api_url - assert "https" in seam.api_url and "localhost" in seam.api_url + assert "http" in seam.api_url and "localhost" in seam.api_url diff --git a/tests/utils/test_deep_attr_dict.py b/tests/utils/test_deep_attr_dict.py new file mode 100644 index 0000000..e8ca8a2 --- /dev/null +++ b/tests/utils/test_deep_attr_dict.py @@ -0,0 +1,7 @@ +from seamapi.utils.deep_attr_dict import DeepAttrDict + + +def test_deep_attr_dict(): + attrdict = DeepAttrDict({"a": {"b": {"c": 5}}}) + + assert attrdict.a.b.c == 5 diff --git a/tests/workspaces/test_workspaces_get.py b/tests/workspaces/test_workspaces_get.py index 5ac04f7..91af96d 100644 --- a/tests/workspaces/test_workspaces_get.py +++ b/tests/workspaces/test_workspaces_get.py @@ -3,5 +3,4 @@ def test_workspaces_get(seam: Seam): ws = seam.workspaces.get() - assert ws.name == "PythonLibrarySandbox" assert ws.is_sandbox == True