Skip to content

Commit

Permalink
Added ConnectLife API test server (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
oyvindwe authored Jul 12, 2024
1 parent 5e10e34 commit 78e2cf4
Show file tree
Hide file tree
Showing 10 changed files with 958 additions and 732 deletions.
4 changes: 0 additions & 4 deletions DEVELOPMENT

This file was deleted.

33 changes: 33 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Development environment

## Prerequisites:

- [pyenv](https://github.com/pyenv/pyenv)

## Install environment

```bash
pyenv install
python -m venv venv
source venv/bin/activate
pip install .
```

## Test server

Test server that mocks the ConnectLife API. Runs on `http://localhost:8080`.

The server reads all JSON files in the current directory, and serves them as appliances. Properties can be updated,
but is not persisted. The only validation is that the `puid` and `property` exists, it assumes that all properties
are writable and that any value is legal.

```bash
cd dumps
python -m test_server
```

To use the test server, provide the URL to the test server:
```python
from connectlife.api import ConnectLifeApi
api = ConnectLifeApi(username="[email protected]", password="password", test_server="http://localhost:8080")
```
77 changes: 43 additions & 34 deletions connectlife/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,6 @@

from .appliance import ConnectLifeAppliance

API_KEY = "4_yhTWQmHFpZkQZDSV1uV-_A"
CLIENT_ID = "5065059336212"
CLIENT_SECRET = "07swfKgvJhC3ydOUS9YV_SwVz0i4LKqlOLGNUukYHVMsJRF1b-iWeUGcNlXyYCeK"

LOGIN_URL = "https://accounts.eu1.gigya.com/accounts.login"
JWT_URL = "https://accounts.eu1.gigya.com/accounts.getJWT"

OAUTH2_REDIRECT = "https://api.connectlife.io/swagger/oauth2-redirect.html"
OAUTH2_AUTHORIZE = "https://oauth.hijuconn.com/oauth/authorize"
OAUTH2_TOKEN = "https://oauth.hijuconn.com/oauth/token"

APPLIANCES_URL = "https://connectlife.bapi.ovh/appliances"

_LOGGER = logging.getLogger(__name__)


Expand All @@ -32,8 +19,29 @@ class LifeConnectAuthError(Exception):


class ConnectLifeApi:
def __init__(self, username: str, password: str):
api_key = "4_yhTWQmHFpZkQZDSV1uV-_A"
client_id = "5065059336212"
client_secret = "07swfKgvJhC3ydOUS9YV_SwVz0i4LKqlOLGNUukYHVMsJRF1b-iWeUGcNlXyYCeK"

login_url = "https://accounts.eu1.gigya.com/accounts.login"
jwt_url = "https://accounts.eu1.gigya.com/accounts.getJWT"

oauth2_redirect = "https://api.connectlife.io/swagger/oauth2-redirect.html"
oauth2_authorize = "https://oauth.hijuconn.com/oauth/authorize"
oauth2_token = "https://oauth.hijuconn.com/oauth/token"

appliances_url = "https://connectlife.bapi.ovh/appliances"

def __init__(self, username: str, password: str, test_server: str = None):
"""Initialize the auth."""
if test_server:
self.login_url = f"{test_server}/accounts.login"
self.jwt_url = f"{test_server}/accounts.getJWT"
self.oauth2_redirect = f"{test_server}/swagger/oauth2-redirect.html"
self.oauth2_authorize = f"{test_server}/oauth/authorize"
self.oauth2_token = f"{test_server}/oauth/token"
self.appliances_url = f"{test_server}/appliances"

self._username = username
self._password = password
self._access_token: str | None = None
Expand All @@ -44,10 +52,10 @@ def __init__(self, username: str, password: str):
async def authenticate(self) -> bool:
"""Test if we can authenticate with the host."""
async with aiohttp.ClientSession() as session:
async with session.post(LOGIN_URL, data={
async with session.post(self.login_url, data={
"loginID": self._username,
"password": self._password,
"APIKey": API_KEY,
"APIKey": self.api_key,
}) as response:
if response.status == 200:
body = await self._json(response)
Expand All @@ -67,7 +75,7 @@ async def get_appliances_json(self) -> Any:
"""Make a request and return the response as text."""
await self._fetch_access_token()
async with aiohttp.ClientSession() as session:
async with session.get(APPLIANCES_URL, headers={
async with session.get(self.appliances_url, headers={
"User-Agent": "connectlife-api-connector 2.1.4",
"X-Token": self._access_token
}) as response:
Expand All @@ -86,7 +94,7 @@ async def update_appliance(self, puid: str, properties: dict[str, str]):
_LOGGER.debug("Updating appliance with puid %s to %s", puid, json.dumps(properties))
await self._fetch_access_token()
async with aiohttp.ClientSession() as session:
async with session.post(APPLIANCES_URL, json=data, headers={
async with session.post(self.appliances_url, json=data, headers={
"User-Agent": "connectlife-api-connector 2.1.4",
"X-Token": self._access_token
}) as response:
Expand All @@ -102,10 +110,10 @@ async def _fetch_access_token(self):

async def _initial_access_token(self):
async with aiohttp.ClientSession() as session:
async with session.post(LOGIN_URL, data={
async with session.post(self.login_url, data={
"loginID": self._username,
"password": self._password,
"APIKey": API_KEY,
"APIKey": self.api_key
}) as response:
if response.status != 200:
_LOGGER.debug(f"Response status code: {response.status}")
Expand All @@ -116,8 +124,8 @@ async def _initial_access_token(self):
uid = body["UID"]
login_token = body["sessionInfo"]["cookieValue"]

async with session.post(JWT_URL, data={
"APIKey": API_KEY,
async with session.post(self.jwt_url, data={
"APIKey": self.api_key,
"login_token": login_token
}) as response:
if response.status != 200:
Expand All @@ -128,9 +136,9 @@ async def _initial_access_token(self):
body = await self._json(response)
id_token = body["id_token"]

async with session.post(OAUTH2_AUTHORIZE, json={
"client_id": CLIENT_ID,
"redirect_uri": OAUTH2_REDIRECT,
async with session.post(self.oauth2_authorize, json={
"client_id": self.client_id,
"redirect_uri": self.oauth2_redirect,
"idToken": id_token,
"response_type": "code",
"thirdType": "CDC",
Expand All @@ -144,10 +152,10 @@ async def _initial_access_token(self):
body = await response.json()
code = body["code"]

async with session.post(OAUTH2_TOKEN, data={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": OAUTH2_REDIRECT,
async with session.post(self.oauth2_token, data={
"client_id": self.client_id,
"client_secret": self.client_secret,
"redirect_uri": self.oauth2_redirect,
"grant_type": "authorization_code",
"code": code,
}) as response:
Expand All @@ -164,10 +172,10 @@ async def _initial_access_token(self):

async def _refresh_access_token(self) -> None:
async with aiohttp.ClientSession() as session:
async with session.post(OAUTH2_TOKEN, data={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": OAUTH2_REDIRECT,
async with session.post(self.oauth2_token, data={
"client_id": self.client_id,
"client_secret": self.client_secret,
"redirect_uri": self.oauth2_redirect,
"grant_type": "refresh_token",
"refresh_token": self._refresh_token,
}) as response:
Expand All @@ -181,7 +189,8 @@ async def _refresh_access_token(self) -> None:
# Renew 90 seconds before expiration
self._expires = dt.datetime.now() + dt.timedelta(0, body["expires_in"] - 90)

async def _json(self, response: aiohttp.ClientResponse) -> Any:
@staticmethod
async def _json(response: aiohttp.ClientResponse) -> Any:
# response may have wrong content-type, cannot use response.json()
text = await response.text()
_LOGGER.debug(f"response: {text}")
Expand Down
Loading

0 comments on commit 78e2cf4

Please sign in to comment.